我试图连接到一个需要身份验证的SSL服务器。为了在Apache MINA上使用SSL,我需要一个合适的JKS文件。然而,我只收到了一个.PEM文件。
我该如何从PEM文件创建JKS文件?
我试图连接到一个需要身份验证的SSL服务器。为了在Apache MINA上使用SSL,我需要一个合适的JKS文件。然而,我只收到了一个.PEM文件。
我该如何从PEM文件创建JKS文件?
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();
在这里,我需要提供一些免责声明,我是该库的维护者。
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数据中的行尾)。
将SunX509
和JKS
替换为PKIX
。仅在第一种情况下是必要的,在第二种情况下可能是无关紧要的;如果您正在使用已初始化的PrivateKey
等对象填充KeyStore
,则我认为它没有任何意义。实际上,我在createKeyStore
中使用了getDefaultAlgorithm()
来代替"JKS",尽管该默认值目前为"jks",但使用PKIX
作为算法创建的KeyManagerFactory
中的密钥库工作正常。
我还应该注意,我没有使用createSSLFactory
方法,而是使用createKeyStore()
的输出来初始化KeyManagerFactory
并提取用于初始化SSLContext
的KeyManagers
:
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()
是朝这个方向迈出的一步,但我从未需要过这样做)。它只需要在上述两个用途中匹配即可。certtool
创建密钥时,请使用--pkcs8
)。请注意,这并不意味着PKCS#8密钥不能潜在地基于RSA,而是关于用于存储和提取密钥数据的协议。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
}
}
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