¿Cómo resolver el error de javax.net.ssl.SSLHandshakeException?

101

Me conecté con VPN para configurar la API de inventario para obtener la lista de productos y funciona bien. Una vez que obtengo el resultado del servicio web y me vinculo a UI. Y también integré PayPal con mi aplicación para realizar el pago rápido cuando realizo una llamada de pago. Me enfrento a este error. Utilizo servlet para el proceso de back-end. ¿Alguien puede decir cómo solucionar este problema?

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: 
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
selladurai
fuente
1
lo que me gustaría saber, qué objetivo exacto era en ese caso ... Obtienes una excepción, pero no obtienes información sobre el objetivo, que podría ser diferente de lo que esperas ... Tengo tal caso, estoy seguro de que mi enlace tiene certificado y sigo recibiendo esta excepción
ante.sabo

Respuestas:

151

Primero, debe obtener el certificado público del servidor al que intenta conectarse. Eso se puede hacer de varias formas, como contactando al administrador del servidor y solicitándolo, usando OpenSSL para descargarlo o, dado que parece ser un servidor HTTP, conectándose a él con cualquier navegador, viendo la información de seguridad de la página. y guardando una copia del certificado. (Google debería poder decirle exactamente qué hacer con su navegador específico).

Ahora que tiene el certificado guardado en un archivo, debe agregarlo al almacén de confianza de su JVM. En $JAVA_HOME/jre/lib/security/para JRE o $JAVA_HOME/lib/securitypara JDK, hay un archivo llamado cacerts, que viene con Java y contiene los certificados públicos de las Autoridades de Certificación conocidas. Para importar el nuevo certificado, ejecute keytool como un usuario que tiene permiso para escribir en cacerts:

keytool -import -file <the cert file> -alias <some meaningful name> -keystore <path to cacerts file>

Lo más probable es que le pida una contraseña. La contraseña predeterminada que se envía con Java es changeit. Casi nadie lo cambia. Después de completar estos pasos relativamente simples, se comunicará de manera segura y con la seguridad de que está hablando con el servidor correcto y solo con el servidor correcto (siempre que no pierdan su clave privada).

Ryan Stewart
fuente
12
Necesitaré un poco más de detalle que "no funciona". Intente actualizar su pregunta con lo que ha intentado y algún resultado de error. Desafortunadamente, ya pasó mi hora de dormir, así que tal vez alguien más pueda responder a sus preguntas. Esta también es una situación muy común. Puede encontrar mucha información en línea, incluidos los documentos de keytool .
Ryan Stewart
Básicamente necesito incluir el certificado de PayPal. Hice tus pasos y certifiqué también en la ubicación apropiada. luego ejecuto la aplicación y dice el mismo error?
selladurai
En realidad, funciona bien, sin problemas con el navegador y conecté VPN para obtener la lista de inventario en ese momento hago el pago con PayPal, dice un error de SSL pero uso datos fuera de línea o datos codificados en caliente significa que funciona.
selladurai
4
@selladurai: No debería necesitar importar un certificado para PayPal. A menos que su archivo cacerts haya sido dañado o modificado, debe contener todos los certificados raíz de confianza, y el certificado de PayPal debe poder rastrearse hasta uno de esos. Puede intentar obtener una copia de cacerts que sepa que es buena y probar esa en su lugar. Si el problema persiste, es posible que no se esté conectando a PayPal.
Ryan Stewart
@RyanStewart, ¿necesito importarlo a glassfish de alguna manera? Porque agregué el archivo .cer a mi cacert en mi directorio de inicio de Java, pero Glassfish no parece usarlo, así que sigo recibiendo el error.
Ced
15

Ahora resolví este problema de esta manera,

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.OutputStream; 

// Create a trust manager that does not validate certificate chains like the default 

TrustManager[] trustAllCerts = new TrustManager[]{
        new X509TrustManager() {

            public java.security.cert.X509Certificate[] getAcceptedIssuers()
            {
                return null;
            }
            public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
            {
                //No need to implement.
            }
            public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
            {
                //No need to implement.
            }
        }
};

// Install the all-trusting trust manager
try 
{
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} 
catch (Exception e) 
{
    System.out.println(e);
}

Por supuesto, esta solución solo debe usarse en escenarios, donde no es posible instalar los certificados requeridos usando, keytoolpor ejemplo, pruebas locales con certifcados temporales.

selladurai
fuente
52
Vaya, dudaría en llamar a eso una "solución". Básicamente, acaba de desactivar la seguridad. Sí, los datos seguirán encriptados, pero no tiene ninguna garantía de que está hablando con quien espera hablar. La solución correcta es obtener la clave pública de su servidor de destino e importarla al almacén de confianza de la JVM que realiza la conexión.
Ryan Stewart
1
vea mi respuesta para una solución apropiada
Ryan Stewart
¿Ejemplo de que? Mi respuesta debe tener todos los pasos que necesita.
Ryan Stewart
3
El código que se muestra aquí parece útil si está escribiendo una prueba muy simple contra un servidor de desarrollo, sin embargo, no confiaría en él para la producción.
Jason D
12

Siempre que intentemos conectarnos a la URL,

Si el servidor en el otro sitio se ejecuta en el protocolo https y exige que nos comuniquemos a través de la información proporcionada en el certificado, tenemos la siguiente opción:

1) solicite el certificado (descargue el certificado), importe este certificado en trustore. Los usos predeterminados de trustore java se pueden encontrar en \ Java \ jdk1.6.0_29 \ jre \ lib \ security \ cacerts, entonces si volvemos a intentar conectarnos a la URL, la conexión sería aceptada.

2) En casos comerciales normales, es posible que nos conectemos a URL internas en organizaciones y sepamos que son correctas. En tales casos, confía en que es la URL correcta. En los casos anteriores, se puede utilizar un código que no obligará a almacenar el certificado para conectarse a una URL en particular.

para el punto no 2 tenemos que seguir los siguientes pasos:

1) escriba a continuación el método que establece HostnameVerifier para HttpsURLConnection que devuelve verdadero para todos los casos, lo que significa que confiamos en el trustStore.

  // trusting all certificate 
 public void doTrustToCertificates() throws Exception {
        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        return;
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        return;
                    }
                }
        };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HostnameVerifier hv = new HostnameVerifier() {
            public boolean verify(String urlHostName, SSLSession session) {
                if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) {
                    System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
                }
                return true;
            }
        };
        HttpsURLConnection.setDefaultHostnameVerifier(hv);
    }

2) escriba el método siguiente, que llama a doTrustToCertificates antes de intentar conectarse a la URL

    // connecting to URL
    public void connectToUrl(){
     doTrustToCertificates();//  
     URL url = new URL("https://www.example.com");
     HttpURLConnection conn = (HttpURLConnection)url.openConnection(); 
     System.out.println("ResponseCode ="+conn.getResponseCode());
   }

Esta llamada devolverá el código de respuesta = 200 significa que la conexión es exitosa.

Para obtener más detalles y un ejemplo de muestra, puede consultar la URL .

Shirishkumar Bari
fuente
1
La tienda Google Play lo rechazará. Verán que está ignorando X509Certificate durante el escaneo de APK.
Oliver Dixon
0

Creo que estás intentando conectarte a algo usando SSL, pero ese algo está proporcionando un certificado que no está verificado por las autoridades de certificación raíz como verisign. En esencia, por defecto, las conexiones seguras solo se pueden establecer si la persona que intenta conectarse sabe las claves de las contrapartes o algún otro proveedor como Verisign pueden intervenir y decir que la clave pública que se proporciona es correcta.

TODOS los sistemas operativos confían en un puñado de autoridades de certificación y los emisores de certificados más pequeños deben ser certificados por uno de los grandes certificadores que forman una cadena de certificadores si entiendes lo que quiero decir ...

De todos modos, volviendo al punto ... Tuve un problema similar al programar un applet de Java y un servidor de Java (Con suerte, algún día escribiré una publicación de blog completa sobre cómo conseguí que funcionara toda la seguridad :))

En esencia, lo que tenía que hacer era extraer las claves públicas del servidor y almacenarlas en un almacén de claves dentro de mi applet y cuando me conecté al servidor, usé este almacén de claves para crear una fábrica de confianza y esa fábrica de confianza para crear el ssl conexión. También existen procedimientos alternativos, como agregar la clave al host de confianza de la JVM y modificar el almacén de confianza predeterminado al iniciar.

Hice esto hace unos dos meses y no tengo el código fuente en mí en este momento ... usa Google y deberías poder resolver este problema. Si no puede enviarme un mensaje y puedo proporcionarle el código fuente relevante para el proyecto ... No sé si esto resuelve su problema, ya que no ha proporcionado el código que causa estas excepciones. Además, estaba trabajando con applets, pensé que no podía ver por qué no funcionaría en Serverlets ...

PD: No puedo obtener el código fuente antes del fin de semana porque SSH externo está deshabilitado en mi oficina :(

Osama Javed
fuente
1
PD: Encontré este código java en línea para extraer y almacenar las claves públicas de mi servidor, así que sigue buscando en Google o espera hasta el domingo
Osama Javed
0

SSLHandshakeException se puede resolver de 2 formas.

  1. Incorporando SSL

    • Obtenga el SSL (preguntando al administrador del sistema de origen, también se puede descargar con el comando openssl, o cualquier navegador descarga los certificados)

    • Agregue el certificado en el almacén de confianza (cacerts) ubicado en JRE / lib / security

    • proporcione la ubicación del almacén de confianza en los argumentos vm como "-Djavax.net.ssl.trustStore ="

  2. Ignorando SSL

    Para este # 2, visite mi otra respuesta en otro sitio web de stackoverflow: Cómo ignorar la verificación SSL Ignore los errores del certificado SSL con Java

Amit Kaneria
fuente
-8

Ahora resolví este problema de esta manera,

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.OutputStream;
// Create a trust manager that does not validate certificate chains like the 
default TrustManager[] trustAllCerts = new TrustManager[] {
    new X509TrustManager() {
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }
        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            //No need to implement. 
        }
        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            //No need to implement. 
        }
    }
};
// Install the all-trusting trust manager
try {
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
    System.out.println(e);
}
Evelyn Condes
fuente
4
'No es necesario implementar' es total y absolutamente incorrecto. Acaba de hacer que su conexión SSL sea insegura. No hagas esto.
Marqués de Lorne