将 PEM 导入 Java 密钥库

发布于 2024-08-19 08:44:35 字数 124 浏览 11 评论 0 原文

我正在尝试连接到需要我进行身份验证的 SSL 服务器。为了通过 Apache MINA 使用 SSL,我需要一个合适的 JKS 文件。但是,我只得到了一个 .PEM 文件。

我将如何从 PEM 文件创建 JKS 文件?

I am trying to connect to an SSL server which requires me to authenticate myself. In order to use SSL over Apache MINA I need a suitable JKS file. However, I have only been given a .PEM file.

How would I go about creating a JKS file from a PEM file?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

暖树树初阳… 2024-08-26 08:44:35

首先,将您的证书转换为 DER 格式:

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

然后,将其导入密钥库:

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

First, convert your certificate in a DER format :

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

And after, import it in the keystore :

keytool -import -alias your-alias -keystore cacerts -file certificate.der
她如夕阳 2024-08-26 08:44:35

如果您只想将 PEM 格式的证书导入密钥库,keytool 将完成这项工作:

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

If you only want to import a certificate in PEM format into a keystore, keytool will do the job:

keytool -import -alias *alias* -keystore cacerts -file *cert.pem*
旧时光的容颜 2024-08-26 08:44:35

如果您需要一种简单的方法来在 Java 中加载 PEM 文件而无需处理外部工具(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));
    }

}

玩得开心。

If you need an easy way to load PEM files in Java without having to deal with external tools (opensll, keytool), here is my code I use in production :

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

}

Have fun.

护你周全 2024-08-26 08:44:35

我开发了 https://github.com/alastairmccormack/keyutil 它将 PEM 证书直接导入到Java 密钥库。其主要目的是导入多部分 PEM 操作系统证书包,例如 ca-bundle.crt。这些通常包括 keytool 无法处理的标头。

I've developed https://github.com/alastairmccormack/keyutil which imports PEM certificates straight into a Java keystore. Its primary purpose is to import a multi-part PEM Operating System certificate bundles such as ca-bundle.crt. These often includes headers which keytool cannot handle.

海夕 2024-08-26 08:44:35

就我而言,我有一个 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.pemcert2.pempkey.pem

使用 openssl 和以下语法将 pkey.pem 转换为 DER 格式:

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=.., 2014 年 9 月 2 日,trustedCertEntry,
    证书指纹 (SHA1):2C:B8: ...

  • importkey,2014 年 9 月 2 日,PrivateKeyEntry,
    证书指纹 (SHA1):9C:B0: ...

  • cn=...,o=....,2014 年 9 月 2 日,trustedCertEntry,
    证书指纹 (SHA1): 83:63: ...

(可选)针对 SSL 服务器测试新密钥存储中的证书和私钥:
(您可能希望将调试作为 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..

最后,如果计划使用 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);

In my case I had a pem file which contained two certificates and an encrypted private key to be used in mutual SSL authentication.
So my pem file looked like this:

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

Here is what I did

Split the file into three separate files, so that each one contains just one entry,
starting with ---BEGIN.. and ending with ---END.. lines. Lets assume we now have three files: cert1.pem, cert2.pem, and pkey.pem.

Convert pkey.pem into DER format using openssl and the following syntax:

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

Note, that if the private key is encrypted you need to supply a password( obtain it from the supplier of the original pem file ) to convert to DER format,
openssl will ask you for the password like this: "enter a passphrase for pkey.pem: ".

If conversion is successful, you will get a new file called pkey.der.

Create a new java keystore and import the private key and the certificates:

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

(optional) Verify the content of your new key store:

$ keytool -list -keystore mykeystore -storepass password

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 3 entries:

  • cn=...,ou=...,o=.., Sep 2, 2014, trustedCertEntry,
    Certificate fingerprint (SHA1): 2C:B8: ...

  • importkey, Sep 2, 2014, PrivateKeyEntry,
    Certificate fingerprint (SHA1): 9C:B0: ...

  • cn=...,o=...., Sep 2, 2014, trustedCertEntry,
    Certificate fingerprint (SHA1): 83:63: ...

(optional) Test your certificates and private key from your new key store against your SSL server:
( You may want to enable debugging as an VM option: -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..

Finally register your certificates with HttpsURLConnection if plan to use it:

        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);
属性 2024-08-26 08:44:35

我使用 Keystore Explorer

  1. 使用私钥打开 JKS
  2. 检查来自 CA 的签名 PEM
  3. 导入密钥
  4. 保存 JKS

I used Keystore Explorer

  1. Open JKS with a private key
  2. Examine signed PEM from CA
  3. Import key
  4. Save JKS
救星 2024-08-26 08:44:35

我总是忘记如何做到这一点,因为这是我偶尔做的事情,这是一种可能的解决方案,而且它确实有效:

  1. 转到您最喜欢的浏览器并从安全网站下载主证书。
  2. 执行以下两行代码:

    $ openssl x509 -outform der -in GlobalSignRootCA.crt -out GlobalSignRootCA.der
    $ keytool -import -alias GlobalSignRootCA -keystore GlobalSignRootCA.jks -文件 GlobalSignRootCA.der
    
  3. 如果在 Java SE 环境中执行,请添加以下选项:

    $ java -Djavax.net.ssl.trustStore=GlobalSignRootCA.jks -Djavax.net.ssl.trustStorePassword=trustStorePassword -jar MyJar.jar
    
  4. 或者将以下内容添加到 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

I'm always forgetting how to do this because it's something that I just do once in a while, this is one possible solution, and it just works:

  1. Go to your favourite browser and download the main certificate from the secured website.
  2. Execute the two following lines of code:

    $ openssl x509 -outform der -in GlobalSignRootCA.crt -out GlobalSignRootCA.der
    $ keytool -import -alias GlobalSignRootCA -keystore GlobalSignRootCA.jks -file GlobalSignRootCA.der
    
  3. If executing in Java SE environment add the following options:

    $ java -Djavax.net.ssl.trustStore=GlobalSignRootCA.jks -Djavax.net.ssl.trustStorePassword=trustStorePassword -jar MyJar.jar
    
  4. Or add the following to the java code:

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

The other option for step 2 is to just using the keytool command. Bellow is an example with a chain of certificates:

$ 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
浮云落日 2024-08-26 08:44:35

OpenJDK keytool 现在可以本地处理 PEM 证书(并且已经有几个版本了,但我不确定从什么时候开始)。

keytool 建议不要像任何其他密钥库一样指定 cacerts 文件路径,而是使用 -cacerts 选项。

因此,适用于 OpenJDK 18(可能还有许多早期版本)的命令行是:

keytool -cacerts -import -alias <alias> -file <path_to_cert.pem>

OpenJDK keytool handles PEM certificates natively now (and has been for a few releases but I'm not sure since when).

keytool recommends against specifying the cacerts file path like any other keystore but using -cacerts option instead.

So the command line which works with OpenJDK 18 (and probably many earlier versions) is:

keytool -cacerts -import -alias <alias> -file <path_to_cert.pem>
獨角戲 2024-08-26 08:44:35

还有一个 GUI 工具,允许可视化 JKS 创建和证书导入。

http://portecle.sourceforge.net/

Portecle 是一个用户友好的 GUI 应用程序,用于创建、管理和检查密钥库、密钥、证书、证书请求、证书吊销列表等。

There is also a GUI tool that allows visual JKS creation and certificates importing.

http://portecle.sourceforge.net/

Portecle is a user friendly GUI application for creating, managing and examining keystores, keys, certificates, certificate requests, certificate revocation lists and more.

甲如呢乙后呢 2024-08-26 08:44:35

我从互联网上得到的。它对于包含多个条目的 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>

I got it from internet. It works pretty good for pem files that contains multiple entries.

#!/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>
在风中等你 2024-08-26 08:44:35

尽管这个问题已经很老了并且已经有很多答案,但我认为提供一个替代方案是值得的。使用本机 java 类使得仅使用 pem 文件变得非常冗长,并且几乎迫使您想要将 pem 文件转换为 p12 或 jks 文件,因为使用 p12 或 jks 文件要容易得多。我想为任何想要替代已提供答案的人提供帮助。

GitHub - SSLContext Kickstart

var keyManager = PemUtils.loadIdentityMaterial("certificate-chain.pem", "private-key.pem");
var trustManager = PemUtils.loadTrustMaterial("some-trusted-certificate.pem");

var sslFactory = SSLFactory.builder()
          .withIdentityMaterial(keyManager)
          .withTrustMaterial(trustManager)
          .build();

var sslContext = sslFactory.getSslContext();

我需要在这里提供一些免责声明,我是库维护者

Although this question is pretty old and it has already a-lot answers, I think it is worth to provide an alternative. Using native java classes makes it very verbose to just use pem files and almost forces you wanting to convert the pem files into p12 or jks files as using p12 or jks files are much easier. I want to give anyone who wants an alternative for the already provided answers.

GitHub - SSLContext Kickstart

var keyManager = PemUtils.loadIdentityMaterial("certificate-chain.pem", "private-key.pem");
var trustManager = PemUtils.loadTrustMaterial("some-trusted-certificate.pem");

var sslFactory = SSLFactory.builder()
          .withIdentityMaterial(keyManager)
          .withTrustMaterial(trustManager)
          .build();

var sslContext = sslFactory.getSslContext();

I need to provide some disclaimer here, I am the library maintainer

囚我心虐我身 2024-08-26 08:44:35

如果您想在 Android 上执行此操作,可以将一些小的调整添加到 PEMImporter 类中>这个很好的答案。总结一下:

  • 首先我使用 Android Studio 翻译成 Kotlin(这不是必需的,我只是更喜欢它)。原始类包含所有静态方法,因此这会产生一个命名对象。

  • javax.xml.bind.DatatypeConverter 已从版本 11 中的 java 核心中删除。尽管您仍然可以导入它(在 gradle 中:
    implementation("javax.xml.bind:jaxb-api:2.4.0-b180830.0359"),这在Android上不起作用,使用java.util.Base64更简单 它执行的任务(即,将 base64 转换为字节)。输出是相同的(尽管您需要修剪原始 PEM 数据中的行结尾)。

  • SunX509JKS 替换为 PKIX。仅在第一种情况下才有必要,而在第二种情况下可能无关紧要;如果您像此处所做的那样使用已初始化的 PrivateKey 等对象填充 KeyStore ,我认为它没有任何意义。事实上,我在 createKeyStore 中使用了 getDefaultAlgorithm() 代替了“JKS”,尽管当前默认值是“jks”,但密钥存储在 中工作得很好。使用 PKIX 作为算法创建的 KeyManagerFactory

我还应该注意,我没有使用 createSSLFactory 方法,而是使用 createKeyStore() 的输出来初始化 KeyManagerFactory 并提取 < code>KeyManagers 用于初始化 SSLContext

val context = SSLContext.getInstance(contextProtocol)
val password = String(...)
val ks : KeyStore = try {
            PEMImporter.createKeyStore(
                File(keyPath),
                File(certPath),
                password
            )
        } catch (ex : Throwable) { ... }

val kmf = KeyManagerFactory.getInstance("PKIX")
        try { kmf.init(ks, password.toCharArray()) }

这里的密码可能并不重要,因为 PEMImporter 使用已经未加密的密钥数据 -除非您想将 PrivateKey 写回文件(我认为 getEncoded() 是朝着这个方向迈出的一步,但我从来没有需要这样做)。它只需与上述两种用途相匹配即可。

我还添加了 RSA PRIVATE KEYS 的捕获,事实证明,它是
与第一行没有“RSA”的 PEM 密钥不同;这是我以前没有意识到的微妙之处。前者是PKCS #1,后者是PKCS #8;您应该能够使用通常使用的任何工具来处理这些问题(例如,在使用 certtool 创建密钥时,使用 --pkcs8)。请注意,这并不意味着 PKCS #8 密钥可能不是基于 RSA 的,它只是与用于存储和提取密钥数据的协议有关。

这是我在 Kotlin 中使用的 PEMImporter Android 版本:

import java.io.*
import java.security.*
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.*
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLServerSocketFactory

object PEMImporter {

    @Throws(Exception::class)
    fun createSSLFactory(
        privateKeyPem: File,
        certificatePem: File?,
        password: String
    ): SSLServerSocketFactory {
        val context = SSLContext.getInstance("TLS")
        val keystore = createKeyStore(privateKeyPem, certificatePem, password)
        val kmf = KeyManagerFactory.getInstance("PKIX")
        kmf.init(keystore, password.toCharArray())
        val km = kmf.keyManagers
        context.init(km, null, null)
        return context.serverSocketFactory
    }

    /**
     * Create a KeyStore from standard PEM files
     *
     * @param privateKeyPem the private key PEM file
     * @param certificatePem the certificate(s) PEM file
     * @param password the password to set to protect the private key
     */
    @Throws(
        Exception::class,
        KeyStoreException::class,
        IOException::class,
        NoSuchAlgorithmException::class,
        CertificateException::class
    )
    fun createKeyStore(privateKeyPem: File, certificatePem: File?, password: String): KeyStore {
        val cert = createCertificates(certificatePem)
        val keystore = KeyStore.getInstance(KeyStore.getDefaultType())
        keystore.load(null)
        // Import private key
        val key = createPrivateKey(privateKeyPem)
        keystore.setKeyEntry(privateKeyPem.name, key, password.toCharArray(), cert)
        return keystore
    }

    @Throws(Exception::class)
    private fun createPrivateKey(privateKeyPem: File): PrivateKey {
        val r = BufferedReader(FileReader(privateKeyPem))
        var s = r.readLine()
        if (s.contains("BEGIN RSA PRIVATE KEY")) {
            r.close()
            throw IllegalArgumentException(privateKeyPem.name +
                " is a PKCS #1 key, not a PKCS #8.")
        }
        if (s == null || (!s.contains("BEGIN PRIVATE KEY"))) {
            r.close()
            throw IllegalArgumentException("Bad private key header (${privateKeyPem.name}): $s")
        }
        val b = StringBuilder()
        s = ""
        while (s != null) {
            if (s.contains("END PRIVATE KEY")) {
                break
            }
            b.append(s.trimEnd())
            s = r.readLine()
        }
        r.close()
        val hexString = b.toString()
        // Base64 is in java.util.
        val bytes = Base64.getDecoder().decode(hexString)
        return generatePrivateKeyFromDER(bytes)
    }

    @Throws(Exception::class)
    private fun createCertificates(certificatePem: File?): Array<X509Certificate> {
        val result = mutableListOf<X509Certificate>()
        val r = BufferedReader(FileReader(certificatePem))
        var s = r.readLine()
        if (s == null || !s.contains("BEGIN CERTIFICATE")) {
            r.close()
            throw IllegalArgumentException("No CERTIFICATE found")
        }
        var b = StringBuilder()
        while (s != null) {
            if (s.contains("END CERTIFICATE")) {
                val hexString = b.toString()
                val bytes = Base64.getDecoder().decode(hexString.trimEnd())
                val cert = generateCertificateFromDER(bytes)
                result.add(cert)
                b = StringBuilder()
            } else {
                if (!s.startsWith("----")) {
                    b.append(s)
                }
            }
            s = r.readLine()
        }
        r.close()
        return result.toTypedArray()
    }

    @Throws(InvalidKeySpecException::class, NoSuchAlgorithmException::class)
    private fun generatePrivateKeyFromDER(keyBytes: ByteArray): RSAPrivateKey {
        val spec = PKCS8EncodedKeySpec(keyBytes)
        val factory = KeyFactory.getInstance("RSA")
        return factory.generatePrivate(spec) as RSAPrivateKey
    }

    @Throws(CertificateException::class)
    private fun generateCertificateFromDER(certBytes: ByteArray): X509Certificate {
        val factory = CertificateFactory.getInstance("X.509")
        return factory.generateCertificate(ByteArrayInputStream(certBytes)) as X509Certificate
    }
}

If you want to do this on Android, there are a few minor tweaks that can be added to the PEMImporter class from this great answer. To summarize those:

  • First I used Android Studio to translate into Kotlin (this is not necessary, I just prefer it). The original class contained all static methods, so this resulted in a named object instead.

  • javax.xml.bind.DatatypeConverter was removed from the java core in version 11. Although you can still import it (in gradle:
    implementation("javax.xml.bind:jaxb-api:2.4.0-b180830.0359"), this does not work on Android and it is simpler to use java.util.Base64 for the tasks it performed (namely, translating base64 to bytes). The output is identical (although you need to trim line endings in the raw PEM data).

  • Replace SunX509 and JKS with PKIX. It is only necessary in the first case, and in the second case probably inconsequential; I do not think it has any significance if you are populating a KeyStore with already initialized PrivateKey etc. objects as is done here. I in fact used getDefaultAlgorithm() in place of "JKS" in createKeyStore, and although that default is currently "jks", the key store worked fine in a KeyManagerFactory created with PKIX as the algorithm.

I should also note that I am not using the createSSLFactory method and am instead using the output of createKeyStore() to initialize a KeyManagerFactory and extract KeyManagers used to initialize anSSLContext:

val context = SSLContext.getInstance(contextProtocol)
val password = String(...)
val ks : KeyStore = try {
            PEMImporter.createKeyStore(
                File(keyPath),
                File(certPath),
                password
            )
        } catch (ex : Throwable) { ... }

val kmf = KeyManagerFactory.getInstance("PKIX")
        try { kmf.init(ks, password.toCharArray()) }

It probably doesn't matter much what the password is here since the PEMImporter works with already unencrypted key data -- unless you want to write a PrivateKey back to a file (I presume getEncoded() is a step in that direction but I've never had need to do this). It just has to match in the two uses above.

I also added a catch for RSA PRIVATE KEYS, which, as it turns out, are
not the same as PEM keys with no "RSA" in the first line; a subtlety I was previously unaware of. The former are PKCS #1, the latter PKCS #8; you should be able to use whatever tool you normally use to deal with these (eg., when creating keys with certtool, use --pkcs8). Note that doesn't mean PKCS #8 keys aren't potentially RSA based, it's just about the protocol used to store and extract the key data.

Here's my Android version of PEMImporter in Kotlin:

import java.io.*
import java.security.*
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.*
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLServerSocketFactory

object PEMImporter {

    @Throws(Exception::class)
    fun createSSLFactory(
        privateKeyPem: File,
        certificatePem: File?,
        password: String
    ): SSLServerSocketFactory {
        val context = SSLContext.getInstance("TLS")
        val keystore = createKeyStore(privateKeyPem, certificatePem, password)
        val kmf = KeyManagerFactory.getInstance("PKIX")
        kmf.init(keystore, password.toCharArray())
        val km = kmf.keyManagers
        context.init(km, null, null)
        return context.serverSocketFactory
    }

    /**
     * Create a KeyStore from standard PEM files
     *
     * @param privateKeyPem the private key PEM file
     * @param certificatePem the certificate(s) PEM file
     * @param password the password to set to protect the private key
     */
    @Throws(
        Exception::class,
        KeyStoreException::class,
        IOException::class,
        NoSuchAlgorithmException::class,
        CertificateException::class
    )
    fun createKeyStore(privateKeyPem: File, certificatePem: File?, password: String): KeyStore {
        val cert = createCertificates(certificatePem)
        val keystore = KeyStore.getInstance(KeyStore.getDefaultType())
        keystore.load(null)
        // Import private key
        val key = createPrivateKey(privateKeyPem)
        keystore.setKeyEntry(privateKeyPem.name, key, password.toCharArray(), cert)
        return keystore
    }

    @Throws(Exception::class)
    private fun createPrivateKey(privateKeyPem: File): PrivateKey {
        val r = BufferedReader(FileReader(privateKeyPem))
        var s = r.readLine()
        if (s.contains("BEGIN RSA PRIVATE KEY")) {
            r.close()
            throw IllegalArgumentException(privateKeyPem.name +
                " is a PKCS #1 key, not a PKCS #8.")
        }
        if (s == null || (!s.contains("BEGIN PRIVATE KEY"))) {
            r.close()
            throw IllegalArgumentException("Bad private key header (${privateKeyPem.name}): $s")
        }
        val b = StringBuilder()
        s = ""
        while (s != null) {
            if (s.contains("END PRIVATE KEY")) {
                break
            }
            b.append(s.trimEnd())
            s = r.readLine()
        }
        r.close()
        val hexString = b.toString()
        // Base64 is in java.util.
        val bytes = Base64.getDecoder().decode(hexString)
        return generatePrivateKeyFromDER(bytes)
    }

    @Throws(Exception::class)
    private fun createCertificates(certificatePem: File?): Array<X509Certificate> {
        val result = mutableListOf<X509Certificate>()
        val r = BufferedReader(FileReader(certificatePem))
        var s = r.readLine()
        if (s == null || !s.contains("BEGIN CERTIFICATE")) {
            r.close()
            throw IllegalArgumentException("No CERTIFICATE found")
        }
        var b = StringBuilder()
        while (s != null) {
            if (s.contains("END CERTIFICATE")) {
                val hexString = b.toString()
                val bytes = Base64.getDecoder().decode(hexString.trimEnd())
                val cert = generateCertificateFromDER(bytes)
                result.add(cert)
                b = StringBuilder()
            } else {
                if (!s.startsWith("----")) {
                    b.append(s)
                }
            }
            s = r.readLine()
        }
        r.close()
        return result.toTypedArray()
    }

    @Throws(InvalidKeySpecException::class, NoSuchAlgorithmException::class)
    private fun generatePrivateKeyFromDER(keyBytes: ByteArray): RSAPrivateKey {
        val spec = PKCS8EncodedKeySpec(keyBytes)
        val factory = KeyFactory.getInstance("RSA")
        return factory.generatePrivate(spec) as RSAPrivateKey
    }

    @Throws(CertificateException::class)
    private fun generateCertificateFromDER(certBytes: ByteArray): X509Certificate {
        val factory = CertificateFactory.getInstance("X.509")
        return factory.generateCertificate(ByteArrayInputStream(certBytes)) as X509Certificate
    }
}
臻嫒无言 2024-08-26 08:44:35

如果您有一个包含多个证书的 pem 文件,您可以使用脚本来提取它们并一次添加一个,如下所示:

    index=0
    while read -r line; do
        if [ "$line" = "-----BEGIN CERTIFICATE-----" ]; then
            echo "$line" > temp_cert.pem
        elif [ "$line" = "-----END CERTIFICATE-----" ]; then
            echo "$line" >> temp_cert.pem
            let "index++"
            keytool -importcert -alias "your-alias_$index" -keystore cacerts -file temp_cert.pem
            rm temp_cert.pem
        else
            echo "$line" >> temp_cert.pem
        fi
    done < multi_certs.pem

If you have a pem file with multiple certificates you can use a script to extract them and add them one at a time something like:

    index=0
    while read -r line; do
        if [ "$line" = "-----BEGIN CERTIFICATE-----" ]; then
            echo "$line" > temp_cert.pem
        elif [ "$line" = "-----END CERTIFICATE-----" ]; then
            echo "$line" >> temp_cert.pem
            let "index++"
            keytool -importcert -alias "your-alias_$index" -keystore cacerts -file temp_cert.pem
            rm temp_cert.pem
        else
            echo "$line" >> temp_cert.pem
        fi
    done < multi_certs.pem
2024-08-26 08:44:35

这是动态构建商店的 Scala 代码下方。

它可以这样使用:

def getServerSocket(): ServerSocket =
    SSL
      .createSSLFactory(Path.of(sslPrivateKey), Path.of(sslCertificate))
      .createServerSocket(port)

我正在使用通过 Let's Encrypt 为 Google 域获得的证书进行测试。

它运行良好,但不要忘记使用“fullchain.pem”作为证书文件,使用“privkey.pem”作为私钥。

谢谢大家。


package cc.lemieux.web.protocol.ssl

import cc.lemieux.web.util.fail

import java.io.{ByteArrayInputStream, File}
import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Path}
import java.security.cert.{CertificateFactory, X509Certificate}
import java.security.interfaces.RSAPrivateKey
import java.security.spec.PKCS8EncodedKeySpec
import java.security.{KeyFactory, KeyStore}
import java.util.Base64
import javax.net.ssl.{KeyManagerFactory, SSLContext, SSLServerSocketFactory}

object SSL:

  def createSSLFactory(privateKeyPemPath: Path, certificatePemPath: Path): SSLServerSocketFactory =
    try
      val privateKeyPem = privateKeyPemPath.toFile()
      val certificatePem = certificatePemPath.toFile()
      val password = ""
      val kmf = KeyManagerFactory.getInstance("SunX509")
      kmf.init(createKeyStore(privateKeyPem, certificatePem), password.toCharArray())
      val context = SSLContext.getInstance("TLS")
      context.init(kmf.getKeyManagers(), null, null)
      context.getServerSocketFactory()
    catch
      case e: Exception =>
        fail(
          s"Can't create SSL factory from private key '$privateKeyPemPath' and certificate '$certificatePemPath'.",
          e
        )

  private def loadTextFile(file: File, nature: String): Array[Array[Byte]] =
    try
      val begin = s"-----BEGIN $nature-----"
      val end = s"-----END $nature-----"
      val lines =
        Files.readString(file.toPath(), StandardCharsets.UTF_8).split('\n').toSeq.filter(_.nonEmpty).map(_.trim())
      if lines.length < 3 then fail("Mot at least 3 lines.")
      def chunk(seq: Seq[String]): Array[Array[Byte]] =
        if seq.isEmpty then Array.empty
        else
          val posBegin = seq.indexOf(begin)
          val posEnd = seq.indexOf(end)
          if posBegin == -1 || posEnd == -1 then fail("Missing header.")
          val chunkData = seq.slice(posBegin + 1, posEnd).mkString
          Base64.getDecoder().decode(chunkData) +: chunk(seq.drop(posEnd + 1))
      chunk(lines)
    catch case e: Exception => fail(s"Can't parse file '${file.getAbsolutePath()}'.", e)

  private def createKeyStore(privateKeyPem: File, certificatePem: File): KeyStore =
    val chunks = loadTextFile(certificatePem, "CERTIFICATE")
    val password = ""
    val certs = chunks.map { bytes =>
      CertificateFactory
        .getInstance("X.509")
        .generateCertificate(new ByteArrayInputStream(bytes))
    }
    val keystore = KeyStore.getInstance("JKS")
    keystore.load(null)
    val key =
      KeyFactory
        .getInstance("RSA")
        .generatePrivate(
          new PKCS8EncodedKeySpec(loadTextFile(privateKeyPem, "PRIVATE KEY").headOption.getOrElse(fail("No key.")))
        )
        .asInstanceOf[RSAPrivateKey]
    keystore.setKeyEntry(privateKeyPem.getName(), key, password.toCharArray(), certs)
    keystore

This is below the Scala code to build a store on the fly.

It can be used this way:

def getServerSocket(): ServerSocket =
    SSL
      .createSSLFactory(Path.of(sslPrivateKey), Path.of(sslCertificate))
      .createServerSocket(port)

I am testing with a certificate I got for a Google Domain via Let's Encrypt.

It works well, but do not forget to use the "fullchain.pem" for the certificates file, and the "privkey.pem" for the private key.

Thanks to everyone.


package cc.lemieux.web.protocol.ssl

import cc.lemieux.web.util.fail

import java.io.{ByteArrayInputStream, File}
import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Path}
import java.security.cert.{CertificateFactory, X509Certificate}
import java.security.interfaces.RSAPrivateKey
import java.security.spec.PKCS8EncodedKeySpec
import java.security.{KeyFactory, KeyStore}
import java.util.Base64
import javax.net.ssl.{KeyManagerFactory, SSLContext, SSLServerSocketFactory}

object SSL:

  def createSSLFactory(privateKeyPemPath: Path, certificatePemPath: Path): SSLServerSocketFactory =
    try
      val privateKeyPem = privateKeyPemPath.toFile()
      val certificatePem = certificatePemPath.toFile()
      val password = ""
      val kmf = KeyManagerFactory.getInstance("SunX509")
      kmf.init(createKeyStore(privateKeyPem, certificatePem), password.toCharArray())
      val context = SSLContext.getInstance("TLS")
      context.init(kmf.getKeyManagers(), null, null)
      context.getServerSocketFactory()
    catch
      case e: Exception =>
        fail(
          s"Can't create SSL factory from private key '$privateKeyPemPath' and certificate '$certificatePemPath'.",
          e
        )

  private def loadTextFile(file: File, nature: String): Array[Array[Byte]] =
    try
      val begin = s"-----BEGIN $nature-----"
      val end = s"-----END $nature-----"
      val lines =
        Files.readString(file.toPath(), StandardCharsets.UTF_8).split('\n').toSeq.filter(_.nonEmpty).map(_.trim())
      if lines.length < 3 then fail("Mot at least 3 lines.")
      def chunk(seq: Seq[String]): Array[Array[Byte]] =
        if seq.isEmpty then Array.empty
        else
          val posBegin = seq.indexOf(begin)
          val posEnd = seq.indexOf(end)
          if posBegin == -1 || posEnd == -1 then fail("Missing header.")
          val chunkData = seq.slice(posBegin + 1, posEnd).mkString
          Base64.getDecoder().decode(chunkData) +: chunk(seq.drop(posEnd + 1))
      chunk(lines)
    catch case e: Exception => fail(s"Can't parse file '${file.getAbsolutePath()}'.", e)

  private def createKeyStore(privateKeyPem: File, certificatePem: File): KeyStore =
    val chunks = loadTextFile(certificatePem, "CERTIFICATE")
    val password = ""
    val certs = chunks.map { bytes =>
      CertificateFactory
        .getInstance("X.509")
        .generateCertificate(new ByteArrayInputStream(bytes))
    }
    val keystore = KeyStore.getInstance("JKS")
    keystore.load(null)
    val key =
      KeyFactory
        .getInstance("RSA")
        .generatePrivate(
          new PKCS8EncodedKeySpec(loadTextFile(privateKeyPem, "PRIVATE KEY").headOption.getOrElse(fail("No key.")))
        )
        .asInstanceOf[RSAPrivateKey]
    keystore.setKeyEntry(privateKeyPem.getName(), key, password.toCharArray(), certs)
    keystore
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文