Я пытаюсь подключиться к 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 CertificateCollection и проверяет их все таким образом.
Чуть дальше он устанавливает сертификаты так:
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url_string);
httpWebRequest.ClientCertificates = SSC_Certs;
Все это выглядит довольно логично, даже если я не знаю, как он находит сертификаты, но я все еще не смог найти Java-эквивалент этого.
UPDATE
Соединение, которое я устанавливаю, является частью более крупного приложения, которое зависит от JDK 5, но мне удалось просто использовать банку sunmscapi, чтобы найти сертификат, который я ищу. Однако при попытке подключиться с использованием хранилища ключей 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 не находятся в файле cacerts Java, поэтому он получает список доверенных CA с сервера, но не распознает их и впоследствии не отправляет ни одного сертификата обратно, вызывая сбой соединения.
Таким образом, проблема остается: как заставить Java использовать хранилище ключей в качестве хранилища доверенных сертификатов или программно добавлять сертификаты в хранилища (без необходимости указывать пути к файлам)? Потому что, если это невозможно, это оставляет мне секретный вариант C, voodoo . На всякий случай я начну колоть куклу Герцога иголками.