Importa PEM in Java Key Store


Risposte:


235

Innanzitutto, converti il ​​tuo certificato in un formato DER:

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

E dopo, importalo nel keystore:

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

7
Non funziona se il file .pem contiene più di un certificato.
MarioVilas,

14
Ho un unico certificato .pem e questo non funziona. 1795: errore: 0906D06C: routine PEM: PEM_read_bio: nessuna linea di partenza: / usr / src / secure / lib / libcrypto /../../../ crypto / openssl / crypto / pem / pem_lib.c: 648: in attesa : CERTIFICATO DI FIDUCIA
Brian Knoblauch,

4
Ho trovato la soluzione Pre-pend i certificati root e intermedi al .pem, quindi converti.
Brian Knoblauch,

1
@Anthony questo comando dice solo come importare un PEM in JKS. Potrebbe essere una buona idea aggiungere un comando per esportare JKS dal negozio.
Vishal Biyani,

2
Se ho più certificati sul .pem, come posso importare in un keystore Java?
Erick,

55

Se vuoi solo importare un certificato in formato PEM in un keystore, keytool farà il lavoro:

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

11
Se vado in questo modo ottengo un errore: errore keytool: java.lang.Exception: input non un certificato X.509
frandevel

1
@frandevel, questo errore può essere causato dal file di input PEM con un'intestazione sopra il delimitatore --- BEGIN o con più PEM in un file o entrambi. Rimuovere tutti i dati estranei e inserire in ogni PEM uno alla volta o utilizzare il mio strumento, come indicato nella mia risposta.
Alastair McCormack,

Grazie @Fuzzyfelt, darò un'occhiata
Frandevel,

1
Stesso problema e il file .PEM è pulito, con tutte le intestazioni appropriate.
Brian Knoblauch,

17

Ho sviluppato http://code.google.com/p/java-keyutil/ che importa i certificati PEM direttamente in un keystore Java. Il suo scopo principale è importare un pacchetto di certificati del sistema operativo PEM in più parti come ca-bundle.crt. Questi includono spesso le intestazioni che keytool non può gestire

</self promotion>

4
Non è un brutto progetto giocattolo, ma keytoolfa già tutto questo per te (e altro). (A proposito, dovresti chiudere il tuo FileOutputStream, e chiudere i tuoi flussi I / O finally, se si verifica un'eccezione.)
Bruno,

8
Ciao Bruno, grazie per i suggerimenti. Il vero caso d'uso è importare tutte le voci di /etc/pki/tls/certs/ca-bundle.crt (RHEL / CentOS) in una volta sola. AFAIK, keytool importerà solo la prima voce. Ho visto un certo numero di persone fare diversamente, ma di solito comporta invocare il keytool più volte per ogni certificato. Ubuntu ha uno script di aggiornamento che fa esattamente questo, tranne che Ubuntu memorizza i suoi certificati in una directory. Aggiungerò il supporto per le directory nel prossimo futuro. Grazie ancora per la revisione del codice.
Alastair McCormack,

14

Nel mio caso avevo un file pem che conteneva due certificati e una chiave privata crittografata da utilizzare nell'autenticazione SSL reciproca. Quindi il mio file pem era simile al seguente:

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

Ecco cosa ho fatto

Dividi il file in tre file separati, in modo che ognuno contenga una sola voce, iniziando con ---BEGIN..e finendo con le ---END..righe. Assumiamo ora abbiamo tre file: cert1.pem, cert2.pem, e pkey.pem.

Converti pkey.pemin formato DER usando openssl e la sintassi seguente:

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

Si noti che se la chiave privata è crittografata è necessario fornire una password (ottenerla dal fornitore del file pem originale) per convertirla in formato DER, opensslverrà richiesta la password in questo modo: "inserire una passphrase per pkey.pem:".

Se la conversione ha esito positivo, otterrai un nuovo file chiamato pkey.der.

Crea un nuovo keystore java e importa la chiave privata e i certificati:

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

(facoltativo) Verifica il contenuto del tuo nuovo keystore:

$ keytool -list -keystore mykeystore -storepass password

Tipo di archivio chiavi: Fornitore di archivio chiavi JKS: SUN

Il tuo keystore contiene 3 voci:

  • cn = ..., ou = ..., o = .., 2 set 2014, trustedCertEntry, impronta digitale del certificato (SHA1): 2C: B8: ...

  • importkey, 2 set 2014, PrivateKeyEntry, impronta digitale del certificato (SHA1): 9C: B0: ...

  • cn = ..., o = ...., 2 set 2014, trustedCertEntry, impronta digitale del certificato (SHA1): 83:63: ...

(facoltativo) Testa i tuoi certificati e la tua chiave privata dal tuo nuovo archivio chiavi sul tuo server SSL: (Potresti voler abilitare il debug come opzione 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..

Infine registra i tuoi certificati con HttpsURLConnection se intendi utilizzarlo:

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

Il tuo verificatore del nome host è errato, session.getPeerHost()non restituisce il nome nel certificato, ma il nome con cui ti sei collegato (ovvero il urlHostNamequi), quindi sarà sempre vero. trueComunque ritorni sempre .
Bruno,

9

Se hai bisogno di un modo semplice per caricare file PEM in Java senza dover gestire strumenti esterni (opensll, keytool) , ecco il mio codice che uso in produzione:

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

}

Divertiti.


La domanda riguardava "SSL over Apache MINA" che è più semplice da configurare con la funzione "SSLServerSocketFactory fornita dalla funzione PEM", consultare mina.apache.org/mina-project/userguide/ch11-ssl-filter/… .
BluEOS

8

Dimentico sempre come farlo perché è qualcosa che faccio solo una volta ogni tanto, questa è una possibile soluzione e funziona:

  1. Vai al tuo browser preferito e scarica il certificato principale dal sito Web protetto.
  2. Eseguire le due seguenti righe di codice:

    $ openssl x509 -outform der -in GlobalSignRootCA.crt -out GlobalSignRootCA.der
    $ keytool -import -alias GlobalSignRootCA -keystore GlobalSignRootCA.jks -file GlobalSignRootCA.der
  3. Se in esecuzione in ambiente Java SE aggiungere le seguenti opzioni:

    $ java -Djavax.net.ssl.trustStore=GlobalSignRootCA.jks -Djavax.net.ssl.trustStorePassword=trustStorePassword -jar MyJar.jar
  4. Oppure aggiungi quanto segue al codice java:

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

L'altra opzione per il passaggio 2 consiste semplicemente nell'utilizzare il keytoolcomando. Bellow è un esempio con una catena di certificati:

$ 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

7

Ho usato Keystore Explorer

  1. Apri JKS con una chiave privata
  2. Esaminare PEM firmato da CA
  3. Chiave di importazione
  4. Salva JKS

3
Keystore Explorer è fantastico e molto versatile. Risparmia una volta spendendo qualche minuto senza cervello sul terminal.
TheRealChx101,

3

Esiste anche uno strumento GUI che consente la creazione visiva di JKS e l'importazione di certificati.

http://portecle.sourceforge.net/

Portecle è un'applicazione GUI intuitiva per la creazione, la gestione e l'esame di keystore, chiavi, certificati, richieste di certificati, elenchi di revoche di certificati e altro ancora.


1
Key Store Explorer è la versione moderna di Portecle. non c'è alcuna differenza tra i loro menu e funzionalità.
Setmax,

0

L'ho preso da internet. Funziona abbastanza bene per i file pem che contengono più voci.

#!/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>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.