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"


CategoryLinux

TomcatSSL (last edited 2008-03-26 17:42:38 by DavidKeen)