Estou tentando conectar-me a um servidor SSL que exige que eu me autentique. Para usar o SSL no Apache MINA, preciso de um arquivo JKS adequado. No entanto, recebi apenas um arquivo .PEM.
Como eu criaria um arquivo JKS a partir de um arquivo PEM?
Se eu for assim, recebo um erro: keytool error: java.lang.Exception: Input not a X.509 certificate
frandevel
1
@frandevel, esse erro pode ser causado pelo arquivo de entrada PEM ter um cabeçalho acima do delimitador --- BEGIN ou ter vários PEMs em um arquivo ou em ambos. Remova todos os dados estranhos e alimente cada PEM de cada vez ou use minha ferramenta, conforme detalhado na minha resposta.
Alastair McCormack
Obrigado @Fuzzyfelt, vou dar uma olhada
frandevel
1
O mesmo problema e o arquivo .PEM está limpo, com todos os cabeçalhos apropriados.
Brian Knoblauch
17
Desenvolvi o http://code.google.com/p/java-keyutil/ que importa certificados PEM diretamente para um keystore Java. Seu principal objetivo é importar um pacote de certificados do sistema operacional PEM com várias partes, como ca-bundle.crt. Isso geralmente inclui cabeçalhos que o keytool não suporta
Não é um projeto ruim de brinquedo, mas keytooljá faz tudo isso para você (e muito mais). (By the way, você deve fechar o seu FileOutputStream, e fechar o I / O ribeiros no finally, se uma exceção acontece.)
de Bruno
8
Oi Bruno, obrigado por dicas. O caso de uso real é importar todas as entradas do /etc/pki/tls/certs/ca-bundle.crt (RHEL / CentOS) de uma só vez. AFAIK, keytool importará apenas a primeira entrada. Eu já vi várias pessoas fazendo isso de maneira diferente, mas geralmente envolve invocar o keytool várias vezes para cada certificado. O Ubuntu possui um script de atualização que faz exatamente isso, exceto que o Ubuntu armazena seus certificados em um diretório. Eu adicionarei suporte para diretórios em um futuro próximo. Obrigado novamente por revisar o código.
Alastair McCormack
14
No meu caso, eu tinha um arquivo pem que continha dois certificados e uma chave privada criptografada para ser usada na autenticação SSL mútua. Então, meu arquivo pem ficou assim:
Divida o arquivo em três arquivos separados, para que cada um contenha apenas uma entrada, começando com ---BEGIN..e terminando com ---END..linhas. Vamos supor agora temos três arquivos: cert1.pem, cert2.pem, e pkey.pem.
Converta pkey.pemno formato DER usando o openssl e a seguinte sintaxe:
Observe que, se a chave privada estiver criptografada, você precisará fornecer uma senha (obtenha-a do fornecedor do arquivo pem original) para converter para o formato DER,
opensslsolicitará a senha da seguinte maneira: "insira uma senha para pkey.pem:".
Se a conversão for bem sucedida, você receberá um novo arquivo chamado pkey.der.
Crie um novo keystore java e importe a chave privada e os certificados:
String keypass ="password";// this is a new password, you need to come up with to protect your java key store fileString defaultalias ="importkey";KeyStore ks =KeyStore.getInstance("JKS","SUN");// this section does not make much sense to me, // but I will leave it intact as this is how it was in the original example I found on internet:
ks.load(null, keypass.toCharArray());
ks.store(newFileOutputStream("mykeystore"), keypass.toCharArray());
ks.load(newFileInputStream("mykeystore"), keypass.toCharArray());// end of section..// read the key file from disk and create a PrivateKeyFileInputStream fis =newFileInputStream("pkey.der");DataInputStream dis =newDataInputStream(fis);byte[] bytes =newbyte[dis.available()];
dis.readFully(bytes);ByteArrayInputStream bais =newByteArrayInputStream(bytes);byte[] key =newbyte[bais.available()];KeyFactory kf =KeyFactory.getInstance("RSA");
bais.read(key,0, bais.available());
bais.close();
PKCS8EncodedKeySpec keysp =new PKCS8EncodedKeySpec ( key );PrivateKey ff = kf.generatePrivate (keysp);// read the certificates from the files and load them into the key store:Collection col_crt1 =CertificateFactory.getInstance("X509").generateCertificates(newFileInputStream("cert1.pem"));Collection col_crt2 =CertificateFactory.getInstance("X509").generateCertificates(newFileInputStream("cert2.pem"));Certificate crt1 =(Certificate) col_crt1.iterator().next();Certificate crt2 =(Certificate) col_crt2.iterator().next();Certificate[] chain =newCertificate[]{ crt1, crt2 };String alias1 =((X509Certificate) crt1).getSubjectX500Principal().getName();String alias2 =((X509Certificate) crt2).getSubjectX500Principal().getName();
ks.setCertificateEntry(alias1, crt1);
ks.setCertificateEntry(alias2, crt2);// store the private key
ks.setKeyEntry(defaultalias, ff, keypass.toCharArray(), chain );// save the key store to a file
ks.store(newFileOutputStream("mykeystore"),keypass.toCharArray());
(opcional) Verifique o conteúdo do seu novo armazenamento de chaves:
cn = ..., ou = ..., o = .., 2 de setembro de 2014, trustCertEntry, impressão digital do certificado (SHA1): 2C: B8: ...
importkey, 2 de setembro de 2014, PrivateKeyEntry, impressão digital do certificado (SHA1): 9C: B0: ...
cn = ..., o = ...., 2 de setembro de 2014, trustCertEntry, impressão digital do certificado (SHA1): 83:63: ...
(opcional) Teste seus certificados e chave privada do seu novo armazenamento de chaves em seu servidor SSL: (convém ativar a depuração como uma opção de VM: -Djavax.net.debug = all)
char[] passw ="password".toCharArray();KeyStore ks =KeyStore.getInstance("JKS","SUN");
ks.load(newFileInputStream("mykeystore"), passw );KeyManagerFactory kmf =KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, passw);TrustManagerFactory tmf =TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);TrustManager[] tm = tmf.getTrustManagers();SSLContext sclx =SSLContext.getInstance("TLS");
sclx.init( kmf.getKeyManagers(), tm,null);SSLSocketFactory factory = sclx.getSocketFactory();SSLSocket socket =(SSLSocket) factory.createSocket("192.168.1.111",443);
socket.startHandshake();//if no exceptions are thrown in the startHandshake method, then everything is fine..
Por fim, registre seus certificados no HttpsURLConnection se planeja usá-lo:
Seu verificador de nome de host está errado, session.getPeerHost()não retorna o nome no certificado, mas o nome com o qual você se conectou (ou seja, o urlHostNameaqui), portanto sempre será verdade. Você está sempre voltando de truequalquer maneira.
Bruno Bruno
9
Se você precisar de uma maneira fácil de carregar arquivos PEM em Java sem precisar lidar com ferramentas externas (opensll, keytool) , eis o meu código que uso na produção:
import java.io.BufferedReader;import java.io.ByteArrayInputStream;import java.io.File;import java.io.FileReader;import java.io.IOException;import java.security.KeyFactory;import java.security.KeyStore;import java.security.KeyStoreException;import java.security.NoSuchAlgorithmException;import java.security.PrivateKey;import java.security.cert.CertificateException;import java.security.cert.CertificateFactory;import java.security.cert.X509Certificate;import java.security.interfaces.RSAPrivateKey;import java.security.spec.InvalidKeySpecException;import java.security.spec.PKCS8EncodedKeySpec;import java.util.ArrayList;import java.util.List;import javax.net.ssl.KeyManager;import javax.net.ssl.KeyManagerFactory;import javax.net.ssl.SSLContext;import javax.net.ssl.SSLServerSocketFactory;import javax.xml.bind.DatatypeConverter;publicclassPEMImporter{publicstaticSSLServerSocketFactory createSSLFactory(File privateKeyPem,File certificatePem,String password)throwsException{finalSSLContext context =SSLContext.getInstance("TLS");finalKeyStore keystore = createKeyStore(privateKeyPem, certificatePem, password);finalKeyManagerFactory kmf =KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, password.toCharArray());finalKeyManager[] km = kmf.getKeyManagers();
context.init(km,null,null);return context.getServerSocketFactory();}/**
* Create a KeyStore from standard PEM files
*
* @param privateKeyPem the private key PEM file
* @param certificatePem the certificate(s) PEM file
* @param the password to set to protect the private key
*/publicstaticKeyStore createKeyStore(File privateKeyPem,File certificatePem,finalString password)throwsException,KeyStoreException,IOException,NoSuchAlgorithmException,CertificateException{final X509Certificate[] cert = createCertificates(certificatePem);finalKeyStore keystore =KeyStore.getInstance("JKS");
keystore.load(null);// Import private keyfinalPrivateKey key = createPrivateKey(privateKeyPem);
keystore.setKeyEntry(privateKeyPem.getName(), key, password.toCharArray(), cert);return keystore;}privatestaticPrivateKey createPrivateKey(File privateKeyPem)throwsException{finalBufferedReader r =newBufferedReader(newFileReader(privateKeyPem));String s = r.readLine();if(s ==null||!s.contains("BEGIN PRIVATE KEY")){
r.close();thrownewIllegalArgumentException("No PRIVATE KEY found");}finalStringBuilder b =newStringBuilder();
s ="";while(s !=null){if(s.contains("END PRIVATE KEY")){break;}
b.append(s);
s = r.readLine();}
r.close();finalString hexString = b.toString();finalbyte[] bytes =DatatypeConverter.parseBase64Binary(hexString);return generatePrivateKeyFromDER(bytes);}privatestatic X509Certificate[] createCertificates(File certificatePem)throwsException{finalList<X509Certificate> result =newArrayList<X509Certificate>();finalBufferedReader r =newBufferedReader(newFileReader(certificatePem));String s = r.readLine();if(s ==null||!s.contains("BEGIN CERTIFICATE")){
r.close();thrownewIllegalArgumentException("No CERTIFICATE found");}StringBuilder b =newStringBuilder();while(s !=null){if(s.contains("END CERTIFICATE")){String hexString = b.toString();finalbyte[] bytes =DatatypeConverter.parseBase64Binary(hexString);
X509Certificate cert = generateCertificateFromDER(bytes);
result.add(cert);
b =newStringBuilder();}else{if(!s.startsWith("----")){
b.append(s);}}
s = r.readLine();}
r.close();return result.toArray(new X509Certificate[result.size()]);}privatestaticRSAPrivateKey generatePrivateKeyFromDER(byte[] keyBytes)throwsInvalidKeySpecException,NoSuchAlgorithmException{final PKCS8EncodedKeySpec spec =new PKCS8EncodedKeySpec(keyBytes);finalKeyFactory factory =KeyFactory.getInstance("RSA");return(RSAPrivateKey) factory.generatePrivate(spec);}privatestatic X509Certificate generateCertificateFromDER(byte[] certBytes)throwsCertificateException{finalCertificateFactory factory =CertificateFactory.getInstance("X.509");return(X509Certificate) factory.generateCertificate(newByteArrayInputStream(certBytes));}}
Portecle é um aplicativo GUI amigável para a criação, gerenciamento e análise de keystores, chaves, certificados, solicitações de certificados, listas de revogação de certificados e muito mais.
O key store explorer é a versão moderna do portecle. não há diferença entre os menus e as funcionalidades.
Setmax 23/07
0
Eu peguei na internet. Funciona muito bem para arquivos pem que contêm várias entradas.
#!/bin/bash
pemToJks(){# number of certs in the PEM file
pemCerts=$1
certPass=$2
newCert=$(basename "$pemCerts")
newCert="${newCert%%.*}"
newCert="${newCert}"".JKS"##echo $newCert $pemCerts $certPass
CERTS=$(grep 'END CERTIFICATE' $pemCerts| wc -l)
echo $CERTS
#For every cert in the PEM file, extract it and import into the JKS keystore
# awk command: step 1,if line is in the desired cert, print the line
# step 2, increment counter when last line of cert is found
for N in $(seq 0 $(($CERTS -1)));do
ALIAS="${pemCerts%.*}-$N"
cat $pemCerts |
awk "n==$N { print }; /END CERTIFICATE/ { n++ }"|
$KEYTOOLCMD -noprompt -import-trustcacerts \
-alias $ALIAS -keystore $newCert -storepass $certPass
done
}
pemToJks <pem to import><pass fornew jks>
Respostas:
Primeiro, converta seu certificado em um formato DER:
E depois, importe-o no keystore:
fonte
Se você deseja importar apenas um certificado no formato PEM para um keystore, o keytool fará o trabalho:
fonte
Desenvolvi o http://code.google.com/p/java-keyutil/ que importa certificados PEM diretamente para um keystore Java. Seu principal objetivo é importar um pacote de certificados do sistema operacional PEM com várias partes, como ca-bundle.crt. Isso geralmente inclui cabeçalhos que o keytool não suporta
fonte
keytool
já faz tudo isso para você (e muito mais). (By the way, você deve fechar o seuFileOutputStream
, e fechar o I / O ribeiros nofinally
, se uma exceção acontece.)No meu caso, eu tinha um arquivo pem que continha dois certificados e uma chave privada criptografada para ser usada na autenticação SSL mútua. Então, meu arquivo pem ficou assim:
Aqui está o que eu fiz
Divida o arquivo em três arquivos separados, para que cada um contenha apenas uma entrada, começando com
---BEGIN..
e terminando com---END..
linhas. Vamos supor agora temos três arquivos:cert1.pem
,cert2.pem
, epkey.pem
.Converta
pkey.pem
no formato DER usando o openssl e a seguinte sintaxe:Observe que, se a chave privada estiver criptografada, você precisará fornecer uma senha (obtenha-a do fornecedor do arquivo pem original) para converter para o formato DER,
openssl
solicitará a senha da seguinte maneira: "insira uma senha parapkey.pem
:".Se a conversão for bem sucedida, você receberá um novo arquivo chamado
pkey.der
.Crie um novo keystore java e importe a chave privada e os certificados:
(opcional) Verifique o conteúdo do seu novo armazenamento de chaves:
Tipo de keystore: provedor de keystore JKS: SUN
Seu keystore contém 3 entradas:
cn = ..., ou = ..., o = .., 2 de setembro de 2014, trustCertEntry, impressão digital do certificado (SHA1): 2C: B8: ...
importkey, 2 de setembro de 2014, PrivateKeyEntry, impressão digital do certificado (SHA1): 9C: B0: ...
cn = ..., o = ...., 2 de setembro de 2014, trustCertEntry, impressão digital do certificado (SHA1): 83:63: ...
(opcional) Teste seus certificados e chave privada do seu novo armazenamento de chaves em seu servidor SSL: (convém ativar a depuração como uma opção de VM: -Djavax.net.debug = all)
Por fim, registre seus certificados no HttpsURLConnection se planeja usá-lo:
fonte
session.getPeerHost()
não retorna o nome no certificado, mas o nome com o qual você se conectou (ou seja, ourlHostName
aqui), portanto sempre será verdade. Você está sempre voltando detrue
qualquer maneira.Se você precisar de uma maneira fácil de carregar arquivos PEM em Java sem precisar lidar com ferramentas externas (opensll, keytool) , eis o meu código que uso na produção:
Diverta-se.
fonte
Estou sempre esquecendo como fazer isso, porque é algo que eu faço de vez em quando, essa é uma solução possível e simplesmente funciona:
Execute as duas seguintes linhas de código:
Se estiver executando no ambiente Java SE, adicione as seguintes opções:
Ou adicione o seguinte ao código java:
A outra opção para a etapa 2 é usar apenas o
keytool
comando Abaixo está um exemplo com uma cadeia de certificados:fonte
Eu usei o Keystore Explorer
fonte
Há também uma ferramenta GUI que permite a criação visual do JKS e a importação de certificados.
http://portecle.sourceforge.net/
fonte
Eu peguei na internet. Funciona muito bem para arquivos pem que contêm várias entradas.
fonte