将 PEM 导入 Java 密钥存储

回显日期(‘ D,M jS Y’,$month _ end) 回显日期(‘ D,M jS Y’,$year _ start)

所有这些特殊的 php 表达,在 first day of ...的精神是伟大的,虽然他们走出我的头一次又一次。

回波日期(‘ D,M jS Y’,$year _ end) E > < code > (new TheFirstDayOfThisMonth (new Now ()))-> value () ;

因此,我决定构建一些基本的日期时间抽象和大量的特定实现,这些都是由任何 IDE 自动完成的。关键是要找到 什么之类的东西。比如,todaynowthe first day of a previous month等等。所有这些我都列出了 的日期时间。因此,有一个名为 ISO8601DateTime的接口或抽象类,以及实现它的特定日期时间。

特定情况下的代码如下所示:

(new TheFirstDayOfThisMonth(new Now()))->value();

有关这种方法的更多信息,请参见 这个条目

460125 次浏览
$startOfThisMonth = strtotime (“本月第一天00:00:00”) ;

如果不首选 DateTime 对象,我将提供这个答案作为替代的一行

$endOfThisMonth = strtotime (“下个月的第一天”,$startOfThisMonth)-1;

我开发了将 PEM 证书直接导入 Java 密钥存储库的 http://code.google.com/p/java-keyutil/。它的主要用途是导入多部分 PEM 操作系统证书包,比如 ca-bundle。对不起。这些通常包括 keytool 无法处理的头部

</self promotion>

如果你正在使用作曲家,你可以安装 :

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

openssl x509 -outform der -in certificate.pem -out certificate.der
composer require nesbot/carbon

然后,将其导入到密钥存储库中:

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

这很简单:

use Carbon/Carbon;


$startOfMonth = Carbon::now()->startOfMonth()->toDateTime();
你.. ——开始证书——

在我的例子中,我有一个 pem 文件,其中包含两个证书和一个用于相互 SSL 身份验证的加密私钥。 ... ——结束证书—— 我的文件是这样的:

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

---BEGIN..开始,以 ---END..行结束。让我们假设现在有三个文件: cert1.pemcert2.pempkey.pem

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

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

Openssl pkcs8-topk8-nocrypt-in pkey.PEM-information PEM-out pkey.DER-outform DER

Openssl pkcs8-topk8-nocrypt-in pkey.PEM-information PEM-out pkey.DER-outform DER

注意,如果私钥被加密,则需要提供一个密码(从原始 pem 文件的提供者获取)来转换为 DER 格式,

注意,如果私钥被加密,则需要提供一个密码(从原始 pem 文件的提供者获取)来转换为 DER 格式, openssl会像这样要求您输入密码: “为 pkey.pem:”输入密码短语。

openssl会像这样要求您输入密码: “为 pkey.pem:”输入密码短语。

如果转换成功,您将得到一个名为 pkey.der的新文件。

如果转换成功,您将得到一个名为 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());

创建一个新的 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

(可选)验证新密钥存储的内容:

$ keytool -list -keystore mykeystore -storepass password

密钥存储类型: JKS

密钥存储类型: JKS 密钥存储提供商: SUN

密钥存储提供商: SUN

您的密钥存储库包含3个条目:

    您的密钥存储库包含3个条目:

    • Cn = ... ,ou = ... ,o = . ,Sep 2,2014,trust dCertEntry,

    • Cn = ... ,ou = ... ,o = . ,Sep 2,2014,trust dCertEntry, 证书指纹(SHA1) : 2C: B8: ...

    • 证书指纹(SHA1) : 2C: B8: ...

    • 进口键,2014年9月2日,

    • 进口键,2014年9月2日, 证书指纹(SHA1) : 9C: B0: ...

    • 证书指纹(SHA1) : 9C: B0: ...

    • Cn = ... ,o = ... ,Sep 2,2014,trust dCertEntry,

    • Cn = ... ,o = ... ,Sep 2,2014,trust dCertEntry, 证书指纹(SHA1) : 83:63: ...

    证书指纹(SHA1) : 83:63: ...

(可选)在 SSL 服务器上测试新密钥存储中的证书和私钥:

(可选)在 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..
(您可能希望将调试作为 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);

我总是忘记该怎么做,因为这是我偶尔会做的事情,这是一个可能的解决方案,而且它很有效:

    知道是什么原因吗?

  1. 进入你最喜欢的浏览器,从受保护的网站下载主证书。
  2. 找不到 Spring XML 模式的 NamespaceHandler

  3. 执行以下两行代码:

    $ openssl x509 -outform der -in GlobalSignRootCA.crt -out GlobalSignRootCA.der
    $ keytool -import -alias GlobalSignRootCA -keystore GlobalSignRootCA.jks -file GlobalSignRootCA.der
    
  4. 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
    
  5. 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
命名空间 Ity/spring-security-3.0. xsd”> ... [翻译/校对: pestwave ]翻译: pestwave 翻译: pestwave 翻译: pestwave 翻译: pestwave 翻译: pestwave 翻译: pestwave 翻译: pestwave 翻译: pestwave 翻译: pestwave 翻译: pestwave 翻译: pestwave 翻译: pestwave 翻译: pestwave 翻译: pestwave 翻译

org.springframework.web.context.ContextLoader initWebApplicationContext: Context initialization failed
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/security]
Offending resource: ServletContext resource [/WEB-INF/applicationContext.xml]

这是我的 applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
...
</beans:beans>

在 pom.xml 中,我有:

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-openid</artifactId>
<version>3.0.1.RELEASE</version>
</dependency>

如果您需要一种在 Java不需要处理外部工具(opensll,keytool)中加载 PEM 文件的简单方法,以下是我在生产中使用的代码:

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


}

我在尝试部署应用程序时也出现了相同的错误消息。在 Spring 中,安全配置 xml 可以与 applicationContext.xml 不同,通常是 WEB-INF 文件夹中的 applicationContext-security.xml。要应用的更改是针对 web.xml 的

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext.xml
/WEB-INF/applicationContext-security.xml
</param-value>
</context-param>

玩得开心。

Xml 应该是这样的:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">


<http auto-config='true'>
<intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login login-page='login.jsp'/>
</http>


</beans:beans>

我从网上找到的,对于包含多个条目的 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>

即使进行了这些更改,名称空间错误仍将存在。要解决这个问题,请将以下 jar 文件添加到 WEB-INF/lib 中,然后添加到库中:

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

  • Spring-security-acl-3.1.0. M2.jar
  • Http://portecle.sourceforge.net/

  • Spring-security-config-3.1.0. M2.jar
  • Spring-security-core-3.1.0. M2.jar
  • Portecle 是一个用户友好的 GUI 应用程序,用于创建、管理和检查密钥存储库、密钥、证书、证书请求、证书撤销列表等。

在 Project-> Properties-> DeploymentAssembly 中,只有一些 jar 被复制。

如果你已经有了所有的依赖项,试试:

为了解决这个问题,我点击“添加”,然后点击“ JavaBuild 路径整体”,最后点击“ MavenDependency”。

我一直在搜索所以和网络的最后一个小时寻找这一点,所以希望这有助于其他人。


1. 从你的“ org-> springFramework”文件夹中删除所有下载的罐子
2. 建立一个干净的专家体型。

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

GitHub-SSLContext 启动

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();
并且几乎迫使您将 pem 文件转换为 p12或 jks 文件,因为使用 p12或 jks 文件要容易得多。我想给任何想要替代已经提供的答案的人。

GitHub-SSLContext 启动

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

我需要在这里提供一些免责声明,我是图书馆维护人员

OpenJDKkeytool现在可以本地处理 PEM 证书(已经发布了几个版本,但我不确定是什么时候发布的)。

一个很好的 Maven 依赖列表存在于: Spring Site

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

需要的主要文物有:

  1. 弹簧安全核心
  2. 所以使用 OpenJDK 18(可能还有许多早期版本)的命令行是:

    keytool -cacerts -import -alias <alias> -file <path_to_cert.pem>
    
  • 弹簧安全网
  • Spring-security-config 弹簧-安全-配置
  • 翻译成科特林(这是不必要的,我只是喜欢它)。原始类包含所有静态方法,因此这导致了一个命名对象。

    如果你想在 Android 上这样做,有一些小的调整,可以添加到 PEMImporter类从 这个伟大的答案。总结一下:

    • 在11版中,从 java 核心中移除了 javax.xml.bind.DatatypeConverter:

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

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

    • implementation("javax.xml.bind:jaxb-api:2.4.0-b180830.0359") ,这在 Android 上不起作用,对于它执行的任务(即将 base64转换为字节) ,使用 java.util.Base64更简单。输出是相同的(尽管您需要修剪原始 PEM 数据中的行结尾)。

    • PKIX代替 SunX509JKS。它只在第一种情况下是必要的,而在第二种情况下可能是无关紧要的; 我不认为它有任何意义,如果您正在填充一个 KeyStore与已经初始化的 PrivateKey等对象,正如这里所做的。实际上,我在 createKeyStore中使用 getDefaultAlgorithm()代替“ JKS”,尽管默认值是“ JKS”,但密钥存储在使用 PKIX作为算法创建的 KeyManagerFactory中工作得很好。

  • PKIX代替 SunX509JKS。它只在第一种情况下是必要的,而在第二种情况下可能是无关紧要的; 我不认为它有任何意义,如果您正在填充一个 KeyStore与已经初始化的 PrivateKey等对象,正如这里所做的。实际上,我在 createKeyStore中使用 getDefaultAlgorithm()代替“ JKS”,尽管默认值是“ JKS”,但密钥存储在使用 PKIX作为算法创建的 KeyManagerFactory中工作得很好。

  • 我还应该注意到,我没有使用 createSSLFactory方法,而是使用 createKeyStore()的输出来初始化一个 KeyManagerFactory,并提取用于初始化一个 SSLContextKeyManagers:

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

    我还应该注意到,我没有使用 createSSLFactory方法,而是使用 createKeyStore()的输出来初始化一个 KeyManagerFactory,并提取用于初始化一个 SSLContextKeyManagers:

    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()是朝这个方向迈出的一步,但我从未需要这样做)。它只需要在以上两种用途中匹配即可。

    这里的密码可能并不重要,因为 PEMImporter可以处理已经未加密的密钥数据——除非您想将 PrivateKey写回文件(我假设 getEncoded()是朝这个方向迈出的一步,但我从未需要这样做)。它只需要在以上两种用途中匹配。

    我还为 RSA PRIVATE KEYS添加了一个捕获,结果是

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

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

    以下是我在 Kotlin 的安卓版 PEMImporter:

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