Não foi possível encontrar o caminho de certificação válido para o destino solicitado - erro mesmo após a importação do certificado

205

Eu tenho um cliente Java tentando acessar um servidor com um certificado autoassinado.

Quando tento postar no servidor, recebo o seguinte erro:

incapaz de encontrar o caminho de certificação válido para o destino solicitado

Depois de fazer algumas pesquisas sobre o assunto, fiz o seguinte.

  1. Salvei o nome de domínio dos meus servidores como um root.cerarquivo.
  2. No JRE do meu servidor Glassfish, executei o seguinte:
    keytool -import -alias example -keystore cacerts -file root.cer
  3. Para verificar se o certificado foi adicionado ao meu cacert com êxito, fiz o seguinte:
    keytool -list -v -keystore cacerts
    Posso ver que o certificado está presente.
  4. Reiniciei o Glassfish e aposentei o 'post'.

Ainda estou recebendo o mesmo erro.

Sinto que isso ocorre porque meu Glassfish não está realmente lendo o arquivo cacert que eu alterei, mas talvez outro.

Algum de vocês já teve esse problema e pode me levar na direção certa?

TheCoder
fonte
1
Apenas para esclarecer "Eu tenho um cliente Java tentando acessar um servidor com um certificado autoassinado": você está falando em usar certificados de cliente autoassinados, não é? Existe alguma configuração específica para as configurações do seu conector no Glassfish (configurações de armazenamento confiável, em particular)?
de Bruno
"Eu tenho um cliente Java tentando acessar um servidor com um certificado autoassinado.": Você está falando em usar certificados de cliente autoassinados, não é? - sim.
TheCoder
1
Encontrei 2 configurações na Glassfish JVM: -Djavax.net.ssl.keyStore = $ {com.sun.aas.instanceRoot} /config/keystore.jks e -Djavax.net.ssl.trustStore = $ {com.sun. aas.instanceRoot} /config/cacerts.jks. Agora preciso adicionar por certificado a um desses. Você pode confirmar que é o keystore ao qual eu o adiciono?
TheCoder
4
No servidor, o keystore é para o certificado do servidor e sua chave privada (keystore é para o que "pertence" à parte local). O armazenamento confiável é para os certificados usados ​​para verificar a confiança na parte remota. Você deve adicionar o certificado do cliente ao armazenamento confiável do servidor. (Veja este também, embora Glassfish não parece estar usando local padrão do JRE.)
de Bruno
Isso funcionou Bruno. Adicionei-o ao meu armazenamento confiável Glassfish. Muito obrigado por sua ajuda. Você também Dirk.
TheCoder

Respostas:

155

Infelizmente - pode haver muitas coisas - e muitos servidores de aplicativos e outros 'invólucros' de java tendem a brincar com propriedades e com sua própria 'visão' de chaveiros e o que não. Portanto, pode estar olhando algo totalmente diferente.

Com falta de treliça - eu tentaria:

java -Djavax.net.debug=all -Djavax.net.ssl.trustStore=trustStore ...

para ver se isso ajuda. Em vez de 'all', também é possível configurá-lo como 'ssl', gerenciador de chaves e gerenciador de confiança - o que pode ajudar no seu caso. Configurá-lo para 'ajuda' listará algo como abaixo na maioria das plataformas.

Independentemente disso - certifique-se de entender completamente a diferença entre o keystore (no qual você possui a chave privada e o certificado com os quais você comprova sua própria identidade) e o armazenamento confiável (que determina em quem você confia) - e o fato de que sua própria identidade também tem uma 'cadeia' de confiança na raiz - que é separada de qualquer cadeia em uma raiz, você precisa descobrir 'em quem' você confia.

all            turn on all debugging
ssl            turn on ssl debugging

The   following can be used with ssl:
    record       enable per-record tracing
    handshake    print each handshake message
    keygen       print key generation data
    session      print session activity
    defaultctx   print default SSL initialization
    sslctx       print SSLContext tracing
    sessioncache print session cache tracing
    keymanager   print key manager tracing
    trustmanager print trust manager tracing
    pluggability print pluggability tracing

    handshake debugging can be widened with:
    data         hex dump of each handshake message
    verbose      verbose handshake message printing

    record debugging can be widened with:
    plaintext    hex dump of record plaintext
    packet       print raw SSL/TLS packets

Fonte: # Consulte http://download.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#Debug

Dirk-Willem van Gulik
fonte
6
Muito obrigado! -Djavax.net.ssl.trustStore = / location_of / trustStore resolveu meu problema e as informações de depuração também foram muito úteis.
RHE 19/07
5
java -Djavax.net.debug = all -Djavax.net.ssl.trustStore = trustStore ... dá a abaixo de erro: Erro: Não foi possível localizar ou carregar a classe principal ...
Rohith
1
Claro que também pode ser necessário especificar a senha do armazenamento confiável com -Djavax.net.ssl.trustStorePassword = changeit
user1754036
18

Aqui está a solução, siga o link abaixo passo a passo:

http://www.mkyong.com/webservices/jax-ws/suncertpathbuilderexception-unable-to-find-valid-certification-path-to-requested-target/

ARQUIVO JAVA: que está faltando no blog

/*
 * Copyright 2006 Sun Microsystems, Inc.  All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Sun Microsystems nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */



import java.io.*;
import java.net.URL;

import java.security.*;
import java.security.cert.*;

import javax.net.ssl.*;

public class InstallCert {

    public static void main(String[] args) throws Exception {
    String host;
    int port;
    char[] passphrase;
    if ((args.length == 1) || (args.length == 2)) {
        String[] c = args[0].split(":");
        host = c[0];
        port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
        String p = (args.length == 1) ? "changeit" : args[1];
        passphrase = p.toCharArray();
    } else {
        System.out.println("Usage: java InstallCert <host>[:port] [passphrase]");
        return;
    }

    File file = new File("jssecacerts");
    if (file.isFile() == false) {
        char SEP = File.separatorChar;
        File dir = new File(System.getProperty("java.home") + SEP
            + "lib" + SEP + "security");
        file = new File(dir, "jssecacerts");
        if (file.isFile() == false) {
        file = new File(dir, "cacerts");
        }
    }
    System.out.println("Loading KeyStore " + file + "...");
    InputStream in = new FileInputStream(file);
    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
    ks.load(in, passphrase);
    in.close();

    SSLContext context = SSLContext.getInstance("TLS");
    TrustManagerFactory tmf =
        TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(ks);
    X509TrustManager defaultTrustManager = (X509TrustManager)tmf.getTrustManagers()[0];
    SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
    context.init(null, new TrustManager[] {tm}, null);
    SSLSocketFactory factory = context.getSocketFactory();

    System.out.println("Opening connection to " + host + ":" + port + "...");
    SSLSocket socket = (SSLSocket)factory.createSocket(host, port);
    socket.setSoTimeout(10000);
    try {
        System.out.println("Starting SSL handshake...");
        socket.startHandshake();
        socket.close();
        System.out.println();
        System.out.println("No errors, certificate is already trusted");
    } catch (SSLException e) {
        System.out.println();
        e.printStackTrace(System.out);
    }

    X509Certificate[] chain = tm.chain;
    if (chain == null) {
        System.out.println("Could not obtain server certificate chain");
        return;
    }

    BufferedReader reader =
        new BufferedReader(new InputStreamReader(System.in));

    System.out.println();
    System.out.println("Server sent " + chain.length + " certificate(s):");
    System.out.println();
    MessageDigest sha1 = MessageDigest.getInstance("SHA1");
    MessageDigest md5 = MessageDigest.getInstance("MD5");
    for (int i = 0; i < chain.length; i++) {
        X509Certificate cert = chain[i];
        System.out.println
            (" " + (i + 1) + " Subject " + cert.getSubjectDN());
        System.out.println("   Issuer  " + cert.getIssuerDN());
        sha1.update(cert.getEncoded());
        System.out.println("   sha1    " + toHexString(sha1.digest()));
        md5.update(cert.getEncoded());
        System.out.println("   md5     " + toHexString(md5.digest()));
        System.out.println();
    }

    System.out.println("Enter certificate to add to trusted keystore or 'q' to quit: [1]");
    String line = reader.readLine().trim();
    int k;
    try {
        k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;
    } catch (NumberFormatException e) {
        System.out.println("KeyStore not changed");
        return;
    }

    X509Certificate cert = chain[k];
    String alias = host + "-" + (k + 1);
    ks.setCertificateEntry(alias, cert);

    OutputStream out = new FileOutputStream("jssecacerts");
    ks.store(out, passphrase);
    out.close();

    System.out.println();
    System.out.println(cert);
    System.out.println();
    System.out.println
        ("Added certificate to keystore 'jssecacerts' using alias '"
        + alias + "'");
    }

    private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();

    private static String toHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 3);
        for (int b : bytes) {
            b &= 0xff;
            sb.append(HEXDIGITS[b >> 4]);
            sb.append(HEXDIGITS[b & 15]);
            sb.append(' ');
        }
        return sb.toString();
    }

    private static class SavingTrustManager implements X509TrustManager {

    private final X509TrustManager tm;
    private X509Certificate[] chain;

    SavingTrustManager(X509TrustManager tm) {
        this.tm = tm;
    }

    public X509Certificate[] getAcceptedIssuers() {
        throw new UnsupportedOperationException();
    }

    public void checkClientTrusted(X509Certificate[] chain, String authType)
        throws CertificateException {
        throw new UnsupportedOperationException();
    }

    public void checkServerTrusted(X509Certificate[] chain, String authType)
        throws CertificateException {
        this.chain = chain;
        tm.checkServerTrusted(chain, authType);
    }
    }

}
user6258309
fonte
3
Esta solução funcionou para mim, mas precisa de uma pequena alteração na classe privada SavingTrustManager:public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0];}
Richard
1
@ Richard, @ Paul, @ user6258309 Recebi o erro Exceção no segmento "main" java.net.SocketTimeoutException: leitura excedida em java.net.SocketInputStream.socketRead0 (método nativo) em java.net.SocketInputStream.socketRead (fonte desconhecida ) Como posso corrigir isso
Renjith Krishnan
5
Essa "solução" é radicalmente insegura. Não use.
Marquês de Lorne
9
Esta resposta é principalmente código. Não explica por que ou por que não funciona.
13

Você precisa configurar as Propriedades do sistema JSSE, apontar especificamente para o armazenamento de certificados do cliente.

Via linha de comando:

java -Djavax.net.ssl.trustStore=truststores/client.ts com.progress.Client

ou via código Java:

import java.util.Properties;
    ...
    Properties systemProps = System.getProperties();
    systemProps.put("javax.net.ssl.keyStorePassword","passwordForKeystore");
    systemProps.put("javax.net.ssl.keyStore","pathToKeystore.ks");
    systemProps.put("javax.net.ssl.trustStore", "pathToTruststore.ts");
    systemProps.put("javax.net.ssl.trustStorePassword","passwordForTrustStore");
    System.setProperties(systemProps);
    ...

Para mais informações, consulte os detalhes no site RedHat .

Vic
fonte
Teve o mesmo problema com o Spring Boot, os microsserviços Spring Cloud e um certificado SSL autoassinado. Consegui definir keyStore e keyStorePassword em application.properties e fazê-lo funcionar assim imediatamente, mas não tive êxito em fazer o mesmo com trustStore e trustStorePassword. Essa resposta funcionou para mim na loja de confiança.
Mate Šimović 30/06
7

(repost da minha outra resposta )
Use o utilitário cli keytool da distribuição de software java para importar (e confiar! ) os certificados necessários

Amostra:

  1. De cli altere dir para jre \ bin

  2. Verifique o keystore (arquivo encontrado no diretório jre \ bin)
    keytool -list -keystore .. \ lib \ security \ cacerts A
    senha foi alterada

  3. Faça o download e salve todos os certificados em cadeia do servidor necessário.

  4. Adicione certificados (antes de precisar remover o atributo "somente leitura" no arquivo ".. \ lib \ security \ cacerts"), execute: keytool -alias REPLACE_TO_ANY_UNIQ_NAME -import -keystore .. \ lib \ security \ cacerts -file "r: \ root.crt "

acidentalmente encontrei uma dica tão simples. Outras soluções requerem o uso de InstallCert.Java e JDK

fonte: http://www.java-samples.com/showtutorial.php?tutorialid=210

Алексей Присяжный
fonte
6

Eu tive o mesmo problema com o sbt .
Ele tentou buscar dependências do repo1.maven.org pelo ssl,
mas disse que "não conseguiu encontrar o caminho de certificação válido para o URL de destino solicitado".
então segui esta postagem e ainda não consegui verificar uma conexão.
Então, li sobre o assunto e descobri que o
certificado raiz não é suficiente, como sugerido pela publicação, então - o que funcionou para mim foi importar os certificados CA intermediários para o keystore .
Na verdade, adicionei todos os certificados da cadeia e funcionou como um encanto.

Danny Mor
fonte
4

Solução ao migrar do JDK 8 para o JDK 10

JDK 10

root@c339504909345:/opt/jdk-minimal/jre/lib/security #  keytool -cacerts -list
Enter keystore password:
Keystore type: JKS
Keystore provider: SUN

Your keystore contains 80 entries

JDK 8

root@c39596768075:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/security/cacerts #  keytool -cacerts -list
Enter keystore password:
Keystore type: JKS
Keystore provider: SUN

Your keystore contains 151 entries

Etapas para corrigir

  • Excluí o certificado JDK 10 e o substitui pelo JDK 8
  • Como estou criando imagens do Docker, eu poderia fazer isso rapidamente usando compilações de vários estágios
    • Estou construindo um JRE mínimo usando jlinkcomo/opt/jdk/bin/jlink \ --module-path /opt/jdk/jmods...

Então, aqui estão os diferentes caminhos e a sequência dos comandos ...

# Java 8
COPY --from=marcellodesales-springboot-builder-jdk8 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/security/cacerts /etc/ssl/certs/java/cacerts

# Java 10
RUN rm -f /opt/jdk-minimal/jre/lib/security/cacerts
RUN ln -s /etc/ssl/certs/java/cacerts /opt/jdk-minimal/jre/lib/security/cacerts
Marcello de Sales
fonte
2

Estou trabalhando em um tutorial para serviços web REST em www.udemy.com (Java Web Services REST). O exemplo no tutorial dizia que, para ter SSL, precisamos ter uma pasta chamada "trust_store" no projeto eclipse "client" que deve conter um arquivo "key store" (tivemos um projeto "client" para chamar o serviço e projeto "service" que continha o serviço da web REST - 2 projetos no mesmo espaço de trabalho do eclipse, um no cliente e outro no serviço). Para simplificar, eles disseram para copiar "keystore.jks" do servidor de aplicativos glassfish (glassfish \ domínios \ domínio1 \ config \ keystore.jks) que estamos usando e colocá-lo nesta pasta "trust_store" que eles me pediram para criar o projeto do cliente. Isso parece fazer sentido: os certificados autoassinados no servidor ' s key_store corresponderia aos certificados no cliente trust_store. Agora, ao fazer isso, estava recebendo o erro mencionado na postagem original. Pesquisei isso no Google e li que o erro ocorre devido ao arquivo "keystore.jks" no cliente que não contém um certificado confiável / assinado, que o certificado encontrado é autoassinado.

Para manter as coisas claras, deixe-me dizer que, como eu o entendo, o "keystore.jks" contém certificados autoassinados e o arquivo "cacerts.jks" contém certificados de autoridade de certificação (assinados pela autoridade de certificação). O "keystore.jks" é o "keystore" e o "cacerts.jks" é o "armazenamento confiável". Como "Bruno", um comentarista, diz acima, "keystore.jks" é local e "cacerts.jks" é para clientes remotos.

Então, eu disse a mim mesmo: ei, o glassfish também tem o arquivo "cacerts.jks", que é o arquivo trust_store do glassfish. O cacerts.jsk deve conter certificados de CA. E, aparentemente, eu preciso da minha pasta trust_store para conter um arquivo de armazenamento de chaves que tenha pelo menos um certificado de CA. Portanto, tentei colocar o arquivo "cacerts.jks" na pasta "trust_store" que eu havia criado, no meu projeto do cliente, e alterar as propriedades da VM para apontar para "cacerts.jks" em vez de "keystore.jks". Isso se livrou do erro. Acho que tudo o que precisava era de um certificado da CA para funcionar.

Isso pode não ser ideal para produção, ou mesmo para desenvolvimento, além de apenas conseguir algo para funcionar. Por exemplo, você provavelmente poderia usar o comando "keytool" para adicionar certificados da CA ao arquivo "keystore.jks" no cliente. Mas, de qualquer forma, espero que isso pelo menos restrinja os possíveis cenários que poderiam estar acontecendo aqui para causar o erro.

TAMBÉM: minha abordagem parecia útil para o cliente (certificado de servidor adicionado ao cliente trust_store), parece que os comentários acima para resolver a postagem original são úteis para o servidor (certificado de cliente adicionado ao servidor trust_store). Felicidades.

Configuração do projeto Eclipse:

  • MyClientProject
  • src
  • teste
  • Biblioteca do sistema JRE
  • ...
  • trust_store
    --- cacerts.jks --- keystore.jks

Snippet do arquivo MyClientProject.java:

static {
  // Setup the trustStore location and password
  System.setProperty("javax.net.ssl.trustStore","trust_store/cacerts.jks");
  // comment out below line
  System.setProperty("javax.net.ssl.trustStore","trust_store/keystore.jks");
  System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
  //System.setProperty("javax.net.debug", "all");

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

  });
}
Steve T
fonte
1

Meu problema foi que um NetSkope, um Agente de Segurança para Acesso à Nuvem, foi instalado no meu laptop de trabalho por meio de uma atualização de software. Isso estava alterando a cadeia de certificados e eu ainda não consegui me conectar ao servidor através do meu cliente java depois de importar toda a cadeia para o meu keystore cacerts. Desativei o NetSkope e consegui me conectar com sucesso.

gary69
fonte
1

No meu caso, eu estava enfrentando o problema porque, no meu processo de tomcat, o keystore específico foi fornecido usando

-Djavax.net.ssl.trustStore=/pathtosomeselfsignedstore/truststore.jks

Onde eu estava importando o certificado para o cacert de JRE / lib / security e as mudanças não estavam refletindo. Então eu fiz abaixo do comando onde /tmp/cert1.test contém o certificado do servidor de destino

keytool -import -trustcacerts -keystore /pathtosomeselfsignedstore/truststore.jks -storepass password123 -noprompt -alias rapidssl-myserver -file /tmp/cert1.test

Podemos verificar se a importação do certificado foi bem-sucedida

keytool -list -v -keystore /pathtosomeselfsignedstore/truststore.jks

e veja se o seu servidor de taget é encontrado no apelido rapidssl-myserver

Sanjay Bharwani
fonte
0

Verifique se o arquivo $JAVA_HOME/lib/security/cacertsexiste! No meu caso, não era um arquivo, mas um link para /etc/ssl/certs/java/cacertse também era um link para si mesmo (O QUE ???), portanto, devido a isso, a JVM não pode encontrar o arquivo.

Solução: Copie o arquivo cacerts real ( você pode fazê-lo de outro JDK ) no /etc/ssl/certs/java/diretório e ele resolverá o seu problema :)

0x7E1
fonte
na verdade, o JDK mantém um link e talvez para compatibilidade com APIs e SDKs mais antigos ... Consegui fazer o mesmo com sucesso ... Estou passando do JDK 8 para o JDK 10 e usando o Docker, então só precisava copiar os cacerts de um imagem para outra ... Eu criei o link e mesma maneira exata ...rm -f /opt/jdk-minimal/jre/lib/security/cacerts ; ln -s /etc/ssl/certs/java/cacerts /opt/jdk-minimal/jre/lib/security/cacerts
Marcello de Sales
-9

Digamos que você esteja usando variáveis ​​de caminho de classe como $ {JAVA_HOME} no pom.xml.

<target>
                    <property name="compile_classpath" refid="maven.compile.classpath"/>
                    <property name="runtime_classpath" refid="maven.runtime.classpath"/>
                    <property name="test_classpath" refid="maven.test.classpath"/>
                    <property name="plugin_classpath" refid="maven.plugin.classpath"/>
                    <property name="jaxb-api.jar" value="${maven.dependency.javax.xml.bind.jaxb-api.jar.path}"/>
                    <property name="project_home" value="${PROJECT_HOME}"/>
                    <property name="java_home" value="${JAVA_HOME}"/>
                    <property name="ant_home" value="${ANT_HOME}"/>
                    <property name="common_home" value="${COMMON_HOME}"/>
                    <property name="JAXP_HOME" value="${common_home}/lib"/>
                    <property name="ejfw_home" value="${PROJECT_HOME}/lib"/>
                    <property name="weblogic_home" value="${WL_HOME}"/>
                    <property name="fw_home" value="${FW_HOME}"/>
                    <property name="env" value="${BUILDENV}"/>
                    <property name="tokenfile" value="${BUILDENV}${BUILDENV_S2S}.properties"/>

Nos objetivos, adicione as variáveis ​​do caminho de classe. ou seja, -DANT_HOME, -DJAVA_HOME

clean install -e -DPROJECT_HOME=..... -DANT_HOME=C:\bea1036\modules\org.apache.ant_1.7.1 -DJAVA_HOME=C:\bea1036\jdk160_31
jayakar jayaraman
fonte
1
Os caminhos e certificados de classe não têm nada a ver um com o outro.
Marquês de Lorne
1
Eu acredito que você não entendeu a pergunta.
Dawood ibn Kareem