Как я могу использовать аутентификацию сертификата с HttpsURLConnection?

Я пытаюсь подключиться к URL-адресу HTTPS, но мне нужно использовать аутентификацию клиента с помощью сертификата, установленного в моей системе сторонним программным обеспечением.

Я не имею ни малейшего представления о том, как я должен либо найти, либо использовать его, и все, на что я могу ориентироваться, это пример кода на C#, который значительно отличается от всех ответов на Java, которые я нашел по этому поводу. (например, KeyStore требует какого-то пароля?)

Вот пример кода на C#, который у меня есть

System.Security.Cryptography.X509Certificates.X509CertificateCollection SSC_Certs = 
    new System.Security.Cryptography.X509Certificates.X509CertificateCollection();

Microsoft.Web.Services2.Security.X509.X509CertificateStore WS2_store =
    Microsoft.Web.Services2.Security.X509.X509CertificateStore.CurrentUserStore(
    Microsoft.Web.Services2.Security.X509.X509CertificateStore.MyStore);

WS2_store.OpenRead();

Microsoft.Web.Services2.Security.X509.X509CertificateCollection WS2_store_Certs = WS2_store.Certificates;

А затем он просто перебирает коллекцию сертификатов WS2_store_Certs и проверяет их все таким образом. Чуть дальше он устанавливает сертификаты следующим образом:

HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url_string);
httpWebRequest.ClientCertificates = SSC_Certs;

Все это выглядит довольно логично, даже если я понятия не имею, как он находит сертификаты, но я все еще не смог найти Java-эквивалент этого.

UPDATE

Подключение, которое я делаю, является частью более крупного приложения, которое зависит от JDK 5, но мне удалось просто использовать sunmscapi jar для поиска нужного сертификата. Однако при попытке подключиться с помощью хранилища windows он выдает ошибку, поэтому я решил обойти эту проблему, получив нужный сертификат из хранилища windows и вставив его в стандартный сертификат java. Теперь я получаю EOFException, за которым следует SSLHandshakeException, говорящий "Удаленный хост закрыл соединение во время квитирования". Отладочная трассировка ssl не выявляет для меня непосредственной проблемы, поскольку нужный мне сертификат отображается в цепочке сертификатов здесь.

Он выполняет всю процедуру ClientKeyExchange, говорит, что она завершена, а затем последние сообщения, которые я получаю из журнала отладки сразу после этого -

[write] MD5 and SHA1 hashes:  len = 16
0000: 14 00 00 0C D3 E1 E7 3D   C2 37 2F 41 F9 38 26 CC  .......=.7/A.8&.
Padded plaintext before ENCRYPTION:  len = 32
0000: 14 00 00 0C D3 E1 E7 3D   C2 37 2F 41 F9 38 26 CC  .......=.7/A.8&.
0010: CB 10 05 A1 3D C3 13 1C   EC 39 ED 93 79 9E 4D B0  ....=....9..y.M.
AWT-EventQueue-1, WRITE: TLSv1 Handshake, length = 32
[Raw write]: length = 37
0000: 16 03 01 00 20 06 B1 D8   8F 9B 70 92 F4 AD 0D 91  .... .....p.....
0010: 25 9C 7D 3E 65 C1 8C A7   F7 DA 09 C0 84 FF F4 4A  %..>e..........J
0020: CE FD 4D 65 8D                                     ..Me.
AWT-EventQueue-1, received EOFException: error

и код, который я использую для установки соединения -

KeyStore jks = KeyStore.getInstance(KeyStore.getDefaultType());
jks.load(null, null);

KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
jks.setCertificateEntry("alias", cert1); //X509Certificate obtained from windows keystore
kmf.init(jks, new char[0]);

SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(kmf.getKeyManagers(), new TrustManager[]{tm}, null);

sslsocketfactory = sslContext.getSocketFactory();
System.setProperty("https.proxyHost", proxyurl);
System.setProperty("https.proxyPort", proxyport);

Authenticator.setDefault(new MyAuthenticator("proxyID", "proxyPassword"));
URL url = new URL(null, urlStr, new sun.net.www.protocol.https.Handler());
HttpsURLConnection uc = (HttpsURLConnection) url.openConnection();
uc.setSSLSocketFactory(sslsocketfactory);
uc.setAllowUserInteraction(true);
uc.setRequestMethod("POST");
uc.connect();

(Я еще не пробовал HttpClient, потому что я понятия не имею, как найти файл сертификата, и я также не уверен, что это всегда будет одинаково на каждой клиентской системе. )

ДРУГОЕ ОБНОВЛЕНИЕ

Я нашел CA для сертификата, который мне нужен, в хранилище ключей WINDOWS-ROOT (и проверил с помощью .verify(), чтобы убедиться, что они все проверены), я добавил их в хранилище ключей java, но все равно ничего не изменилось. Я предполагаю, что они должны попасть в TrustStore, но я пока не нашел способа сделать это программно. (Я бы предпочел не полагаться на конечных пользователей в этом вопросе, так как все, что я могу гарантировать от них, это то, что сертификат и CA будут присутствовать благодаря стороннему программному обеспечению, упомянутому в начале этого смехотворно длинного вопроса).

ЕЩЕ ОБНОВЛЕНИЯ

Добавляя к предыдущему обновлению, я пришел к выводу, что моя проблема заключается в том, что моих CA нет в файле cacerts Java, поэтому она получает список доверенных CA от сервера, но не распознает их и впоследствии не отправляет ни одного сертификата обратно, что приводит к сбою соединения. Итак, проблема остается, как заставить Java либо использовать свое хранилище ключей в качестве хранилища доверия, либо добавлять сертификаты в cacerts программно (без необходимости указания путей к файлам)? Потому что если это невозможно, то у меня остается только секретный вариант C, voodoo. На всякий случай я начну колоть иголками куклу Дюка.

13
задан Valyrion 6 December 2011 в 14:53
поделиться