首页 > 解决方案 > HttpClient 始终采用 KeyStore 的第一个证书条目

问题描述

我有两个不同的 keyStoresservice1.jkssevice2.jks相应的两个不同的服务(但是它们具有相同的 RootSertificate)。这是我httpClient与这两种服务一起成功使用的方法:

  public HttpClient getHttpClient(String filePath, String password) {
    KeyStore keyStore = KeyStore.getInstance(new File(filePath), password.toCharArray());

    SSLContext sslContext = new SSLContextBuilder()
            .loadKeyMaterial(keyStore, password.toCharArray())
            .build();

    SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, (hostname, session) -> true);

    return HttpClients.custom()
            .setSSLSocketFactory(socketFactory)
            .build();
  }

我只需要在我的项目中使用一个共享密钥库。所以我合并service1.jkssevice2.jks一个通用的 keyStore 中common.jks

cp sevice2.jks common.jks
keytool -importkeystore -srckeystore service1.jks -srcstoretype jks -srcstorepass changeit -destkeystore common.jks -deststoretype jks -deststorepass changeit

所以common.jks有两个项目:

$ keytool -list -storepass changeit -keystore server.keystore.jks
Keystore type: JKS
Keystore provider: SUN

Your keystore contains 2 entries

service1-alias, 8 сент. 2020 г., PrivateKeyEntry,
Certificate fingerprint (SHA-256): 
....
service2-alias, 8 сент. 2020 г., PrivateKeyEntry,
Certificate fingerprint (SHA-256): 
...

然后事实证明,我的HttpClientnow 仅与第一个一起成功service1,而请求service2变得未经授权。对我来说,我HttpClient现在只需要第一个输入表common.jks。如何HttpClient为请求使用正确的别名?

标签: javasslopensslkeystorekeytool

解决方案


我相信这是默认行为。SSLContextBuilder 的 Javadocs 中有一条注释:

请注意:SSLContext.init(KeyManager[], TrustManager[], SecureRandom) 的默认 Oracle JSSE 实现接受多个密钥和信任管理器,但只使用第一个匹配类型。参见例如:SSLContext.html#init

许多 Java 库和框架都有这个问题——证书可以存储在带有别名的密钥库中,原则上,如果应用程序必须提供证书,它可以选择使用哪个别名。但是,执行此操作的工具通常从库中省略,假设应用程序永远不需要使用多个密钥库。


推荐阅读