Importuj PEM do Java Key Store

144

Próbuję połączyć się z serwerem SSL, który wymaga ode mnie uwierzytelnienia. Aby korzystać z SSL przez Apache MINA, potrzebuję odpowiedniego pliku JKS. Jednak dostałem tylko plik .PEM.

Jak bym zabrał się do tworzenia pliku JKS z pliku PEM?

jwoolard
źródło
1
Może ten link może być pomocny: http://www.agentbob.info/agentbob/79-AB.html
Laurent K

Odpowiedzi:

235

Najpierw przekonwertuj swój certyfikat na format DER:

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

Następnie zaimportuj go do magazynu kluczy:

keytool -import -alias your-alias -keystore cacerts -file certificate.der
Anthony O.
źródło
7
Nie działa, jeśli plik .pem zawiera więcej niż jeden certyfikat.
MarioVilas
14
Mam jeden certyfikat .pem i to nie działa. 1795: błąd: 0906D06C: procedury PEM: PEM_read_bio: brak linii startowej: / usr / src / secure / lib / libcrypto /../../../ crypto / openssl / crypto / pem / pem_lib.c: 648: Oczekiwanie : ZAUFANY CERTYFIKAT
Brian Knoblauch
4
Znalazłem rozwiązanie. Wcześniej umieść certyfikaty główne i pośrednie w pliku .pem, a następnie przekonwertuj.
Brian Knoblauch,
1
@Anthony to polecenie mówi tylko, jak zaimportować PEM do JKS. Dobrym pomysłem może być dodanie polecenia eksportującego JKS ze sklepu.
Vishal Biyani
2
Jeśli mam wiele certyfikatów w pliku .pem, jak zaimportować je do magazynu kluczy Java?
Erick,
55

Jeśli chcesz zaimportować tylko certyfikat w formacie PEM do magazynu kluczy, narzędzie keytool wykona zadanie:

keytool -import -alias *alias* -keystore cacerts -file *cert.pem*
Zastrzelić
źródło
11
Jeśli pójdę w ten sposób, pojawia się błąd: błąd narzędzia keytool: java.lang Wyjątek: dane wejściowe nie są certyfikatem X.509
poziom frandevel
1
@frandevel, ten błąd może być spowodowany tym, że plik wejściowy PEM ma nagłówek nad ogranicznikiem --- BEGIN lub ma wiele PEM w jednym pliku lub w obu. Usuń wszystkie zbędne dane i podaj dane z każdego PEM pojedynczo lub użyj mojego narzędzia, jak opisano w mojej odpowiedzi.
Alastair McCormack
Dzięki @ Fuzzyfelt, przyjrzę się
frandevel
1
Ten sam problem i plik .PEM jest czysty, ze wszystkimi odpowiednimi nagłówkami.
Brian Knoblauch,
17

Opracowałem http://code.google.com/p/java-keyutil/, który importuje certyfikaty PEM bezpośrednio do magazynu kluczy Java. Jego głównym celem jest importowanie wieloczęściowych pakietów certyfikatów systemu operacyjnego PEM, takich jak ca-bundle.crt. Często zawierają one nagłówki, których keytool nie obsługuje

</self promotion>
Alastair McCormack
źródło
4
Niezły projekt zabawki, ale keytooljuż robi to wszystko dla Ciebie (i nie tylko). (Nawiasem mówiąc, powinieneś zamknąć swoje FileOutputStreami zamknąć strumienie I / O finally, jeśli zdarzy się wyjątek.)
Bruno
8
Cześć Bruno, dzięki za wskazówki. Prawdziwym przypadkiem użycia jest zaimportowanie wszystkich wpisów z /etc/pki/tls/certs/ca-bundle.crt (RHEL / CentOS) za jednym razem. AFAIK, keytool zaimportuje tylko pierwszy wpis. Widziałem, jak wiele osób robi to inaczej, ale zwykle wymaga to wielokrotnego wywoływania keytool dla każdego certyfikatu. Ubuntu ma skrypt aktualizacyjny, który robi dokładnie to, z wyjątkiem tego, że Ubuntu przechowuje swoje certyfikaty w katalogu. W najbliższej przyszłości dodam obsługę katalogów. Jeszcze raz dziękujemy za przejrzenie kodu.
Alastair McCormack
14

W moim przypadku miałem plik pem, który zawierał dwa certyfikaty i zaszyfrowany klucz prywatny do wykorzystania we wzajemnym uwierzytelnianiu SSL. Więc mój plik pem wyglądał tak:

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

Oto co zrobiłem

Podziel plik na trzy oddzielne pliki, tak aby każdy z nich zawierał tylko jeden wpis, zaczynając ---BEGIN..od ---END..linii i kończąc na nich . Załóżmy teraz mamy trzy pliki: cert1.pem, cert2.pem, i pkey.pem.

Konwertuj pkey.pemna format DER przy użyciu openssl i następującej składni:

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

Zwróć uwagę, że jeśli klucz prywatny jest zaszyfrowany, musisz podać hasło (uzyskać je od dostawcy oryginalnego pliku pem), aby przekonwertować go do formatu DER, opensslpoprosi Cię o hasło w ten sposób: „wprowadź hasło dla pkey.pem:”.

Jeśli konwersja się powiedzie, otrzymasz nowy plik o nazwie pkey.der.

Utwórz nowy magazyn kluczy java i zaimportuj klucz prywatny oraz certyfikaty:

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

(opcjonalnie) Sprawdź zawartość nowego magazynu kluczy:

$ keytool -list -keystore mykeystore -storepass password

Typ magazynu kluczy: JKS Dostawca magazynu kluczy: SUN

Twój magazyn kluczy zawiera 3 wpisy:

  • cn = ..., ou = ..., o = .., 2 września 2014 r., trustCertEntry, odcisk cyfrowy certyfikatu (SHA1): 2C: B8: ...

  • importkey, 2 września 2014 r., PrivateKeyEntry, odcisk cyfrowy certyfikatu (SHA1): 9C: B0: ...

  • cn = ..., o = ...., 2 września 2014 r., trustCertEntry, odcisk cyfrowy certyfikatu (SHA1): 83:63: ...

(opcjonalnie) Przetestuj swoje certyfikaty i klucz prywatny z nowego magazynu kluczy na serwerze SSL: (możesz chcieć włączyć debugowanie jako opcję maszyny wirtualnej: -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..

Na koniec zarejestruj swoje certyfikaty za pomocą HttpsURLConnection, jeśli planujesz z niego korzystać:

        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
źródło
Weryfikator Twojej nazwy hosta jest błędny, session.getPeerHost()nie zwraca nazwy w certyfikacie, ale nazwę, z którą się połączyłeś (czyli urlHostNametutaj), więc to zawsze będzie prawda. I tak zawsze truewracasz.
Bruno,
9

Jeśli potrzebujesz prostego sposobu na ładowanie plików PEM w Javie bez konieczności korzystania z zewnętrznych narzędzi (opensll, keytool) , oto mój kod, którego używam na produkcji:

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

}

Baw się dobrze.

BluEOS
źródło
Pytanie dotyczyło „SSL przez Apache MINA”, który jest prostszy do skonfigurowania za pomocą dostarczonej funkcji „SSLServerSocketFactory z PEMs”, patrz mina.apache.org/mina-project/userguide/ch11-ssl-filter/… .
BluEOS,
8

Zawsze zapominam, jak to zrobić, ponieważ jest to coś, co robię od czasu do czasu, jest to jedno z możliwych rozwiązań i po prostu działa:

  1. Przejdź do swojej ulubionej przeglądarki i pobierz główny certyfikat z zabezpieczonej strony internetowej.
  2. Wykonaj dwa następujące wiersze kodu:

    $ openssl x509 -outform der -in GlobalSignRootCA.crt -out GlobalSignRootCA.der
    $ keytool -import -alias GlobalSignRootCA -keystore GlobalSignRootCA.jks -file GlobalSignRootCA.der
  3. W przypadku wykonywania w środowisku Java SE dodaj następujące opcje:

    $ java -Djavax.net.ssl.trustStore=GlobalSignRootCA.jks -Djavax.net.ssl.trustStorePassword=trustStorePassword -jar MyJar.jar
  4. Lub dodaj do kodu java:

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

Inną opcją dla kroku 2 jest użycie keytoolpolecenia. Poniżej przykład z łańcuchem certyfikatów:

$ 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
źródło
7

Użyłem Keystore Explorer

  1. Otwórz JKS za pomocą klucza prywatnego
  2. Sprawdź podpisany PEM z CA
  3. Klucz importu
  4. Zapisz JKS
Leos Literak
źródło
3
Keystore Explorer jest niesamowity i bardzo wszechstronny. Oszczędza czas przed spędzeniem kilku bezmyślnych minut na terminalu.
TheRealChx101
3

Istnieje również narzędzie GUI, które umożliwia wizualne tworzenie JKS i importowanie certyfikatów.

http://portecle.sourceforge.net/

Portecle to przyjazna dla użytkownika aplikacja GUI do tworzenia, zarządzania i sprawdzania magazynów kluczy, kluczy, certyfikatów, żądań certyfikatów, list odwołania certyfikatów i nie tylko.

Vadzim
źródło
1
eksplorator magazynu kluczy to nowoczesna wersja portecle. nie ma żadnej różnicy między ich menu a funkcjami.
Setmax
0

Mam to z internetu. Działa całkiem dobrze w przypadku plików PEM, które zawierają wiele wpisów.

#!/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
źródło