我如何提供特定信任库,同时使用Java中的默认密钥库(JSSE)(How do I provide

2019-07-21 04:06发布

概观

JSSE允许用户通过指定的javax.net.ssl提供默认的信任存储区和密钥存储。*参数。 我想我的应用程序提供了一个非默认的TrustManager,同时允许用户指定的KeyManager像往常一样,但似乎没有什么办法来实现这一目标。

细节

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

假设UNIX机器上我想允许用户使用PKCS12密钥存储进行身份验证,而在OS XI希望允许用户使用系统钥匙串。 在OS X如下的应用程序可能启动:

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

这将很好地工作:当应用程序访问需要相互身份验证(客户端证书身份验证),那么用户将被提示允许访问自己的钥匙串HTTPS服务器。

问题

现在假设我要捆绑在我的申请自签名的证书颁发机构。 我可以通过构建的TrustManagerFactory并传递包含我的证书(密钥库覆盖缺省信任管理器的javadoc )。 但是,要使用这种非默认的信任经理,我需要创建和初始化的SSLContext中。 在这里,在这就是问题所在。

SSLContexts通过调用初始化的init(..) ,并通过既有的KeyManager和的TrustManager。 然而,逻辑使用javax.net.ssl中创建的KeyManager *参数嵌入在默认SSLContexts实施 - 我不能找到一种方式来获得的KeyManager或使用默认行为,同时指定一个的KeyManagerFactory非默认的TrustManager或的TrustManagerFactory。 因此,似乎是不可能的使用,例如,适当的操作系统特定钥匙扣实施,同时还提供用于认证远程服务器根证书。

Answer 1:

这听起来像你面临着类似的问题这个问题 ,在使用null在的TrustManager参数SSLContext.init(...)恢复为默认信任管理器,而它不为的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);

( 此实用工具类也可能是感兴趣,更具体getKeyStoreDefaultLoader()

编辑:(追随你的附加评论)

恐怕似乎没有成为在Oracle和IBM的JSSE默认的行为,当你想自定义的只有一半SSLContext 。 您在Oracle JSSE文档中链接到一节说,“ 如果是由javax.net.ssl.keyStore系统属性和适当的javax.net.ssl.keyStorePassword系统属性,那么通过的KeyManager默认的SSLContext创建指定的密钥库将用于管理指定密钥库的KeyManager实现。“这不会真正适用在这里,因为你正在使用自定义SSLContext ,而不是默认的一个无论如何(即使你自定义的一部分)。

无论如何,在Oracle JSSE参考指南和IBM JSSE参考指南对这个问题有所不同。 (我不知道有多少,这是意味着是“标准”和一个是否原则上应符合对方,但这显然不是这样的。)

这两个“ 创建SSLContext对象 ”部分几乎是相同的,但它们是不同的。

在甲骨文JSSE参考指南说:

如果的KeyManager []参数为空,则空的KeyManager将这个上下文中定义。

在IBM JSSE参考指南说:

如果的KeyManager [] paramater为空,已安装的安全供应商将要搜索的的KeyManagerFactory,从适当的KeyManager将获得的最高优先级执行。

不幸的是,如果你想在安装了不同规格的实现相同的行为,你必须写一些代码,即使这是有效地复制什么实现的一个已经这样做了。



Answer 2:

这不是太难写有默认行为的KeyManager。 这只是几行代码。 这是令人惊讶的是SSLContexts不都那样做WRT的的KeyManager,因为他们做的WRT的TrustManager。 IBM的JSSE不那样做的。 但它不是很难合成自己:

SSLContext  context = SSLContext.getInstance("TLS");
String  keyStore = System.getProperty("javax.net.ssl.keyStore");
String  keyStoreType = System.getProperty("javax.net.ssl.keyStoreType", KeyStore.getDefaultType());
String  keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword","");
KeyManager[]    kms = null;
if (keyStore != null)
{
    KeyManagerFactory   kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    KeyStore    ks = KeyStore.getInstance(keyStoreType);
    if (keyStore != null && !keyStore.equals("NONE")) {
        fs = new FileInputStream(keyStore);
    ks.load(fs, keyStorePassword.toCharArray());
    if (fs != null)
        fs.close();
    char[]  password = null;
    if (keyStorePassword.length() > 0)
        password = keyStorePassword.toCharArray();
    kmf.init(ks,password);
    kms = kmf.getKeyManagers();
}
context.init(kms,null,null);


文章来源: How do I provide a specific TrustStore while using the default KeyStore in Java (JSSE)