¿Cómo arreglar el error “java.security.cert.CertificateException: No hay nombres alternativos del sujeto presente”?

Tengo un cliente de servicio web Java, que consume un servicio web a través de HTTPS.

import javax.xml.ws.Service; @WebServiceClient(name = "ISomeService", targetNamespace = "http://tempuri.org/", wsdlLocation = "...") public class ISomeService extends Service { public ISomeService() { super(__getWsdlLocation(), ISOMESERVICE_QNAME); } 

Cuando me conecto a la URL del servicio ( https://AAA.BBB.CCC.DDD:9443/ISomeService ), obtengo la excepción java.security.cert.CertificateException: No subject alternative names present .

Para solucionarlo, primero ejecuté openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt y obtuvo el siguiente contenido en el archivo certs.txt :

 CONNECTED(00000003) --- Certificate chain 0 s:/CN=someSubdomain.someorganisation.com i:/CN=someSubdomain.someorganisation.com -----BEGIN CERTIFICATE----- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -----END CERTIFICATE----- --- Server certificate subject=/CN=someSubdomain.someorganisation.com issuer=/CN=someSubdomain.someorganisation.com --- No client certificate CA names sent --- SSL handshake has read 489 bytes and written 236 bytes --- New, TLSv1/SSLv3, Cipher is RC4-MD5 Server public key is 512 bit Compression: NONE Expansion: NONE SSL-Session: Protocol : TLSv1 Cipher : RC4-MD5 Session-ID: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Session-ID-ctx: Master-Key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Key-Arg : None Start Time: 1382521838 Timeout : 300 (sec) Verify return code: 21 (unable to verify the first certificate) --- 

AFAIK, ahora necesito

  1. extraer la parte de certs.txt entre -----BEGIN CERTIFICATE----- y -----END CERTIFICATE----- ,
  2. AAA.BBB.CCC.DDD para que el nombre del certificado sea igual a AAA.BBB.CCC.DDD y
  3. luego importe el resultado usando keytool -importcert -file fileWithModifiedCertificate (donde fileWithModifiedCertificate es el resultado de las operaciones 1 y 2).

¿Es esto correcto?

Si es así, ¿cómo puedo hacer que el certificado del paso 1 funcione con adddress basado en IP ( AAA.BBB.CCC.DDD )?

Actualización 1 (23.10.2013 15:37 MSK): en una respuesta a una pregunta similar , leí lo siguiente:

Si no tiene el control de ese servidor, use su nombre de host (siempre que haya al menos un CN que coincida con ese nombre de host en el certificado existente).

¿Qué significa exactamente “usar”?

Solucioné el problema desactivando las comprobaciones HTTPS usando el enfoque presentado aquí :

Puse el siguiente código en la clase ISomeService :

 static { disableSslVerification(); } private static void disableSslVerification() { try { // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; // Install the all-trusting trust manager SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); // Create all-trusting host name verifier HostnameVerifier allHostsValid = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; // Install the all-trusting host verifier HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } } 

Dado que estoy usando el https://AAA.BBB.CCC.DDD:9443/ISomeService para fines de prueba, es una solución lo suficientemente buena.

Tengo el mismo problema y lo resuelvo con este código. Pongo este código antes de la primera llamada a mis servicios web.

 javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier( new javax.net.ssl.HostnameVerifier(){ public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) { return hostname.equals("localhost"); } }); 

Es simple y funciona bien.

Aquí está la fuente original.

La verificación de la identidad del certificado se realiza según lo que el cliente solicite.

Cuando su cliente utiliza https://xxx.xxx.xxx.xxx/something (donde xxx.xxx.xxx.xxx es una dirección IP), la identidad del certificado se compara con esta dirección IP (en teoría, solo se usa una SAN IP) extensión).

Si su certificado no tiene SAN IP, pero SAN DNS (o si no hay SAN DNS, un Nombre común en el DN Asunto), puede hacer que esto funcione haciendo que su cliente use una URL con ese nombre de host en su lugar (o un nombre de host) para lo cual el certificado sería válido, si hay múltiples valores posibles). Por ejemplo, si cert tiene un nombre para www.example.com , use https://www.example.com/something .

Por supuesto, necesitará ese nombre de host para resolver esa dirección IP.

Además, si hay SAN DNS, se ignorará la CN en el DN del asunto, por lo tanto, utilice un nombre que coincida con una de las SAN DNS en este caso.

Para importar el certificado:

  1. Extraiga el certificado del servidor, por ejemplo, openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt Esto extraerá certs en formato PEM.
  2. Convierta el cert en formato DER ya que esto es lo que keytool espera, por ejemplo, openssl x509 -in certs.txt -out certs.der -outform DER
  3. Ahora quiere importar este certificado en el archivo ‘cacert’ predeterminado del sistema. Ubique el archivo ‘cacerts’ predeterminado del sistema para su instalación de Java. Eche un vistazo a ¿Cómo obtener la ubicación de los cacerts de la instalación Java predeterminada?
  4. Importe los certs en ese archivo cacerts: sudo keytool -importcert -file certs.der -keystore La contraseña predeterminada de cacerts es ‘changeit’.

Si el certificado se emite para un FQDN y usted está tratando de conectarse por la dirección IP en su código Java, probablemente esto debería ser corregido en su código en lugar de interferir con el certificado en sí. Cambie su código para conectarse por FQDN. Si el FQDN no se puede resolver en su máquina de desarrollo, simplemente agréguela a su archivo de hosts o configure su máquina con el servidor DNS que puede resolver este FQDN.

mi problema con la obtención de este error se resolvió utilizando la URL completa “qatest.ourCompany.com/webService” en lugar de solo “qatest / webService”. La razón fue que nuestro certificado de seguridad tenía un comodín, es decir, “* .ourCompany.com”. Una vez que ingresé la dirección completa, la excepción desapareció. Espero que esto ayude.

Es posible que no desee deshabilitar toda la verificación de SSL y, por lo tanto, puede deshabilitar la verificación de HostName por medio de esto, que es un poco menos aterrador que la alternativa:

 HttpsURLConnection.setDefaultHostnameVerifier( SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); 

[EDITAR]

Como mencionó con conapart3 SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER ahora está en desuso, por lo que puede ser eliminado en una versión posterior, por lo que puede que en el futuro se vea forzado a rodar el suyo propio, aunque yo diría que me alejaría de cualquier solución donde toda verificación sea apagado.

He resuelto el problema de la siguiente manera.

1. Creando una clase. La clase tiene algunas implementaciones vacías

 class MyTrustManager implements X509TrustManager { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public void checkServerTrusted(X509Certificate[] certs, String authType) { } @Override public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException { // TODO Auto-generated method stub } @Override public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException { // TODO Auto-generated method stub } 

2. Creando un método

 private static void disableSSL() { try { TrustManager[] trustAllCerts = new TrustManager[] { new MyTrustManager() }; // Install the all-trusting trust manager SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HostnameVerifier allHostsValid = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } catch (Exception e) { e.printStackTrace(); } } 

  1. Llame al método disableSSL () donde se lanza la excepción. Funcionó bien

Agregue su dirección IP en el archivo hosts. Que se encuentra en la carpeta de C: \ Windows \ System32 \ drivers \ etc. También agregue IP y nombre de dominio de la dirección IP. ejemplo: aaa.bbb.ccc.ddd abc@def.com