如何使用PyCrypto与RSA加密私钥?

7

我正在使用OpenSSL生成密钥,从stdin提供密码:

openssl genpkey -algorithm RSA -out private-key.pem -outform PEM -pass stdin -des3 -pkeyopt rsa_keygen_bits:4096

那么这个密钥看起来像这样:
-----BEGIN ENCRYPTED PRIVATE KEY-----
XXX...
-----END ENCRYPTED PRIVATE KEY-----

我的Python代码如下:

from Crypto.PublicKey import RSA
# ...
f = open('private-key.pem', 'r')
r = RSA.importKey(f.read(),  passphrase='some-pass')
f.close()

但我收到了一个异常:

  File "/usr/lib/python2.7/dist-packages/Crypto/PublicKey/RSA.py", line 665, in importKey
    return self._importKeyDER(der)
  File "/usr/lib/python2.7/dist-packages/Crypto/PublicKey/RSA.py", line 588, in _importKeyDER
    raise ValueError("RSA key format is not supported")
ValueError: RSA key format is not supported

有什么问题吗?

是否可以生成加密的RSA密钥,将其存储在文件中,并稍后使用PyCrypto使用它?是否可以使用OpenSSL进行此操作?支持哪些格式?

导入公钥很好用,但它没有加密。


有趣的是,根据堆栈跟踪,它试图以二进制DER格式导入文件,而不是PEM格式。您确定提供了正确的文件吗? - Oleg Gryb
2个回答

5

假设 #1

查看源代码后,我认为我解决了这个谜团。对于使用密码加密的PEM密钥,导入方式是将PEM解密为DER,然后调用importKeyDER函数。如果提供的密码不正确,则生成的DER表示形式的格式也不正确,您将收到异常提示。为了确认这一点,我进行了以下两个快速测试:

>>> from Crypto.PublicKey import RSA
>>> f = open('<some-path>/private-key.pem','r')
>>> r=RSA.importKey(f.read(),passphrase='foo')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/Crypto/PublicKey/RSA.py", line 665, in importKey
    return self._importKeyDER(der)
  File "/usr/local/lib/python2.7/dist-packages/Crypto/PublicKey/RSA.py", line 588, in    _importKeyDER
    raise ValueError("RSA key format is not supported")
ValueError: RSA key format is not supported
>>> f = open('<some-path>/private-key.pem','r')
>>> r=RSA.importKey(f.read(),passphrase='<valid-pass-phrase>')
>>> r
<_RSAobj @0xb7237b2c n(4096),e,d,p,q,u,private>

在收到作者提供的PEM之后,我意识到假设1对他的情况无效。我仍然希望将其保留在此处作为导入失败的可能原因之一,以便其他用户知晓。

假设2 - 这是作者的情况。

RSA.py在PEM文件中查找以下内容,以确定应用了哪种加密方式:

 Proc-Type: 4,ENCRYPTED

当使用"openssl genrsa ..."命令生成密钥时,该字符串以明文形式存在于PEM中,但是当使用"openssl genpkey ..."时,"Proc-Type"不存在。如果未找到"Proc-Type",RSA.py甚至不会尝试解密PEM:
  # The encrypted PEM format
  if lines[1].startswith(b('Proc-Type:4,ENCRYPTED')):
     DEK = lines[2].split(b(':'))
     ....

所以,我目前的结论是,“openssl genpkey”生成的密钥不受 PyCrypto v2.6.1 的支持。
重要更新
在 PyCrypto 的最新版本 2.7a1 中可以正常工作。您可以从这里下载:http://ftp.dlitz.net/pub/dlitz/crypto/pycrypto/pycrypto-2.7a1.tar.gz
>>> f = open('key.pem','r')
>>> r = RSA.importKey(f.read(),  passphrase='123456')
>>> r
<_RSAobj @0xb6f342ec n(2048),e,d,p,q,u,private>

请尝试使用此密钥:http://pastebin.com/tcKJjdmw。密码是“123456”。我使用以下命令可以正常工作:openssl rsa -inform PEM -outform PEM -in private-key.pem -pubout,但在PyCrypto中无法正常工作。 - stf
你使用的 OpenSSL 版本是什么? - Oleg Gryb
再次检查了 Checked RSA.py,并发现它会寻找“Proc-Type:4,ENCRYPTED” 如果没有找到,甚至不会将PEM视为加密的。这是我的openssl生成的:“-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,AC78286040A62849”。看起来新版本的openssl使用了新格式。尝试以旧方式生成RSA密钥:“openssl genrsa -des3 -out privkey.pem 2048”。 - Oleg Gryb
顺便提一下,即使是旧版本的openssl也支持带有和不带有“Proc-Type”的两种格式,所以这是PyCrypto的问题。 - Oleg Gryb
刚用2.7a1测试了一下,能够正常工作-请查看答案更新。 - Oleg Gryb

2

解决此问题的另一种方法是,不需要安装被放弃的 PyCrypto 的实验版本。该库可以安全地被 pycryptodome (https://github.com/Legrandin/pycryptodome) 取代 - 它可以作为 pycrypto 的替代品,也可作为另一种库来使用 (pycryptodomex)。


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