Я пытаюсь подключиться к серверу SSL, который требует от меня аутентификации. Чтобы использовать SSL поверх Apache MINA, мне нужен подходящий файл JKS. Однако мне дали только файл .PEM.
Как мне создать файл JKS из файла PEM?
Я пытаюсь подключиться к серверу SSL, который требует от меня аутентификации. Чтобы использовать SSL поверх Apache MINA, мне нужен подходящий файл JKS. Однако мне дали только файл .PEM.
Как мне создать файл JKS из файла PEM?
Ответы:
Сначала преобразуйте свой сертификат в формат DER:
openssl x509 -outform der -in certificate.pem -out certificate.der
А после импортируйте его в хранилище ключей:
keytool -import -alias your-alias -keystore cacerts -file certificate.der
Если вы хотите только импортировать сертификат в формате PEM в хранилище ключей, keytool выполнит эту работу:
keytool -import -alias *alias* -keystore cacerts -file *cert.pem*
Я разработал http://code.google.com/p/java-keyutil/, который импортирует сертификаты PEM прямо в хранилище ключей Java. Его основная цель - импортировать состоящие из нескольких частей пакеты сертификатов операционной системы PEM, такие как ca-bundle.crt. Они часто включают заголовки, которые keytool не может обрабатывать.
</self promotion>
keytool
все это уже делает за вас (и не только). (Между прочим, вы должны закрыть свои FileOutputStream
и закрыть свои потоки ввода / вывода finally
, если произойдет исключение.)
В моем случае у меня был файл pem, который содержал два сертификата и зашифрованный закрытый ключ для использования во взаимной SSL-аутентификации. Итак, мой файл pem выглядел так:
-----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-----
Разделите файл на три отдельных файла так, чтобы каждый из них содержал только одну запись, начинающуюся ---BEGIN..
со ---END..
строк и заканчивающуюся строками. Давайте предположим , что мы теперь имеем три файла: cert1.pem
, cert2.pem
и pkey.pem
.
Преобразование pkey.pem
в формат DER с использованием openssl и следующего синтаксиса:
openssl pkcs8 -topk8 -nocrypt -in pkey.pem -inform PEM -out pkey.der -outform DER
Обратите внимание, что если закрытый ключ зашифрован, вам необходимо указать пароль (получить его у поставщика исходного файла pem) для преобразования в формат DER,
openssl
вам будет предложено ввести пароль следующим образом: «введите парольную фразу для pkey.pem
:».
Если преобразование прошло успешно, вы получите новый файл с именем pkey.der
.
Создайте новое хранилище ключей Java и импортируйте закрытый ключ и сертификаты:
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());
(необязательно) Проверьте содержимое вашего нового хранилища ключей:
$ keytool -list -keystore mykeystore -storepass password
Тип хранилища ключей: JKS Поставщик хранилища ключей: SUN
Ваше хранилище ключей содержит 3 записи:
cn = ..., ou = ..., o = .., 2 сентября 2014 г., trustedCertEntry, отпечаток сертификата (SHA1): 2C: B8: ...
importkey, 2 сентября 2014 г., PrivateKeyEntry, отпечаток сертификата (SHA1): 9C: B0: ...
cn = ..., o = ...., 2 сентября 2014 г., trustedCertEntry, отпечаток сертификата (SHA1): 83:63: ...
(необязательно) Протестируйте свои сертификаты и закрытый ключ из нового хранилища ключей на сервере SSL: (Вы можете включить отладку в качестве параметра виртуальной машины: -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..
Наконец, зарегистрируйте свои сертификаты с помощью HttpsURLConnection, если планируете его использовать:
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()
возвращает не имя в сертификате, а имя, с которым вы связались (т.е. urlHostName
здесь), так что это всегда будет правдой. Ты все true
равно всегда возвращаешься .
Если вам нужен простой способ загрузки файлов PEM на Java без использования внешних инструментов (opensll, keytool) , вот мой код, который я использую в производстве:
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));
}
}
Радоваться, веселиться.
Я всегда забываю, как это сделать, потому что это то, что я делаю время от времени, это одно из возможных решений, и оно просто работает:
Выполните две следующие строки кода:
$ openssl x509 -outform der -in GlobalSignRootCA.crt -out GlobalSignRootCA.der
$ keytool -import -alias GlobalSignRootCA -keystore GlobalSignRootCA.jks -file GlobalSignRootCA.der
При выполнении в среде Java SE добавьте следующие параметры:
$ java -Djavax.net.ssl.trustStore=GlobalSignRootCA.jks -Djavax.net.ssl.trustStorePassword=trustStorePassword -jar MyJar.jar
Или добавьте в код java следующее:
System.setProperty("javax.net.ssl.trustStore", "GlobalSignRootCA.jks");
System.setProperty("javax.net.ssl.trustStorePassword","trustStorePassword");
Другой вариант для шага 2 - просто использовать keytool
команду. Ниже приведен пример цепочки сертификатов:
$ 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
Я использовал Keystore Explorer
Существует также инструмент с графическим интерфейсом, который позволяет визуально создавать JKS и импортировать сертификаты.
http://portecle.sourceforge.net/
Portecle - это удобное приложение с графическим интерфейсом для создания, управления и проверки хранилищ ключей, ключей, сертификатов, запросов на сертификаты, списков отзыва сертификатов и многого другого.
Получил из интернета. Он очень хорошо работает для файлов pem, содержащих несколько записей.
#!/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>