如何在PyCrypto中使用加密的RSA私钥?
我正在使用OpenSSL生成一个密钥,并且是通过标准输入提供密码的:
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可以做到吗?支持哪些格式呢?
导入公钥是没问题的,但它并没有加密。
2 个回答
给那些想解决这个问题但又不想安装已经很久没更新的PyCrypto的朋友们一个快速更新。你可以安全地用pycryptodome来替代它(https://github.com/Legrandin/pycryptodome)。这个库不仅可以直接替代pycrypto,还可以作为一个替代库使用(叫做pycryptodomex)。
假设 #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文件中查找以下内容,以确定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 v 2.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>