1 /*
2  * @(#) $Id: ExportCommand.java,v 1.3 2003/07/08 08:13:53 pankaj Exp $
3  *
4  * Copyright (c) 2002-03 by Pankaj Kumar (http://www.pankaj-k.net). 
5  * All rights reserved.
6  *
7  * The license governing the use of this file can be found in the 
8  * root directory of the containing software.
9  */
10package org.jstk.crypt;
11
12import java.util.*;
13import java.security.*;
14import java.io.*;
15import java.security.cert.Certificate;
16import javax.crypto.SecretKey;
17import javax.crypto.KeyGenerator;
18
19import org.jstk.JSTKCommand;
20import org.jstk.JSTKCommandAdapter;
21import org.jstk.JSTKException;
22import org.jstk.JSTKArgs;
23import org.jstk.JSTKOptions;
24import org.jstk.JSTKResult;
25
26import org.jstk.pem.PEMData;
27
28public class ExportCommand extends JSTKCommandAdapter{
29    private static HashMap defaults = new HashMap();
30    private boolean pkcs12Output = false;
31    static {
32        defaults.put("kstype", "JKS");
33        defaults.put("keystore", "my.keystore");
34        defaults.put("storepass", "changeit");
35        defaults.put("outform", "PEM");
36    }
37    public String briefDescription(){
38        return "exports key or cert entries from keystore to files";
39    }
40    public String optionsDescription(){
41        return
42            "  -keystore <keystore>: the keystore.[" +
43            defaults.get("keystore") + "]\n" +
44            "  -storepass <storepass>: Password for keystore.[" +
45            defaults.get("storepass") + "]\n" +
46            "  -kstype <type>        : the keystore type.[" +
47            defaults.get("type") + "]\n" +
48            "  -alias <alias>       : alias to access the key in the keystore.[" +
49            defaults.get("alias") + "]\n" +
50            "  -keypass <keypass>   : Password for key in the keystore.[" +
51            defaults.get("keypass") + "]\n" +
52            "  -outform <outform>   : Format of exported data(DER|PEM|PKCS12).[" +
53            defaults.get("keypass") + "]\n" +
54            "  -provider <provider> : provider name for KeyStore.\n";
55
56    }
57
58    public String[] useForms(){
59        String[] forms = {
60                "[-keystore <keystore>] [-kstype (JCEKS|JKS|PKCS12)]\n" +
61                "\t[-storepass <storepass>] [-alias <alias>] [-keypass <keypass>]\n" +
62                "\t[-outform <outform>][-provider <provider>]"
63        };
64        return forms;
65    }
66
67    public String[] sampleUses(){
68        String[] uses = {
69            "",
70            "-keystore test.ks -storepass testpass",
71            "-alias test.key -outform PKCS12"
72        };
73        return uses;
74    }
75
76    private void exportKey(Key key, String alias, StringBuffer sb, String outform) throws Exception{
77        String keytype = (key instanceof SecretKey ? "SecretKey" :
78                            (key instanceof PrivateKey ? "PrivateKey" : "PublicKey"));
79        String keyfile = alias + (outform.equalsIgnoreCase("PEM") ? ".pem" : ".key.der");
80        FileOutputStream fos = new FileOutputStream(keyfile);
81        byte[] derEncodedKey = key.getEncoded();
82        if (outform.equalsIgnoreCase("PEM")){
83            PEMData pemData = new PEMData(derEncodedKey);
84            String base64Data = pemData.encode();
85            String preEB = "-----BEGIN PRIVATE KEY-----\n";
86            String postEB = "\n-----END PRIVATE KEY-----\n";
87            fos.write(preEB.getBytes());
88            fos.write(base64Data.getBytes());
89            fos.write(postEB.getBytes());
90        } else {
91            fos.write(derEncodedKey);
92        }
93        fos.close();
94        sb.append("Exported " + keytype + " to file: " + keyfile + "\n");
95    }
96
97    private void exportCertChain(Certificate[] certs, String alias, StringBuffer sb, String outform) throws Exception{
98        if (certs == null)
99            return;
00
01        for (int i = 0; i < certs.length; i++){
02            String cerfile = alias + (outform.equalsIgnoreCase("PEM") ? ".pem" : ".crt." + i + ".der");
03            boolean append = (outform.equalsIgnoreCase("PEM") ? true : false);
04            FileOutputStream fos = new FileOutputStream(cerfile, append);
05            byte[] derEncodedCert = certs[i].getEncoded();
06            if (outform.equalsIgnoreCase("PEM")){
07                PEMData pemData = new PEMData(derEncodedCert);
08                String base64Data = pemData.encode();
09                String preEB = "-----BEGIN CERTIFICATE-----\n";
10                String postEB = "\n-----END CERTIFICATE-----\n";
11                fos.write(preEB.getBytes());
12                fos.write(base64Data.getBytes());
13                fos.write(postEB.getBytes());
14            } else {
15                fos.write(derEncodedCert);
16            }
17            fos.close();
18            sb.append("Appended Certificate#" + i + " to file: " + cerfile + "\n");
19        }
20    }
21
22    private String exportEntry(KeyStore ks, String alias, String keypass, String outform) throws Exception{
23        StringBuffer sb = new StringBuffer();
24        if (ks.isKeyEntry(alias)){
25            try {
26                Key key = ks.getKey(alias, keypass.toCharArray());
27                exportKey(key, alias, sb, outform);
28
29                Certificate[] certs = ks.getCertificateChain(alias);
30                exportCertChain(certs, alias, sb, outform);
31                if (pkcs12Output){  // Convert the <alias>.pem file into a <alias>.p12 file.
32                    String pemfile = alias + ".pem";
33                    String p12file = alias + ".p12";
34                    pem2pkcs12(pemfile, p12file, keypass, sb);
35                }
36            } catch (UnrecoverableKeyException e){
37                sb.append("Cannot Receover Key from KeyStore.\n");
38            }
39        } else {
40            Certificate[] certs = ks.getCertificateChain(alias);
41            exportCertChain(certs, alias, sb, outform);
42        }
43        return sb.toString();
44    }
45
46    // This function relies on openssl. Not pure Java.
47    private void pem2pkcs12(String pemfile, String p12file, String keypass, StringBuffer sb){
48        // Check if openssl is present.
49        if (!opensslPresent()){
50            sb.append("*** ERROR *** Couldn't find openssl. Cannot convert PEM to PKCS12.");
51        } else {
52            String cmd = "openssl pkcs12 -export -in " + pemfile +
53                            " -out " + p12file + " -password pass:" + keypass;
54            int result = runOSCommand(cmd);
55            if (result == 0){
56                sb.append("Converted PEM file " + pemfile + " to PKCS12 file " + p12file);
57            } else {
58                sb.append("*** ERROR *** Conversion of PEM file " + pemfile +
59                        "to PKCS12 file " + p12file + " FAILED");
60            }
61        }
62    }
63
64    private boolean opensslPresent(){
65        if (runOSCommand("openssl exit") == 0){
66            return true;
67        } else {
68            return false;
69        }
70    }
71
72    private int runOSCommand(String cmd){
73        try {
74            Process p = Runtime.getRuntime().exec(cmd);
75            return p.waitFor();
76        } catch (IOException ioe){
77            return 1;
78        } catch (InterruptedException ie){
79            return 1;
80        }
81    }
82
83    public Object execute(JSTKArgs args) throws JSTKException{
84        StringBuffer sb = new StringBuffer();
85        try {
86            args.setDefaults(defaults);
87            pkcs12Output = false;
88            String keystore = args.get("keystore");
89            String storepass = args.get("storepass");
90            String type = args.get("kstype");
91            String providerName = args.get("provider");
92            String keypass = args.get("keypass");
93            String outform = args.get("outform");
94            if (outform.equalsIgnoreCase("PKCS12")){
95                pkcs12Output = true;
96                outform = "PEM";
97            }
98            if (keypass == null)
99                keypass = storepass;
00            String alias = args.get("alias");
01
02            FileInputStream fis = new FileInputStream(keystore);
03
04            KeyStore ks;
05            if (providerName != null)
06                ks = KeyStore.getInstance(type, providerName);
07            else
08                ks = KeyStore.getInstance(type);
09
10            ks.load(fis, storepass.toCharArray());
11
12            if (alias != null){
13                if (ks.containsAlias(alias)){
14                    sb.append(exportEntry(ks, alias, keypass, outform));
15                } else {
16                    sb.append("No such Entry: " + alias + ".\n");
17                }
18            } else {
19                Enumeration enum = ks.aliases();
20
21                while (enum.hasMoreElements()){
22                    String alias0 = (String)enum.nextElement();
23                    sb.append(exportEntry(ks, alias0, keypass, outform));
24                }
25            }
26        } catch (Exception exc){
27            throw new JSTKException("ExportCommand.execute() failed", exc);
28        }
29        return new JSTKResult(null, true, sb.toString());
30    }
31}