Сертификаты клиента Java через HTTPS / SSL

Я использую Java 6 и пытаюсь создать HttpsURLConnection на удаленном сервере, используя сертификат клиента.
Сервер использует собственный корневой сертификат и требует наличия защищенного паролем сертификата клиента. Я добавил корневой сертификат сервера и сертификат клиента в хранилище ключей java по умолчанию, которое я нашел в /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts (OSX 10.5). Имя файла хранилища ключей, похоже, предполагает, что сертификат клиента не должен туда заходить?

Во всяком случае, добавление корневого сертификата в этот магазин разрешило javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed' problem.

Тем не менее, я теперь зациклился на том, как использовать сертификат клиента. Я пробовал два подхода и нигде не достал.
Во-первых, и предпочтителен, попробуйте:

 SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); URL url = new URL("https://somehost.dk:3049"); HttpsURLConnection conn = (HttpsURLConnection)url.openConnection(); conn.setSSLSocketFactory(sslsocketfactory); InputStream inputstream = conn.getInputStream(); // The last line fails, and gives: // javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 

Я пробовал пропустить class HttpsURLConnection (не идеальный, так как я хочу поговорить с HTTP с сервером) и сделать это вместо этого:

 SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("somehost.dk", 3049); InputStream inputstream = sslsocket.getInputStream(); // do anything with the inputstream results in: // java.net.SocketTimeoutException: Read timed out 

Я даже не уверен, что проблема с клиентом – вот проблема.

Наконец решил это;). Получил сильный намек здесь (ответ Gandalfs коснулся немного на нем также). Отсутствующие ссылки были (в основном) первым из приведенных ниже параметров, и в некоторой степени я упускал из виду разницу между хранилищами ключей и доверительными магазинами.

Сертификат самозаверяющего сервера должен быть импортирован в доверительный магазин:

keytool -import -alias gridserver -file gridserver.crt -storepass $ PASS -keystore gridserver.keystore

Эти свойства необходимо установить (либо в командной строке, либо в коде):

 -Djavax.net.ssl.keyStoreType=pkcs12 -Djavax.net.ssl.trustStoreType=jks -Djavax.net.ssl.keyStore=clientcertificate.p12 -Djavax.net.ssl.trustStore=gridserver.keystore -Djavax.net.debug=ssl # very verbose debug -Djavax.net.ssl.keyStorePassword=$PASS -Djavax.net.ssl.trustStorePassword=$PASS 

Рабочий пример кода:

 SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); URL url = new URL("https://gridserver:3049/cgi-bin/ls.py"); HttpsURLConnection conn = (HttpsURLConnection)url.openConnection(); conn.setSSLSocketFactory(sslsocketfactory); InputStream inputstream = conn.getInputStream(); InputStreamReader inputstreamreader = new InputStreamReader(inputstream); BufferedReader bufferedreader = new BufferedReader(inputstreamreader); String string = null; while ((string = bufferedreader.readLine()) != null) { System.out.println("Received " + string); } 

Хотя это не рекомендуется, вы также можете отключить проверку SSL-сертификата alltogether:

 import javax.net.ssl.*; import java.security.SecureRandom; import java.security.cert.X509Certificate; public class SSLTool { public static void disableCertificateValidation() { // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = 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) {} }}; // Ignore differences between given hostname and certificate hostname HostnameVerifier hv = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; // Install the all-trusting trust manager try { SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier(hv); } catch (Exception e) {} } } 

Установили ли свойства KeyStore и / или TrustStore System?

 java -Djavax.net.ssl.keyStore=pathToKeystore -Djavax.net.ssl.keyStorePassword=123456 

или с кодом

 System.setProperty("javax.net.ssl.keyStore", pathToKeyStore); 

То же самое с javax.net.ssl.trustStore

Если вы имеете дело с вызовом веб-службы с использованием структуры Axis, есть гораздо более простой ответ. Если все хотят, чтобы ваш клиент мог вызвать веб-службу SSL и игнорировать ошибки сертификата SSL, просто поставьте этот оператор перед вызовом любых веб-сервисов:

System.setProperty("axis.socketSecureFactory", "org.apache.axis.components.net.SunFakeTrustSocketFactory");

Применяются обычные отказы от этого в том, что это очень плохое дело в производственной среде.

Я нашел это в вики Axis .

Для меня это работает с использованием Apache HttpComponents ~ HttpClient 4.x:

  KeyStore keyStore = KeyStore.getInstance("PKCS12"); FileInputStream instream = new FileInputStream(new File("client-p12-keystore.p12")); try { keyStore.load(instream, "helloworld".toCharArray()); } finally { instream.close(); } // Trust own CA and all self-signed certs SSLContext sslcontext = SSLContexts.custom() .loadKeyMaterial(keyStore, "helloworld".toCharArray()) //.loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()) //custom trust store .build(); // Allow TLSv1 protocol only SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); //TODO CloseableHttpClient httpclient = HttpClients.custom() .setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) //TODO .setSSLSocketFactory(sslsf) .build(); try { HttpGet httpget = new HttpGet("https://localhost:8443/secure/index"); System.out.println("executing request" + httpget.getRequestLine()); CloseableHttpResponse response = httpclient.execute(httpget); try { HttpEntity entity = response.getEntity(); System.out.println("----------------------------------------"); System.out.println(response.getStatusLine()); if (entity != null) { System.out.println("Response content length: " + entity.getContentLength()); } EntityUtils.consume(entity); } finally { response.close(); } } finally { httpclient.close(); } 

Файл P12 содержит клиентский сертификат и закрытый ключ клиента, созданный с помощью BouncyCastle:

 public static byte[] convertPEMToPKCS12(final String keyFile, final String cerFile, final String password) throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException, NoSuchProviderException { // Get the private key FileReader reader = new FileReader(keyFile); PEMParser pem = new PEMParser(reader); PEMKeyPair pemKeyPair = ((PEMKeyPair)pem.readObject()); JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter().setProvider("BC"); KeyPair keyPair = jcaPEMKeyConverter.getKeyPair(pemKeyPair); PrivateKey key = keyPair.getPrivate(); pem.close(); reader.close(); // Get the certificate reader = new FileReader(cerFile); pem = new PEMParser(reader); X509CertificateHolder certHolder = (X509CertificateHolder) pem.readObject(); java.security.cert.Certificate x509Certificate = new JcaX509CertificateConverter().setProvider("BC") .getCertificate(certHolder); pem.close(); reader.close(); // Put them into a PKCS12 keystore and write it to a byte[] ByteArrayOutputStream bos = new ByteArrayOutputStream(); KeyStore ks = KeyStore.getInstance("PKCS12", "BC"); ks.load(null); ks.setKeyEntry("key-alias", (Key) key, password.toCharArray(), new java.security.cert.Certificate[]{x509Certificate}); ks.store(bos, password.toCharArray()); bos.close(); return bos.toByteArray(); } 

Я использую пакет клиентских HTTP-пакетов Apache для этого в моем текущем проекте, и он отлично работает с SSL и самозаверяющим сертификатом (после установки его в cacerts, как вы упомянули). Пожалуйста, взгляните на это здесь:

http://hc.apache.org/httpclient-3.x/tutorial.html

http://hc.apache.org/httpclient-3.x/sslguide.html

Я думаю, что у вас есть проблема с сертификатом сервера, это не действительный сертификат (я думаю, что в этом случае означает «handshake_failure»):

Импортируйте сертификат своего сервера в хранилище ключей trustcacerts на JRE клиента. Это легко сделать с помощью keytool :

 keytool -import -alias  -file  -keystore /lib/security/cacerts 

Использование ниже кода

 -Djavax.net.ssl.keyStoreType=pkcs12 

или

 System.setProperty("javax.net.ssl.keyStore", pathToKeyStore); 

вовсе не требуется. Также нет необходимости создавать собственную собственную фабрику SSL.

Я также столкнулся с той же проблемой, в моем случае возникла проблема, заключавшаяся в том, что целая цепочка сертификатов не была импортирована в доверительные магазины. Импортируйте сертификаты с помощью корневого сертификата утилиты keytool, а также вы можете открыть файл cacerts в блокноте и посмотреть, импортирована ли целая цепочка сертификатов. Проверьте на имя псевдонима, которое вы указали при импорте сертификатов, откройте сертификаты и посмотрите, сколько их содержится, то же количество сертификатов должно быть в файле cacerts.

Также файл cacerts должен быть настроен на сервере, на котором выполняется ваше приложение, два сервера будут аутентифицировать друг друга с помощью общедоступных / закрытых ключей.

Давайте будем гением компьютера.