不可恢复的密钥异常:无法恢复密钥

10

我有一个应用程序,其中服务器端代码加载密钥库 -

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);

当我在密钥库中只有一个私钥时,这个方法很有效。当我向密钥库添加了另一个私钥(带有不同的密码)后,就会出现这个错误。

    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)

我随后尝试按照下面链接中指定的方式创建自定义键管理器。

@Bruno 我已经尝试了您提供的建议。然而,它并没有起作用。我的自定义密钥管理器工厂如下所示 -

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;
}   

}

创建了CustomKeyManager对象后,如果我调用getPrivateKey方法,会获得一个非空的私钥 - Sun RSA私有CRT密钥,1024位模数:

modulus: 117260821110864021601500037071432398877761428124640545232618906306796101075244931231861318133902594657774603548686479580347869030216483422242066483203953111970007516384847036243243010603169399491545560497255823475630452314709747201644535089867367118834303975042348737995500693672037616900410158764770570813729......

这告诉我getPrivateKey方法有效。

我以下方式使用CustomKeyManager:

         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

我得到的异常信息是

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

我创建和使用CustomKeyManager的方式是否有误?另一个有趣的事情是,如果我在CustomKeyManager中设置断点,所有方法的入口点都没有命中。


你之后如何使用 SSLContext,因为根据你的断点,你的密钥管理器似乎没有被使用? - Bruno
1个回答

11

您的UnrecoverableKeyException发生是因为密钥管理器没有使用正确的密码。正如您所说,您的两个私钥使用不同的密码。 您链接的代码在这里没有帮助,因为它仅仅是包装现有密钥管理器的行为,而您已经只用其中一个密码初始化了它。

如果您真的想要使用两个不同的密码,您需要在自定义的X509KeyManager中实现getPrivateKey(String alias)以考虑到这一点。特别是,它将不得不使用每个别名的正确密码从您的KeyStore实例加载密钥(请参见getKey(String alias, char[] password))。


我已经编辑了问题并附上了我的评论。 - PAN
你的 getCertificateChain(...) 实现中也有问题,你返回了 null。你需要返回证书链(从密钥库中为该别名加载的)。在 getPrivateKey 中,不要总是从密钥库中加载相同的私钥,也要使用别名(并根据需要更改密码)。根据你如何使用 SSLContext,你可能想使用 X509ExtendedKeyManager 而不是普通的 X509KeyManager - Bruno
@PAN,你如何使用SSLContext?尝试过使用X509ExtendedKeyManager吗? - Bruno
我尝试了X509ExtendedKeyManager。我正在从那里扩展我的自定义实现。但是没有帮助。我创建了SSL上下文,然后将与SSL上下文相关联的引擎传递给Netty。 pipeline.addLast("ssl", new SslHandler(sslContext.createSSLEngine)); - PAN
正如你所指出的,我没有从chooseEngineServerAlias返回别名。一旦我这样做了,它就可以正常工作了:D 非常感谢。 - PAN
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接