Importer PEM dans Java Key Store

144

J'essaye de me connecter à un serveur SSL qui m'oblige à 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 créer un fichier JKS à partir d'un fichier PEM?

jwoolard
la source
1
Ce lien pourrait être utile: http://www.agentbob.info/agentbob/79-AB.html
Laurent K

Réponses:

235

Commencez par convertir votre certificat au format DER:

openssl x509 -outform der -in certificate.pem -out certificate.der

Et après, importez-le dans le keystore:

keytool -import -alias your-alias -keystore cacerts -file certificate.der
Anthony O.
la source
7
Ne fonctionne pas si le fichier .pem contient plusieurs certificats.
MarioVilas
14
J'ai un seul certificat .pem et cela ne fonctionne pas. 1795: erreur: 0906D06C: routines PEM: PEM_read_bio: pas de ligne de départ: / usr / src / secure / lib / libcrypto /../../../ crypto / openssl / crypto / pem / pem_lib.c: 648: attente : CERTIFICAT DE CONFIANCE
Brian Knoblauch
4
J'ai trouvé la solution. Pré-suspendez les certificats racine et intermédiaires au .pem, puis convertissez.
Brian Knoblauch
1
@Anthony cette commande indique uniquement comment importer un PEM dans JKS. Peut être une bonne idée d'ajouter une commande pour exporter JKS depuis le magasin.
Vishal Biyani
2
Si j'ai plusieurs certificats sur le .pem, comment importer dans un keystore Java?
Erick
55

Si vous souhaitez uniquement importer un certificat au format PEM dans un keystore, keytool fera le travail:

keytool -import -alias *alias* -keystore cacerts -file *cert.pem*
Zap
la source
11
Si je vais comme ça, j'obtiens une erreur: keytool error: java.lang.Exception: Input not an X.509 certificate
frandevel
1
@frandevel, cette erreur peut être causée par le fichier d'entrée PEM ayant un en-tête au-dessus du délimiteur --- BEGIN ou par plusieurs PEM dans un fichier ou les deux. Supprimez toutes les données superflues et alimentez chaque PEM en une seule fois ou utilisez mon outil, comme détaillé dans ma réponse.
Alastair McCormack
Merci @Fuzzyfelt, je vais jeter un oeil
frandevel
1
Même problème et le fichier .PEM est propre, avec tous les en-têtes appropriés.
Brian Knoblauch
17

J'ai développé http://code.google.com/p/java-keyutil/ qui importe les certificats PEM directement dans un keystore 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>
Alastair McCormack
la source
4
Ce n'est pas un mauvais projet de jouet, mais fait keytooldéjà tout cela pour vous (et plus). (Au fait, vous devriez fermer votre FileOutputStream, et fermer vos flux d'E / S finally, si une exception se produit.)
Bruno
8
Salut Bruno, merci pour les conseils. Le vrai cas d'utilisation est d'importer toutes les entrées de /etc/pki/tls/certs/ca-bundle.crt (RHEL / CentOS) en une seule fois. AFAIK, keytool importera uniquement la première entrée. J'ai vu un certain nombre de personnes faire cela différemment, mais cela implique généralement d'appeler keytool plusieurs fois pour chaque certificat. Ubuntu a un script de mise à jour qui fait exactement cela, sauf qu'Ubuntu stocke ses certificats dans un répertoire. J'ajouterai la prise en charge des répertoires dans un proche avenir. Merci encore d'avoir lu le code.
Alastair McCormack
14

Dans mon cas, j'avais un fichier pem qui contenait deux certificats et une clé privée cryptée à utiliser dans l'authentification SSL mutuelle. 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-----

Voici ce que j'ai fait

Divisez le fichier en trois fichiers séparés, de sorte que chacun ne contienne qu'une seule entrée, commençant par ---BEGIN..et se terminant par des ---END..lignes. Supposons aussi que nous avons maintenant trois fichiers: cert1.pem, cert2.pemet pkey.pem.

Convertissez pkey.pemau 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 d'origine) pour convertir au format DER, opensslvous demandera le mot de passe comme ceci: "entrez une phrase de passe pour pkey.pem:".

Si la conversion réussit, vous obtiendrez un nouveau fichier appelé pkey.der.

Créez un nouveau keystore 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 fichier de clés: Fournisseur de fichier de clés JKS: SUN

Votre keystore contient 3 entrées:

  • cn = ..., ou = ..., o = .., 2 septembre 2014, trustedCertEntry, Empreinte du certificat (SHA1): 2C: B8: ...

  • importkey, 2 septembre 2014, PrivateKeyEntry, Empreinte du certificat (SHA1): 9C: B0: ...

  • cn = ..., o = ...., 2 septembre 2014, trustedCertEntry, Empreinte du certificat (SHA1): 83:63: ...

(facultatif) Testez vos certificats et votre clé privée de votre nouveau magasin de clés par rapport à 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);
Interkot
la source
Votre vérificateur de nom d'hôte est erroné, session.getPeerHost()ne renvoie pas le nom dans le certificat, mais le nom avec urlHostNamelequel vous vous êtes connecté (c'est-à-dire l' ici), donc ce sera toujours vrai. Vous revenez toujours de truetoute façon.
Bruno le
9

Si vous avez besoin d'un moyen simple de charger des fichiers PEM en Java sans avoir à gérer des outils externes (opensll, keytool) , voici mon 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.

BluEOS
la source
La question portait sur "SSL sur Apache MINA" qui est plus simple à configurer avec la fonction "SSLServerSocketFactory de PEMs" fournie , voir mina.apache.org/mina-project/userguide/ch11-ssl-filter/… .
BluEOS
8

J'oublie toujours comment faire cela parce que c'est quelque chose que je ne fais que de temps en temps, c'est une solution possible, et cela fonctionne:

  1. Accédez à votre navigateur préféré et téléchargez le certificat principal à partir du site Web sécurisé.
  2. 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
  3. Si vous exécutez dans l'environnement Java SE, ajoutez les options suivantes:

    $ java -Djavax.net.ssl.trustStore=GlobalSignRootCA.jks -Djavax.net.ssl.trustStorePassword=trustStorePassword -jar MyJar.jar
  4. Ou ajoutez ce qui suit 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 keytoolcommande. Ci-dessous est 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
Marco
la source
7

J'ai utilisé Keystore Explorer

  1. Ouvrez JKS avec une clé privée
  2. Examiner le PEM signé de CA
  3. Importer la clé
  4. Enregistrer JKS
Léos Literak
la source
3
Keystore Explorer est génial et très polyvalent. Économise une fois de passer quelques minutes stupides sur le terminal.
TheRealChx101
3

Il existe également un outil GUI qui permet la création visuelle de JKS et l'importation de certificats.

http://portecle.sourceforge.net/

Portecle est une application GUI conviviale pour la création, la gestion et l'examen des fichiers de clés, des clés, des certificats, des demandes de certificats, des listes de révocation de certificats, etc.

Vadzim
la source
1
Key Store Explorer est la version moderne de portecle. il n'y a aucune différence entre leurs menus et leurs fonctionnalités.
Setmax le
0

Je l'ai eu sur Internet. Cela fonctionne assez 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>
John Smith
la source