使用 Java 从证书别名到包含私钥的 PEM 文件
From certificate Alias to PEM File with private key included using Java
我有这段代码可以使用别名生成 CER 文件:
public class TestFromAliasToCER {
public static final int KEY_SIZE = 1024;
public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
public static final String END_CERT = "-----END CERTIFICATE-----";
public final static String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) throws FileNotFoundException, IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyStoreException, CertificateException {
KeyStore keyStore = KeyStore.getInstance ("Windows-MY");
keyStore.load (null, null);
Enumeration<String> aux = keyStore.aliases();
String alias = aux.nextElement();
X509Certificate certificate = (X509Certificate) keyStore.getCertificate (alias);
String certString = formatCrtFileContents(certificate);
PrintWriter out = new PrintWriter("cert.CER");
out.println(certString);
out.close();
}
public static String formatCrtFileContents(final Certificate certificate) throws CertificateEncodingException {
final Base64.Encoder encoder = Base64.getMimeEncoder(64, LINE_SEPARATOR.getBytes());
final byte[] rawCrtText = certificate.getEncoded();
final String encodedCertText = new String(encoder.encode(rawCrtText));
final String prettified_cert = BEGIN_CERT + LINE_SEPARATOR + encodedCertText + LINE_SEPARATOR + END_CERT;
return prettified_cert;
}
}
这将创建带有
的cer文件
-----BEGIN CERTIFICATE-----
data
-----END CERTIFICATE-----
我希望能够创建包含私钥的 PEM 证书,这可能吗?如果不是,为什么?
我不仅限于 Java 并且可以自由使用任何 Java API,但最好尽可能减少用户交互。
数字证书里面没有私钥(私钥不是证书字段的一部分)。证书和私钥是独立的实体,尽管它们相关(缺一不可)。
如果您查看 RFC 5280 中的证书字段,您会发现只有 public 密钥 是其中的一部分:
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING }
TBSCertificate ::= SEQUENCE {
... lots of fields
subjectPublicKeyInfo SubjectPublicKeyInfo,
... lots of other fields
}
subjectPublicKeyInfo
是 public 密钥,没有私钥字段。
那是因为证书本来就是 public(您可以详细了解为什么 public 查看 Public Key Infrastructure 的工作原理)。
虽然证书是 public,但在某处总有对应的私钥,通常由证书所有者持有(最好不是其他人)。
无论如何,您获得的文件(带有 BEGIN CERTIFICATE 和 END CERTIFICATE headers)只有数字格式证书(但不是私钥)。
如果您有私钥和相应的证书,您可以创建一个包含这两者的文件。此类文件最常见的格式是:JKS(也称为 Keystore)和 PFX.
还有另一种“格式”:Windows 存储库(您在 KeyStore.getInstance("Windows-MY")
时正在阅读的存储库)。我不知道它的文件到底是什么格式,但是 KeyStore
class 抽象了它。
如果私钥存在,它将与相应的证书一起使用相同的别名。您可以使用此代码检查密钥是否存在:
String alias = aux.nextElement();
if (keyStore.isKeyEntry(alias)) { // alias contains a private key
Key key = keyStore.getKey(alias, "password".toCharArray()); // need to know the password
// key is the private key
// cert is the key's corresponding certificate
X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
} else if (keyStore.isCertificateEntry(alias)) { // alias doesn't contain a key
X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
}
获得密钥后,可以使用以下代码将其保存到另一个密钥库:
// create another keystore
KeyStore output = KeyStore.getInstance("JKS");
// "alias" - choose to whatever name you want
// privateKey is the object you've got from keyStore.getKey()
// "password" is the password for this alias
// cert will be stored in the same alias
output.setKeyEntry("alias", privateKey, "password".toCharArray(), new Certificate[] { cert });
// save the keystore to a file
output.store(new FileOutputStream("outputfile.jks"), "keystore password".toCharArray());
上面的代码创建了包含证书和私钥的文件 outputfile.jks
。
如果你想让文件成为PFX,你可以把上面的代码改成:
// PKCS12 == PFX format
KeyStore output = KeyStore.getInstance("PKCS12");
// alternative: in pfx, I think that alias can't have specific passwords
// so you can use this as it doesn't require a password for the alias entry
output.setKeyEntry("alias", privateKey.getEncoded(), new Certificate[] { cert });
// change file extension to ".pfx"
output.store(new FileOutputStream("outputfile.pfx"), "keystore password".toCharArray());
虽然我没有看到它的文档,但根据消息来源,SunMSCAPI 提供程序只为 getEncoded
实现了一个存根并且 无法导出 Windows 私钥 所以你不能用 JCA 做到这一点。
你当然可以写JNI或JNA来调用Windows CAPI,但这并不简单
要在没有用户交互的情况下使用现有工具,您可以使用 Runtime
或 ProcessBuilder
到
运行 certutil
带参数 -exportpfx -user -p password certid filename
运行 powershell
并告诉它 select cert:\currentuser\my
中的一个对象并调用 Export('PFX','password')
方法 -- examples for machine rather than user cert here
或(仅)在最近的 powershell 中使用 Export-PFXCertificate
cmdlet documentation here
并在其中任何一个之后,使用 openssl pkcs12
从 pkcs12 提取到 PEM,或者如果您更喜欢使用 Java 通过:
加载 PKCS12 密钥库并获取私钥条目
调用 getEncoded
并将结果编码为折叠 (MIME) base64,就像您对证书所做的那样,除了使用 -----BEGIN/END PRIVATE KEY-----
警告:Java 生成未加密的 (PKCS8) 私钥,因此请确保没有未经授权的用户或程序访问此文件、您的 disk/filesystem 或任何备份。
我有这段代码可以使用别名生成 CER 文件:
public class TestFromAliasToCER {
public static final int KEY_SIZE = 1024;
public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
public static final String END_CERT = "-----END CERTIFICATE-----";
public final static String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) throws FileNotFoundException, IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyStoreException, CertificateException {
KeyStore keyStore = KeyStore.getInstance ("Windows-MY");
keyStore.load (null, null);
Enumeration<String> aux = keyStore.aliases();
String alias = aux.nextElement();
X509Certificate certificate = (X509Certificate) keyStore.getCertificate (alias);
String certString = formatCrtFileContents(certificate);
PrintWriter out = new PrintWriter("cert.CER");
out.println(certString);
out.close();
}
public static String formatCrtFileContents(final Certificate certificate) throws CertificateEncodingException {
final Base64.Encoder encoder = Base64.getMimeEncoder(64, LINE_SEPARATOR.getBytes());
final byte[] rawCrtText = certificate.getEncoded();
final String encodedCertText = new String(encoder.encode(rawCrtText));
final String prettified_cert = BEGIN_CERT + LINE_SEPARATOR + encodedCertText + LINE_SEPARATOR + END_CERT;
return prettified_cert;
}
}
这将创建带有
的cer文件-----BEGIN CERTIFICATE-----
data
-----END CERTIFICATE-----
我希望能够创建包含私钥的 PEM 证书,这可能吗?如果不是,为什么?
我不仅限于 Java 并且可以自由使用任何 Java API,但最好尽可能减少用户交互。
数字证书里面没有私钥(私钥不是证书字段的一部分)。证书和私钥是独立的实体,尽管它们相关(缺一不可)。
如果您查看 RFC 5280 中的证书字段,您会发现只有 public 密钥 是其中的一部分:
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING }
TBSCertificate ::= SEQUENCE {
... lots of fields
subjectPublicKeyInfo SubjectPublicKeyInfo,
... lots of other fields
}
subjectPublicKeyInfo
是 public 密钥,没有私钥字段。
那是因为证书本来就是 public(您可以详细了解为什么 public 查看 Public Key Infrastructure 的工作原理)。
虽然证书是 public,但在某处总有对应的私钥,通常由证书所有者持有(最好不是其他人)。
无论如何,您获得的文件(带有 BEGIN CERTIFICATE 和 END CERTIFICATE headers)只有数字格式证书(但不是私钥)。
如果您有私钥和相应的证书,您可以创建一个包含这两者的文件。此类文件最常见的格式是:JKS(也称为 Keystore)和 PFX.
还有另一种“格式”:Windows 存储库(您在 KeyStore.getInstance("Windows-MY")
时正在阅读的存储库)。我不知道它的文件到底是什么格式,但是 KeyStore
class 抽象了它。
如果私钥存在,它将与相应的证书一起使用相同的别名。您可以使用此代码检查密钥是否存在:
String alias = aux.nextElement();
if (keyStore.isKeyEntry(alias)) { // alias contains a private key
Key key = keyStore.getKey(alias, "password".toCharArray()); // need to know the password
// key is the private key
// cert is the key's corresponding certificate
X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
} else if (keyStore.isCertificateEntry(alias)) { // alias doesn't contain a key
X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
}
获得密钥后,可以使用以下代码将其保存到另一个密钥库:
// create another keystore
KeyStore output = KeyStore.getInstance("JKS");
// "alias" - choose to whatever name you want
// privateKey is the object you've got from keyStore.getKey()
// "password" is the password for this alias
// cert will be stored in the same alias
output.setKeyEntry("alias", privateKey, "password".toCharArray(), new Certificate[] { cert });
// save the keystore to a file
output.store(new FileOutputStream("outputfile.jks"), "keystore password".toCharArray());
上面的代码创建了包含证书和私钥的文件 outputfile.jks
。
如果你想让文件成为PFX,你可以把上面的代码改成:
// PKCS12 == PFX format
KeyStore output = KeyStore.getInstance("PKCS12");
// alternative: in pfx, I think that alias can't have specific passwords
// so you can use this as it doesn't require a password for the alias entry
output.setKeyEntry("alias", privateKey.getEncoded(), new Certificate[] { cert });
// change file extension to ".pfx"
output.store(new FileOutputStream("outputfile.pfx"), "keystore password".toCharArray());
虽然我没有看到它的文档,但根据消息来源,SunMSCAPI 提供程序只为 getEncoded
实现了一个存根并且 无法导出 Windows 私钥 所以你不能用 JCA 做到这一点。
你当然可以写JNI或JNA来调用Windows CAPI,但这并不简单
要在没有用户交互的情况下使用现有工具,您可以使用 Runtime
或 ProcessBuilder
到
运行
certutil
带参数-exportpfx -user -p password certid filename
运行
powershell
并告诉它 selectcert:\currentuser\my
中的一个对象并调用Export('PFX','password')
方法 -- examples for machine rather than user cert here或(仅)在最近的 powershell 中使用
Export-PFXCertificate
cmdlet documentation here
并在其中任何一个之后,使用 openssl pkcs12
从 pkcs12 提取到 PEM,或者如果您更喜欢使用 Java 通过:
加载 PKCS12 密钥库并获取私钥条目
调用
getEncoded
并将结果编码为折叠 (MIME) base64,就像您对证书所做的那样,除了使用-----BEGIN/END PRIVATE KEY-----
警告:Java 生成未加密的 (PKCS8) 私钥,因此请确保没有未经授权的用户或程序访问此文件、您的 disk/filesystem 或任何备份。