从PKCS8编码数据创建任何PrivateKey实例(RSA或DSA或EC)

9
我有一个未加密的PKCS8编码文件,表示私钥。它可以是这些私钥类型之一 - RSA、DSA或EC。我在ASN1解码器(https://lapo.it/asn1js/)中查看了这些文件,可以在数据中看到类型(RSA、DSA或EC)。
是否有一种方法将PKC8私钥数据读入正确的私钥Java对象中,而不需要在代码中指定密钥类型,就像这样 -
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pkcs8key);
KeyFactory factory = KeyFactory.getInstance("RSA"); // Avoid "RSA" here?
PrivateKey privateKey = factory.generatePrivate(spec);

有没有一种方法可以避免在KeyFactory.getInstance("RSA")中指定算法?这不应该从PKCS8EncodedKeySpec中确定吗,因为它在PKCS8数据中可用吗?

未加密的PKCS8数据示例及其ASN1解码,显示密钥类型 -

DSA - 链接

EC - 链接

RSA - 链接

根据 https://docs.oracle.com/javase/7/docs/api/java/security/spec/PKCS8EncodedKeySpec.html,似乎在密钥/对象中有一个“PrivateKeyInfo”描述,其中包含所使用的算法。使用它来加载正确的“KeyFactory”实例。 - Progman
那么你的意思是说,PKCS8EncodedKeySpec.getEncoded() 返回包含 PrivateKey 算法的 ASN1 编码数据?我在 JDK 类中找不到任何名为 PrivateKeyInfo 的类来加载此数据(即 getEncoded() 返回的数据)。这就是我需要帮助的地方。如何使用通用 Java API 读取此私钥数据并获取算法类型,而无需专门编写 RSA/DSA/EC 的代码? - MediumOne
请解释一下为什么要给我点踩。如果问题不清楚,请告诉我,我会重新表述。假设你偶然发现了一个PEM编码的未加密私钥文件(注意,此数据包含PrivateKey算法)。你将如何编写代码来读取此数据并获取一个PrivateKey实例,而不在代码中指定私钥算法类型?我认为这是一个有效的问题。 - MediumOne
2
https://www.programcreek.com/java-api-examples/?api=org.bouncycastle.asn1.pkcs.PrivateKeyInfo 看起来很有前途,而且是谷歌搜索的第一个结果。你可以尝试将其适应于你的代码。 - Progman
谢谢。那个链接很有帮助。我没有想到会使用BouncyCastle库(我目前没有使用),但这对我应该有效。如果您可以将链接中的代码发布为答案,我可以接受它作为有效答案。再次感谢。 - MediumOne
你可以自己添加答案并接受它。我不会提供答案,因为我没有测试过,只提供链接(可能会被删除)。但是既然你已经测试过并且可能有一个可行的代码库,你可以将答案添加到这个问题中。 - Progman
1个回答

6
可以借助BouncyCastle API来实现这一点 -
/** Read a PKCS#8 format private key. */
private static PrivateKey readPrivateKey(InputStream input)
throws IOException, GeneralSecurityException {
    try {
        byte[] buffer = new byte[4096];
        int size = input.read(buffer);
        byte[] bytes = Arrays.copyOf(buffer, size);
        /* Check to see if this is in an EncryptedPrivateKeyInfo structure. */
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        /*
         * Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm
         * OID and use that to construct a KeyFactory.
         */
        ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded()));
        PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
        String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();
        return KeyFactory.getInstance(algOid).generatePrivate(spec);
    } finally {
        input.close();
    }
}

1
谢谢您提供这个有用的答案!在您的代码片段之后,我甚至找到了一种更简单的方法。PrivateKey privateKey = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey(privateKeyInfo); 我认为在这里分享它会很方便。通过这种方式,可以省略关于algorithmId和KeyFactory的最后两行。 - Hakan54
这个可行吗?据我所知,你不能使用 OID 来提供 KeyFactory.getInstance(),而是需要提供提供者的名称,例如 RSA 或 EC。 - Jasper Siepkes
没错,Java的默认提供程序不支持OID,但Bouncy Castle提供程序可以(使用 KeyFactory.getInstance(algOid, "BC");)。 - Jasper Siepkes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接