2012-07-31 14 views
7

En una aplicación Java 1.7 que se ejecuta en Windows 7, intento hacer un SSL bidireccional con un servidor (un token de tarjeta inteligente proporciona certificados de mi cliente a través de openSC). El cliente verifica que el certificado del servidor está bien, pero el cliente no responde a la solicitud de certificado del servidor. Creo que es porque el cliente no puede hacer una cadena desde mi certificado a una de las solicitadas por el servidor (incluso si existe una cadena).Mi cliente SSL (Java) no está enviando un certificado de vuelta al servidor en el protocolo de enlace SSL bidireccional

Aquí está la depuración de SSL de solicitud de certificado del servidor y el cliente respuesta vacía:

*** CertificateRequest 
Cert Types: RSA, DSS, ECDSA 
Cert Authorities: 
<CN=c4isuite-SDNI-DC02-CA, DC=c4isuite, DC=local> 
<CN=DoD Root CA 2, OU=PKI, OU=DoD, O=U.S. Government, C=US> 
    ... 
*** ServerHelloDone 
*** Certificate chain 
*** 

Mi certificado de cliente es el siguiente:

found key for : Certificate for PIV Authentication 
chain [0] = [ 
[ 
    Version: V3 
    Subject: CN=<...>, OU=CONTRACTOR, OU=PKI, OU=DoD, O=U.S. Government, C=US 
    Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5 

    Key: Sun RSA public key, 2048 bits 

    Issuer: CN=DOD CA-30, OU=PKI, OU=DoD, O=U.S. Government, C=US 
    SerialNumber: [ 05bf13] 

Via-herramienta clave, también instalado en el truststore (archivo java cacerts), cuál debería ser el vínculo entre el emisor de mi cert, DOD CA-30, y lo que el servidor está solicitando, DoD Root CA 2.

De depuración de SSL:

adding as trusted cert: 
    Subject: CN=DOD CA-30, OU=PKI, OU=DoD, O=U.S. Government, C=US 
    Issuer: CN=DoD Root CA 2, OU=PKI, OU=DoD, O=U.S. Government, C=US 
    Algorithm: RSA; Serial number: 0x1b5 
    Valid from Thu Sep 08 10:59:24 CDT 2011 until Fri Sep 08 10:59:24 CDT 2017 

adding as trusted cert: 
    Subject: CN=DoD Root CA 2, OU=PKI, OU=DoD, O=U.S. Government, C=US 
    Issuer: CN=DoD Root CA 2, OU=PKI, OU=DoD, O=U.S. Government, C=US 
    Algorithm: RSA; Serial number: 0x5 
    Valid from Mon Dec 13 09:00:10 CST 2004 until Wed Dec 05 09:00:10 CST 2029 

Entonces la pregunta es, ¿por qué el cliente no puede hacer la cadena de certificados para la respuesta? Aquí está el código correspondiente:

// Create the keyStore from the SmartCard certs 
    Provider provider = new sun.security.pkcs11.SunPKCS11(configName); 

    Security.addProvider(provider); 
    keyStore = KeyStore.getInstance("PKCS11", "SunPKCS11-SCR3310test"); 
    char[] pin = PIN.toCharArray(); 
    keyStore.load(null, pin); 

     // Init the trustmanager 
     TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
     tmf.init(trustStore); 

     // Create the client key manager 
     LOG.info("Installing keystore with pin"); 
     KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); 
     keyManagerFactory.init(clientKeyStore, clientKeyPassword.toCharArray());   

     sslContext.init(keyManagerFactory.getKeyManagers(), tmf.getTrustManagers(), null); 

     // Init SSL context 
     SSLSocketFactory socketFactory = sslContext.getSocketFactory(); 


     URL url = new URL(urlString); 
     URLConnection connection = url.openConnection(); 
     if (connection instanceof HttpsURLConnection) { 
      LOG.info("Connection is HTTPS"); 
      ((HttpsURLConnection) connection).setSSLSocketFactory(socketFactory); 
     } 

     // Send the request. 
     connection.connect(); 

     InputStreamReader in = new InputStreamReader((InputStream) connection.getContent()); 
     ... 

Y el error que consigo de vuelta es que el servidor devuelve un 403. Lo más probable porque el cliente no envió un certificado de cliente.

Respuesta

3

Aunque parece que solo ha copiado parte de la lista CA enviada por el servidor a esta pregunta, supongo que CN=DOD CA-30, OU=PKI, OU=DoD, O=U.S. Government, C=US no se encuentra en esta lista.

Lo que parece faltar en la cadena es este certificado (que se menciona más adelante):

Subject: CN=DOD CA-30, OU=PKI, OU=DoD, O=U.S. Government, C=US 
    Issuer: CN=DoD Root CA 2, OU=PKI, OU=DoD, O=U.S. Government, C=US 
    Algorithm: RSA; Serial number: 0x1b5 
    Valid from Thu Sep 08 10:59:24 CDT 2011 until Fri Sep 08 10:59:24 CDT 2017 

Importación de certificados en almacén de confianza de su cliente no tiene absolutamente ningún efecto en el certificado del cliente envía. El certificado de cliente (y su clave privada) debe configurarse en el cliente keystore. Además, si desea enviar una cadena de certificado de cliente (que se requerirá aquí, si el servidor no ofrece este certificado CA intermedio en su lista), deberá asociar la cadena completa a esa entrada de certificado. No es suficiente poner los otros certificados en el almacén de claves.

Para solucionar esto, debe configurar su entrada de almacén de claves con la cadena de certificado de cliente. Esto se puede hacer como se describe en this answer. Sin embargo, es posible que el hecho de que este es un token de hardware al que se accede a través de PKCS # 11 pueda complicarlo un poco más (quizás haya otra herramienta de administración de certificados con la tarjeta, posiblemente independiente de Java).

+0

Gracias por indicarme directamente al cliente que no utiliza el depósito de confianza para encontrar la cadena de certificados completa. Como el certificado proviene de una SmartCard, es difícil o imposible modificar el certificado. Me las arreglé para evitarlo de otra manera, que detallo a continuación como la respuesta, provista para que cualquier otra persona en la misma situación pueda tener una respuesta. – PaulP

2

Como sé qué certificado necesito usar para la autenticación en el servidor, puedo forzar al cliente a enviar ese certificado específico extendiendo X509ExtendedKeyManager y Anulando el método chooseClientAlias ​​() para devolver siempre el alias de ese certificado. Código:

public class MyX509KeyManager extends X509ExtendedKeyManager 
    { 
    X509KeyManager defaultKeyManager; 

    public MyX509KeyManager(X509KeyManager inKeyManager) { 
     defaultKeyManager = inKeyManager; 
    } 

    public String chooseEngineClientAlias(String[] keyType, 
      Principal[] issuers, SSLEngine engine) { 
     return "<Alias of my cert>"; 
    } 

    @Override 
    public String chooseClientAlias(String[] strings, Principal[] prncpls, Socket socket) { 
     return "<Alias of my cert>"; 
    } 

    @Override 
    public String[] getClientAliases(String string, Principal[] prncpls) { 
     return defaultKeyManager.getClientAliases(string, prncpls); 
    } 

    @Override 
    public String[] getServerAliases(String string, Principal[] prncpls) { 
     return defaultKeyManager.getServerAliases(string, prncpls); 
    } 

    ... 

Así como se puede ver, disfrutar de un defaultKeyManager el que delego en para nada más que lo que desea anular.Luego, para usar esto en su sslContext, haga lo siguiente:

// clientKeyStore is initialized elsewhere from the SmartCard 
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); 
keyManagerFactory.init(clientKeyStore, clientKeyPassword.toCharArray()); 

MyX509KeyManager customKeyManager = new MyX509KeyManager((X509KeyManager) keyManagerFactory.getKeyManagers()[0]); 
sslContext.init(new KeyManager[] {customKeyManager}, tmf.getTrustManagers(), null); 
+1

Tiene sentido hacer que el cliente envíe este certificado, pero es casi seguro que habrá un problema si al servidor le falta el certificado intermedio para construir la cadena con el fin de validarlo. (Esto solo puede funcionar si el servidor conoce el certificado intermedio pero no envía su nombre en el mensaje de solicitud de certificado, lo cual es posible). – Bruno

+0

Lo que también puede hacer es anular 'getCertificateChain' para agregar este certificado al cadena manualmente – Bruno

+0

Correcto, esto supone que el servidor tiene la cadena de certificación completa disponible. – PaulP

Cuestiones relacionadas