[Pki-devel] [PATCH] 528 Added CLI to import/export certificates with private keys.

Endi Sukma Dewata edewata at redhat.com
Mon Oct 6 23:01:37 UTC 2014


New CLI commands have been added to import/export certificates and
private keys into/from the client security database. The CLI can
also be used to generate the file needed by Python client library
for client certificate authentication.

-- 
Endi S. Dewata
-------------- next part --------------
From 6b60698a2c35cb530a379a2970f7fc11af7e5798 Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata at redhat.com>
Date: Mon, 6 Oct 2014 12:20:42 -0400
Subject: [PATCH] Added CLI to import/export certificates with private keys.

New CLI commands have been added to import/export certificates and
private keys into/from the client security database. The CLI can
also be used to generate the file needed by Python client library
for client certificate authentication.
---
 .../src/com/netscape/certsrv/client/PKIClient.java |   6 +
 base/java-tools/man/man1/pki-client.1              |  44 +++-
 .../src/com/netscape/cmstools/cli/MainCLI.java     |   4 +-
 .../com/netscape/cmstools/client/ClientCLI.java    |   2 +
 .../cmstools/client/ClientCertImportCLI.java       | 172 +++++++++++---
 .../cmstools/client/ClientCertModifyCLI.java       | 126 ++++++++++
 .../cmstools/client/ClientCertShowCLI.java         | 256 +++++++++++++++++++++
 7 files changed, 578 insertions(+), 32 deletions(-)
 create mode 100644 base/java-tools/src/com/netscape/cmstools/client/ClientCertModifyCLI.java
 create mode 100644 base/java-tools/src/com/netscape/cmstools/client/ClientCertShowCLI.java

diff --git a/base/common/src/com/netscape/certsrv/client/PKIClient.java b/base/common/src/com/netscape/certsrv/client/PKIClient.java
index c23e8b600b125d0a2da14dcec1880fb05598f42a..e06b4db54569d5ed46fdd138af3ded93e6fd1878 100644
--- a/base/common/src/com/netscape/certsrv/client/PKIClient.java
+++ b/base/common/src/com/netscape/certsrv/client/PKIClient.java
@@ -104,6 +104,12 @@ public class PKIClient {
         this.verbose = verbose;
     }
 
+    public X509Certificate getCert(String nickname)
+            throws NotInitializedException, ObjectNotFoundException, TokenException {
+        CryptoManager manager = CryptoManager.getInstance();
+        return manager.findCertByNickname(nickname);
+    }
+
     public X509Certificate[] getCerts() throws NotInitializedException {
         CryptoManager manager = CryptoManager.getInstance();
         return manager.getPermCerts();
diff --git a/base/java-tools/man/man1/pki-client.1 b/base/java-tools/man/man1/pki-client.1
index 0364f84efb97a7263194799bf44ce625519311e1..87da255ff0fd5e385aec845f9364ad977d9f7aad 100644
--- a/base/java-tools/man/man1/pki-client.1
+++ b/base/java-tools/man/man1/pki-client.1
@@ -22,7 +22,9 @@ pki-client \- Command-Line Interface for managing the security database on Certi
 \fBpki\fR [CLI options] \fBclient-init\fR [command options]
 \fBpki\fR [CLI options] \fBclient-cert-find\fR [command options]
 \fBpki\fR [CLI options] \fBclient-cert-request\fR <subject DN> [command options]
-\fBpki\fR [CLI options] \fBclient-cert-import\fR <nickname> [command options]
+\fBpki\fR [CLI options] \fBclient-cert-import\fR [nickname] [command options]
+\fBpki\fR [CLI options] \fBclient-cert-mod\fR <nickname> [command options]
+\fBpki\fR [CLI options] \fBclient-cert-show\fR <nickname> [command options]
 \fBpki\fR [CLI options] \fBclient-cert-del\fR <nickname> [command options]
 .fi
 
@@ -50,7 +52,17 @@ This command is to list certificates in the client security database.
 This command is to generate and submit a certificate request.
 .RE
 .PP
-\fBpki\fR [CLI options] \fBclient-cert-import\fR <nickname> [command options]
+\fBpki\fR [CLI options] \fBclient-cert-import\fR [nickname] [command options]
+.RS 4
+This command is to import a certificate into the client security database.
+.RE
+.PP
+\fBpki\fR [CLI options] \fBclient-cert-mod\fR <nickname> [command options]
+.RS 4
+This command is to modify a certificate in the client security database.
+.RE
+.PP
+\fBpki\fR [CLI options] \fBclient-cert-show\fR <nickname> [command options]
 .RS 4
 This command is to view a certificate in the client security database.
 .RE
@@ -80,11 +92,15 @@ To request a certificate:
 
 To import a certificate from a file into the security database:
 
-.B pki -d <security database location> -c <security database password> client-cert-import <nickname> --cert <certificate file>
+.B pki -d <security database location> -c <security database password> client-cert-import <nickname> --cert <path>
 
 To import a CA certificate from a file into the security database:
 
-.B pki -d <security database location> -c <security database password> client-cert-import <nickname> --ca-cert <CA certificate file>
+.B pki -d <security database location> -c <security database password> client-cert-import <nickname> --ca-cert <path>
+
+To import certificates and private keys from a PKCS #12 file into the security database:
+
+.B pki -d <security database location> -c <security database password> client-cert-import --pkcs12 <path> --pkcs12-password <password>
 
 To import a certificate from CA server into the security database:
 
@@ -94,6 +110,26 @@ To import a CA certificate from CA server into the security database:
 
 .B pki -d <security database location> -c <security database password> client-cert-import <nickname> --ca-server
 
+To modify a certificate's trust attributes in the security database:
+
+.B pki -d <security database location> -c <security database password> client-cert-mod <nickname> --trust <trust attributes>
+
+To display a certificate in the security database:
+
+.B pki -d <security database location> -c <security database password> client-cert-show <nickname>
+
+To export a certificate from the security database into a PEM file:
+
+.B pki -d <security database location> -c <security database password> client-cert-show <nickname> --cert <file>
+
+To export a certificate chain with the private key from the security database into a PKCS #12 file:
+
+.B pki -d <security database location> -c <security database password> client-cert-show <nickname> --pkcs12 <file> --pkcs12-password <password>
+
+To export a client certificate with the private key from the security database into a PEM file:
+
+.B pki -d <security database location> -c <security database password> client-cert-show <nickname> --client-cert <file>
+
 To delete a certificate from the security database:
 
 .B pki -d <security database location> -c <security database password> client-cert-del <nickname>
diff --git a/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java b/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java
index 066a7d580553836df62f68188929afc4de20d536..8c3805e007b7384f0b073e92bad88076e39ab2b6 100644
--- a/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java
@@ -494,7 +494,9 @@ public class MainCLI extends CLI {
         String command = cmdArgs[0];
         if (!command.equals("client-init") &&
                 !command.equals("client-cert-import") &&
-                !command.equals("client-cert-request")) {
+                !command.equals("client-cert-mod") &&
+                !command.equals("client-cert-request") &&
+                !command.equals("client-cert-show")) {
             init();
         }
 
diff --git a/base/java-tools/src/com/netscape/cmstools/client/ClientCLI.java b/base/java-tools/src/com/netscape/cmstools/client/ClientCLI.java
index 443d48bdfbe87253bb9f64e7eae5302c4dcce85c..c9c71521ab631a02d7c386bd4e1b38f8ecbc1e7f 100644
--- a/base/java-tools/src/com/netscape/cmstools/client/ClientCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/client/ClientCLI.java
@@ -35,8 +35,10 @@ public class ClientCLI extends CLI {
         addModule(new ClientInitCLI(this));
         addModule(new ClientCertFindCLI(this));
         addModule(new ClientCertImportCLI(this));
+        addModule(new ClientCertModifyCLI(this));
         addModule(new ClientCertRemoveCLI(this));
         addModule(new ClientCertRequestCLI(this));
+        addModule(new ClientCertShowCLI(this));
     }
 
     public String getFullName() {
diff --git a/base/java-tools/src/com/netscape/cmstools/client/ClientCertImportCLI.java b/base/java-tools/src/com/netscape/cmstools/client/ClientCertImportCLI.java
index 5080c55ea0d2a47dc38e5d63367b5dc00840f5e6..afa91d65935e77a5ceda09d9c70dae335e130885 100644
--- a/base/java-tools/src/com/netscape/cmstools/client/ClientCertImportCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/client/ClientCertImportCLI.java
@@ -21,6 +21,7 @@ package com.netscape.cmstools.client;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.FileWriter;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.URI;
 import java.util.Arrays;
@@ -51,21 +52,33 @@ public class ClientCertImportCLI extends CLI {
     }
 
     public void printHelp() {
-        formatter.printHelp(getFullName() + " <nickname> [OPTIONS...]", options);
+        formatter.printHelp(getFullName() + " [nickname] [OPTIONS...]", options);
     }
 
     public void createOptions() {
-        Option option = new Option(null, "cert", true, "Import certificate file");
+        Option option = new Option(null, "cert", true, "Certificate file to import.");
         option.setArgName("path");
         options.addOption(option);
 
-        option = new Option(null, "ca-cert", true, "Import CA certificate file");
+        option = new Option(null, "ca-cert", true, "CA certificate file to import.");
+        option.setArgName("path");
+        options.addOption(option);
+
+        option = new Option(null, "pkcs12", true, "PKCS #12 file to import.");
+        option.setArgName("path");
+        options.addOption(option);
+
+        option = new Option(null, "pkcs12-password", true, "PKCS #12 password.");
+        option.setArgName("password");
+        options.addOption(option);
+
+        option = new Option(null, "pkcs12-password-file", true, "PKCS #12 password file.");
         option.setArgName("path");
         options.addOption(option);
 
         options.addOption(null, "ca-server", false, "Import CA certificate from CA server");
 
-        option = new Option(null, "serial", true, "Serial number of certificate in CA");
+        option = new Option(null, "serial", true, "Serial number of certificate to import from CA server");
         option.setArgName("serial number");
         options.addOption(option);
 
@@ -117,30 +130,72 @@ public class ClientCertImportCLI extends CLI {
             nickname = mainCLI.config.getCertNickname();
         }
 
-        if (nickname == null) {
-            System.err.println("Error: Missing certificate nickname.");
-            System.exit(-1);
-        }
+        // nickname is not required to import PKCS #12 file
 
         String certPath = cmd.getOptionValue("cert");
         String caCertPath = cmd.getOptionValue("ca-cert");
+        String pkcs12Path = cmd.getOptionValue("pkcs12");
+        String pkcs12Password = cmd.getOptionValue("pkcs12-password");
+        String pkcs12PasswordPath = cmd.getOptionValue("pkcs12-password-file");
         boolean importFromCAServer = cmd.hasOption("ca-server");
         String serialNumber = cmd.getOptionValue("serial");
         String trustAttributes = cmd.getOptionValue("trust", "u,u,u");
 
-        File certFile;
-
         // load the certificate
         if (certPath != null) {
-            if (verbose) System.out.println("Loading certificate from " + certPath + ".");
-            certFile = new File(certPath);
+
+            if (verbose) System.out.println("Importing certificate from " + certPath + ".");
+
+            importCert(
+                    mainCLI.certDatabase.getAbsolutePath(),
+                    certPath,
+                    nickname,
+                    trustAttributes);
 
         } else if (caCertPath != null) {
-            if (verbose) System.out.println("Loading CA certificate from " + caCertPath + ".");
-            certFile = new File(caCertPath);
+
+            if (verbose) System.out.println("Importing CA certificate from " + caCertPath + ".");
 
             trustAttributes = "CT,c,";
 
+            importCert(
+                    mainCLI.certDatabase.getAbsolutePath(),
+                    caCertPath,
+                    nickname,
+                    trustAttributes);
+
+        } else if (pkcs12Path != null) {
+
+            if (verbose) System.out.println("Importing certificates from " + pkcs12Path + ".");
+
+            if (pkcs12Password != null && pkcs12PasswordPath != null) {
+                throw new Exception("PKCS #12 password and password file are mutually exclusive");
+
+            } else if (pkcs12Password != null) {
+                // store password into a temporary file
+                File pkcs12PasswordFile = File.createTempFile("pki-client-cert-import-", ".pwd");
+                pkcs12PasswordFile.deleteOnExit();
+
+                try (PrintWriter out = new PrintWriter(new FileWriter(pkcs12PasswordFile))) {
+                    out.print(pkcs12Password);
+                }
+
+                pkcs12PasswordPath = pkcs12PasswordFile.getAbsolutePath();
+
+            } else if (pkcs12PasswordPath != null) {
+                // nothing to do
+
+            } else {
+                throw new Exception("Missing PKCS #12 password");
+            }
+
+            // import certificates and private key into PKCS #12 file
+            importPKCS12(
+                    mainCLI.certDatabase.getAbsolutePath(),
+                    mainCLI.config.getCertPassword(),
+                    pkcs12Path,
+                    pkcs12PasswordPath);
+
         } else if (importFromCAServer) {
 
             // late initialization
@@ -152,10 +207,10 @@ public class ClientCertImportCLI extends CLI {
             String caServerURI = serverURI.getScheme() + "://" +
                 serverURI.getHost() + ":" + serverURI.getPort() + "/ca";
 
-            if (verbose) System.out.println("Downloading CA certificate from " + caServerURI + ".");
+            if (verbose) System.out.println("Importing CA certificate from " + caServerURI + ".");
             byte[] bytes = client.downloadCACertChain(caServerURI);
 
-            certFile = File.createTempFile("pki-client-cert-import-", ".crt", mainCLI.certDatabase);
+            File certFile = File.createTempFile("pki-client-cert-import-", ".crt");
             certFile.deleteOnExit();
 
             try (FileOutputStream out = new FileOutputStream(certFile)) {
@@ -164,6 +219,12 @@ public class ClientCertImportCLI extends CLI {
 
             trustAttributes = "CT,c,";
 
+            importCert(
+                    mainCLI.certDatabase.getAbsolutePath(),
+                    certFile.getAbsolutePath(),
+                    nickname,
+                    trustAttributes);
+
         } else if (serialNumber != null) {
 
             // connect to CA anonymously
@@ -172,12 +233,15 @@ public class ClientCertImportCLI extends CLI {
             config.setCertPassword(null);
             config.setCertNickname(null);
 
+            URI serverURI = config.getServerURI();
+            if (verbose) System.out.println("Importing certificate " + serialNumber + " from " + serverURI + ".");
+
             PKIClient client = new PKIClient(config, null);
             CertClient certClient = new CertClient(client, "ca");
 
             CertData certData = certClient.getCert(new CertId(serialNumber));
 
-            certFile = File.createTempFile("pki-client-cert-import-", ".crt", mainCLI.certDatabase);
+            File certFile = File.createTempFile("pki-client-cert-import-", ".crt");
             certFile.deleteOnExit();
 
             String encoded = certData.getEncoded();
@@ -185,6 +249,12 @@ public class ClientCertImportCLI extends CLI {
                 out.write(encoded);
             }
 
+            importCert(
+                    mainCLI.certDatabase.getAbsolutePath(),
+                    certFile.getAbsolutePath(),
+                    nickname,
+                    trustAttributes);
+
         } else {
             System.err.println("Error: Missing certificate to import");
             printHelp();
@@ -192,23 +262,71 @@ public class ClientCertImportCLI extends CLI {
             return;
         }
 
-        String[] commands = {
-                "/usr/bin/certutil", "-A",
-                "-d", mainCLI.certDatabase.getAbsolutePath(),
-                "-i", certFile.getAbsolutePath(),
+        if (nickname == null) {
+            MainCLI.printMessage("Imported certificates from PKCS #12 file");
+
+        } else {
+            MainCLI.printMessage("Imported certificate \"" + nickname + "\"");
+        }
+    }
+
+    public void importCert(
+            String dbPath,
+            String certPath,
+            String nickname,
+            String trustAttributes) throws Exception {
+
+        if (nickname == null) {
+            System.err.println("Error: Missing certificate nickname.");
+            System.exit(-1);
+        }
+
+        String[] command = {
+                "/bin/certutil", "-A",
+                "-d", dbPath,
+                "-i", certPath,
                 "-n", nickname,
                 "-t", trustAttributes
         };
 
+        try {
+            run(command);
+
+        } catch (Exception e) {
+            throw new Exception("Unable to import certificate file", e);
+        }
+    }
+
+    public void importPKCS12(
+            String dbPath,
+            String dbPassword,
+            String pkcs12Path,
+            String pkcs12PasswordPath) throws Exception {
+
+        String[] command = {
+                "/bin/pk12util",
+                "-d", dbPath,
+                "-K", dbPassword,
+                "-i", pkcs12Path,
+                "-w", pkcs12PasswordPath
+        };
+
+        try {
+            run(command);
+
+        } catch (Exception e) {
+            throw new Exception("Unable to import PKCS #12 file", e);
+        }
+    }
+
+    public void run(String[] command) throws IOException, InterruptedException {
+
         Runtime rt = Runtime.getRuntime();
-        Process p = rt.exec(commands);
-
+        Process p = rt.exec(command);
         int rc = p.waitFor();
+
         if (rc != 0) {
-            MainCLI.printMessage("Import failed");
-            return;
+            throw new IOException("Command failed. RC: " + rc);
         }
-
-        MainCLI.printMessage("Imported certificate \"" + nickname + "\"");
     }
 }
diff --git a/base/java-tools/src/com/netscape/cmstools/client/ClientCertModifyCLI.java b/base/java-tools/src/com/netscape/cmstools/client/ClientCertModifyCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..738dca07c3e5c98d89e821846d74a6cd45f64004
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/client/ClientCertModifyCLI.java
@@ -0,0 +1,126 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2014 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cmstools.client;
+
+import java.io.IOException;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmstools.cli.MainCLI;
+
+/**
+ * @author Endi S. Dewata
+ */
+public class ClientCertModifyCLI extends CLI {
+
+    public ClientCLI clientCLI;
+
+    public ClientCertModifyCLI(ClientCLI clientCLI) {
+        super("cert-mod", "Modify certificate in client security database", clientCLI);
+        this.clientCLI = clientCLI;
+
+        createOptions();
+    }
+
+    public void printHelp() {
+        formatter.printHelp(getFullName() + " <nickname> [OPTIONS...]", options);
+    }
+
+    public void createOptions() {
+        Option option = new Option(null, "trust", true, "Trust attributes. Default: u,u,u.");
+        option.setArgName("trust attributes");
+        options.addOption(option);
+    }
+
+    public void execute(String[] args) throws Exception {
+
+        CommandLine cmd = null;
+
+        try {
+            cmd = parser.parse(options, args);
+
+        } catch (Exception e) {
+            System.err.println("Error: " + e.getMessage());
+            printHelp();
+            System.exit(-1);
+        }
+
+        if (cmd.hasOption("help")) {
+            // Display usage
+            printHelp();
+            System.exit(0);
+        }
+
+        String[] cmdArgs = cmd.getArgs();
+
+        if (cmdArgs.length > 1) {
+            System.err.println("Error: Too many arguments specified.");
+            printHelp();
+            System.exit(-1);
+        }
+
+        if (cmdArgs.length == 0) {
+            System.err.println("Error: Missing certificate nickname.");
+            printHelp();
+            System.exit(-1);
+        }
+
+        MainCLI mainCLI = (MainCLI)parent.getParent();
+
+        String nickname = cmdArgs[0];
+
+        String trustAttributes = cmd.getOptionValue("trust", "u,u,u");
+
+        int rc = modifyCert(
+                mainCLI.certDatabase.getAbsolutePath(),
+                nickname,
+                trustAttributes);
+
+        if (rc != 0) {
+            MainCLI.printMessage("Modified failed");
+            return;
+        }
+
+        MainCLI.printMessage("Modified certificate \"" + nickname + "\"");
+    }
+
+    public int modifyCert(
+            String dbPath,
+            String nickname,
+            String trustAttributes) throws IOException, InterruptedException {
+
+        String[] command = {
+                "/usr/bin/certutil", "-M",
+                "-d", dbPath,
+                "-n", nickname,
+                "-t", trustAttributes
+        };
+
+        return run(command);
+    }
+
+    public int run(String[] command) throws IOException, InterruptedException {
+
+        Runtime rt = Runtime.getRuntime();
+        Process p = rt.exec(command);
+        return p.waitFor();
+    }
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/client/ClientCertShowCLI.java b/base/java-tools/src/com/netscape/cmstools/client/ClientCertShowCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..bdd9462f3097c2213b665a4dd0c57413af0d049a
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/client/ClientCertShowCLI.java
@@ -0,0 +1,256 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2014 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cmstools.client;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.commons.lang.StringUtils;
+import org.mozilla.jss.crypto.X509Certificate;
+
+import com.netscape.certsrv.cert.CertData;
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmstools.cli.MainCLI;
+import com.netscape.cmsutil.util.Utils;
+
+/**
+ * @author Endi S. Dewata
+ */
+public class ClientCertShowCLI extends CLI {
+
+    public ClientCLI clientCLI;
+
+    public ClientCertShowCLI(ClientCLI clientCLI) {
+        super("cert-show", "Show certificate in client security database", clientCLI);
+        this.clientCLI = clientCLI;
+
+        createOptions();
+    }
+
+    public void printHelp() {
+        formatter.printHelp(getFullName() + " <nickname> [OPTIONS...]", options);
+    }
+
+    public void createOptions() {
+        Option option = new Option(null, "cert", true, "PEM file to store the certificate.");
+        option.setArgName("file");
+        options.addOption(option);
+
+        option = new Option(null, "client-cert", true, "PEM file to store the certificate and the private key.");
+        option.setArgName("file");
+        options.addOption(option);
+
+        option = new Option(null, "pkcs12", true, "PKCS #12 file to store the certificate chain and the private key.");
+        option.setArgName("password");
+        options.addOption(option);
+
+        option = new Option(null, "pkcs12-password", true, "PKCS #12 file password.");
+        option.setArgName("password");
+        options.addOption(option);
+    }
+
+    public void execute(String[] args) throws Exception {
+
+        CommandLine cmd = null;
+
+        try {
+            cmd = parser.parse(options, args);
+
+        } catch (Exception e) {
+            System.err.println("Error: " + e.getMessage());
+            printHelp();
+            System.exit(-1);
+        }
+
+        if (cmd.hasOption("help")) {
+            // Display usage
+            printHelp();
+            System.exit(0);
+        }
+
+        String[] cmdArgs = cmd.getArgs();
+
+        if (cmdArgs.length > 1) {
+            System.err.println("Error: Too many arguments specified.");
+            printHelp();
+            System.exit(-1);
+        }
+
+        if (cmdArgs.length == 0) {
+            System.err.println("Error: Missing certificate nickname.");
+            printHelp();
+            System.exit(-1);
+        }
+
+        MainCLI mainCLI = (MainCLI)parent.getParent();
+
+        String nickname = cmdArgs[0];
+        String certPath = cmd.getOptionValue("cert");
+        String pkcs12Path = cmd.getOptionValue("pkcs12");
+        String pkcs12Password = cmd.getOptionValue("pkcs12-password");
+        String clientCertPath = cmd.getOptionValue("client-cert");
+
+        if (certPath != null) {
+
+            if (verbose) System.out.println("Exporting certificate to " + clientCertPath + ".");
+
+            // late initialization
+            mainCLI.init();
+
+            client = mainCLI.getClient();
+            X509Certificate cert = client.getCert(nickname);
+
+            try (PrintWriter out = new PrintWriter(new FileWriter(certPath))) {
+                out.println(CertData.HEADER);
+                out.println(Utils.base64encode(cert.getEncoded()));
+                out.println(CertData.FOOTER);
+            }
+
+        } else if (pkcs12Path != null) {
+
+            if (verbose) System.out.println("Exporting certificate chain and private key to " + pkcs12Path + ".");
+
+            if (pkcs12Password == null) {
+                throw new Exception("Missing PKCS #12 password");
+            }
+
+            // store password into a temporary file
+            File pkcs12PasswordFile = File.createTempFile("pki-client-cert-show-", ".pwd");
+            pkcs12PasswordFile.deleteOnExit();
+
+            try (PrintWriter out = new PrintWriter(new FileWriter(pkcs12PasswordFile))) {
+                out.print(pkcs12Password);
+            }
+
+            // export certificate chain and private key into PKCS #12 file
+            exportPKCS12(
+                    mainCLI.certDatabase.getAbsolutePath(),
+                    mainCLI.config.getCertPassword(),
+                    pkcs12Path,
+                    pkcs12PasswordFile.getAbsolutePath(),
+                    nickname);
+
+        } else if (clientCertPath != null) {
+
+            if (verbose) System.out.println("Exporting client certificate and private key to " + clientCertPath + ".");
+
+            // generate random PKCS #12 password
+            pkcs12Password = RandomStringUtils.randomAlphanumeric(16);
+
+            // store password into a temporary file
+            File pkcs12PasswordFile = File.createTempFile("pki-client-cert-show-", ".pwd");
+            pkcs12PasswordFile.deleteOnExit();
+
+            try (PrintWriter out = new PrintWriter(new FileWriter(pkcs12PasswordFile))) {
+                out.print(pkcs12Password);
+            }
+
+            // export certificate chain and private key into a temporary PKCS #12 file
+            File pkcs12File = File.createTempFile("pki-client-cert-show-", ".p12");
+            pkcs12File.deleteOnExit();
+
+            exportPKCS12(
+                    mainCLI.certDatabase.getAbsolutePath(),
+                    mainCLI.config.getCertPassword(),
+                    pkcs12File.getAbsolutePath(),
+                    pkcs12PasswordFile.getAbsolutePath(),
+                    nickname);
+
+            // export client certificate and private key into a PEM file
+            exportClientCertificate(
+                    pkcs12File.getAbsolutePath(),
+                    pkcs12PasswordFile.getAbsolutePath(),
+                    clientCertPath);
+
+        } else {
+            // late initialization
+            mainCLI.init();
+
+            client = mainCLI.getClient();
+            X509Certificate cert = client.getCert(nickname);
+
+            ClientCLI.printCertInfo(cert);
+        }
+    }
+
+    public void exportPKCS12(
+            String dbPath,
+            String dbPassword,
+            String pkcs12Path,
+            String pkcs12PasswordPath,
+            String nickname) throws Exception {
+
+        String[] command = {
+                "/bin/pk12util",
+                "-d", dbPath,
+                "-K", dbPassword,
+                "-o", pkcs12Path,
+                "-w", pkcs12PasswordPath,
+                "-n", nickname
+        };
+
+        try {
+            run(command);
+
+        } catch (Exception e) {
+            throw new Exception("Unable to export PKCS #12 file", e);
+        }
+    }
+
+    public void exportClientCertificate(
+            String pkcs12Path,
+            String pkcs12PasswordPath,
+            String clientCertPath) throws Exception {
+
+        String[] command = {
+                "/bin/openssl",
+                "pkcs12",
+                "-clcerts", // client certificate only
+                "-nodes",   // no encryption
+                "-in",      pkcs12Path,
+                "-passin",  "file:" + pkcs12PasswordPath,
+                "-out",     clientCertPath
+        };
+
+        try {
+            run(command);
+
+        } catch (Exception e) {
+            throw new Exception("Unable to export client certificate", e);
+        }
+    }
+
+    public void run(String[] command) throws IOException, InterruptedException {
+
+        if (verbose) System.out.println("Command: " + StringUtils.join(command, " "));
+
+        Runtime rt = Runtime.getRuntime();
+        Process p = rt.exec(command);
+        int rc = p.waitFor();
+
+        if (rc != 0) {
+            throw new IOException("Command failed. RC: " + rc);
+        }
+    }
+}
-- 
1.8.4.2



More information about the Pki-devel mailing list