如何在Java中从pem字符串生成RSA私钥

6

我想在Java中从字符串(一个.pem文件)生成私钥。

private static final String test = "-----BEGIN RSA PRIVATE KEY-----\n" +
         "MIIEpAIBAAKCAQEAvcCH8WsT1xyrZqq684VPJzOF3hN5DNbowZ96Ie//PN0BtRW2\n" +
// and so on
         "-----END RSA PRIVATE KEY-----";

try {
    String privKeyPEM = test.replace("-----BEGIN RSA PRIVATE KEY-----\n", "");
    privKeyPEM = privKeyPEM.replace("-----END RSA PRIVATE KEY-----", "");

    byte [] encoded = Base64.decode(privKeyPEM);

    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    PrivateKey privKey = kf.generatePrivate(keySpec);
}
catch (Exception e) {
    e.printStackTrace();
}

最后一行(generatePrivate函数)抛出了这个异常:

java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
    at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(Unknown Source)
    at java.security.KeyFactory.generatePrivate(Unknown Source)
    at Test.main(Test.java:52)
Caused by: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
    at sun.security.pkcs.PKCS8Key.decode(Unknown Source)
    at sun.security.pkcs.PKCS8Key.decode(Unknown Source)
    at sun.security.rsa.RSAPrivateCrtKeyImpl.<init>(Unknown Source)
    at sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(Unknown Source)
    at sun.security.rsa.RSAKeyFactory.generatePrivate(Unknown Source)
    ... 3 more

如果我将私钥更改为来自.der文件的值,则可以正常工作,但我需要从.pem文件生成私钥文件。
我附上了打印为字符串的字节的屏幕截图(一次硬编码带有\n,一次硬编码不带\n,以及一次从文件中读取)。 更大的图片

Output

文件的输出结果与字符串的输出结果不同,这很奇怪。
如果我试图使用Base64对一个.der文件进行编码,结果与.pem文件中的字符串不同。为什么会这样?

你找到答案了吗? - Sankar
@SankarP 不是的,真的没什么。 - Niklas
好的。我已经找到了解决方案。以“-----BEGIN RSA PRIVATE KEY”开头的密钥是pkcs1编码文件。这种pkcs1编码在Java中不受支持,除非您使用像BouncyCastle这样的外部库。昨天我遇到了同样的问题,经过大约8个小时的努力,终于找到了解决方案。如果您使用pkcs8对私钥进行编码,则它将以“--- BEGIN PRIVATE KEY---”开头,这将由原始Java处理。希望能对您有所帮助。 - Sankar
复制 https://dev59.com/AXA75IYBdhLWcg3wipmH - dave_thompson_085
1个回答

1

你说最后一行代码会抛出异常,即

PrivateKey privKey = kf.generatePrivate(keySpec);

以上代码行的运行取决于正确设置keyspec,即

PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);

因此实际问题在于编码字节数组。在byte [] encoded = Base64.decode(privKeyPEM);之后,您是否执行了System.out并查看了输出结果。

我了解如果消息是以MIME格式发送的,那么在某些字符之后,它会附加换行符和回车符的组合,以便字符串不会过长,适用于电子邮件系统或您正在使用的其他地方。

最终的String测试文本中包含一些原始文本中的'\n',您使用了其中的一些文本,在下面的代码中已经去除了其他文本。

String privKeyPEM = test.replace("-----BEGIN RSA PRIVATE KEY-----\n", "");
    privKeyPEM = privKeyPEM.replace("-----END RSA PRIVATE KEY-----", "");

但是,请看这个字符串:

"MIIEpAIBAAKCAQEAvcCH8WsT1xyrZqq684VPJzOF3hN5DNbowZ96Ie//PN0BtRW2\n" +
// and so on
         "-----END RSA PRIVATE KEY-----";

可能仍有一些 '\n' 未处理,导致生成 keyspec 时出现一些不需要的字符。尝试更多的 System.out,查看编码后的字节数组的情况,在此之前检查 String privKeyPEM 是否还有任何多余的字符。

希望这可以帮到你。


1
我上传了encoded的输出。http://pastebin.com/YUsNdhgW。我还将测试字符串更改为不带`\n`的纯字符串,但也没有起作用。 - Niklas
我还上传了一个字符串输出的截图。 - Niklas
Base64解码会忽略换行符和通常的所有空格。 - dave_thompson_085

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