Importing certificate and key into Tomcat keystore
As this post describes, it's easy enough to import a certificate into a keystore using java's keytool. It is also easy to import a certificate response you have received for a CSR generated using keytool.
What isn't so easy is importing both the certificate and key into a keystore. For example, say you already have a certificate you are using on your apache web server and you now want to use it in your Tomcat server. This site describes the process using the following java code:
1 import java.security.*;
2 import java.io.IOException;
3 import java.io.InputStream;
4 import java.io.FileInputStream;
5 import java.io.DataInputStream;
6 import java.io.ByteArrayInputStream;
7 import java.io.FileOutputStream;
8 import java.security.spec.*;
9 import java.security.cert.Certificate;
10 import java.security.cert.CertificateFactory;
11 import java.util.Collection;
12 import java.util.Iterator;
13
14 /**
15 * ImportKey.java
16 *
17 * <p>This class imports a key and a certificate into a keystore
18 * (<code>$home/keystore.ImportKey</code>). If the keystore is
19 * already present, it is simply deleted. Both the key and the
20 * certificate file must be in <code>DER</code>-format. The key must be
21 * encoded with <code>PKCS#8</code>-format. The certificate must be
22 * encoded in <code>X.509</code>-format.</p>
23 *
24 * <p>Key format:</p>
25 * <p><code>openssl pkcs8 -topk8 -nocrypt -in YOUR.KEY -out YOUR.KEY.der
26 * -outform der</code></p>
27 * <p>Format of the certificate:</p>
28 * <p><code>openssl x509 -in YOUR.CERT -out YOUR.CERT.der -outform
29 * der</code></p>
30 * <p>Import key and certificate:</p>
31 * <p><code>java comu.ImportKey YOUR.KEY.der YOUR.CERT.der</code></p><br />
32 *
33 * <p><em>Caution:</em> the old <code>keystore.ImportKey</code>-file is
34 * deleted and replaced with a keystore only containing <code>YOUR.KEY</code>
35 * and <code>YOUR.CERT</code>. The keystore and the key has no password;
36 * they can be set by the <code>keytool -keypasswd</code>-command for setting
37 * the key password, and the <code>keytool -storepasswd</code>-command to set
38 * the keystore password.
39 * <p>The key and the certificate is stored under the alias
40 * <code>importkey</code>; to change this, use <code>keytool -keyclone</code>.
41 *
42 * Created: Fri Apr 13 18:15:07 2001
43 * Updated: Fri Apr 19 11:03:00 2002
44 *
45 * @author Joachim Karrer, Jens Carlberg
46 * @version 1.1
47 **/
48 public class ImportKey {
49
50 /**
51 * <p>Creates an InputStream from a file, and fills it with the complete
52 * file. Thus, available() on the returned InputStream will return the
53 * full number of bytes the file contains</p>
54 * @param fname The filename
55 * @return The filled InputStream
56 * @exception IOException, if the Streams couldn't be created.
57 **/
58 private static InputStream fullStream ( String fname ) throws IOException {
59 FileInputStream fis = new FileInputStream(fname);
60 DataInputStream dis = new DataInputStream(fis);
61 byte[] bytes = new byte[dis.available()];
62 dis.readFully(bytes);
63 ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
64 return bais;
65 }
66
67 /**
68 * <p>Takes two file names for a key and the certificate for the key,
69 * and imports those into a keystore. Optionally it takes an alias
70 * for the key.
71 * <p>The first argument is the filename for the key. The key should be
72 * in PKCS8-format.
73 * <p>The second argument is the filename for the certificate for the key.
74 * <p>If a third argument is given it is used as the alias. If missing,
75 * the key is imported with the alias importkey
76 * <p>The name of the keystore file can be controlled by setting
77 * the keystore property (java -Dkeystore=mykeystore). If no name
78 * is given, the file is named <code>keystore.ImportKey</code>
79 * and placed in your home directory.
80 * @param args [0] Name of the key file, [1] Name of the certificate file
81 * [2] Alias for the key.
82 **/
83 public static void main ( String args[]) {
84
85 // change this if you want another password by default
86 String keypass = "importkey";
87
88 // change this if you want another alias by default
89 String defaultalias = "importkey";
90
91 // change this if you want another keystorefile by default
92 String keystorename = System.getProperty("keystore");
93
94 if (keystorename == null)
95 keystorename = System.getProperty("user.home")+
96 System.getProperty("file.separator")+
97 "keystore.ImportKey"; // especially this ;-)
98
99
100 // parsing command line input
101 String keyfile = "";
102 String certfile = "";
103 if (args.length < 2 || args.length>3) {
104 System.out.println("Usage: java comu.ImportKey keyfile certfile [alias]");
105 System.exit(0);
106 } else {
107 keyfile = args[0];
108 certfile = args[1];
109 if (args.length>2)
110 defaultalias = args[2];
111 }
112
113 try {
114 // initializing and clearing keystore
115 KeyStore ks = KeyStore.getInstance("JKS", "SUN");
116 ks.load( null , keypass.toCharArray());
117 System.out.println("Using keystore-file : "+keystorename);
118 ks.store(new FileOutputStream ( keystorename ),
119 keypass.toCharArray());
120 ks.load(new FileInputStream ( keystorename ),
121 keypass.toCharArray());
122
123 // loading Key
124 InputStream fl = fullStream (keyfile);
125 byte[] key = new byte[fl.available()];
126 KeyFactory kf = KeyFactory.getInstance("RSA");
127 fl.read ( key, 0, fl.available() );
128 fl.close();
129 PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec ( key );
130 PrivateKey ff = kf.generatePrivate (keysp);
131
132 // loading CertificateChain
133 CertificateFactory cf = CertificateFactory.getInstance("X.509");
134 InputStream certstream = fullStream (certfile);
135
136 Collection c = cf.generateCertificates(certstream) ;
137 Certificate[] certs = new Certificate[c.toArray().length];
138
139 if (c.size() == 1) {
140 certstream = fullStream (certfile);
141 System.out.println("One certificate, no chain.");
142 Certificate cert = cf.generateCertificate(certstream) ;
143 certs[0] = cert;
144 } else {
145 System.out.println("Certificate chain length: "+c.size());
146 certs = (Certificate[])c.toArray();
147 }
148
149 // storing keystore
150 ks.setKeyEntry(defaultalias, ff,
151 keypass.toCharArray(),
152 certs );
153 System.out.println ("Key and certificate stored.");
154 System.out.println ("Alias:"+defaultalias+" Password:"+keypass);
155 ks.store(new FileOutputStream ( keystorename ),
156 keypass.toCharArray());
157 } catch (Exception ex) {
158 ex.printStackTrace();
159 }
160 }
161
162 }// KeyStore
Both the certificate and the key must be in the binary DER format rather than the standard ASCII PEM format. You can use openssl to convert them like this:
openssl pkcs8 -topk8 -nocrypt -in key.pem -inform PEM -out key.der -outform DER openssl x509 -in cert.pem -inform PEM -out cert.der -outform DER
Then it is a simple matter of:
java ImportKey key.der cert.der
Truststores
To make tomcat use a particular truststore set the following CATALINA_OPTS:
CATALINA_OPTS="-Djavax.net.ssl.trustStore=your_path_to/truststore.jks -Djavax.net.ssl.trustStorePassword=your_password"
