62

How do I list and export a private key from a keystore?

ScArcher2
  • 85,501
  • 44
  • 121
  • 160

9 Answers9

108

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.

botchniaque
  • 4,698
  • 3
  • 35
  • 63
Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
  • 2
    The back-story: Needed this today so we could put wireshark between a Java webservice client and a Java webservice server; it worked like a dream and we fixed our bug (a config problem in cookie handling). I thought I'd share as it's convenient to not have to write any code to do this sort of thing, and many people will already have the required tools installed, unlike with the other answers. – Donal Fellows Apr 08 '11 at 14:54
  • 2
    “Make sure you use the same password for both files […] or you will get odd failures” I THINK I LOVE YOU! -- It made sense to me that the new storage could have a different password. Why the keytool prompts for two different passwords if it can’t handle them being different is beyond me. – Ape-inago Oct 09 '13 at 21:07
  • 1
    @Ape-inago No idea; it adds lots of complexity for only the most marginal security benefits, since any legitimate use is going to end up having to deal with password management anyway and that's where the weak point in a practical cryptosystem usually lies. “Let's expose it because we can!” seems to be a feature of many crypto libraries, and is probably why they almost universally have a fearsome reputation… Meh. – Donal Fellows Oct 13 '13 at 07:14
32

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
Glenn Bech
  • 6,103
  • 4
  • 39
  • 56
ConroyP
  • 40,958
  • 16
  • 80
  • 86
  • This worked great. I had a customer that created a certificate request using the tomcat instructions, so the private key was installed in they keystore. I needed it to work with apache. – ScArcher2 Sep 29 '08 at 19:34
  • 1
    A year and a bit later and you've solved someone else's problem (i.e. mine). Good job! :) – Brian Beckett Feb 23 '10 at 16:26
  • Actually, This should be updated with an additional parameter: [3] Key password and the line `Key key = ks.getKey(args[2], args[1].toCharArray());` should read `Key key = ks.getKey(args[2], args[3].toCharArray());`. The original code assumes that the KEY password is the same as the KEYSTORE password, when they are not necessarily identical. – REW Jul 02 '10 at 15:27
  • @REW: I have edited the answer to include a more robust version of the key dumper. – VonC Sep 06 '10 at 06:52
  • The link to how to list the aliases is apparently dead now. Not sure what it was actually containing so not able to find a suitable replacement. – slm Apr 29 '13 at 16:54
  • @VonC what value should I put for args[3] , isn't it the alias key itself ? – Prateek Aug 22 '13 at 09:53
  • @REW what if I forget args[3] – Prateek Aug 22 '13 at 10:04
  • @prateek `args[3]` is the `keyPassword` (passphrase associated to the key). If you don't include a forth argument, that passphrase would be the same than the keystore password (default case). – VonC Aug 22 '13 at 10:26
  • @VonC I was trying this example to recover the keyPassword from a certificate I have generated and have forgot the alias password. This may not be a part of this question but can you tell me if its possible – Prateek Aug 22 '13 at 10:33
  • @prateek I am not sure you can find back a password, unless it is the default one (which is "`changeit`") – VonC Aug 22 '13 at 10:53
  • @VonC I know I changed the password to something else and now I just can't remember it , going for brute force and dictionary attacks now to recollect my own passoword :( – Prateek Aug 22 '13 at 11:07
  • @VonC Will let you know if it did – Prateek Aug 22 '13 at 11:38
  • I have 2 certs with the same alias. If i get certs in loop over aliases enumeration, I get two times the same cert (newer). I can't access to my secont cert with the same alias. – Krzysztof Szewczyk Feb 19 '15 at 06:57
6

If you don't need to do it programatically, but just want to manage your keys, then I've used IBM's free KeyMan tool for a long time now. Very nice for exporting a private key to a PFX file (then you can easily use OpenSSL to manipulate it, extract it, change pwds, etc).

https://www.ibm.com/developerworks/mydeveloperworks/groups/service/html/communityview?communityUuid=6fb00498-f6ea-4f65-bf0c-adc5bd0c5fcc

Select your keystore, select the private key entry, then File->Save to a pkcs12 file (*.pfx, typically). You can then view the contents with:

$ openssl pkcs12 -in mykeyfile.pfx -info

ArtOfWarfare
  • 20,617
  • 19
  • 137
  • 193
DustinB
  • 11,037
  • 5
  • 46
  • 54
  • 1
    KeyMan page link has changed to https://www.ibm.com/developerworks/mydeveloperworks/groups/service/html/communityview?communityUuid=6fb00498-f6ea-4f65-bf0c-adc5bd0c5fcc (never ending url 404/rewritings!) – manikanta Sep 04 '12 at 19:25
  • The download link is in the top right corner of the page. You'll need an IBM ID... you can create one for free. I chose to download the first zip option. To make it work on Windows, you either need to unzip into C:\Program Files\IBM\BlueZ\KeyMan, or you need to unzip wherever you want and change the first line of km.bat to have the path to the folder that contains km.bat. Then just run km.bat without any arguments. Should work as long as java is on your path. – ArtOfWarfare Apr 07 '16 at 17:43
5

Here is a shorter version of the above code, in Groovy. Also has built-in base64 encoding:

import java.security.Key
import java.security.KeyStore

if (args.length < 3)
        throw new IllegalArgumentException('Expected args: <Keystore file> <Keystore format> <Keystore password> <alias> <key password>')

def keystoreName = args[0]
def keystoreFormat = args[1]
def keystorePassword = args[2]
def alias = args[3]
def keyPassword = args[4]

def keystore = KeyStore.getInstance(keystoreFormat)
keystore.load(new FileInputStream(keystoreName), keystorePassword.toCharArray())
def key = keystore.getKey(alias, keyPassword.toCharArray())

println "-----BEGIN PRIVATE KEY-----"
println key.getEncoded().encodeBase64()
println "-----END PRIVATE KEY-----"
jrk
  • 656
  • 6
  • 16
4

This question came up on stackexchange security, one of the suggestions was to use Keystore explorer

https://security.stackexchange.com/questions/3779/how-can-i-export-my-private-key-from-a-java-keytool-keystore

Having just tried it, it works really well and I strongly recommend it.

Community
  • 1
  • 1
PhilDin
  • 2,802
  • 4
  • 23
  • 38
4

For android development, to convert keystore created in eclipse ADT into public key and private key used in SignApk.jar:

export private key:

keytool.exe -importkeystore -srcstoretype JKS -srckeystore my-release-key.keystore -deststoretype PKCS12 -destkeystore keys.pk12.der
openssl.exe pkcs12 -in keys.pk12.der -nodes -out private.rsa.pem

edit private.rsa.pem and leave "-----BEGIN PRIVATE KEY-----" to "-----END PRIVATE KEY-----" paragraph, then:

openssl.exe base64 -d -in private.rsa.pem -out private.rsa.der

export public key:

keytool.exe -exportcert -keystore my-release-key.keystore -storepass <KEYSTORE_PASSWORD> -alias alias_name -file public.x509.der

sign apk:

java -jar SignApk.jar public.x509.der private.rsa.der input.apk output.apk
diyism
  • 12,477
  • 5
  • 46
  • 46
  • I tried your suggestion however it fails at the first openssl command -- it reports decryption errors. The original keystore I tested with is routines used with jarsigner. The failure happened both under cygwin's tools, as well as Fedora's tools. – mah Feb 29 '12 at 14:37
  • It worked for me, I searched all over the web for this solution, thanks diyism – Mohsen Afshin Aug 23 '12 at 10:58
3

First of all, be careful! All of your security depends on the… er… privacy of your private keys. Keytool doesn't have key export built in to avoid accidental disclosure of this sensitive material, so you might want to consider some extra safeguards that could be put in place to protect your exported keys.

Here is some simple code that gives you unencrypted PKCS #8 PrivateKeyInfo that can be used by OpenSSL (see the -nocrypt option of its pkcs8 utility):

KeyStore keys = ...
char[] password = ...
Enumeration<String> aliases = keys.aliases();
while (aliases.hasMoreElements()) {
  String alias = aliases.nextElement();
  if (!keys.isKeyEntry(alias))
    continue;
  Key key = keys.getKey(alias, password);
  if ((key instanceof PrivateKey) && "PKCS#8".equals(key.getFormat())) {
    /* Most PrivateKeys use this format, but check for safety. */
    try (FileOutputStream os = new FileOutputStream(alias + ".key")) {
      os.write(key.getEncoded());
      os.flush();
    }
  }
}

If you need other formats, you can use a KeyFactory to get a transparent key specification for different types of keys. Then you can get, for example, the private exponent of an RSA private key and output it in your desired format. That would make a good topic for a follow-up question.

erickson
  • 265,237
  • 58
  • 395
  • 493
3

Another great tool is KeyStore Explorer: http://keystore-explorer.sourceforge.net/

Davio
  • 4,609
  • 2
  • 31
  • 58
1

Another less-conventional but arguably easier way of doing this is with JXplorer. Although this tool is designed to browse LDAP directories, it has an easy-to-use GUI for manipulating keystores. One such function on the GUI can export private keys from a JKS keystore.

Kkkev
  • 4,716
  • 5
  • 27
  • 43
  • Because I somehow get an error. Maybe its because my keystore dates back from ..........1900 or so. The only way to export is using JXplorer. Thanks! :) – Digital Human Aug 27 '18 at 13:10