10

I have an application where the server side code loads the keystore -

KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(keyStoreFile), "password".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "privatepassword".toCharArray());
SSLContext sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(kmf.getKeyManagers(), null, null);

This worked fine when I had only one private key in the keystore. When I added another private key (with a different password) to the keystore I got this error

    java.security.UnrecoverableKeyException: Cannot recover key
    at sun.security.provider.KeyProtector.recover(KeyProtector.java:311)
    at sun.security.provider.JavaKeyStore.engineGetKey(JavaKeyStore.java:121)
    at sun.security.provider.JavaKeyStore$JKS.engineGetKey(JavaKeyStore.java:38)
    at java.security.KeyStore.getKey(KeyStore.java:763)
    at com.sun.net.ssl.internal.ssl.SunX509KeyManagerImpl.<init>(SunX509KeyManagerImpl.java:113)
    at com.sun.net.ssl.internal.ssl.KeyManagerFactoryImpl$SunX509.engineInit(KeyManagerFactoryImpl.java:48)

I then tried creating a custom keymanager as specified in the link below

@Bruno I tried the suggestion you have given. However that does not work. My custom key manager factory looks like this -

class CustomKeyManager implements X509KeyManager {
private final KeyStore ks;
private final String alias;

public CustomKeyManager(KeyStore ks, String alias) {
    this.ks = ks;
    this.alias = alias;
}
@Override
public String[] getClientAliases(String paramString,
        Principal[] paramArrayOfPrincipal) {
    return new String[]{alias};
}

@Override
public String chooseClientAlias(String[] paramArrayOfString,
        Principal[] paramArrayOfPrincipal, Socket paramSocket) {
    // TODO Auto-generated method stub
    return alias;
}

@Override
public String[] getServerAliases(String paramString,
        Principal[] paramArrayOfPrincipal) {
    // TODO Auto-generated method stub
    return new String[] {alias};
}

@Override
public String chooseServerAlias(String paramString,
        Principal[] paramArrayOfPrincipal, Socket paramSocket) {
    // TODO Auto-generated method stub
    return alias;
}

@Override
public X509Certificate[] getCertificateChain(String paramString) {
    // TODO Auto-generated method stub
    return null;
}

@Override
public PrivateKey getPrivateKey(String paramString) {
    PrivateKey pk = null;
    try { //have hardcoded this to the key i am working with
        pk = (PrivateKey) ks.getKey("mykey", "privatepassword".toCharArray());
    } catch (UnrecoverableKeyException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (KeyStoreException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return pk;
}   

}

After I create an object of CustomKeyManager if I make a call to getPrivateKey I get a non null private key -

Sun RSA private CRT key, 1024 bits modulus: 117260821110864021601500037071432398877761428124640545232618906306796101075244931231861318133902594657774603548686479580347869030216483422242066483203953111970007516384847036243243010603169399491545560497255823475630452314709747201644535089867367118834303975042348737995500693672037616900410158764770570813729 .......

This tells me that my getPrivateKey is working.

I use the CustomKeyManager in the following manner

         KeyStore ks = KeyStore.getInstance("JKS");
         ks.load(new FileInputStream(keyStoreFile), "password".toCharArray());
         SSLContext sslCtx = SSLContext.getInstance("TLS");
         CustomKeyManager ck = new CustomKeyManager(ks, "mykey");
         KeyManager[] kms = new KeyManager[1];
         kms[0] = ck;
         System.out.println(ck.getPrivateKey("mykey")); //returns a non null value
         sslCtx.init(kms , null, null); //throws an exception

The exception I get is

javax.net.ssl.SSLHandshakeException: no cipher suites in common

Is there a mistake in the way I am creating and using the CustomKeyManager. The other thing of interest is that if I set breakpoints to all the method entry points in the CustomKeyManager, none of them are hit.

PAN
  • 105
  • 1
  • 1
  • 5
  • How are you using the `SSLContext` afterwards, since your key manager doesn't seem to be used (according to your breakpoints)? – Bruno Aug 26 '14 at 09:50

1 Answers1

11

Your UnrecoverableKeyException happens because the keymanager isn't using the correct password. As you said, your two private keys use different passwords. The code you link to isn't going to help here, since it's merely wrapping the behaviour of the existing keymanager, which you've initialised with only one of the two passwords.

If you really want to use two distinct passwords, you'll need to implement getPrivateKey(String alias) in your custom X509KeyManager to take this into account. In particular, it will have to load the keys from your KeyStore instance with the right password for each alias (see getKey(String alias, char[] password)).

Bruno
  • 119,590
  • 31
  • 270
  • 376
  • I have edited the question with my remarks – PAN Aug 26 '14 at 08:43
  • There's also something wrong in your implementation of `getCertificateChain(...)`, you're returning `null`. You'll need to return the certificate chain (as loaded from the keystore for that alias). Instead of always loading the same private key from your keystore in `getPrivateKey`, use the alias too (and change the password as necessary). Depending on how you use your `SSLContext`, you might want to use an `X509ExtendedKeyManager` instead of a plain `X509KeyManager`. – Bruno Aug 26 '14 at 10:27
  • Implmentation for getCertificateChain //cert and xcert decl cert = ks.getCertificateChain(alias); xcert = new X509Certificate[cert.length]; for(int i = 0; i < cert.length; i++) xcert[i] = (X509Certificate) cert[i]; return xcert; and the getPrivateKey as pk = (PrivateKey) ks.getKey(alias, "privatepassword".toCharArray()); When breakpoints set on these methods they are never hit. Any sysout in any of the methods not printed. I continute to get the error - no cipher suites in common. Client side error is Received fatal alert: handshake_failure. Is problem extrinsic to above 2 methods. – PAN Aug 26 '14 at 11:44
  • @PAN, how do you use that `SSLContext`? Have you tried with an `X509ExtendedKeyManager`? – Bruno Aug 26 '14 at 11:53
  • I tried the X509ExtendedKeyManager. I am extending my custom implementation from that. That did not help. I create the SSL context and then pass the engine associated with the ssl context to netty. pipeline.addLast("ssl", new SslHandler(sslContext.createSSLEngine)); – PAN Aug 26 '14 at 13:01
  • Did you make sure `chooseEngineServerAlias` (and client if that's the side you're using) were returning the right alias, matching the one you use in `getPrivateKey`? Not sure what you've updated from your example, but you're still returning `null` for `getCertificateChain`, which is definitely not going to work. – Bruno Aug 26 '14 at 13:15
  • As you pointed out I was not returning the alias from chooseEngineServerAlias. Once I did that it worked just fine :D Thanks a lot. – PAN Aug 26 '14 at 13:51