【问题标题】:Using Java's SunMSCAPI/Windows-MY to access smartcard certificates for TLS/SSL connection with client authentication使用 Java 的 SunMSCAPI/Windows-MY 访问智能卡证书以进行客户端身份验证的 TLS/SSL 连接
【发布时间】:2019-02-11 03:22:48
【问题描述】:

我有一个 Java 应用程序,它使用智能卡中的证书来执行 TLS/SSL 客户端身份验证。 智能卡有 2 个证书,一个用于签名,另一个用于身份验证。我就是这样做的:

    // loading windows-my store
    KeyStore windowsMyKeyStore = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
    windowsMyKeyStore.load(null, null);
    // loading keymanager 
    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    keyManagerFactory.init(windowsMyKeyStore, null);
    // building truststore
    TrustManager[] trustAllManager = new TrustManager[]{new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
        public void checkClientTrusted(X509Certificate[] certs, String authType) {
        }
        public void checkServerTrusted(X509Certificate[] certs, String authType) {
        }
    }};
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(keyManagerFactory.getKeyManagers(), trustAllManager, new SecureRandom());
    SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
            new String[]{"TLSv1.2", "TLSv1.1"},
            null,
            SSLConnectionSocketFactory.getDefaultHostnameVerifier());
    HttpClient httpClient = HttpClients.custom()
            .setSSLSocketFactory(sslConnectionSocketFactory)
            .build();
    HttpGet get = new HttpGet(...);

出现问题是因为 Java 选择了与来自服务器的 CertificateRequest 匹配的 第一个 证书(错误的),正如-Djavax.net.debug=all 时的这段摘录中所见:

*** ServerHelloDone
[read] MD5 and SHA1 hashes:  len = 4
0000: 0E 00 00 00                                        ....
matching alias: <<alias for SIGNING certificate>>
matching alias: <<alias for AUTHENTICATION certificate>> 
*** Certificate chain
chain [0] = [
    << SIGNING certificate >> 
]

是否可以配置 Java 使其使用正确的证书?

【问题讨论】:

  • 要配置 Java,您可以编写自己的KeyManager,它主要包含标准的,但可以根据需要调整chooseClientAlias(以及可选的getClientAliases)。使用 HttpClient,您还可以使用org.apache.http.ssl.SSLContextBuilder(在旧版本中为..http.conn.ssl..),它提供了一个使用PrivateKeyStrategy 的包装器(KeyManagerDelegate),您可以提供它来控制别名选择。
  • @dave_thompson_085 谢谢!我尝试了很多东西!我什至最终清除了“Windows-my store”(使用 certutil.exe)并只安装了正确的证书。能否请您提出您的答案,以便我接受,然后我将对其进行编辑以添加代码。
  • 我没有做足够的工作来回答,只是一个提示。如果您提出了一个好的答案,请继续;明确允许自我回答(尽管您可能需要等待接受 IIRC),或者如果您愿意,可以将其设为社区。​​span>

标签: java windows ssl smartcard client-certificates


【解决方案1】:

同样的问题,我是这样解决的:

KeyStore windowsMyKeyStore = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
windowsMyKeyStore.load(null, null);

SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(windowsMyKeyStore, null, new PrivateKeyStrategy() {
  @Override
  public String chooseAlias(Map<String, PrivateKeyDetails> aliases, Socket socket) {
    for (String alias : aliases.keySet()) {
      PrivateKeyDetails privateKeyDetails = aliases.get(alias);
      for (X509Certificate certificate : privateKeyDetails.getCertChain()) {
        try {
          certificate.checkValidity();
          List<String> extKeyUsage = certificate.getExtendedKeyUsage();
          if (extKeyUsage != null && extKeyUsage.contains("1.3.6.1.5.5.7.3.2"))
            return alias;
        } catch (CertificateExpiredException | CertificateNotYetValidException | CertificateParsingException e) {
          continue;
        }
      }
    }

    return null;
  }
}).build(); 

【讨论】:

    【解决方案2】:

    感谢@dave_thompson_085 的帮助,我可以解决这个问题:

    SSLContextBuilder sslContextBuilder = SSLContextBuilder.create();
    sslContextBuilder.loadKeyMaterial(windowsMyKeyStore, null, new PrivateKeyStrategy() {
        @Override
        public String chooseAlias(Map<String, PrivateKeyDetails> aliases, Socket socket) {
            for (String alias : aliases.keySet()) {
                PrivateKeyDetails privateKeyDetails = aliases.get(alias);
                for (X509Certificate certificate : privateKeyDetails.getCertChain()) {
                    if (requiredCertificate.getSerialNumber().equals(certificate.getSerialNumber())) {
                        return alias;
                    }
                }
            }
            throw new IllegalStateException("required certificate not found");
        }
    });
    SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContextBuilder.build());
    

    还有其他解决方案:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-31
      • 1970-01-01
      • 2014-08-24
      • 1970-01-01
      • 2014-04-04
      • 2021-02-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多