Keytool - Problema ao importar certificado para o KeyStore
15 respostas
pierobom
Pessoal,
Na empresa onde trabalho utilizamos o httpclient para realizar algumas operações de monitoramento em páginas web.
Nos casos onde a aplicação responde via https, precisamos importar (utilizando o Keytool) o certificado (arquivo .cer obtido clicando no cadeado do browser) ao KeyStore (arquivo cacerts), para que o httpclient consiga realizar as requisições.
Na maioria do certificados funciona tudo bem. Mas tem alguns certificados que acontece de não conseguirmos importar, pois ocorre uma exceção. Vejam abaixo:
keytool error: java.security.SignatureException: Signature does not match.
Só lembro de ter visto este erro quando o certificado estava “corrompido” (a CA mandou um certificado errado para o cliente e a gente ficou batendo cabeça para descobrir que o problema era do certificado).
Pelo o que eu entendi que você escreveu, não é o seu caso. Você está importando um certificado de um site externo (não é um certificado gerado para o seu site). Tente verficar se o certificado está OK (um double-click em um arquivo .cer no Windows abre uma aplicação que mostra detalhes do certificado).
T
thingol
O arquivo .cer deve estar em formato Base-64, não em formato binário. Dica: se ele estiver assim:
importjava.io.File;importjava.io.FileInputStream;importjava.io.FileNotFoundException;importjavax.security.cert.CertificateException;importjavax.security.cert.X509Certificate;publicclassCertificadoSSLLoader{publicstaticX509Certificateload(StringcerFileName)throwsCertificateException{Filefile=newFile(cerFileName);FileInputStreamfis;try{fis=newFileInputStream(file);}catch(FileNotFoundExceptione){thrownewIllegalArgumentException("Arquivo "+cerFileName+" não existe.");}X509Certificatecertificado=X509Certificate.getInstance(fis);returncertificado;}}
TestaValidadeCertificadoSSL
importjava.security.InvalidKeyException;importjava.security.NoSuchAlgorithmException;importjava.security.NoSuchProviderException;importjava.security.SignatureException;importjavax.security.cert.CertificateException;importjavax.security.cert.X509Certificate;publicclassTestaValidadeCertificadoSSL{publicstaticvoidmain(String[]args){try{X509Certificatecertificado=CertificadoSSLLoader.load("certificado.cer");certificado.verify(certificado.getPublicKey());}catch(InvalidKeyExceptione){System.out.println("Chave inválida.");e.printStackTrace();}catch(NoSuchAlgorithmExceptione){System.out.println("Algoritmo inválido.");e.printStackTrace();}catch(NoSuchProviderExceptione){System.out.println("Provedor inválido.");e.printStackTrace();}catch(SignatureExceptione){System.out.println("Assinatura inválida.");e.printStackTrace();}catch(CertificateExceptione){System.out.println("Ocorreu um erro ao carregar o certificado");e.printStackTrace();}}}
Assinatura inválida.
java.security.SignatureException: Signature does not match.
at sun.security.x509.X509CertImpl.verify(Unknown Source)
at sun.security.x509.X509CertImpl.verify(Unknown Source)
at com.sun.security.cert.internal.x509.X509V1CertImpl.verify(Unknown Source)
at TestaValidadeCertificadoSSL.main(TestaValidadeCertificadoSSL.java:16)
Pelo teste que você fez o certificado que você está tentando importar está “inválido”. Isto pode acontecer se o certificado diz que está assinado por um “certificado root” que você tem no seu cacerts e isto não é verdade (assinatura não bate). Ou o “certificado root” que você tem no seu cacerts não é mais válido ou é “fake” ou o certificado que voce está tentando importar é “fake”. Tente ver a cadeia de certificados que assina o certificado que voce está tentando importar e veja se não tem algo no seu cacerts que invalide-o.
Thingol
Só para te dar um toque: antigamente eu também já tive problemas de importar certificados que estavam em formato binário (só aceitava base-64). mas fiz uns testes agora e realmente ele aceita certificados em formato binário. Não tenho uma JDK 1.3 aqui na minha máquina para fazer o teste, mas eu lembro pelo menos que em JDK 1.3.1 não dava para fazer o import de formato binário. Com JDK 1.4.2 eu consegui aqui.
T
thingol
Legal, não sabia disso. (É que muitas das ferramentas que uso precisam especificar explicitamente o formato; não são espertas o suficiente para fazer a auto-detecção. Seria relativamente simples, já que o primeiro byte é sempre 0x30 (‘0’) que quer dizer SEQUENCE em ASN.1 codificação DER ou BER, se o formato for binário. )
pierobom
Oyama,
Me diz uma coisa... Como identifico a cadeia de certificados que assina o certificado que estou está tentando importar?
É o CN (Common Name, Emitido Por)?
Obrigado,
Jean Pierobom
O
oyama
pierobom:
Me diz uma coisa… Como identifico a cadeia de certificados que assina o certificado que estou está tentando importar?
É o CN (Common Name, Emitido Por)?
Para identificar quem assinou o seu certificado, verifique o Issuer (acho que em portugues é o “emitido por”).
O método getIssuerDN() da classe X509Certificate retorna o DN (Distinguished Name) do Issuer (“assinador”) do certificado. Procure um certificado no seu cacerts que tenha o mesmo DN em Subject. DN é todo o campo, e não só o campo CN.
pierobom
Oyama,
Obrigado pelas respostas.
Não. Não existre outro certificado com o mesmo DN no cacerts. Inclusive eu tentei importar o certificado em outra máquina (com um cacerts vazio, sem certificados) e ocorreu o mesmo problema.
Acho mais provável a sua outra sugestão, a de que o certificado que estou tentando importar é "fake". Agora, como posso me certificar disso?
Obrigado,
Jean Pierobom
O
oyama
Quando voce diz “cacerts vaziou”, voce “zerou” o arquivo $JAVA_HOME/jre/lib/security/cacerts? Pois é com base nos “certificados root” deste arquivo que a JVM verifica a assinatura de um certificado.
Com qual JDK/JRE voce está fazendo o import do certificado?
Está dando a mensagem “Trust this certificate? [no]” quando voce está fazendo o import?
Já devia ter pedido isto antes, mas posta o arquivo com o certificado para podemos dar uma olhada.
pierobom
Oyama,
Achoquemeexpresseimal.Nãoéumcacertsvazio.Oqueeupretendidizeréquetesteitambémcomumcacertsdeumjdkrecém-instalado,ouseja,umcacertsparaoqualnãofoiimportadonenhumcertificadoainda.Eokeytoolnãochegaafazerapergunta"Trust this certificate? [no]".Aoexecutarocomandokeytool-importjáocorreaexceção.Dáumaolhadanocertificado(arquivo.cer).Segueanexo.
Obrigado,
Jean Pierobom
O
oyama
pierobom:
Acho que me expressei mal. Não é um cacerts vazio. O que eu pretendi dizer é que testei também com um cacerts de um jdk recém-instalado, ou seja, um cacerts para o qual não foi importado nenhum certificado ainda.
Eokeytoolnãochegaafazerapergunta"Trust this certificate? [no]".Aoexecutarocomandokeytool-importjáocorreaexceção.Dáumaolhadanocertificado(arquivo.cer).Segueanexo.</blockquote>
Jean
Olhei o arquivo. Realmente, o “meliante” que gerou este certificado fez alguma besteira. Ele é um certificado “auto-assinado” (não foi assinado por uma CA), ou seja, não dá para saber se é confiável. Não sei porque os browsers (testei no IE e no Firefox) não detectam o problema da assinatura do certificado (eu desconfio que por ser “auto-assinado” , os browsers nem verificam a assinatura).
O jeito seria pedir para o responsável pelo site gerar um certificado correto (será que o governo do Paraná não tem convenho com o Serpro para gerar certificados “autenticos”?).
Voltando ao seu problema original: você importava o certificado dos sites para poder acessá-los via httpclient. Se você não importar o certificado provavelmente dá algum erro. Voce poderia tentar gerar um cacerts (que nada mais é que uma keystore só com certificados) criando um programa que não validade a assinatura no import. Faça um programa que leia um certificado e gere uma keystore. Vai ser uma gambiarra…
pierobom
Oyama,
Estou verificando com o pessoal que gerou o certificado.
Depois lhe passo o que consegui.
Obrigado,
Jean Pierobom
pierobom
Oyama,
O certificado foi gerado novamente no openssl e o problema foi resolvido.
Muito obrigado pela ajuda.