Certificats de client Java sur HTTPS / SSL

J’utilise Java 6 et j’essaie de créer un HttpsURLConnection sur un serveur distant, en utilisant un certificate client.
Le serveur utilise un certificate racine auto-signé et requirejs la présentation d’un certificate client protégé par mot de passe. J’ai ajouté le certificate racine du serveur et le certificate client à un magasin de /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts Java par défaut que j’ai trouvé dans / /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts / /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts / /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts / /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts / /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts / /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts / /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts / /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts / /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts / /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts (OSX 10.5). Le nom du fichier de clés semble suggérer que le certificate client n’est pas censé y aller?

Quoi qu’il en soit, l’ajout du certificate racine à ce magasin a résolu le problème sortingstement célèbre de javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed' problem.

Cependant, je suis maintenant bloqué sur la façon d’utiliser le certificate client. J’ai essayé deux approches et je ne trouve nulle part ailleurs.
Tout d’abord, et préféré, essayez:

 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 

J’ai essayé d’ignorer la classe HttpsURLConnection (ce qui n’est pas idéal car je veux parler HTTP avec le serveur), et faites ceci à la place:

 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 

Je ne suis même pas sûr que le certificate client soit le problème ici.

    Enfin résolu;). Vous avez un indice fort ici (la réponse de Gandalfs l’a aussi touché). Les liens manquants étaient (principalement) le premier des parameters ci-dessous et, dans une certaine mesure, j’ai ignoré la différence entre les magasins de clés et les magasins de clés de confiance.

    Le certificate de serveur auto-signé doit être importé dans un fichier de clés certifiées:

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

    Ces propriétés doivent être définies (sur la ligne de commande ou dans le code):

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

    Exemple de code de travail:

     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); Ssortingng ssortingng = null; while ((ssortingng = bufferedreader.readLine()) != null) { System.out.println("Received " + ssortingng); } 

    Bien que cela ne soit pas recommandé, vous pouvez également désactiver la validation du certificate SSL dans son ensemble:

     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 certificatee chains TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } public void checkClientTrusted(X509Certificate[] certs, Ssortingng authType) {} public void checkServerTrusted(X509Certificate[] certs, Ssortingng authType) {} }}; // Ignore differences between given hostname and certificatee hostname HostnameVerifier hv = new HostnameVerifier() { public boolean verify(Ssortingng 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) {} } } 

    Avez-vous défini les propriétés du magasin de clés et / ou du système rnatttore?

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

    ou à partir du code

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

    Même avec javax.net.ssl.trustStore

    Si vous traitez avec un appel de service Web utilisant le framework Axis, la réponse est beaucoup plus simple. Si tous souhaitent que votre client puisse appeler le service Web SSL et ignorer les erreurs de certificate SSL, il vous suffit de placer cette instruction avant d’appeler des services Web:

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

    Les avertissements habituels à ce sujet étant une très mauvaise chose à faire dans un environnement de production s’appliquent.

    Je l’ai trouvé sur le wiki Axis .

    Pour moi, c’est ce qui a fonctionné avec 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 Ssortingng[] { "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(); } 

    Le fichier P12 contient le certificate client et la clé privée du client, créés avec BouncyCastle:

     public static byte[] convertPEMToPKCS12(final Ssortingng keyFile, final Ssortingng cerFile, final Ssortingng 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 certificatee 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(); } 

    J’utilise le package de client HTTP Apache commons pour le faire dans mon projet actuel et cela fonctionne bien avec SSL et un certificate auto-signé (après l’avoir installé dans des cacerts comme vous l’avez mentionné). S’il vous plaît jeter un oeil ici:

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

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

    Je pense que vous avez un problème avec votre certificate de serveur, n’est pas un certificate valide (je pense que c’est ce que signifie “handshake_failure” dans ce cas):

    Importez votre certificate de serveur dans votre fichier de clés trustcacerts sur le JRE du client. Cela se fait facilement avec keytool :

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

    En utilisant le code ci-dessous

     -Djavax.net.ssl.keyStoreType=pkcs12 

    ou

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

    n’est pas du tout nécessaire De plus, il n’est pas nécessaire de créer votre propre usine SSL personnalisée.

    J’ai également rencontré le même problème, dans mon cas, il y avait un problème lié au fait que la chaîne de certificates complète n’était pas imscope dans les magasins de clés de confiance. Importez les certificates en utilisant le certificate racine de l’utilitaire keytool, vous pouvez également ouvrir le fichier cacerts dans le bloc-notes et voir si la chaîne de certificates complète est imscope ou non. Vérifiez le nom d’alias que vous avez fourni lors de l’importation des certificates, ouvrez les certificates et vérifiez le nombre de certificates contenus dans le fichier cacerts.

    Le fichier cacerts doit également être configuré sur le serveur sur lequel vous exécutez votre application. Les deux serveurs s’authentifieront avec des clés publiques / privées.