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
Selladurai
fonte
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).

Ryan Stewart
fonte
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.

Selladurai
fonte
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 .

Shirishkumar Bari
fonte
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 :(

Osama Javed
fonte
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

Amit Kaneria
fonte
-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);
}
Evelyn Condes
fonte
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