当前位置: 首页 > 知识库问答 >
问题:

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

高夜洛
2023-03-14

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并传入包含我的证书(javadoc)的KeyStore来覆盖默认信任管理器。但是,要使用这个非默认信任管理器,我需要创建并初始化一个SSLContext。问题就在这里。

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

共有2个答案

莫典
2023-03-14

编写一个具有默认行为的KeyManager并不难。这只是几行代码。令人惊讶的是,SSLContext的行为并不都像密钥管理器w.r.t.一样,就像信任管理器w.r.t.一样。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);
燕青青
2023-03-14

听起来您正面临与此问题类似的问题,即在SSLContext中对trustmanager参数使用null。初始化(…) 恢复为默认的信任管理器,而它不适用于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())

编辑:(在附加评论之后)

当您只想自定义SSLContext的一半时,恐怕Oracle和IBMJSSE似乎都没有默认行为。您在Oracle JSSE留档中链接到的部分说,“如果javax.net.ssl.keyStore系统属性和适当的javax.net.ssl.keyStorePassword系统属性指定了密钥库,那么由默认SSLContext创建的KeyManager将是用于管理指定密钥库的KeyManager实现。”这在这里并不真正适用,因为您使用的是自定义SSLContext,而不是默认的(即使您正在自定义其中的一部分)。

无论如何,Oracle JSSE参考指南和IBMJSSE参考指南在这个问题上有所不同。(我不确定其中有多少是“标准的”,以及原则上一个是否应该与另一个兼容,但事实显然并非如此。)

两个“创建SSLContext对象”部分几乎相同,但不同。

Oracle JSSE参考指南说:

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

《IBM JSSE参考指南》指出:

如果KeyManager[]参数为null,则将搜索已安装的安全提供程序以查找KeyManagerFactory的最高优先级实现,并从中获取适当的KeyManager。

不幸的是,如果您希望在具有不同规范的实现之间实现相同的行为,那么您必须编写一些代码,即使这实际上重复了其中一个实现已经完成的工作。

 类似资料:
  • 问题内容: JSSE允许用户通过指定javax.net.ssl。*参数来提供默认的信任库和密钥库。我想为我的应用程序提供一个非默认的TrustManager,同时允许用户照常指定KeyManager,但是似乎没有任何方法可以实现此目的。 细节 http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGui

  • 在Java8上运行的一小部分示例代码中,我试图遵循bc-fips-1.0.2.jar用户指南Legion of the Bouncy Castle Inc. BC-FJA 1.0.2(Bouncy Castle FIPSJavaAPI)用户指南Date: 09/14/19: 注意:对FIPS模式的支持在Java 1.9中似乎已经开始消失,已经确认从Java 11开始它是一个空操作。对于Java 1

  • 如果我创建一个提供者并将其绑定到一个类,就像这样 然后

  • 但是现在我想为任何未显式设置的字段提供默认值。因此,如果我这样做了,那么将创建一个具有所有默认值的对象:

  • 问题内容: 在Swift中处理可选选项的习惯用法似乎过于冗长,如果您只想在nil为零的情况下提供默认值,则它是: 涉及不必要地复制代码,或 这不需要是常数。 Scala的Option monad(与Swift的Optional基本相同)具有用于此目的的方法: 我想念什么吗?Swift已经有一种紧凑的方式做到这一点吗?否则,是否可以在扩展名中为Optional 定义? 问题答案: 更新资料 苹果现在

  • 为我在原始问题中提出的错误代码示例道歉。我试图把我的问题简单化,但结果却相当令人困惑。 我稍微修改了一下代码: 我得到了第一个错误: 所以我把最后一行改为: 然后在Orelse上弹出一个新的错误: Optional>类型中的方法orElse(Capture#4-of?extends Map.Entry)不适用于参数(AbstractMap.SimpleEntry) 我最初的问题是,我不知道如何为这