Importieren Sie PEM in den Java Key Store


144

Ich versuche, eine Verbindung zu einem SSL-Server herzustellen, für den ich mich authentifizieren muss. Um SSL über Apache MINA verwenden zu können, benötige ich eine geeignete JKS-Datei. Ich habe jedoch nur eine .PEM-Datei erhalten.

Wie würde ich eine JKS-Datei aus einer PEM-Datei erstellen?


1
Möglicherweise könnte dieser Link hilfreich sein: http://www.agentbob.info/agentbob/79-AB.html
Laurent K

Antworten:


234

Konvertieren Sie zunächst Ihr Zertifikat in ein DER-Format:

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

Und danach importieren Sie es in den Keystore:

keytool -import -alias your-alias -keystore cacerts -file certificate.der

7
Funktioniert nicht, wenn die PEM-Datei mehr als ein Zertifikat enthält.
MarioVilas

14
Ich habe ein einzelnes Zertifikat .pem und das funktioniert nicht. 1795: Fehler: 0906D06C: PEM-Routinen: PEM_read_bio: Keine Startzeile: / usr / src / secure / lib / libcrypto /../../../ crypto / openssl / crypto / pem / pem_lib.c: 648: Erwarten : VERTRAUENES ZERTIFIKAT
Brian Knoblauch

4
Ich habe die Lösung gefunden. Hängen Sie die Root- und Zwischenzertifikate an die .pem-Datei an und konvertieren Sie sie dann.
Brian Knoblauch

1
@Anthony Dieser Befehl gibt nur an, wie eine PEM in JKS importiert wird. Möglicherweise empfiehlt es sich, einen Befehl zum Exportieren von JKS aus dem Store hinzuzufügen.
Vishal Biyani

2
Wie importiere ich in einen Java-Keystore, wenn ich mehrere Zertifikate in der .pem-Datei habe?
Erick

55

Wenn Sie nur ein Zertifikat im PEM-Format in einen Keystore importieren möchten, erledigt keytool die Aufgabe:

keytool -import -alias *alias* -keystore cacerts -file *cert.pem*

11
Wenn ich so vorgehe, erhalte ich eine Fehlermeldung: keytool error: java.lang.Exception: Geben Sie kein X.509-Zertifikat ein
frandevel

1
@frandevel, dieser Fehler kann dadurch verursacht werden, dass die PEM-Eingabedatei einen Header über dem --- BEGIN-Trennzeichen hat oder mehrere PEMs in einer oder beiden Dateien. Entfernen Sie entweder alle überflüssigen Daten und geben Sie jede PEM einzeln ein oder verwenden Sie mein Tool, wie in meiner Antwort beschrieben.
Alastair McCormack

Danke @Fuzzyfelt, ich werde einen Blick darauf werfen
frandevel

1
Gleiches Problem und die .PEM-Datei ist sauber, mit allen entsprechenden Headern.
Brian Knoblauch

17

Ich habe http://code.google.com/p/java-keyutil/ entwickelt, das PEM-Zertifikate direkt in einen Java-Keystore importiert. Der Hauptzweck besteht darin, ein mehrteiliges PEM-Betriebssystem-Zertifikatspaket wie ca-bundle.crt zu importieren. Dazu gehören häufig Header, die Keytool nicht verarbeiten kann

</self promotion>

4
Kein schlechtes Spielzeugprojekt, aber keytooldas alles erledigt es bereits für Sie (und mehr). (Übrigens sollten Sie Ihre FileOutputStreamund Ihre E / A-Streams schließen finally, wenn eine Ausnahme auftritt.)
Bruno

8
Hallo Bruno, danke für die Tipps. Der eigentliche Anwendungsfall besteht darin, alle Einträge von /etc/pki/tls/certs/ca-bundle.crt (RHEL / CentOS) auf einmal zu importieren. AFAIK, Keytool importiert nur den ersten Eintrag. Ich habe eine Reihe von Leuten gesehen, die dies anders gemacht haben, aber normalerweise wird das Keytool für jedes Zertifikat mehrmals aufgerufen. Ubuntu hat ein Update-Skript, das genau dies tut, außer dass Ubuntu seine Zertifikate in einem Verzeichnis speichert. Ich werde in naher Zukunft Unterstützung für Verzeichnisse hinzufügen. Nochmals vielen Dank für die Überprüfung des Codes.
Alastair McCormack

14

In meinem Fall hatte ich eine PEM-Datei, die zwei Zertifikate und einen verschlüsselten privaten Schlüssel zur gegenseitigen SSL-Authentifizierung enthielt. Meine PEM-Datei sah also so aus:

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

Hier ist was ich getan habe

Teilen Sie die Datei in drei separate Dateien auf, sodass jede nur einen Eintrag enthält, beginnend mit ---BEGIN..und endend mit ---END..Zeilen. Nehmen wir an , wir haben jetzt drei Dateien: cert1.pem, cert2.pem, und pkey.pem.

Konvertieren Sie pkey.pemmit openssl und der folgenden Syntax in das DER-Format:

openssl pkcs8 -topk8 -nocrypt -in pkey.pem -inform PEM -out pkey.der -outform DER

Beachten Sie, dass Sie, wenn der private Schlüssel verschlüsselt ist, ein Kennwort angeben müssen (dieses vom Lieferanten der ursprünglichen PEM-Datei erhalten), um es in das DER-Format zu konvertieren. opensslSie werden dann nach dem Kennwort gefragt: " Geben Sie eine Passphrase ein für pkey.pem:".

Wenn die Konvertierung erfolgreich ist, erhalten Sie eine neue Datei mit dem Namen pkey.der.

Erstellen Sie einen neuen Java-Keystore und importieren Sie den privaten Schlüssel und die Zertifikate:

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());

(optional) Überprüfen Sie den Inhalt Ihres neuen Schlüsselspeichers:

$ keytool -list -keystore mykeystore -storepass password

Keystore-Typ: JKS Keystore-Anbieter: SUN

Ihr Keystore enthält 3 Einträge:

  • cn = ..., ou = ..., o = .., 2. September 2014, trustedCertEntry, Zertifikatfingerabdruck (SHA1): 2C: B8: ...

  • importkey, 2. September 2014, PrivateKeyEntry, Zertifikatfingerabdruck (SHA1): 9C: B0: ...

  • cn = ..., o = ...., 2. September 2014, trustedCertEntry, Zertifikatfingerabdruck (SHA1): 83:63: ...

(optional) Testen Sie Ihre Zertifikate und Ihren privaten Schlüssel aus Ihrem neuen Schlüsselspeicher mit Ihrem SSL-Server: (Möglicherweise möchten Sie das Debuggen als VM-Option aktivieren: -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..

Registrieren Sie abschließend Ihre Zertifikate bei HttpsURLConnection, wenn Sie diese verwenden möchten:

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

Ihr Hostname-Verifizierer ist falsch und session.getPeerHost()gibt nicht den Namen im Zertifikat zurück, sondern den Namen, mit dem Sie verbunden sind (dh den urlHostNamehier). Das wird also immer wahr sein. Du kommst truesowieso immer zurück .
Bruno

9

Wenn Sie eine einfache Möglichkeit zum Laden von PEM-Dateien in Java benötigen, ohne sich mit externen Tools (opensll, keytool) befassen zu müssen, ist hier mein Code, den ich in der Produktion verwende:

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));
    }

}

Habe Spaß.


Die Frage betraf "SSL über Apache MINA", das mit der bereitgestellten Funktion "SSLServerSocketFactory aus PEMs" einfacher zu konfigurieren ist, siehe mina.apache.org/mina-project/userguide/ch11-ssl-filter/… .
BluEOS

8

Ich vergesse immer, wie man das macht, weil es etwas ist, das ich nur ab und zu mache. Dies ist eine mögliche Lösung und es funktioniert einfach:

  1. Gehen Sie zu Ihrem bevorzugten Browser und laden Sie das Hauptzertifikat von der gesicherten Website herunter.
  2. Führen Sie die beiden folgenden Codezeilen aus:

    $ openssl x509 -outform der -in GlobalSignRootCA.crt -out GlobalSignRootCA.der
    $ keytool -import -alias GlobalSignRootCA -keystore GlobalSignRootCA.jks -file GlobalSignRootCA.der
    
  3. Fügen Sie bei Ausführung in einer Java SE-Umgebung die folgenden Optionen hinzu:

    $ java -Djavax.net.ssl.trustStore=GlobalSignRootCA.jks -Djavax.net.ssl.trustStorePassword=trustStorePassword -jar MyJar.jar
  4. Oder fügen Sie dem Java-Code Folgendes hinzu:

    System.setProperty("javax.net.ssl.trustStore", "GlobalSignRootCA.jks");
    System.setProperty("javax.net.ssl.trustStorePassword","trustStorePassword");
    

Die andere Option für Schritt 2 besteht darin, nur den keytoolBefehl zu verwenden. Unten ist ein Beispiel mit einer Kette von Zertifikaten:

$ 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

6

Ich habe den Keystore Explorer verwendet

  1. Öffnen Sie JKS mit einem privaten Schlüssel
  2. Untersuchen Sie die signierte PEM von CA.
  3. Schlüssel importieren
  4. Speichern Sie JKS

3
Keystore Explorer ist fantastisch und sehr vielseitig. Spart einmalig ein paar sinnlose Minuten am Terminal.
TheRealChx101

3

Es gibt auch ein GUI-Tool, mit dem visuelle JKS erstellt und Zertifikate importiert werden können.

http://portecle.sourceforge.net/

Portecle ist eine benutzerfreundliche GUI-Anwendung zum Erstellen, Verwalten und Überprüfen von Schlüsselspeichern, Schlüsseln, Zertifikaten, Zertifikatanforderungen, Zertifikatsperrlisten und mehr.


1
Key Store Explorer ist die moderne Version von Portecle. Es gibt überhaupt keinen Unterschied zwischen ihren Menüs und Funktionen.
Setmax vor

0

Ich habe es aus dem Internet bekommen. Es funktioniert ziemlich gut für PEM-Dateien, die mehrere Einträge enthalten.

#!/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>
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.