编程语言
首页 > 编程语言> > 如何在Java(JSSE)中使用默认的KeyStore时提供特定的TrustStore

如何在Java(JSSE)中使用默认的KeyStore时提供特定的TrustStore

作者:互联网

概观

JSSE允许用户通过指定javax.net.ssl.*参数来提供默认信任存储和密钥存储.我想为我的应用程序提供一个非默认的TrustManager,同时允许用户像往常一样指定KeyManager,但似乎没有任何方法可以实现这一点.

细节

http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html#CustomizingStores

假设在unix机器上我想允许用户使用pkcs12密钥库进行身份验证,而在OS X上我希望允许用户使用系统密钥链.在OS X上,应用程序可能会按如下方式启动:

java -Djavax.net.ssl.keyStore=NONE -Djavax.net.ssl.keyStoreType=KeychainStore \
     -Djavax.net.ssl.keyStorePassword=- -jar MyApplication.jar

这将正常工作:当应用程序访问需要相互身份验证(客户端证书身份验证)的https服务器时,将提示用户允许访问其钥匙串.

问题

现在假设我想将自签名证书颁发机构与我的应用程序捆绑在一起.我可以通过构造一个TrustManagerFactory并传入一个包含我的证书的KeyStore来覆盖默认的信任管理器(javadoc).但是,要使用此非默认信任管理器,我需要创建并初始化SSLContext.这里存在问题.

通过调用init(..)并传递KeyManager和TrustManager来初始化SSLContexts.但是,使用javax.net.ssl.*参数创建KeyManager的逻辑嵌入在默认SSLContexts的实现中 – 我找不到使用默认行为获取KeyManager或KeyManagerFactory的方法,同时还指定了非默认的TrustManager或TrustManagerFactory.因此,似乎不可能使用例如适当的操作系统特定的钥匙串实现,同时还提供用于验证远程服务器的根证书.

解决方法:

听起来你遇到类似于this question的问题,因为在SSLContext.init(…)中使用null for trustmanager参数会恢复为默认的信任管理器,而对于keymanager则不会.

这就是说,使用默认系统属性初始化KeyManager并不困难.这样的东西应该可以工作(代码直接写在这个答案中,所以你可能需要修改一些小东西):

String provider = System.getProperty("javax.net.ssl.keyStoreProvider");
String keystoreType = System.getProperty("javax.net.ssl.keyStoreType", KeyStore.getDefaultType());
KeyStore ks = null;
if (provider != null) {
    ks = KeyStore.getInstance(keystoreType, provider);
} else {
    ks = KeyStore.getInstance(keystoreType);
}
InputStream ksis = null;
String keystorePath = System.getProperty("javax.net.ssl.keyStore");
String keystorePassword = System.getProperty("javax.net.ssl.keyStorePassword");
if (keystorePath != null && !"NONE".equals(keystorePath)) {
    ksis = new FileInputStream(keystorePath);
}
try {
    ks.load(ksis, keystorePassword.toCharArray());
} finally {
     if (ksis != null) { ksis.close(); }
}

KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, keystorePassword.toCharArray());
// Note that there is no property for the key password itself, which may be different.
// We're using the keystore password too.

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), ..., null);

(This utility class也可能是有意义的,更具体地说是getKeyStoreDefaultLoader().)

编辑:(在您的附加评论后)

当您想要仅自定义一半SSLContext时,我担心Oracle和IBM JSSE似乎都没有默认行为.您在Oracle JSSE文档中链接到的部分说:“如果密钥库由javax.net.ssl.keyStore系统属性和适当的javax.net.ssl.keyStorePassword系统属性指定,则由默认SSLContext创建的KeyManager将是用于管理指定密钥库的KeyManager实现.“
这不适用于此,因为您使用的是自定义SSLContext,而不是默认的SSLContext(即使您正在自定义其中的一部分).

无论如何,Oracle JSSE参考指南和IBM JSSE参考指南在这个主题上有所不同. (我不确定这有多少意味着“标准”,原则上是否应该与另一方相符,但事实显然并非如此.)

“创建SSLContext对象”部分几乎完全相同,但它们是不同的.

Oracle JSSE Reference guide说:

If the KeyManager[] parameter is null, then an empty KeyManager will
be defined for this context.

IBM JSSE Reference guide说:

If the KeyManager[] paramater is null, the installed security
providers will be searched for the highest-priority implementation of
the KeyManagerFactory, from which an appropriate KeyManager will be
obtained.

遗憾的是,如果您希望在具有不同规范的实现中具有相同的行为,则必须编写一些代码,即使这有效地复制了其中一个实现已经执行的操作.

标签:java,ssl,client-certificates,jsse
来源: https://codeday.me/bug/20190517/1122940.html