Saya mencoba terhubung ke server SSL yang mengharuskan saya untuk mengotentikasi diri. Untuk menggunakan SSL melalui Apache MINA saya perlu file JKS yang cocok. Namun, saya hanya diberi file .PEM.
Bagaimana cara saya membuat file JKS dari file PEM?
Saya mencoba terhubung ke server SSL yang mengharuskan saya untuk mengotentikasi diri. Untuk menggunakan SSL melalui Apache MINA saya perlu file JKS yang cocok. Namun, saya hanya diberi file .PEM.
Bagaimana cara saya membuat file JKS dari file PEM?
Jawaban:
Pertama, konversi sertifikat Anda dalam format DER:
openssl x509 -outform der -in certificate.pem -out certificate.der
Dan setelah itu, impor di keystore:
keytool -import -alias your-alias -keystore cacerts -file certificate.der
Jika Anda hanya ingin mengimpor sertifikat dalam format PEM ke dalam keystore, keytool akan melakukan pekerjaan:
keytool -import -alias *alias* -keystore cacerts -file *cert.pem*
Saya telah mengembangkan http://code.google.com/p/java-keyutil/ yang mengimpor sertifikat PEM langsung ke Java keystore. Tujuan utamanya adalah mengimpor bundel sertifikat Sistem Operasi PEM multi-bagian seperti ca-bundle.crt. Ini sering termasuk header yang tidak bisa ditangani keytool
</self promotion>
keytool
sudah melakukan semua ini untuk Anda (dan banyak lagi). (Omong-omong, Anda harus menutup FileOutputStream
, dan menutup aliran I / O Anda finally
, jika pengecualian terjadi.)
Dalam kasus saya, saya memiliki file pem yang berisi dua sertifikat dan kunci pribadi terenkripsi untuk digunakan dalam otentikasi SSL bersama. Jadi file pem saya terlihat seperti ini:
-----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-----
Membagi file menjadi tiga file terpisah, sehingga masing-masing berisi hanya satu entri, dimulai dengan ---BEGIN..
dan diakhiri dengan ---END..
garis. Mari kita asumsikan kita sekarang memiliki tiga file: cert1.pem
, cert2.pem
, dan pkey.pem
.
Konversikan pkey.pem
ke dalam format DER menggunakan openssl dan sintaks berikut:
openssl pkcs8 -topk8 -nocrypt -in pkey.pem -informasi PEM-keluar pkey.der -melakukan DER
Perhatikan, bahwa jika kunci pribadi dienkripsi Anda perlu memberikan kata sandi (mendapatkannya dari pemasok file pem yang asli) untuk dikonversi ke format DER,
openssl
akan meminta kata sandi seperti ini: "masukkan frasa sandi untuk pkey.pem
:".
Jika konversi berhasil, Anda akan mendapatkan file baru bernama pkey.der
.
Buat java keystore baru dan impor kunci pribadi dan sertifikat:
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());
(opsional) Verifikasi konten penyimpanan kunci baru Anda:
$ keytool -list -keystore mykeystore -storepass password
Jenis Keystore: JKS Keystore penyedia: SUN
Keystore Anda berisi 3 entri:
cn = ..., ou = ..., o = .., 2 Sep 2014, trustCertEntry, Sertifikat sidik jari (SHA1): 2C: B8: ...
importkey, 2 Sep 2014, PrivateKeyEntry, Sertifikat sidik jari (SHA1): 9C: B0: ...
cn = ..., o = ...., 2 Sep 2014, trustCertEntry, Sertifikat sidik jari (SHA1): 83:63: ...
(opsional) Uji sertifikat dan kunci pribadi Anda dari penyimpanan kunci baru Anda terhadap server SSL Anda: (Anda mungkin ingin mengaktifkan debugging sebagai opsi VM: -Djavax.net.debug = semua)
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..
Akhirnya daftarkan sertifikat Anda dengan HttpsURLConnection jika berencana untuk menggunakannya:
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);
session.getPeerHost()
tidak mengembalikan nama dalam sertifikat, tetapi nama yang terhubung dengan Anda (yaitu di urlHostName
sini), jadi itu selalu benar. Lagi true
pula, kau selalu kembali .
Jika Anda memerlukan cara mudah untuk memuat file PEM di Jawa tanpa harus berurusan dengan alat eksternal (opensll, keytool) , berikut adalah kode saya yang saya gunakan dalam produksi:
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));
}
}
Selamat bersenang-senang.
Saya selalu lupa bagaimana melakukan ini karena itu adalah sesuatu yang hanya saya lakukan sesekali, ini adalah salah satu solusi yang mungkin, dan itu hanya berfungsi:
Jalankan dua baris kode berikut:
$ openssl x509 -outform der -in GlobalSignRootCA.crt -out GlobalSignRootCA.der
$ keytool -import -alias GlobalSignRootCA -keystore GlobalSignRootCA.jks -file GlobalSignRootCA.der
Jika mengeksekusi di lingkungan Java SE tambahkan opsi berikut:
$ java -Djavax.net.ssl.trustStore=GlobalSignRootCA.jks -Djavax.net.ssl.trustStorePassword=trustStorePassword -jar MyJar.jar
Atau tambahkan yang berikut ini ke kode java:
System.setProperty("javax.net.ssl.trustStore", "GlobalSignRootCA.jks");
System.setProperty("javax.net.ssl.trustStorePassword","trustStorePassword");
Opsi lain untuk langkah 2 adalah dengan hanya menggunakan keytool
perintah. Di bawah ini adalah contoh dengan rangkaian sertifikat:
$ 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
Saya menggunakan Keystore Explorer
Ada juga alat GUI yang memungkinkan pembuatan JKS visual dan impor sertifikat.
http://portecle.sourceforge.net/
Portecle adalah aplikasi GUI yang mudah digunakan untuk membuat, mengelola, dan memeriksa keystores, kunci, sertifikat, permintaan sertifikat, daftar pencabutan sertifikat, dan lainnya.
Saya mendapatkannya dari internet. Ini berfungsi cukup baik untuk file pem yang berisi banyak entri.
#!/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>