How do I list / export private keys from a keystore?
Solution 1:
You can extract a private key from a keystore with Java6 and OpenSSL. This all depends on the fact that both Java and OpenSSL support PKCS#12-formatted keystores. To do the extraction, you first use keytool
to convert to the standard format. Make sure you use the same password for both files (private key password, not the keystore password) or you will get odd failures later on in the second step.
keytool -importkeystore -srckeystore keystore.jks \
-destkeystore intermediate.p12 -deststoretype PKCS12
Next, use OpenSSL to do the extraction to PEM:
openssl pkcs12 -in intermediate.p12 -out extracted.pem -nodes
You should be able to handle that PEM file easily enough; it's plain text with an encoded unencrypted private key and certificate(s) inside it (in a pretty obvious format).
When you do this, take care to keep the files created secure. They contain secret credentials. Nothing will warn you if you fail to secure them correctly. The easiest method for securing them is to do all of this in a directory which doesn't have any access rights for anyone other than the user. And never put your password on the command line or in environment variables; it's too easy for other users to grab.
Solution 2:
A portion of code originally from Example Depot for listing all of the aliases in a key store:
// Load input stream into keystore
keystore.load(is, password.toCharArray());
// List the aliases
Enumeration aliases = keystore.aliases();
for (; aliases.hasMoreElements(); ) {
String alias = (String)aliases.nextElement();
// Does alias refer to a private key?
boolean b = keystore.isKeyEntry(alias);
// Does alias refer to a trusted certificate?
b = keystore.isCertificateEntry(alias);
}
The exporting of private keys came up on the Sun forums a couple of months ago, and u:turingcompleter came up with a DumpPrivateKey class to stitch into your app.
import java.io.FileInputStream;
import java.security.Key;
import java.security.KeyStore;
import sun.misc.BASE64Encoder;
public class DumpPrivateKey {
/**
* Provides the missing functionality of keytool
* that Apache needs for SSLCertificateKeyFile.
*
* @param args <ul>
* <li> [0] Keystore filename.
* <li> [1] Keystore password.
* <li> [2] alias
* </ul>
*/
static public void main(String[] args)
throws Exception {
if(args.length < 3) {
throw new IllegalArgumentException("expected args: Keystore filename, Keystore password, alias, <key password: default same tha
n keystore");
}
final String keystoreName = args[0];
final String keystorePassword = args[1];
final String alias = args[2];
final String keyPassword = getKeyPassword(args,keystorePassword);
KeyStore ks = KeyStore.getInstance("jks");
ks.load(new FileInputStream(keystoreName), keystorePassword.toCharArray());
Key key = ks.getKey(alias, keyPassword.toCharArray());
String b64 = new BASE64Encoder().encode(key.getEncoded());
System.out.println("-----BEGIN PRIVATE KEY-----");
System.out.println(b64);
System.out.println("-----END PRIVATE KEY-----");
}
private static String getKeyPassword(final String[] args, final String keystorePassword)
{
String keyPassword = keystorePassword; // default case
if(args.length == 4) {
keyPassword = args[3];
}
return keyPassword;
}
}
Note: this use Sun package, which is a "bad thing".
If you can download apache commons code, here is a version which will compile without warning:
javac -classpath .:commons-codec-1.4/commons-codec-1.4.jar DumpPrivateKey.java
and will give the same result:
import java.io.FileInputStream;
import java.security.Key;
import java.security.KeyStore;
//import sun.misc.BASE64Encoder;
import org.apache.commons.codec.binary.Base64;
public class DumpPrivateKey {
/**
* Provides the missing functionality of keytool
* that Apache needs for SSLCertificateKeyFile.
*
* @param args <ul>
* <li> [0] Keystore filename.
* <li> [1] Keystore password.
* <li> [2] alias
* </ul>
*/
static public void main(String[] args)
throws Exception {
if(args.length < 3) {
throw new IllegalArgumentException("expected args: Keystore filename, Keystore password, alias, <key password: default same tha
n keystore");
}
final String keystoreName = args[0];
final String keystorePassword = args[1];
final String alias = args[2];
final String keyPassword = getKeyPassword(args,keystorePassword);
KeyStore ks = KeyStore.getInstance("jks");
ks.load(new FileInputStream(keystoreName), keystorePassword.toCharArray());
Key key = ks.getKey(alias, keyPassword.toCharArray());
//String b64 = new BASE64Encoder().encode(key.getEncoded());
String b64 = new String(Base64.encodeBase64(key.getEncoded(),true));
System.out.println("-----BEGIN PRIVATE KEY-----");
System.out.println(b64);
System.out.println("-----END PRIVATE KEY-----");
}
private static String getKeyPassword(final String[] args, final String keystorePassword)
{
String keyPassword = keystorePassword; // default case
if(args.length == 4) {
keyPassword = args[3];
}
return keyPassword;
}
}
You can use it like so:
java -classpath .:commons-codec-1.4/commons-codec-1.4.jar DumpPrivateKey $HOME/.keystore changeit tomcat