J'essaie de me connecter à un serveur SSL sur lequel je dois m'authentifier. Pour utiliser SSL sur Apache MINA, j'ai besoin d'un fichier JKS approprié. Cependant, je n'ai reçu qu'un fichier .PEM.
Comment pourrais-je créer un fichier JKS à partir d'un fichier PEM?
Commencez par convertir votre certificat au format DER:
openssl x509 -outform der -in certificate.pem -out certificate.der
Et ensuite, importez-le dans le magasin de clés:
keytool -import -alias your-alias -keystore cacerts -file certificate.der
Si vous souhaitez uniquement importer un certificat au format PEM dans un magasin de clés, keytool fera le travail:
keytool -import -alias *alias* -keystore cacerts -file *cert.pem*
J'ai développé http://code.google.com/p/Java-keyutil/ qui importe les certificats PEM directement dans un magasin de clés Java. Son objectif principal est d'importer des ensembles de certificats de système d'exploitation PEM en plusieurs parties, tels que ca-bundle.crt. Ceux-ci incluent souvent des en-têtes que keytool ne peut pas gérer
</self promotion>
Dans mon cas, j'avais un fichier PEM contenant deux certificats et une clé privée cryptée à utiliser dans l'authentification mutuelle SSL. Donc, mon fichier PEM ressemblait à ceci:
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,C8BF220FC76AA5F9
...
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
Divisez le fichier en trois fichiers distincts, de sorte que chacun ne contienne qu'une entrée, commençant par ---BEGIN..
et se terminant par ---END..
lignes. Supposons que nous ayons maintenant trois fichiers: cert1.pem
, cert2.pem
et pkey.pem
.
Convertissez pkey.pem
au format DER en utilisant openssl et la syntaxe suivante:
openssl pkcs8 -topk8 -nocrypt -in pkey.pem -inform PEM -out pkey.der -outform DER
Notez que si la clé privée est cryptée, vous devez fournir un mot de passe (obtenez-le auprès du fournisseur du fichier pem original) pour le convertir au format DER, openssl
vous demandera le mot de passe de la manière suivante: "entrez un phrase secrète pour pkey.pem
: ".
Si la conversion aboutit, vous obtiendrez un nouveau fichier appelé pkey.der
.
Créez un nouveau magasin de clés Java et importez la clé privée et les certificats:
String keypass = "password"; // this is a new password, you need to come up with to protect your Java key store file
String 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( new FileOutputStream ( "mykeystore" ), keypass.toCharArray());
ks.load( new FileInputStream ( "mykeystore" ), keypass.toCharArray());
// end of section..
// read the key file from disk and create a PrivateKey
FileInputStream fis = new FileInputStream("pkey.der");
DataInputStream dis = new DataInputStream(fis);
byte[] bytes = new byte[dis.available()];
dis.readFully(bytes);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
byte[] key = new byte[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(new FileInputStream("cert1.pem"));
Collection col_crt2 = CertificateFactory.getInstance("X509").generateCertificates(new FileInputStream("cert2.pem"));
Certificate crt1 = (Certificate) col_crt1.iterator().next();
Certificate crt2 = (Certificate) col_crt2.iterator().next();
Certificate[] chain = new Certificate[] { 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(new FileOutputStream ( "mykeystore" ),keypass.toCharArray());
(facultatif) Vérifiez le contenu de votre nouveau magasin de clés:
$ keytool -list -keystore mykeystore -storepass password
Type de magasin de clés: JKS Fournisseur de magasin de clés: Sun
Votre magasin de clés contient 3 entrées:
cn = ..., ou = ..., o = .., 2 septembre 2014, trustedCertEntry, empreinte digitale du certificat (SHA1): 2C: B8: ...
importkey, 2 sept. 2014, PrivateKeyEntry, Empreinte digitale du certificat (SHA1): 9C: B0: ...
cn = ..., o = ...., 2 septembre 2014, TrustedCertEntry, empreinte digitale du certificat (SHA1): 83:63: ...
(facultatif) Testez vos certificats et votre clé privée de votre nouveau magasin de clés sur votre serveur SSL: (Vous pouvez activer le débogage en tant qu'option VM: -Djavax.net.debug = all)
char[] passw = "password".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS", "Sun");
ks.load(new FileInputStream ( "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..
Enfin, enregistrez vos certificats avec HttpsURLConnection si vous prévoyez de l’utiliser:
char[] passw = "password".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS", "Sun");
ks.load(new FileInputStream ( "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);
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.setDefaultSSLSocketFactory( sclx.getSocketFactory() );
HttpsURLConnection.setDefaultHostnameVerifier(hv);
J'oublie toujours comment faire cela parce que c'est quelque chose que je fais juste de temps en temps, c'est une solution possible, et ça marche:
Exécutez les deux lignes de code suivantes:
$ openssl x509 -outform der -in GlobalSignRootCA.crt -out GlobalSignRootCA.der
$ keytool -import -alias GlobalSignRootCA -keystore GlobalSignRootCA.jks -file GlobalSignRootCA.der
Si vous travaillez dans l'environnement Java SE, ajoutez les options suivantes:
$ Java -Djavax.net.ssl.trustStore=GlobalSignRootCA.jks -Djavax.net.ssl.trustStorePassword=trustStorePassword -jar MyJar.jar
Ou ajoutez les éléments suivants au code Java:
System.setProperty("javax.net.ssl.trustStore", "GlobalSignRootCA.jks");
System.setProperty("javax.net.ssl.trustStorePassword","trustStorePassword");
L'autre option pour l'étape 2 consiste simplement à utiliser la commande keytool
. Ci-dessous, un exemple avec une chaîne de certificats:
$ keytool -import -file org.eu.crt -alias orgcrt -keystore globalsignrs.jks
$ keytool -import -file GlobalSignOrganizationValidationCA-SHA256-G2.crt -alias globalsignorgvalca -keystore globalsignrs.jks
$ keytool -import -file GlobalSignRootCA.crt -alias globalsignrootca -keystore globalsignrs.jks
Si vous avez besoin d'un moyen simple de charger des fichiers PEM dans Java sans avoir à utiliser d'outils externes (opensll, keytool), voici le code que j'utilise en production:
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;
public class PEMImporter {
public static SSLServerSocketFactory createSSLFactory(File privateKeyPem, File certificatePem, String password) throws Exception {
final SSLContext context = SSLContext.getInstance("TLS");
final KeyStore keystore = createKeyStore(privateKeyPem, certificatePem, password);
final KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, password.toCharArray());
final KeyManager[] 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
*/
public static KeyStore createKeyStore(File privateKeyPem, File certificatePem, final String password)
throws Exception, KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
final X509Certificate[] cert = createCertificates(certificatePem);
final KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null);
// Import private key
final PrivateKey key = createPrivateKey(privateKeyPem);
keystore.setKeyEntry(privateKeyPem.getName(), key, password.toCharArray(), cert);
return keystore;
}
private static PrivateKey createPrivateKey(File privateKeyPem) throws Exception {
final BufferedReader r = new BufferedReader(new FileReader(privateKeyPem));
String s = r.readLine();
if (s == null || !s.contains("BEGIN PRIVATE KEY")) {
r.close();
throw new IllegalArgumentException("No PRIVATE KEY found");
}
final StringBuilder b = new StringBuilder();
s = "";
while (s != null) {
if (s.contains("END PRIVATE KEY")) {
break;
}
b.append(s);
s = r.readLine();
}
r.close();
final String hexString = b.toString();
final byte[] bytes = DatatypeConverter.parseBase64Binary(hexString);
return generatePrivateKeyFromDER(bytes);
}
private static X509Certificate[] createCertificates(File certificatePem) throws Exception {
final List<X509Certificate> result = new ArrayList<X509Certificate>();
final BufferedReader r = new BufferedReader(new FileReader(certificatePem));
String s = r.readLine();
if (s == null || !s.contains("BEGIN CERTIFICATE")) {
r.close();
throw new IllegalArgumentException("No CERTIFICATE found");
}
StringBuilder b = new StringBuilder();
while (s != null) {
if (s.contains("END CERTIFICATE")) {
String hexString = b.toString();
final byte[] bytes = DatatypeConverter.parseBase64Binary(hexString);
X509Certificate cert = generateCertificateFromDER(bytes);
result.add(cert);
b = new StringBuilder();
} else {
if (!s.startsWith("----")) {
b.append(s);
}
}
s = r.readLine();
}
r.close();
return result.toArray(new X509Certificate[result.size()]);
}
private static RSAPrivateKey generatePrivateKeyFromDER(byte[] keyBytes) throws InvalidKeySpecException, NoSuchAlgorithmException {
final PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
final KeyFactory factory = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) factory.generatePrivate(spec);
}
private static X509Certificate generateCertificateFromDER(byte[] certBytes) throws CertificateException {
final CertificateFactory factory = CertificateFactory.getInstance("X.509");
return (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(certBytes));
}
}
S'amuser.
J'ai utilisé Keystore Explorer
Il existe également un outil graphique permettant la création visuelle de JKS et l’importation de certificats.
http://portecle.sourceforge.net/
Portecle est une application graphique conviviale permettant de créer, gérer et examiner des fichiers de clés, des clés, des certificats, des demandes de certificat, des listes de révocation de certificats, etc.
Je l'ai eu sur internet. Cela fonctionne très bien pour les fichiers PEM contenant plusieurs entrées.
#!/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 for new jks>