Como resolver o erro javax.net.ssl.SSLHandshakeException?


101

Liguei-me a VPN para configurar a API de inventário para obter a lista de produtos e funciona bem. Depois de obter o resultado do serviço da web e me vincular à IU. E também integrei o PayPal ao meu aplicativo para fazer checkout expresso quando faço uma chamada para pagamento estou enfrentando esse erro. Eu uso servlet para o processo de back-end. Alguém pode dizer como consertar esse 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

1
o que eu gostaria de saber, qual destino exato era naquele caso ... Você obtém uma exceção, mas não obtém nenhuma informação sobre o destino, que pode ser diferente do que você espera .. Eu tenho esse caso, tenho certeza meu link tem certificado e ainda estou recebendo essa exceção
ante.sabo

Respostas:


151

Primeiro, você precisa obter o certificado público do servidor ao qual está tentando se conectar. Isso pode ser feito de várias maneiras, como entrar em contato com o administrador do servidor e solicitá-lo, usando OpenSSL para baixá-lo ou, uma vez que este parece ser um servidor HTTP, conectando-se a ele com qualquer navegador, visualizando as informações de segurança da página e salvando uma cópia do certificado. (O Google deve ser capaz de dizer exatamente o que fazer em seu navegador específico.)

Agora que você salvou o certificado em um arquivo, é necessário adicioná-lo ao armazenamento confiável da JVM. No $JAVA_HOME/jre/lib/security/para JREs ou $JAVA_HOME/lib/securitypara JDKs, há um arquivo chamado cacerts, que vem com Java e contém os certificados públicos das autoridades de certificação conhecidas. Para importar o novo certificado, execute keytool como um usuário que tem permissão para gravar em cacerts:

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

Provavelmente, ele pedirá uma senha. A senha padrão fornecida com o Java é changeit. Quase ninguém muda isso. Depois de concluir essas etapas relativamente simples, você estará se comunicando com segurança e com a garantia de estar falando com o servidor certo e apenas o servidor certo (contanto que eles não percam sua chave privada).


12
Vou precisar de um pouco mais de detalhes do que "não funcionar". Tente atualizar sua pergunta com o que você tentou e alguma saída de erro. Infelizmente, já passou da minha hora de dormir, então talvez outra pessoa possa responder às suas perguntas. Esta também é uma situação muito comum. Você pode encontrar muitas informações sobre ele online, incluindo os documentos do keytool .
Ryan Stewart

Basicamente, eu preciso incluir o certificado do PayPal. Fiz seus passos e cert também adicionei no local apropriado. então eu executo o aplicativo que diz mesmo erro?
selladurai

Na verdade ele funciona bem sem problemas no navegador e eu conectei a VPN para obter a lista de inventário, naquele momento eu faço o pagamento com o PayPal, diz que é um erro de SSL, mas eu uso dados offline ou dados codificados a quente significa que funciona.
selladurai

4
@selladurai: Você não deve precisar importar um certificado para o paypal. A menos que seu arquivo cacerts tenha sido corrompido ou modificado, ele deve conter todos os certificados raiz confiáveis ​​e o certificado do paypal deve ser rastreado até um deles. Você pode tentar obter uma cópia do cacerts que você sabe que é bom e tentar aquele. Se o problema persistir, é possível que você não esteja se conectando ao paypal.
Ryan Stewart

@RyanStewart, preciso importá-lo para o Glassfish de alguma forma? Porque eu adicionei o arquivo .cer ao meu cacert no meu diretório inicial java, mas o glassfish não parece usá-lo, então ainda estou recebendo o erro.
Ced

15

Agora resolvi esse problema desta forma,

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);
}

Claro que esta solução deve ser usada apenas em cenários onde não é possível instalar os certificados necessários usando, keytoolpor exemplo, testes locais com certificados temporários.


52
Uau, eu hesitaria em chamar isso de "conserto". Você basicamente desligou a segurança. Sim, os dados ainda serão criptografados, mas você não tem garantia de que está falando com quem espera falar. A solução correta é obter a chave pública de seu servidor de destino e importá-la para o armazenamento confiável da JVM que está fazendo a conexão.
Ryan Stewart

1
veja minha resposta para uma solução apropriada
Ryan Stewart

Exemplo de quê? Minha resposta deve ter todas as etapas de que você precisa.
Ryan Stewart


3
O código mostrado aqui parece útil se você estiver escrevendo um teste muito simples em um servidor de desenvolvimento, eu não confiaria nele para produção.
Jason D

12

Sempre que tentamos nos conectar a URL,

se o servidor em outro site estiver executando no protocolo https e exigindo que devemos nos comunicar por meio das informações fornecidas no certificado, temos a seguinte opção:

1) peça o certificado (baixe o certificado), importe este certificado no trustore. Os usos do trustore java padrão podem ser encontrados em \ Java \ jdk1.6.0_29 \ jre \ lib \ security \ cacerts, então se tentarmos conectar novamente à URL, a conexão será aceita.

2) Em casos normais de negócios, podemos estar nos conectando a URLs internos em organizações e sabemos que eles estão corretos. Nesses casos, você confia que é o URL correto. Nesses casos acima, o código pode ser usado, o que não obriga a armazenar o certificado para se conectar a determinado URL.

para o ponto 2, temos que seguir os passos abaixo:

1) escreva abaixo o método que define HostnameVerifier para HttpsURLConnection, que retorna true para todos os casos, o que significa que estamos confiando no 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) escreva o método abaixo, que chama doTrustToCertificates antes de tentar se conectar ao 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 chamada retornará o código de resposta = 200 significa que a conexão foi bem-sucedida.

Para obter mais detalhes e exemplos de exemplo, você pode consultar a URL .


1
A loja do Google Play irá rejeitar isso. Eles verão que você está ignorando o certificado X509 durante a verificação do APK.
Oliver Dixon

0

Eu acredito que você está tentando se conectar a algo usando SSL, mas que algo está fornecendo um certificado que não é verificado por autoridades de certificação raiz, como verisign. Em essência, por padrão, as conexões seguras só podem ser estabelecidas se a pessoa que está tentando se conectar souber as chaves das contrapartes ou algum outro verndor, como verisign, podem intervir e dizer que a chave pública fornecida é de fato correta.

A confiança de TODOS OS SOs, um punhado de autoridades de certificação e pequenos emissores de certificados precisam ser certificados por um dos grandes certificadores que formam uma cadeia de certificadores, se é o que quero dizer ...

De qualquer forma, voltando ao ponto ... Eu tive um problema semelhante ao programar um miniaplicativo java e um servidor java (espero que algum dia eu escreva uma postagem de blog completa sobre como fiz toda a segurança funcionar :))

Em essência, o que eu tive que fazer foi extrair as chaves públicas do servidor e armazená-las em um keystore dentro do meu miniaplicativo e quando me conectei ao servidor usei esse armazenamento de chaves para criar um trust factory e esse trust factory para criar o SSL conexão. Existem procedimentos alternantes, bem como adicionar a chave ao host confiável da JVM e modificar o armazenamento confiável padrão na inicialização.

Eu fiz isso há cerca de dois meses e não tenho o código-fonte comigo agora .. use o google e você deve ser capaz de resolver este problema. Se você não puder me enviar uma mensagem de volta e eu puder fornecer o código-fonte relevante para o projeto ... Não sei se isso resolve o seu problema, pois você não forneceu o código que causa essas exceções. Além disso, eu estava trabalhando com miniaplicativos, mas não consigo entender por que não funciona em Serverlets ...

PS Não consigo obter o código-fonte antes do fim de semana, pois o SSH externo está desativado em meu escritório :(


1
PS Eu encontrei este código java online para extrair e armazenar as chaves públicas do meu servidor, então continue pesquisando ou espere até domingo
Osama Javed

0

SSLHandshakeException pode ser resolvido de 2 maneiras.

  1. Incorporando SSL

    • Obtenha o SSL (perguntando ao administrador do sistema de origem, também pode ser baixado pelo comando openssl ou qualquer navegador baixa os certificados)

    • Adicione o certificado no armazenamento confiável (cacerts) localizado em JRE / lib / security

    • forneça o local do armazenamento confiável em argumentos vm como "-Djavax.net.ssl.trustStore ="

  2. Ignorando SSL

    Para este nº 2, visite minha outra resposta em outro site stackoverflow: Como ingore a verificação SSL Ignore Erros de Certificado SSL com Java


-8

Agora resolvi esse problema desta forma,

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);
}

4
'Não há necessidade de implementar' é total e totalmente incorreto. Você acabou de tornar sua conexão SSL insegura. Não faça isso.
Marquês de Lorne
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.