ฉันกำลังพยายามเชื่อมต่อกับเซิร์ฟเวอร์ 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
หลังจากนั้นให้อิมพอร์ตใน keystore:
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
และปิด I / O ของคุณลำธารใน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
ประเภท Keystore: JKS ผู้ให้บริการ Keystore: 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 ของคุณ: (คุณอาจต้องการเปิดใช้งานการดีบักเป็นตัวเลือก VM: -Djavax.net.debug = ทั้งหมด)
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
นอกจากนี้ยังมีเครื่องมือ GUI ที่อนุญาตให้สร้าง JKS แบบเห็นได้และนำเข้าใบรับรอง
http://portecle.sourceforge.net/
Portecle เป็นแอปพลิเคชั่น GUI ที่ใช้งานง่ายสำหรับการสร้างการจัดการและตรวจสอบที่เก็บคีย์, คีย์, ใบรับรอง, การร้องขอใบรับรอง, รายการเพิกถอนใบรับรองและอื่น ๆ
ฉันได้รับจากอินเทอร์เน็ต มันใช้งานได้ดีสำหรับไฟล์ 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>