Pregunta

Estoy intentando utilizar la herramienta de claves (Java) para crear un certificado autofirmado, pero cuando intento utilizarlo obtengo la siguiente excepción (consulte la parte inferior para ver la excepción completa).

...<5 more exceptions above this>
Caused by: sun.security.validator.ValidatorException: No trusted certificate found
        at sun.security.validator.SimpleValidator.buildTrustedChain(SimpleValidator.java:304)
        at sun.security.validator.SimpleValidator.engineValidate(SimpleValidator.java:107)
        at sun.security.validator.Validator.validate(Validator.java:203)
        at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:172)
        at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(SSLContextImpl.java:320)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:841)
        ... 22 more

Sé que puedo evitar esto con este código:

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;

HostnameVerifier hv = new HostnameVerifier() {
    public boolean verify(String urlHostName, SSLSession session) {
        System.out.println("Warning: URL Host: " + urlHostName + " vs. " + session.getPeerHost());
        return true;
    }
};

HttpsURLConnection.setDefaultHostnameVerifier(hv);

(fuente)

Pero no me interesan estas soluciones porque creo que crean un agujero de seguridad.(Por favor, corríjame si estoy equivocado).

¿Alguien puede indicarme la dirección correcta?Estoy probando localmente en este momento, por lo que es bastante fácil cambiar las cosas.Tengo acceso al código del servidor, al código del cliente y al archivo .keystore.

Actualizar

Estaba intentando usar un archivo .keystore tanto para el cliente como para el servidor, pero con la esperanza de simplificar mis problemas, creé server.keystore (ver más abajo) y client.truststore (ver más abajo).Estoy razonablemente seguro de que los certificados son correctos, pero si alguien pudiera verificarlo, se lo agradecería.

servidor.almacén de claves

hostname[username:/this/is/a/path][711]% keytool -list -keystore server.keystore -v
Enter keystore password:

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

Alias name: hostname
Creation date: Feb 4, 2010
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=hostname, OU=hostname, O=hostname, L=hostname, ST=hostname, C=hostname
Issuer: CN=hostname, OU=hostname, O=hostname, L=hostname, ST=hostname, C=hostname
Serial number: 4b6b0ea7
Valid from: Thu Feb 04 13:15:03 EST 2010 until: Wed May 05 14:15:03 EDT 2010
Certificate fingerprints:
         MD5:  81:C0:3F:EC:AD:5B:7B:C4:DA:08:CC:D7:11:1F:1D:38
         SHA1: F1:78:AD:C8:D0:3A:4C:0C:9A:4F:89:C0:2A:2F:E2:E6:D5:13:96:40
         Signature algorithm name: SHA1withDSA
         Version: 3


*******************************************
*******************************************

cliente.truststore

hostname[username:/this/is/a/path][713]% keytool -list -keystore client.truststore -v
Enter keystore password:

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

Alias name: mykey
Creation date: Feb 4, 2010
Entry type: trustedCertEntry

Owner: CN=hostname, OU=hostname, O=hostname, L=hostname, ST=hostname, C=hostname
Issuer: CN=hostname, OU=hostname, O=hostname, L=hostname, ST=hostname, C=hostname
Serial number: 4b6b0ea7
Valid from: Thu Feb 04 13:15:03 EST 2010 until: Wed May 05 14:15:03 EDT 2010
Certificate fingerprints:
         MD5:  81:C0:3F:EC:AD:5B:7B:C4:DA:08:CC:D7:11:1F:1D:38
         SHA1: F1:78:AD:C8:D0:3A:4C:0C:9A:4F:89:C0:2A:2F:E2:E6:D5:13:96:40
         Signature algorithm name: SHA1withDSA
         Version: 3


*******************************************
*******************************************

Actualizar

Pensé que podría resultar útil incluir la excepción completa:

javax.xml.soap.SOAPException: java.io.IOException: Could not transmit message
        at org.jboss.ws.core.soap.SOAPConnectionImpl.callInternal(SOAPConnectionImpl.java:115)
        at org.jboss.ws.core.soap.SOAPConnectionImpl.call(SOAPConnectionImpl.java:66)
        at com.alcatel.tpapps.common.utils.SOAPClient.execute(SOAPClient.java:193)
        at com.alcatel.tpapps.common.utils.SOAPClient.main(SOAPClient.java:280)
Caused by: java.io.IOException: Could not transmit message
        at org.jboss.ws.core.client.RemotingConnectionImpl.invoke(RemotingConnectionImpl.java:192)
        at org.jboss.ws.core.client.SOAPRemotingConnection.invoke(SOAPRemotingConnection.java:77)
        at org.jboss.ws.core.soap.SOAPConnectionImpl.callInternal(SOAPConnectionImpl.java:106)
        ... 3 more
Caused by: org.jboss.remoting.CannotConnectException: Can not connect http client invoker. sun.security.validator.ValidatorException: No trusted certificate found.
        at org.jboss.remoting.transport.http.HTTPClientInvoker.useHttpURLConnection(HTTPClientInvoker.java:368)
        at org.jboss.remoting.transport.http.HTTPClientInvoker.transport(HTTPClientInvoker.java:148)
        at org.jboss.remoting.MicroRemoteClientInvoker.invoke(MicroRemoteClientInvoker.java:141)
        at org.jboss.remoting.Client.invoke(Client.java:1858)
        at org.jboss.remoting.Client.invoke(Client.java:718)
        at org.jboss.ws.core.client.RemotingConnectionImpl.invoke(RemotingConnectionImpl.java:171)
        ... 5 more
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found
        at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:150)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1584)
        at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:174)
        at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:168)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:848)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:106)
        at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:495)
        at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:433)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:877)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1089)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1116)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1100)
        at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:402)
        at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:170)
        at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:857)
        at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:230)
        at org.jboss.remoting.transport.http.HTTPClientInvoker.useHttpURLConnection(HTTPClientInvoker.java:288)
        ... 10 more
Caused by: sun.security.validator.ValidatorException: No trusted certificate found
        at sun.security.validator.SimpleValidator.buildTrustedChain(SimpleValidator.java:304)
        at sun.security.validator.SimpleValidator.engineValidate(SimpleValidator.java:107)
        at sun.security.validator.Validator.validate(Validator.java:203)
        at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:172)
        at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(SSLContextImpl.java:320)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:841)
        ... 22 more
¿Fue útil?

Solución

Debería "establecer confianza" entre su servidor y su cliente (supongo que solo necesita hacer autenticación del lado del servidor). Esto se debe a que usas certificaciones autofirmadas. Eso implica importar el certificado de su servidor en la tienda de confianza del cliente:

En el lado del servidor:

keytool -keystore <keystore file> -alias <alias> -export -file <certfilename>.cert

Copie el archivo .cert al lado del cliente y luego:

keytool -keystore <truststore file> -alias <alias> -import -file <certfilename>.cert

Otros consejos

No puede compartir el almacén de claves entre el cliente y el servidor porque el almacén de claves contiene la clave privada.Al autenticarse, el cliente omite los certificados con claves privadas.Como se dijo anteriormente, es necesario implementar un almacén de confianza en el lado del cliente.

Los certificados en un almacén de claves no se comportan de la misma manera, dependiendo de cómo los generó o importó.

El tipo de entrada de un certificado importado (se ve cuando se enumera detalladamente todo el almacén de claves con -list -v) es "trustedCertEntry".El tipo de entrada de un certificado generado es "PrivateKeyEntry".Cuando exporta un certificado, solo exporta su clave pública y una referencia opcional a su emisor.

Parece que necesita exportar el certificado autofirmado en su almacén de claves como un certificado confiable en su almacén de confianza (los nombres tienen sentido aquí).

Yo no haría eso, porque las implementaciones SSL/TLS probablemente no lo admitan.Desde una perspectiva del mundo real, es como implementar la clave privada secreta de Verisign en algún servidor web oscuro para firmar páginas casuales, mientras que el único propósito de esta clave privada es permanecer en un lugar seguro y firmar otros certificados.Los implementadores de SSL/TLS probablemente no contaminarán su código con tal caso de uso y, de todos modos, la extensión del certificado "KeyUsage" puede restringir el uso del certificado a la firma, evitando el cifrado.

Por eso sugiero reconstruir una cadena de certificados para su prueba.

La documentación de keytool contiene una parte interesante sobre la creación de una cadena (-gencert comando) pero es un ejemplo muy esquelético que no cubre la relación almacén de claves-almacén de confianza.Lo he mejorado para simular una autoridad de certificación de terceros.

una tienda temporal their-keystore.jks representa una autoridad emisora ​​de certificados.Lo alimento con una cadena de certificados de ca2 -> ca1 -> ca con ca siendo considerado como un certificado raíz.La cadena aparece con cada certificado no raíz (es decir, ca1 y ca2) haciendo referencia a su emisor como Certificate[2].Tenga en cuenta que cada certificado es "PrivateKeyEntry".

Luego le doy de comer my-keystore.jks con esos certificados en orden: ca, ca1, ca2.yo importo ca con el -trustcacerts opción que significa que se convierte en un certificado raíz.En my-keystore.jks Cada certificado importado ahora es "trustedCertEntry", lo que significa que solo existe la clave pública.La relación de emisión solo aparece en el campo "Emisor", pero está bien porque la relación de confianza era más importante en el momento de la importación.

En este punto my-keystore.jks Simula un entorno que contiene algunos certificados confiables, como un JRE nuevo.El their-keystore.jks simula a los propietarios de esos certificados, quienes tienen el poder de firmar solicitudes de certificados.

Yo también :Creo un certificado autofirmado e1 en my-keystore.jks, haz que lo firme ca2 (a través de their-keystore.jks) e importar el resultado firmado nuevamente a my-keystore.jks. e1 sigue siendo una "PrivateKeyEntry" (porque su clave privada permanece en my-keystore.jks) pero ahora he construido la siguiente cadena: e1 -> ca2 -> ca1.Parece que ca1 -> ca está implícito con ca ser una autoridad certificadora.

Para construir el almacén de confianza solo importo certificados ca, ca1 y ca2 de la misma manera que lo hice para my-keystore.jks.Tenga en cuenta que no importo e1, ya que espero que el cliente SSL/TLS lo valide con ca2.

Creo que esto se acerca bastante a cómo funcionan las cosas en el mundo real.Lo bueno aquí es que tiene control total sobre los certificados y no depende de los cacerts de JRE.

Aquí está el código poniendo en práctica lo que digo.Parece funcionar con Jetty (cliente y servidor) siempre que desactive la lista de revocación de certificados (un tema que se deja para otro día).

#!/bin/bash

rm  their-keystore.jks 2> /dev/null
rm  my-keystore.jks    2> /dev/null
rm  my-truststore.jks  2> /dev/null

echo "===================================================="
echo "Creating fake third-party chain ca2 -> ca1 -> ca ..."
echo "===================================================="

keytool -genkeypair -alias ca  -dname cn=ca                           \
  -validity 10000 -keyalg RSA -keysize 2048                           \
  -ext BasicConstraints:critical=ca:true,pathlen:10000                \
  -keystore their-keystore.jks -keypass Keypass -storepass Storepass

keytool -genkeypair -alias ca1 -dname cn=ca1                          \
  -validity 10000 -keyalg RSA -keysize 2048                           \
  -keystore their-keystore.jks -keypass Keypass -storepass Storepass

keytool -genkeypair -alias ca2 -dname cn=ca2                          \
  -validity 10000 -keyalg RSA -keysize 2048                           \
  -keystore their-keystore.jks -keypass Keypass -storepass Storepass


  keytool -certreq -alias ca1                                            \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass   \
| keytool -gencert -alias ca                                             \
    -ext KeyUsage:critical=keyCertSign                                   \
    -ext SubjectAlternativeName=dns:ca1                                  \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass   \
| keytool -importcert -alias ca1                                         \
    -keystore   their-keystore.jks -keypass Keypass -storepass Storepass

#echo "Debug exit" ; exit 0

  keytool -certreq -alias ca2                                           \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -gencert -alias ca1                                           \
    -ext KeyUsage:critical=keyCertSign                                  \
    -ext SubjectAlternativeName=dns:ca2                                 \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -importcert -alias ca2                                        \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass

keytool -list -v -storepass Storepass -keystore their-keystore.jks


echo  "===================================================================="
echo  "Fake third-party chain generated. Now generating my-keystore.jks ..."
echo  "===================================================================="
read -p "Press a key to continue."

# Import authority's certificate chain

  keytool -exportcert -alias ca                                         \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -importcert -trustcacerts -noprompt -alias ca                 \
    -keystore  my-keystore.jks -keypass Keypass -storepass Storepass

  keytool -exportcert -alias ca1                                        \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -importcert -noprompt -alias ca1                              \
    -keystore  my-keystore.jks -keypass Keypass -storepass Storepass

  keytool -exportcert -alias ca2                                        \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -importcert -noprompt -alias ca2                              \
    -keystore  my-keystore.jks -keypass Keypass -storepass Storepass

# Create our own certificate, the authority signs it.

keytool -genkeypair -alias e1  -dname cn=e1                        \
  -validity 10000 -keyalg RSA -keysize 2048                        \
  -keystore my-keystore.jks -keypass Keypass -storepass Storepass

  keytool -certreq -alias e1                                            \
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass     \
| keytool -gencert -alias ca2                                           \
    -ext SubjectAlternativeName=dns:localhost                           \
    -ext KeyUsage:critical=keyEncipherment,digitalSignature             \
    -ext ExtendedKeyUsage=serverAuth,clientAuth                         \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -importcert -alias e1                                         \
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass

keytool -list -v  -storepass Storepass -keystore  my-keystore.jks

echo "================================================="
echo "Keystore generated. Now generating truststore ..."
echo "================================================="
read -p "Press a key to continue."

  keytool -exportcert -alias ca                                        \
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass    \
| keytool -importcert -trustcacerts -noprompt -alias ca                \
    -keystore my-truststore.jks -keypass Keypass -storepass Storepass

  keytool -exportcert -alias ca1                                       \
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass    \
| keytool -importcert -noprompt -alias ca1                             \
    -keystore my-truststore.jks -keypass Keypass -storepass Storepass

  keytool -exportcert -alias ca2                                       \
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass    \
| keytool -importcert -noprompt -alias ca2                             \
    -keystore my-truststore.jks -keypass Keypass -storepass Storepass

keytool -list -v  -storepass Storepass -keystore  my-truststore.jks

rm  their-keystore.jks 2> /dev/null

No debes hacer eso. Una tienda de claves es estrictamente privada. Si lo filtra a alguien, ha comprometido fatalmente la seguridad. No tiene sentido hacer este tipo de cosas solo para que funcione, porque no es Trabajando: es solo una violación de seguridad. Debe hacerlo bien: exportar desde el almacén de claves del servidor en la tienda de confianza del cliente y desde el almacén de claves del cliente en la tienda de claves del servidor.

No lo entiendo. ¿Está utilizando el almacén de claves del servidor con el cliente? ¿Cuál es tu caso de uso exactamente? ¿Estás tratando de configurar la autenticación mutua?

En caso afirmativo, estás en el camino equivocado aquí. Necesitará una tienda de claves del cliente (para el certificado autofirmado del cliente y la clave privada) y una tienda de confianza del cliente (para el certificado autofirmado "independiente" del servidor, es decir, sin su clave privada). Ambos son diferentes de la tienda de claves del servidor.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top