代码之家  ›  专栏  ›  技术社区  ›  lmiguelmh

使用Java的SunMSCAPI/Windows MY访问用于TLS/SSL连接和客户端身份验证的智能卡证书

  •  1
  • lmiguelmh  · 技术社区  · 8 年前

    智能卡有两个证书,一个用于签名,另一个用于身份验证。我就是这样做的:

        // 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 >> 
    ]
    

    1 回复  |  直到 8 年前
        1
  •  2
  •   Michael-O    7 年前

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

    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
  •  1
  •   lmiguelmh    8 年前

    感谢@dave\u thompson\u 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());