我有这段代码使用别名生成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,但最好尽可能减少用户交互。
答案 0 :(得分:4)
虽然我没有看到它的文档记录,但根据源代码,SunMSCAPI提供程序仅实现getEncoded
的存根,而无法导出Windows私钥,因此您无法用JCA做到这一点。
您当然可以编写JNI或JNA来调用Windows CAPI,但这并不简单。
要在没有用户互动的情况下使用现有工具,您可以使用Runtime
或ProcessBuilder
使用参数certutil
-exportpfx -user -p password certid filename
运行powershell
并告诉它在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密钥库并获取PrivateKey条目
调用getEncoded
并将结果编码为折叠(MIME)base64,就像您对证书所做的那样,除了使用-----BEGIN/END PRIVATE KEY-----
警告:Java会生成未加密的(PKCS8)私钥,因此请确保没有未经授权的用户或程序可以访问此文件,磁盘/文件系统或任何备份。
答案 1 :(得分:1)
数字证书中没有私人密钥 (私钥不是证书字段的一部分)。证书和私钥是单独的实体,但它们相关(没有另一个就不能存在)。
如果您查看RFC 5280中的证书字段,您会发现只有公钥是其中的一部分:
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING }
TBSCertificate ::= SEQUENCE {
... lots of fields
subjectPublicKeyInfo SubjectPublicKeyInfo,
... lots of other fields
}
subjectPublicKeyInfo
是公钥,私钥没有字段。
这是因为证书是公开的(你可以详细了解他们为什么公开看看Public Key Infrastructure如何运作)。
虽然证书是公开的,但在某处始终存在相应的私钥,通常由证书所有者持有(理想情况下不由其他人持有)。
无论如何,您只有数字证书(但不是私钥)才能获得(带有 BEGIN CERTIFICATE 和 END CERTIFICATE 标题)的文件。
如果您拥有私钥和相应的证书,则可以创建包含这两者的文件。此类文件最常见的格式为: JKS (也称为密钥库)和 PFX 。
还有另一种“格式”:Windows存储库(当您执行KeyStore.getInstance("Windows-MY")
时正在阅读的存储库)。我不确切知道它的文件是什么格式,但KeyStore
类抽象它。
如果存在私钥,它将与相应的证书一起放在同一别名中。您可以使用以下代码检查密钥是否存在:
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());