繁体   English   中英

将 ACM 导出的证书转换为 Java 中的 p12 文件

[英]Convert ACM exported certificate to p12 file in Java

我正在构建一个 MTLS 身份验证系统,其中注册步骤将使用 AWS ACM 私有 CA 生成证书。

所以在注册步骤中,我使用 AWS ACM SDK 先生成证书,然后导出。

RequestCertificateResult requestCertificateResult = client.requestCertificate(request);
String certificateArn = requestCertificateResult.getCertificateArn();

ExportCertificateRequest exportRequest = new ExportCertificateRequest();
exportRequest.setCertificateArn(certificateArn);
exportRequest.setPassphrase(ByteBuffer.wrap(password.getBytes()));

ExportCertificateResult exportCertificateResult = client.exportCertificate(exportRequest);

String certificateChain = exportCertificateResult.getCertificateChain();
String certificate = exportCertificateResult.getCertificate();
String privateKey = exportCertificateResult.getPrivateKey();

我想返回给客户端的是一个 .p12 文件,包括证书和 privateKey。 但是,ACM 的结果包含certificatecertificateChainprivateKey作为字符串。 如何使用 Java 将它们转换为 .p12 文件? 我在互联网上找到的所有内容都在使用openssl ,但由于这将是自动注册步骤的一部分,我需要以编程方式对其进行转换。

非常感谢任何正确方向的建议或指示。

编辑! 这就是我想要做的,但在 Java 中:

openssl pkcs12 -export -inkey private_key.txt -in certificate.txt -certfile certificate_chain.txt -out final_result.p12

并使用字符串而不是 .txt 文件。

如果您使用的是https://docs.aws.amazon.com/acm/latest/APIReference/API_ExportCertificate.html ,它将这些值描述并显示为 PEM 格式,这与您在openssl pkcs12 -export 然而,规范说私钥被标记为BEGIN/END PRIVATE KEY ,它是 PKCS8 未加密的,而示例显示ENCRYPTED PRIVATE KEY是(打赌你猜不到!)PKCS8 加密,尽管该示例有明显的错误让我不信任它。

叶证书和链很简单,只需将它们提供给可以处理 PEM 或“DER”(二进制)的CertificateFactory 要将它们放入 Java 密钥库(任何格式)中,您需要将它们组合在一个数组中,即叶子链:

// note: use java.security.cert.Certificate, not obsoleted java.security.Certificate or javax.security.cert.Certificate 
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate leaf = cf.generateCertificate(new ByteArrayInputStream(certString.getBytes()));
Collection<Certificate> chain = cf.generateCertificates(new ByteArrayInputStream(chainString.getBytes()));
// for general data String.getBytes() omitting/defaulting charset 
// can be dangerous, but PEM data is a strict subset of ASCII and safe
Certificate[] combine = chain.toArray( new Certificate[chain.size()+1] );
System.arraycopy(combine,0,combine,1,combine.length-1);
combine[0] = leaf;

或者组合输入可能更容易,但规范表明它们可能不提供证书的最终换行符(在 PEM END 行上); 如果是这样,您必须添加它以获得有效的 PEM 序列:

String temp = certString.endsWith("\n")? certString: certString + "\n";
Certificate[] combine = CertificateFactory.getInstance("X.509")
    .generateCertificates(new ByteArrayInputStream(temp+chainString))
    .toArray(new Certificate[0]);

如果私钥实际上是 PKCS8 未加密的,那么它几乎同样简单。 KeyFactory处理它,但仅作为“DER”而不是 PEM,因此您需要撤消 PEM“包装”。 一种流行的方式是

String justb64 = privkeyString.replaceAll("-----(BEGIN|END) PRIVATE KEY-----","").replaceAll("\\r?\\n","");
byte[] binary = Base64.getDecoder().decode(justb64);
// or can leave the linebreaks in the data and use getMimeDecoder()
// prior to j8 other base64 decoders like Apache commons were popular,
// although in a pinch you can write your own by hand

// Java wants to know the algorithm of the key _before_ parsing it;
// since we have a known-matching cert, we can use that
PrivateKey pkey = KeyFactory.getInstance(combine[0].getPublicKey().getAlgorithm())
   .generatePrivate(new PKCS8EncodedKeySpec(binary));

另一种方法是

String[] lines = privkeyString.split("\r?\n"); 
// or if reading from a file use BufferedReader or nio.Files.readAllLines
// may want to check that lines[0] and lines[lines.length-1] are in fact
// the desired BEGIN and END lines, if there is any chance the data is wrong
String justb64 = String.join("",Arrays.copyOfRange(lines,1,lines.length-1));
// continue as above

您现在可以将这些放在 PKCS12 密钥库中

KeyStore p12 = KeyStore.getInstance("PKCS12"); p12.load(null);
p12.setKeyEntry(alias, privkey, password, combine);
p12.store(/*OutputStream to desired file or other writable location*/, password);

如果私钥实际上是加密的,标准 Java 就不能轻易将其读取密钥。 但是,如果在 PKCS12 存储中使用(您的)Java 支持的算法之一对其进行加密,则可以使用“预保护”API 作为绕过方法:

byte[] priv_pkcs8enc = // un-PEM privkeyString as before, but _don't_ pass to KeyFactory
...
p12.setKeyEntry(alias, priv_pkcs8enc, combine);

我说“你的”Java,因为 Java 支持的一组 PKCS8 加密随着时间(版本)和实现(即提供程序)而变化,我怀疑会继续。

如果您的 Java 使用 PKCS12支持的算法对私钥进行加密,那么您可能对标准(Oracle、OpenJDK 等)Java 不走运。 但是,如果您想追求这一点,请将测试密钥的 Q 详细信息添加到您的 Q 细节中——请记住,如果亚马逊没有记录他们使用的算法,它可能会有所不同(可能会因地区或服务产品而异)并且可能未来的变化。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM