2011-03-05 15 views
31

He habilitado https en tomcat y tengo un certificado autofirmado para la autenticación del servidor. Creé un cliente http utilizando Apache httpClient. Establecí un administrador de confianza cargando el certificado del servidor. El cliente http puede conectarse con el servidor sin problemas. Para ver lo que está pasando He activado la depuración:Uso de httpclient de Apache para https

System.setProperty("javax.net.debug", "ssl"); 

vi lo siguiente, que no puedo entender en absoluto:

*** 
adding as trusted cert: 
    Subject: CN=Me, OU=MyHouse, O=Home, L=X, ST=X, C=BB 
    Issuer: CN=Me, OU=MyHouse, O=Home, L=X, ST=X, C=BB 
    Algorithm: RSA; Serial number: 0x4d72356b 
    Valid from Sat Mar 05 15:06:51 EET 2011 until Fri Jun 03 16:06:51 EEST 2011 

mi certificado se muestra y se añade al almacén de confianza (como yo lo veo) . Entonces:

trigger seeding of SecureRandom 
done seeding SecureRandom 

Aquí es la parte de las trazas de depuración que no entiendo:

trustStore is: C:\Program Files\Java\jre6\lib\security\cacerts 
trustStore type is : jks 
trustStore provider is : 
init truststore 
adding as trusted cert: 
    Subject: CN=SwissSign Platinum CA - G2, O=SwissSign AG, C=CH 
    Issuer: CN=SwissSign Platinum CA - G2, O=SwissSign AG, C=CH 
    Algorithm: RSA; Serial number: 0x4eb200670c035d4f 
    Valid from Wed Oct 25 11:36:00 EEST 2006 until Sat Oct 25 11:36:00 EEST 2036 

adding as trusted cert: 
    Subject: [email protected], CN=http://www.valicert.com/, OU=ValiCert Class 1 Policy Validation Authority, O="ValiCert, Inc.", L=ValiCert Validation Network 
    Issuer: [email protected], CN=http://www.valicert.com/, OU=ValiCert Class 1 Policy Validation Authority, O="ValiCert, Inc.", L=ValiCert Validation Network 
    Algorithm: RSA; Serial number: 0x1 
    Valid from Sat Jun 26 01:23:48 EEST 1999 until Wed Jun 26 01:23:48 EEST 2019 

Parece que también utiliza el almacén de confianza de Java por defecto! Mi pregunta es por qué sucede esto?

En mi código especifico explícitamente un trust-store específico para usar (a través de truststoremanagers). Esperaba que solo esto fuera usado. Parece que tanto mi almacén de confianza como el valor predeterminado de java se están utilizando. ¿Es así como se supone que debe funcionar?

ACTUALIZACIÓN:
He intentado lo siguiente:

System.out.println("TMF No:"+tmf.getTrustManagers().length); 
System.out.println("Class is "+tmf.getTrustManagers()[0].getClass().getName()); 

pensé que debería ver 2 gerentes de confianza, ya que 2 de claves (la mía y por defecto de Java parecen utilizarse).
¡Pero el resultado fue solo 1 administrador de confianza!

TMF No:1 
Class is com.sun.net.ssl.internal.ssl.X509TrustManagerImpl 

Update2: Como se puede ver en el código abajo especifico mi keystore.My expectativa es que sólo esto se debe utilizar (no esta y cacert así)

HttpClient client = new DefaultHttpClient(); 
    SSLContext sslContext = SSLContext.getInstance("TLS");  

    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
    KeyStore ks = KeyStore.getInstance("JKS"); 
    File trustFile = new File("clientTrustStore.jks"); 
    ks.load(new FileInputStream(trustFile), null); 
    tmf.init(ks); 
    sslContext.init(null, tmf.getTrustManagers(),null); 
    SSLSocketFactory sf = new SSLSocketFactory(sslContext); 
    sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); 
    Scheme scheme = new Scheme("https", sf, 443); 
    client.getConnectionManager().getSchemeRegistry().register(scheme); 
    httpGet = new HttpGet("https://localhost:8443/myApp"); 
    HttpResponse httpResponse = client.execute(httpGet); 

¿El no tiene sentido para mí.

Respuesta

23

junté esta aplicación de prueba para reproducir el problema utilizando el framework de pruebas HTTP desde el paquete Apache HttpClient:

ClassLoader cl = HCTest.class.getClassLoader(); 
URL url = cl.getResource("test.keystore"); 
KeyStore keystore = KeyStore.getInstance("jks"); 
char[] pwd = "nopassword".toCharArray(); 
keystore.load(url.openStream(), pwd); 

TrustManagerFactory tmf = TrustManagerFactory.getInstance(
     TrustManagerFactory.getDefaultAlgorithm()); 
tmf.init(keystore); 
TrustManager[] tm = tmf.getTrustManagers(); 

KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
     KeyManagerFactory.getDefaultAlgorithm()); 
kmfactory.init(keystore, pwd); 
KeyManager[] km = kmfactory.getKeyManagers(); 

SSLContext sslcontext = SSLContext.getInstance("TLS"); 
sslcontext.init(km, tm, null); 

LocalTestServer localServer = new LocalTestServer(sslcontext); 
localServer.registerDefaultHandlers(); 

localServer.start(); 
try { 

    DefaultHttpClient httpclient = new DefaultHttpClient(); 
    TrustStrategy trustStrategy = new TrustStrategy() { 

     public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { 
      for (X509Certificate cert: chain) { 
       System.err.println(cert); 
      } 
      return false; 
     } 

    }; 

    SSLSocketFactory sslsf = new SSLSocketFactory("TLS", null, null, keystore, null, 
      trustStrategy, new AllowAllHostnameVerifier()); 
    Scheme https = new Scheme("https", 443, sslsf); 
    httpclient.getConnectionManager().getSchemeRegistry().register(https); 

    InetSocketAddress address = localServer.getServiceAddress(); 
    HttpHost target1 = new HttpHost(address.getHostName(), address.getPort(), "https"); 
    HttpGet httpget1 = new HttpGet("/random/100"); 
    HttpResponse response1 = httpclient.execute(target1, httpget1); 
    System.err.println(response1.getStatusLine()); 
    HttpEntity entity1 = response1.getEntity(); 
    EntityUtils.consume(entity1); 
    HttpHost target2 = new HttpHost("www.verisign.com", 443, "https"); 
    HttpGet httpget2 = new HttpGet("/"); 
    HttpResponse response2 = httpclient.execute(target2, httpget2); 
    System.err.println(response2.getStatusLine()); 
    HttpEntity entity2 = response2.getEntity(); 
    EntityUtils.consume(entity2); 
} finally { 
    localServer.stop(); 
} 

Aunque la implementación de JSSE de Sun parece leer siempre el material de confianza del almacén de confianza predeterminado por algún motivo, no parece que se agregue al contexto SSL ni afecte el proceso de verificación de confianza durante el protocolo de enlace SSL.

Aquí está el resultado de la aplicación de prueba. Como puede ver, la primera solicitud tiene éxito mientras que la segunda falla ya que la conexión a www.verisign.com se rechaza por no ser de confianza.

[ 
[ 
    Version: V1 
    Subject: CN=Simple Test Http Server, OU=Jakarta HttpClient Project, O=Apache Software Foundation, L=Unknown, ST=Unknown, C=Unknown 
    Signature Algorithm: SHA1withDSA, OID = 1.2.840.10040.4.3 

    Key: Sun DSA Public Key 
    Parameters:DSA 
    p:  fd7f5381 1d751229 52df4a9c 2eece4e7 f611b752 3cef4400 c31e3f80 b6512669 
    455d4022 51fb593d 8d58fabf c5f5ba30 f6cb9b55 6cd7813b 801d346f f26660b7 
    6b9950a5 a49f9fe8 047b1022 c24fbba9 d7feb7c6 1bf83b57 e7c6a8a6 150f04fb 
    83f6d3c5 1ec30235 54135a16 9132f675 f3ae2b61 d72aeff2 2203199d d14801c7 
    q:  9760508f 15230bcc b292b982 a2eb840b f0581cf5 
    g:  f7e1a085 d69b3dde cbbcab5c 36b857b9 7994afbb fa3aea82 f9574c0b 3d078267 
    5159578e bad4594f e6710710 8180b449 167123e8 4c281613 b7cf0932 8cc8a6e1 
    3c167a8b 547c8d28 e0a3ae1e 2bb3a675 916ea37f 0bfa2135 62f1fb62 7a01243b 
    cca4f1be a8519089 a883dfe1 5ae59f06 928b665e 807b5525 64014c3b fecf492a 

    y: 
    f0cc639f 702fd3b1 03fa8fa6 676c3756 ea505448 23cd1147 fdfa2d7f 662f7c59 
    a02ddc1a fd76673e 25210344 cebbc0e7 6250fff1 a814a59f 30ff5c7e c4f186d8 
    f0fd346c 29ea270d b054c040 c74a9fc0 55a7020f eacf9f66 a0d86d04 4f4d23de 
    7f1d681f 45c4c674 5762b71b 808ded17 05b74baf 8de3c4ab 2ef662e3 053af09e 

    Validity: [From: Sat Dec 11 14:48:35 CET 2004, 
       To: Tue Dec 09 14:48:35 CET 2014] 
    Issuer: CN=Simple Test Http Server, OU=Jakarta HttpClient Project, O=Apache Software Foundation, L=Unknown, ST=Unknown, C=Unknown 
    SerialNumber: [ 41bafab3] 

] 
    Algorithm: [SHA1withDSA] 
    Signature: 
0000: 30 2D 02 15 00 85 BE 6B D0 91 EF 34 72 05 FF 1A 0-.....k...4r... 
0010: DB F6 DE BF 92 53 9B 14 27 02 14 37 8D E8 CB AC .....S..'..7.... 
0020: 4E 6C 93 F2 1F 7D 20 A1 2D 6F 80 5F 58 AE 33  Nl.... .-o._X.3 

] 
HTTP/1.1 200 OK 
[ 
[ 
    Version: V3 
    Subject: CN=www.verisign.com, OU=" Production Security Services", O="VeriSign, Inc.", STREET=487 East Middlefield Road, L=Mountain View, ST=California, OID.2.5.4.17=94043, C=US, SERIALNUMBER=2497886, OID.2.5.4.15="V1.0, Clause 5.(b)", OID.1.3.6.1.4.1.311.60.2.1.2=Delaware, OID.1.3.6.1.4.1.311.60.2.1.3=US 
    Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5 

    Key: Sun RSA public key, 2048 bits 
    modulus: 20699622354183393041832954221256409980425015218949582822286196083815087464214375375678538878841956356687753084333860738385445545061253653910861690581771234068858443439641948884498053425403458465980515883570440998475638309355278206558031134532548167239684215445939526428677429035048018486881592078320341210422026566944903775926801017506416629554190534665876551381066249522794321313235316733139718653035476771717662585319643139144923795822646805045585537550376512087897918635167815735560529881178122744633480557211052246428978388768010050150525266771462988042507883304193993556759733514505590387262811565107773578140271 
    public exponent: 65537 
    Validity: [From: Wed May 26 02:00:00 CEST 2010, 
       To: Sat May 26 01:59:59 CEST 2012] 
    Issuer: CN=VeriSign Class 3 Extended Validation SSL SGC CA, OU=Terms of use at https://www.verisign.com/rpa (c)06, OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US 
    SerialNumber: [ 53d2bef9 24a7245e 83ca01e4 6caa2477] 

Certificate Extensions: 10 
[1]: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false 
AuthorityInfoAccess [ 
    [accessMethod: 1.3.6.1.5.5.7.48.1 
    accessLocation: URIName: http://EVIntl-ocsp.verisign.com, accessMethod: 1.3.6.1.5.5.7.48.2 
    accessLocation: URIName: http://EVIntl-aia.verisign.com/EVIntl2006.cer] 
] 

... 

] 
Exception in thread "main" javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated 
    at com.sun.net.ssl.internal.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:345) 
    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128) 
    at org.apache.http.conn.ssl.SSLSocketFactory.createLayeredSocket(SSLSocketFactory.java:446) 
... 
+0

Esta es una buena prueba.Gracias por su ayuda – Cratylus

+0

Hola Oleg, estoy enfrentando un problema similar, pero solo cuando estoy enviando una solicitud POST y no a través de GET. ¿Alguna idea? – Abhishek

+0

Entonces, ¿hay algún error en este lugar que podamos rastrear para encontrar una solución? – pulkitsinghal

2

De acuerdo con la documentation es necesario especificar el almacén de claves:

Protocol authhttps = new Protocol("https", 
     new AuthSSLProtocolSocketFactory(
      new URL("file:my.keystore"), "mypassword", 
      new URL("file:my.truststore"), "mypassword"), 443); 

HttpClient client = new HttpClient(); 
client.getHostConfiguration().setHost("localhost", 443, authhttps); 
+0

No estoy usando commons.I estoy usando http://hc.apache.org/httpcomponents- client-ga /. Estoy haciendo algo similar 'Scheme scheme = new Scheme (" https ", 443, sf); \t \t client.getConnectionManager(). GetSchemeRegistry(). Register (scheme); 'y sf es' SSLSocketFactory sf = new SSLSocketFactory (sslContext, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); 'y en sslContext he configurado mi trust store manager. Puedo actualizar la publicación con un código si ayuda – Cratylus

+0

@ user384706 Me estoy confundiendo un poco. El problema es que _client_ está utilizando 2 TrustManagers (que SSLContext.init dice que no debería) o ¿es el tomcat _server_ el que se comporta de manera extraña? – extraneon

+0

No hay problema en Tomcat.Tomcat envía el certificado que he configurado.Problema está en la parte del cliente. He especificado un trustore de confianza y esperaba que solo se usara. La información de depuración muestra que se usa tanto el mío como el almacén de confianza de java predeterminado .He actualizado mi publicación para mostrar algún código en la parte del cliente – Cratylus

4

Cuando utilicé Apache HTTP Client 4.3, estaba usando los gestores de conexión agrupados o básicos para el cliente HTTP.Me di cuenta, al usar la depuración SSL de Java, que estas clases cargaban el almacén de confianza cacerts y no el que había especificado programáticamente.

PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); 
BasicHttpClientConnectionManager cm = new BasicHttpClientConnectionManager(); 
builder.setConnectionManager(cm); 

quería usar ellos, pero terminó la eliminación de ellos y crear un cliente HTTP sin ellos. Tenga en cuenta que el generador es un HttpClientBuilder.

Confirmé al ejecutar mi programa con los indicadores de depuración Java SSL, y me detuve en el depurador. Utilicé -Djavax.net.debug = ssl como un argumento de VM. Paré mi código en el depurador y cuando se construyó cualquiera de los anteriores * ClientConnectionManager, se cargará el archivo cacerts.

+2

Muchas gracias por esto. No pude entender qué problema era y luego, después de leerlo, me di cuenta de que el constructor ignora básicamente el sslContext que usted proporciona si ha pasado en un administrador de conexión. –

4

Esto es lo que funcionó para mí:

KeyStore keyStore = KeyStore.getInstance("PKCS12"); 
    FileInputStream instream = new FileInputStream(new File("client-p12-keystore.p12")); 
    try { 
     keyStore.load(instream, "password".toCharArray()); 
    } finally { 
     instream.close(); 
    } 

    // Trust own CA and all self-signed certs 
    SSLContext sslcontext = SSLContexts.custom() 
     .loadKeyMaterial(keyStore, "password".toCharArray()) 
     //.loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()) 
     .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(); 
    } 
} 

Este código es una versión modificada de http://hc.apache.org/httpcomponents-client-4.3.x/httpclient/examples/org/apache/http/examples/client/ClientCustomSSL.java

+0

¿Cómo podemos permitir las conexiones a sitios SSL sin certes de manera insegura en el código anterior? –

+0

¿Qué versión de HTTPClient usaste? –

+0

@ user4567570 Creo que fue 4.3.5. Para conectarse inseguramente, debe permitir que el Verificador de nombre de host permita todo, pero también debe especificar el uso de una estrategia que confíe en todos: 'sslContextBuilder.loadTrustMaterial (new TrustAllStrategy());' y 'TrustAllStrategy' implementa' TrustStrategy', y todo lo que hace es 'return true;' – EpicPandaForce

Cuestiones relacionadas