Keytool - Problema ao importar certificado para o KeyStore

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:

Comando para importação do certificado:

/usr/lib/j2sdk1.5-sun/jre/bin/keytool -import -alias aliasdocertificado -file /tmp/arquivo.cer -keystore arquivocacerts -storepass senhadostore

Exceção:

keytool error: java.security.SignatureException: Signature does not match.

Gostaria de saber se alguém conhece uma solução para este problema…

Obrigado,
Jean Pierobom

[quote=pierobom]Exceção:

keytool error: java.security.SignatureException: Signature does not match. [/quote]

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

O arquivo .cer deve estar em formato Base-64, não em formato binário. Dica: se ele estiver assim:

-----BEGIN CERTIFICATE-----
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
-----END CERTIFICATE-----

ele está OK, mas se estiver assim (estou mostrando o dump em hexadecimal senão vai corromper todo o post, mas você deve er entendido a idéia):

0000    30 82 03 66 30 82 02 CF  A0 03 02 01 02 02 10 0D   0..f0...........
0010    8B 4F EE AA D2 18 5B F4  75 6A 9D 29 E1 7F FB 30   .O....[⌠uj.)..√0
0020    0D 06 09 2A 86 48 86 F7  0D 01 01 02 05 00 30 5F   ...*.H.≈......0_
0030    31 0B 30 09 06 03 55 04  06 13 02 55 53 31 17 30   1.0...U....US1.0
0040    15 06 03 55 04 0A 13 0E  56 65 72 69 53 69 67 6E   ...U....VeriSign
0050    2C 20 49 6E 63 2E 31 37  30 35 06 03 55 04 0B 13   , Inc.1705..U...
0060    2E 43 6C 61 73 73 20 31  20 50 75 62 6C 69 63 20   .Class 1 Public
0070    50 72 69 6D 61 72 79 20  43 65 72 74 69 66 69 63   Primary Certific
0080    61 74 69 6F 6E 20 41 75  74 68 6F 72 69 74 79 30   ation Authority0
0090    1E 17 0D 39 38 30 35 31  32 30 30 30 30 30 30 5A   ...980512000000Z
00A0    17 0D 30 38 30 35 31 32  32 33 35 39 35 39 5A 30   ..080512235959Z0
...

não vai conseguir ser importado.

Oyama,

Primeiramente obrigado pela resposta.

É isso mesmo. Estou importando o certificado de um site externo.

Bom... Abrindo o arquivo .cer, o que o windows fala sobre o certificado é apenas que não pode garantir a integridade do mesmo. Mas ele fala isso para os outros certificados também, mesmo os que consegui importar com o Keytool.

Obrigado,
Jean Pierobom

Thingol,

Obrigado pela resposta.

Mas acredito que não seja isto. O arquivo .cer foi gerado em formato binário mesmo. Selecionando a opção "DER encoded binary X.509 (.CER)" no Internet Explorer. É esta a opção de exportação que você se referiu, correto?

Obrigado,
Jean Pierobom

Pessoal,

Criei uma classe de teste. Ela carrega o certificado (arquivo .cer) e utiliza o método java.security.cert.Certificate.verify(PublicKey arg0) (que verifica se a assinatura foi feita utilizando a chave privada que corresponde à chave pública passada como parâmetro).

CertificadoSSLLoader

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

import javax.security.cert.CertificateException;
import javax.security.cert.X509Certificate;

public class CertificadoSSLLoader {

	public static X509Certificate load(String cerFileName) throws CertificateException {
		File file = new File(cerFileName);
		FileInputStream fis;
		try {
			fis = new FileInputStream(file);
		} catch (FileNotFoundException e) {
			throw new IllegalArgumentException("Arquivo " + cerFileName + " não existe.");
		}
		X509Certificate certificado = X509Certificate.getInstance(fis);
		return certificado;
	}
}

TestaValidadeCertificadoSSL

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;

import javax.security.cert.CertificateException;
import javax.security.cert.X509Certificate;

public class TestaValidadeCertificadoSSL {

	public static void main(String[] args) {
		try {
			X509Certificate certificado = CertificadoSSLLoader.load("certificado.cer");
				certificado.verify(certificado.getPublicKey());
		} catch (InvalidKeyException e) {
			System.out.println("Chave inválida.");
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			System.out.println("Algoritmo inválido.");
			e.printStackTrace();
		} catch (NoSuchProviderException e) {
			System.out.println("Provedor inválido.");
			e.printStackTrace();
		} catch (SignatureException e) {
			System.out.println("Assinatura inválida.");
			e.printStackTrace();
		} catch (CertificateException e) {
			System.out.println("Ocorreu um erro ao carregar o certificado");
			e.printStackTrace();
		}
	}
}
A saída deste programa, como esperado, é a mesma do keytool:
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)
O que mais me chamou atenção até aqui foi o comentário javadoc do método.
    abstract void verify(PublicKey key)
          Verifies that this certificate was signed using the private key that corresponds to the specified public key.
Isto me leva a crer que que há grandes chances de ser algum problema ocorrido no momento da geração do certificado no servidor do site. 

Clareou para alguém?

Obrigado,
Jean Pierobom

Jean

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.

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

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

[quote=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)?
[/quote]

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.

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

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.

Oyama,

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.

E o keytool não chega a fazer a pergunta "Trust this certificate? [no]". Ao executar o comando keytool - import já ocorre a exceção.

Dá uma olhada no certificado (arquivo .cer). Segue anexo.

Obrigado,
Jean Pierobom

[quote=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.

E o keytool não chega a fazer a pergunta "Trust this certificate? [no]". Ao executar o comando keytool - import já ocorre a exceção.

Dá uma olhada no certificado (arquivo .cer). Segue anexo.[/quote]

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…

Oyama,

Estou verificando com o pessoal que gerou o certificado.

Depois lhe passo o que consegui.

Obrigado,
Jean Pierobom

Oyama,

O certificado foi gerado novamente no openssl e o problema foi resolvido.

Muito obrigado pela ajuda.

Jean Pierobom