Использование Java SunMSCAPI / Windows-MY для доступа к сертификатам смарт-карт для соединения TLS / SSL с аутентификацией клиента - PullRequest
0 голосов
/ 06 сентября 2018

У меня есть приложение 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 так, чтобы он использовал правильный сертификат?

Ответы [ 2 ]

0 голосов
/ 05 июня 2019

Та же проблема, я решил ее следующим образом:

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(); 
0 голосов
/ 03 октября 2018

Спасибо за помощь @ 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());

Есть и другие решения:

...