Java 如何在新(未弃用)SSLSocketFactory中加载私钥密码?
我当前的TCP客户端正在使用Java 如何在新(未弃用)SSLSocketFactory中加载私钥密码?,java,ssl,keystore,sslsocketfactory,keystore-access,Java,Ssl,Keystore,Sslsocketfactory,Keystore Access,我当前的TCP客户端正在使用org.apache.http.conn.ssl.SSLSocketFactory 我正在迁移到导入javax.net.ssl.SSLSocketFactory 但是,我不确定如何加载客户端证书的私钥,该证书存储在我已有的.p12文件中,作为新SSLSocketFactory的上下文 以下是我使用不推荐使用的SSLSocketFactory的现有代码: import org.apache.http.ssl.SSLContexts; import org.apache.
org.apache.http.conn.ssl.SSLSocketFactory
我正在迁移到导入javax.net.ssl.SSLSocketFactory代码> 但是,我不确定如何加载客户端证书的私钥,该证书存储在我已有的.p12文件中,作为新SSLSocketFactory的上下文 以下是我使用不推荐使用的SSLSocketFactory的现有代码:
import org.apache.http.ssl.SSLContexts;
import org.apache.http.conn.ssl.SSLSocketFactory;
String ksPassphrase = "myKSpass";
String pkPassphrase = "myPKpass";
// Get a keystore instance of type PKCS12:
KeyStore keystore = KeyStore.getInstance("PKCS12");
// Load keystore file and password:
keystore.load(new FileInputStream("myFile.p12"), ksPassphrase.toCharArray());
// Create HTTPS connection socket factory context:
SSLContext context = SSLContexts.custom()
.loadKeyMaterial(keystore, pkPassphrase.toCharArray())
.build();
SSLSocketFactory sslScktFactory = new SSLSocketFactory(context);
以下是我可以从文档中管理的最佳代码:
import javax.net.ssl.*;
// not sure if this is the pass to KS or private key or, somehow both?
String passphrase = "myPassphrase"
SSLContext context = SSLContext.getInstance("TLS");
KeyManagerFactory keyMngFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream("myFile.jks"), passphrase.toCharArray());
keyMngFactory.init(keyStore, passphrase);
context.init(keyMngFactory.getKeyManagers(), null, null);
SSLSocketFactory sslScktFactory = context.getSocketFactory();
- 我认为问题在于我没有新库下上下文的
方法。.custom()
新SSLSocketFactory使用什么 - 是不是JKS密钥库没有存储在其中的私钥的内部密码(到目前为止我只使用PKCS,不熟悉JKS格式),这就是为什么我看不到私钥密码被引用的地方
密钥库的密码。load(InputStream,char[])
是存储密码;传递给KeyManagerFactory.init(KeyStore,char[])
的密码是privatekey密码(“单数”;如果有多个密钥,则所有密钥必须相同),请注意它是char[]
而不是String
——编译器(或IDE)应该告诉您这一点。如果要继续使用现有(或任何其他/新的)PKCS12文件,您确实需要将新代码更改为使用KeyStore.getInstance(“PKCS12”)
注意:API允许您使用不同于storepass的keypass,但您可能希望与之交换或共享PKCS12文件的其他软件(如Windows、MacOSX、OpenSSL、我认为NSS)不支持此功能,为了避免这些问题,Java的命令行keytool
将不会为PKCS12设置与storepass不同的keypass
新SSLSocketFactory使用什么
SSLContext.getSocketFactory()
——与您发布的内容完全相同。事实上,这正是您以前不推荐的org.apache.http.conn.ssl.SSLSocketFactory.SSLSocketFactory(SSLContext)
在内部所做的,只是它还默认使用HostNameVerifier
是不是JKS密钥库没有存储在其中的私钥的内部密码(到目前为止我只使用PKCS,不熟悉JKS格式),这就是为什么我看不到私钥密码被引用的地方
没有;JKS存储区确实有单独的storepass和keypass,如果像我上面描述的那样进行了更正,您发布的代码中将使用它们——尽管JKS使用的privatekey加密的keypass比PKCS12中通常使用的要弱。由于JKS文件无论如何都不能与其他软件互操作,keytool
确实支持keypass与storepass不同。事实上,keytool
(通常是JCA)支持在一个JKS存储中为不同的私钥使用不同的keypass值,但是KeyManagerFactory
(对于SSL/TLS即JSSE)不支持这一点,Apache也不支持(除了添加/包装密钥选择策略之外,它只在下面使用JSSE)
(OP)编辑:以下是根据您的建议生成的一些经过测试的工作代码:
// Consider having both the same, for compatibility with Windows/MacOSX/OpenSSL/etc:
String ksPassphrase = "myKSpass";
String pkPassphrase = "myPKpass";
SSLContext context = SSLContext.getInstance("TLS");
KeyManagerFactory keyMngFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(new FileInputStream("myFile.p12"),
ksPassphrase.toCharArray());
keyMngFactory.init(keyStore, pkPassphrase.toCharArray());
context.init(keyMngFactory.getKeyManagers(), null, null);
SSLSocketFactory sslScktFactory = context.getSocketFactory();
非常有洞察力,谢谢!我希望我添加了一个固定的代码片段来进一步可视化您的建议是可以的。我一直在想:从密钥库中选择密钥的机制不是很粗糙吗?我的意思是:当我在我的程序中查找加密文件时,我给我想要查找的文件命名并提供密码。在这里,我们只提供密码。似乎init()将遍历密钥库,直到它使用密码解锁密钥为止?而且,这对我来说更没有意义,因为并非所有系统都支持不同的私钥!那么,我们如何知道检索到了哪个私钥呢?或者这就像是一个信任库,所有的密钥都一起呈现?这不是效率低下吗?另一个旁注:我实际上有一个JKS给了我,我变懒了,在没有转换到PKCS12的情况下尝试了一下,它仍然有效keystore对象是用.getInstance(“PKCS12”)实例化的,后来在一个JKS文件上执行init(),这就产生了一个好的套接字。我甚至不认为我会费心转换我的JKS文件:D