【问题标题】:Java - how to unlock a passphrase-protected PEM private keyJava - 如何解锁受密码保护的 PEM 私钥
【发布时间】:2019-06-01 17:11:51
【问题描述】:

我有一个由 OpenSSL 生成的受密码保护的 PEM 密钥文件

openssl genrsa -aes128 -passout stdin -out testfile.pem

我还使用 OpenSSL 生成了一个公钥文件

openssl rsa -in testfile.pem -out testfile_pub.pub(密码提示)

我希望能够使用此私钥签署我的声明等,然后发送请求。我正在努力理解(或者更像是确认我的理解)如下:

1) 我的私钥是受密码保护的,这是否意味着没有人可以在不先解锁的情况下真正生成公钥?也就是说,这就是保护的所在?

2) 如果我要在 Java 中读取这个加密的私钥 PEM 文件,我将不得不做类似的事情:

\\ 1. Read file as string \\ 2. Replace all boring bits e.g. begin/end/rsa/private/public/key/enc/-- \\ 3. decode using Base64 \\ 4. read as PKCS8 keyspec and generate PrivateKey object

但这是否意味着实际上没有人阻止我阅读 keyspecs ?我想我想比较的是我们如何使用可选的 keypass/storepass 生成JKS 密钥。但也许我不应该比较这个。

谁能帮我理解一下?

谢谢,

【问题讨论】:

  • 如果您想保留genrsarsa 使用的'传统' (PKCS1) 格式并且可以使用BouncyCastle,请参阅*.com/questions/44681737

标签: java security openssl pem


【解决方案1】:

openssl rsa -in testfile.pem -out testfile_pub.pub 不导出公钥,它实际上以明文形式导出私钥(如果您提供了正确的密码)。要导出公钥,请使用-pubout 选项。

是的,您需要密码才能导出公钥。

要在 Java 中导入私钥,您需要先将其转换为 PKCS8:

openssl pkcs8 -topk8 -in testfile.pem -inform pem -out testfile_pkcs8.pem -outform pem

然后你可以像这样在Java中导入它:

String encrypted = new String(Files.readAllBytes(Paths.get("testfile_pkcs8.pem")));  
encrypted = encrypted.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "");  
encrypted = encrypted.replace("-----END ENCRYPTED PRIVATE KEY-----", "");  
EncryptedPrivateKeyInfo pkInfo = new EncryptedPrivateKeyInfo(Base64.decode(encrypted));  
PBEKeySpec keySpec = new PBEKeySpec("mypassword".toCharArray()); // password  
SecretKeyFactory pbeKeyFactory = SecretKeyFactory.getInstance(pkInfo.getAlgName());  
PKCS8EncodedKeySpec encodedKeySpec = pkInfo.getKeySpec(pbeKeyFactory.generateSecret(keySpec));  
KeyFactory keyFactory = KeyFactory.getInstance("RSA");  
PrivateKey encryptedPrivateKey = keyFactory.generatePrivate(encodedKeySpec);  

不,这并不意味着任何人都可以读取密钥,因为您仍然需要提供密码。

【讨论】:

  • openssl pkcs8 -topk8 -nocrypt -outform der 为您进行解密和去PEM,然后直接读入PKCS8EncodedKeySpec——但如果任何不法分子可以访问该文件,您的安全性就会被破坏。 (如果您将它作为管道标准输入或其他瞬态管道传递,那会有所帮助。)
  • @mnistic 我尝试了这段代码,当我尝试从解码的字节构造 EncryptedPrivateKeyInfo 时它抛出异常。 java.io.IOException: ObjectIdentifier() -- data isn't an object ID (tag = 48)
  • 您是如何创建 PKCS8 文件的?您在我的回答中使用了确切的命令行吗?文件的第一行是“-----BEGIN ENCRYPTED PRIVATE KEY-----”吗?
  • @mnistic 是的,我完全按照上面的方法做了。我继续使用 BouncyCastle 读取输入。似乎 Java 无法可靠地读取加密的私钥文件,并且在 JDK 11 发布之前存在一段时间的 JDK 错误。
  • 我的 pem 文件包含 Bag 属性,如友好名称等。如何删除所有这些?