I am generating a key with OpenSSL, providing the password from stdin:
openssl genpkey -algorithm RSA -out private-key.pem -outform PEM -pass stdin -des3 -pkeyopt rsa_keygen_bits:4096
The key then looks like:
-----BEGIN ENCRYPTED PRIVATE KEY-----
XXX...
-----END ENCRYPTED PRIVATE KEY-----
My Python code looks like:
from Crypto.PublicKey import RSA
# ...
f = open('private-key.pem', 'r')
r = RSA.importKey(f.read(), passphrase='some-pass')
f.close()
but I am getting an exception:
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
What's wrong?
Is it possible to generate an encrypted RSA key, store it in a file and later use it with PyCrypto? Is it possible to do it with OpenSSL? What formats are supported?
Importing the public key works fine, however it is not encrypted.
Hypothesis #1
After looking to the source code, I think, I solved the mystery. The way how import works for PEM keys encrypted with a password is that the PEM gets decrypted to DER and after that importKeyDER function is called. If provided password is not correct, the format of generated DER representation will not be correct too and you would get an exception that you've provided. To confirm that, I ran two quick tests below:
>>> 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>
After receiving the PEM from the author, I've realized that Hypothesis #1 is not valid for his case. I still want to keep it here as one possible reason of import failure, so other users are aware.
Hypothesis #2 - this is the author's case.
RSA.py looks for the following in PEM file to determine what kind of encryption was applied to PEM:
Proc-Type: 4,ENCRYPTED
When key is generated using "openssl genrsa ..." command, this string is present in PEM in clear, however when "opensl genpkey ..." is used the "Proc-Type" is not present.
RSA.py doesn't even try to decrypt the PEM if the "Proc-Type" is not found:
# The encrypted PEM format
if lines[1].startswith(b('Proc-Type:4,ENCRYPTED')):
DEK = lines[2].split(b(':'))
....
So, my conclusion at this time is that keys generated by "openssl genpkey" are not supported by PyCrypto v 2.6.1.
Important Update
It does work in PyCrypto's latest version 2.7a1. You can download it from here: 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>
A quick update for those who seek to solve this problem without installing an experimental release of long-abandoned PyCrypto. The library can be safely replaced by pycryptodome (https://github.com/Legrandin/pycryptodome) - it can provide both a drop-in replacement for pycrypto, and it can be used as an alternative library as well (pycryptodomex).
Related
I tried to decrypt rsa using a private key that exists as a string, but it failed.
-----BEGIN RSA PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCTiP1x58DboFjS
4WgzMm8tuY9VoHwANbOHNuuiElsZ4xIVFS+ZG7lu7Iz9gzmsno/YiqQXms8hXmUH
ouGgVJLJGPTw/NXAnLq6XlwB3C+zQMWpxvcMub4D6/IltP/PUpKNj9QzKKGhZF+6
s5B/QBzmSeNYlYYyGnO9GxyuHyR7P5xqF9AcpSskn9gjYy0koTLmvg/bwFx6jnci
np2qVHo5CFvSTAgrZKBCmKD2c2AGD9O+TSOrT0RDzvJosW4P1QJ9qsCjKFSPW/8K
IvB+AsSA+9xiVMDm/3YABBBM/R7wDcciTmKucZoEuDeWJg+cwLur8kKSVNwwkwCT
MqTIbHn5AgMBAAECggEABl5aemlDpPdl/ixmwBcEP5gL/OlBkQzAZCzVvRhHwHVR
2YEhnd1ZgtVJPMRGYBI0KWfKr44qNMmLWOoKDU65S179a82uOHNLiDH8jMQBdx5r
qemBzpXSAv9TY5dNl5h75Qp5YX/2gi4AB+IqcaPF25cC7lb+BPmpV1dtRILpozxJ
78mE0o0ddAJhg+0lIX6lZqjXbKJh4uiIqJIwezOM/B9U59qWEiJoHurkJIQoc00n
lp+XKUJfUWE5OHdy2DZNbAWEDqvFiD3DBd5baks/CPHy6IZLj7e5y+h/b65veDw6
MWpwXK30vroRmIzfNFcbONuQ3EjHBvCXzQYppUPnAQKBgQDD/k9NVQ8r2EnhjX0g
dQKobT7aCpY/y0QvDZ6aeuOVkfqnFB+rUUSf5WsXbANghqsHtzJa+cDUpP0C1PuE
hexGS+SsLywKI/33S014tvt5iF/MgEu08sLHtoxR/dH3H4k2LDGMNBrKILFnfDYP
40e3QKdogXMsEw4C0tb85YGrIQKBgQDAtJZp+onH0NSDXL0kbWZa8GFuVMGjTz2m
6E+j3NHK5GDGSAme755lDS61TTz2xNYaJFEwUdvYKQq1x/Q7jffKMPcHUJ5uy7bH
QjFho377rvN9bNSk+UdP8AkhvjaLNC0c5K5eNEUgumrd+yDLw2YNvjOe/RjE3QiW
zpGHLigL2QKBgQC2LUufMSJB+fBqlG6rXbgTwD/8wnx4XcNKDVnQc8vZenBHH0B0
qLyl98S8Z60X6vVM3a8TuzTPM3DuUfRccXN2wBKVLd+8qUnmtHsVatiDVgzd9J7q
WgBNTNRPXiPtlcWfsiJ4FPKV0R+1dlJ2ICfIIXO6gyD/5dJPM5WcSuRloQKBgQCq
Tu+gOgwKzEUE07FVwFn1hPyWxdFcw2t5V1cLOt0usyfUFVZrYNpb9IqTMO/wJ4YR
FIesbKwfHiaZAV6YQ5/60cuAa3+Z6/BdqeJ6qERRqw0GjGhiZOzheQKZD8KkxDga
kQCJwShXBGnuRUN20fofqzl0CbsaQT6WCXoUPnamAQKBgEY/ttlyLCrdHhamWlwN
ZkcgfsvQtLc//DGfEOSIiybs0eD0ORutxVQ2pwEgPI79xJTm0bdTHAFZVYziI9Lm
75k5WKxbBsYTgZm3i+LnnDtcHKPqkpXkq1v6WduTfaWpwMDJt0cJMOiTFzom76s1
mdFdmq2IXR+vXwl9f8fboc61
-----END RSA PRIVATE KEY-----
The private key is a string and exists as shown above.
from Crypto.PublicKey import RSA
import base64
from Crypto.Cipher import PKCS1_OAEP
Pkey ="MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCTiP1x58DboFjS4WgzMm8tuY9VoHwANbOHNuuiElsZ4xIVFS+ZG7lu7Iz9gzmsno/YiqQXms8hXmUHouGgVJLJGPTw/NXAnLq6XlwB3C+zQMWpxvcMub4D6/IltP/PUpKNj9QzKKGhZF+6s5B/QBzmSeNYlYYyGnO9GxyuHyR7P5xqF9AcpSskn9gjYy0koTLmvg/bwFx6jncinp2qVHo5CFvSTAgrZKBCmKD2c2AGD9O+TSOrT0RDzvJosW4P1QJ9qsCjKFSPW/8KIvB+AsSA+9xiVMDm/3YABBBM/R7wDcciTmKucZoEuDeWJg+cwLur8kKSVNwwkwCTMqTIbHn5AgMBAAECggEABl5aemlDpPdl/ixmwBcEP5gL/OlBkQzAZCzVvRhHwHVR2YEhnd1ZgtVJPMRGYBI0KWfKr44qNMmLWOoKDU65S179a82uOHNLiDH8jMQBdx5rqemBzpXSAv9TY5dNl5h75Qp5YX/2gi4AB+IqcaPF25cC7lb+BPmpV1dtRILpozxJ78mE0o0ddAJhg+0lIX6lZqjXbKJh4uiIqJIwezOM/B9U59qWEiJoHurkJIQoc00nlp+XKUJfUWE5OHdy2DZNbAWEDqvFiD3DBd5baks/CPHy6IZLj7e5y+h/b65veDw6MWpwXK30vroRmIzfNFcbONuQ3EjHBvCXzQYppUPnAQKBgQDD/k9NVQ8r2EnhjX0gdQKobT7aCpY/y0QvDZ6aeuOVkfqnFB+rUUSf5WsXbANghqsHtzJa+cDUpP0C1PuEhexGS+SsLywKI/33S014tvt5iF/MgEu08sLHtoxR/dH3H4k2LDGMNBrKILFnfDYP40e3QKdogXMsEw4C0tb85YGrIQKBgQDAtJZp+onH0NSDXL0kbWZa8GFuVMGjTz2m6E+j3NHK5GDGSAme755lDS61TTz2xNYaJFEwUdvYKQq1x/Q7jffKMPcHUJ5uy7bHQjFho377rvN9bNSk+UdP8AkhvjaLNC0c5K5eNEUgumrd+yDLw2YNvjOe/RjE3QiWzpGHLigL2QKBgQC2LUufMSJB+fBqlG6rXbgTwD/8wnx4XcNKDVnQc8vZenBHH0B0qLyl98S8Z60X6vVM3a8TuzTPM3DuUfRccXN2wBKVLd+8qUnmtHsVatiDVgzd9J7qWgBNTNRPXiPtlcWfsiJ4FPKV0R+1dlJ2ICfIIXO6gyD/5dJPM5WcSuRloQKBgQCqTu+gOgwKzEUE07FVwFn1hPyWxdFcw2t5V1cLOt0usyfUFVZrYNpb9IqTMO/wJ4YRFIesbKwfHiaZAV6YQ5/60cuAa3+Z6/BdqeJ6qERRqw0GjGhiZOzheQKZD8KkxDgakQCJwShXBGnuRUN20fofqzl0CbsaQT6WCXoUPnamAQKBgEY/ttlyLCrdHhamWlwNZkcgfsvQtLc//DGfEOSIiybs0eD0ORutxVQ2pwEgPI79xJTm0bdTHAFZVYziI9Lm75k5WKxbBsYTgZm3i+LnnDtcHKPqkpXkq1v6WduTfaWpwMDJt0cJMOiTFzom76s1mdFdmq2IXR+vXwl9f8fboc6"
s="AQEAN2+hgPwYzFATKPJfkXyTQKe9kvUTmT6LhASj2YI0T2KhBO7gSpKbNx/EXF4JYaWcwJVhJwiudgCFvoJMK9qJtnpLFmG12f8drygke+MPo5n2flHFPiKRmJCcCRM/VR8gL+xlbFIZNBL/o4onbqC1XfeQygiHe6tKXGiAZGXcJejbnob+/V+sL46x076KjurqLjcFMH+SCXomhuQZOiSqRqmeAsE6kL8wlj2yhTUfqAL/GuTTRziT6Syp0zJ7dprgCYOXBWbkLD9X6Vw39Db75kLd6Vx5zKT5jUQeU8eTN6pYfIiymlXwModf3TFBG1CObxzxzrevTXxFfIahFZGMAhDltmmcy7GUWDB7Qav24psONYaH+P69VfTimRzbrLMCfPb3zqp0cS8glMZ5YQuWqpigQRlQBhGq4rN9TGxBE3F1YRByBg+CHelBCHZj+2swHauVRmgy0CKU2/nmKpMrypKguFjjE6+bBur8b2AE28LSfjqxPZJx2BM="
code_bytes = s.encode('UTF-8')
by=base64.b64decode(code_bytes)
key = PKCS1_OAEP.new(Pkey)
rsadecrypt=key.decrypt(str(by))
Attempted to decode string 's' after base64 decoding.
However, an error was issued as follows:
Traceback (most recent call last):
File "/Users/isin-yeong/Desktop/element/code/rsadecrypt.py", line 14, in <module>
rsadecrypt=key.decrypt(str(by))
^^^^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/Crypto/Cipher/PKCS1_OAEP.py", line 161, in decrypt
modBits = Crypto.Util.number.size(self._key.n)
^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'n'
How do I convert 'Pkey' that exists as a string into a private key and decode 's' decoded with base64?
The following methods are used for decryption:
"RSA/ECB/PKCS1Padding"
Try saving the private key you posted first in a file with the name 'pkey.pem', and instead of writing the string directly into your code, use the following to upload the key:
Pkey = RSA.importKey(open('pkey.pem').read())
For more information you can look here, the documentation of pycryptodome.
edited according to Topaco's comments
Your code should looks as follows:
from Crypto.PublicKey import RSA
import base64
from Crypto.Cipher import PKCS1_v1_5
from Crypto.Random import get_random_bytes
s="AQEAN2+hgPwYzFATKPJfkXyTQKe9kvUTmT6LhASj2YI0T2KhBO7gSpKbNx/EXF4JYaWcwJVhJwiudgCFvoJMK9qJtnpLFmG12f8drygke+MPo5n2flHFPiKRmJCcCRM/VR8gL+xlbFIZNBL/o4onbqC1XfeQygiHe6tKXGiAZGXcJejbnob+/V+sL46x076KjurqLjcFMH+SCXomhuQZOiSqRqmeAsE6kL8wlj2yhTUfqAL/GuTTRziT6Syp0zJ7dprgCYOXBWbkLD9X6Vw39Db75kLd6Vx5zKT5jUQeU8eTN6pYfIiymlXwModf3TFBG1CObxzxzrevTXxFfIahFZGMAhDltmmcy7GUWDB7Qav24psONYaH+P69VfTimRzbrLMCfPb3zqp0cS8glMZ5YQuWqpigQRlQBhGq4rN9TGxBE3F1YRByBg+CHelBCHZj+2swHauVRmgy0CKU2/nmKpMrypKguFjjE6+bBur8b2AE28LSfjqxPZJx2BM="
code_bytes = s.encode('UTF-8')
by=base64.b64decode(code_bytes)
Pkey = RSA.importKey(open('pkey.pem').read())
key = PKCS1_v1_5.new(Pkey)
sentinel = get_random_bytes(16)
rsadecrypt=key.decrypt(by,sentinel)
Pay attention that you still going to get the following error:
ValueError: Ciphertext with incorrect length.
as Topaco explained in the comments
There are a few problems already noted in the comments by #Topaco:
Apart from the incorrect key import, the wrong padding is used. The
PyCryptodome counterpart to RSA/ECB/PKCS1Padding is PKCS1_v1_5 (and
not PKCS1_OAEP). Second, the ciphertext is apparently corrupted: The
posted (and thus compromised) private key has a length of 2048 bits =
256 bytes, i.e. the ciphertext must be of the same length. But the
posted (Base64 decoded) ciphertext is 380 bytes long (len(by)).
Furthermore, in decrypt() not str(by) but by must be passed.
You also have a typo in Pkey, the Base64 encoded body is ...boc61 and
not ...boc6 (i.e. the last character is missing). If this is fixed,
the key can be imported with RSA.importKey(base64.b64decode(Pkey))
When those are addressed we see that s, after base64 decoding, is too long to be the result of RSA encryption with a 2048 bit modulus. However by trying all offsets into the base64-decoded s and taking the next 256 bytes we get a successful decrypt at offset 3.
# https://stackoverflow.com/q/74840474/238704
import base64
from Cryptodome.Cipher import PKCS1_v1_5
from Cryptodome.PublicKey import RSA
from Cryptodome.Random import get_random_bytes
private_key_pem = '''-----BEGIN RSA PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCTiP1x58DboFjS
4WgzMm8tuY9VoHwANbOHNuuiElsZ4xIVFS+ZG7lu7Iz9gzmsno/YiqQXms8hXmUH
ouGgVJLJGPTw/NXAnLq6XlwB3C+zQMWpxvcMub4D6/IltP/PUpKNj9QzKKGhZF+6
s5B/QBzmSeNYlYYyGnO9GxyuHyR7P5xqF9AcpSskn9gjYy0koTLmvg/bwFx6jnci
np2qVHo5CFvSTAgrZKBCmKD2c2AGD9O+TSOrT0RDzvJosW4P1QJ9qsCjKFSPW/8K
IvB+AsSA+9xiVMDm/3YABBBM/R7wDcciTmKucZoEuDeWJg+cwLur8kKSVNwwkwCT
MqTIbHn5AgMBAAECggEABl5aemlDpPdl/ixmwBcEP5gL/OlBkQzAZCzVvRhHwHVR
2YEhnd1ZgtVJPMRGYBI0KWfKr44qNMmLWOoKDU65S179a82uOHNLiDH8jMQBdx5r
qemBzpXSAv9TY5dNl5h75Qp5YX/2gi4AB+IqcaPF25cC7lb+BPmpV1dtRILpozxJ
78mE0o0ddAJhg+0lIX6lZqjXbKJh4uiIqJIwezOM/B9U59qWEiJoHurkJIQoc00n
lp+XKUJfUWE5OHdy2DZNbAWEDqvFiD3DBd5baks/CPHy6IZLj7e5y+h/b65veDw6
MWpwXK30vroRmIzfNFcbONuQ3EjHBvCXzQYppUPnAQKBgQDD/k9NVQ8r2EnhjX0g
dQKobT7aCpY/y0QvDZ6aeuOVkfqnFB+rUUSf5WsXbANghqsHtzJa+cDUpP0C1PuE
hexGS+SsLywKI/33S014tvt5iF/MgEu08sLHtoxR/dH3H4k2LDGMNBrKILFnfDYP
40e3QKdogXMsEw4C0tb85YGrIQKBgQDAtJZp+onH0NSDXL0kbWZa8GFuVMGjTz2m
6E+j3NHK5GDGSAme755lDS61TTz2xNYaJFEwUdvYKQq1x/Q7jffKMPcHUJ5uy7bH
QjFho377rvN9bNSk+UdP8AkhvjaLNC0c5K5eNEUgumrd+yDLw2YNvjOe/RjE3QiW
zpGHLigL2QKBgQC2LUufMSJB+fBqlG6rXbgTwD/8wnx4XcNKDVnQc8vZenBHH0B0
qLyl98S8Z60X6vVM3a8TuzTPM3DuUfRccXN2wBKVLd+8qUnmtHsVatiDVgzd9J7q
WgBNTNRPXiPtlcWfsiJ4FPKV0R+1dlJ2ICfIIXO6gyD/5dJPM5WcSuRloQKBgQCq
Tu+gOgwKzEUE07FVwFn1hPyWxdFcw2t5V1cLOt0usyfUFVZrYNpb9IqTMO/wJ4YR
FIesbKwfHiaZAV6YQ5/60cuAa3+Z6/BdqeJ6qERRqw0GjGhiZOzheQKZD8KkxDga
kQCJwShXBGnuRUN20fofqzl0CbsaQT6WCXoUPnamAQKBgEY/ttlyLCrdHhamWlwN
ZkcgfsvQtLc//DGfEOSIiybs0eD0ORutxVQ2pwEgPI79xJTm0bdTHAFZVYziI9Lm
75k5WKxbBsYTgZm3i+LnnDtcHKPqkpXkq1v6WduTfaWpwMDJt0cJMOiTFzom76s1
mdFdmq2IXR+vXwl9f8fboc61
-----END RSA PRIVATE KEY-----'''
s = "AQEAN2+hgPwYzFATKPJfkXyTQKe9kvUTmT6LhASj2YI0T2KhBO7gSpKbNx/EXF4JYaWcwJVhJwiudgCFvoJMK9qJtnpLFmG12f8drygke+MPo5n2flHFPiKRmJCcCRM/VR8gL+xlbFIZNBL/o4onbqC1XfeQygiHe6tKXGiAZGXcJejbnob+/V+sL46x076KjurqLjcFMH+SCXomhuQZOiSqRqmeAsE6kL8wlj2yhTUfqAL/GuTTRziT6Syp0zJ7dprgCYOXBWbkLD9X6Vw39Db75kLd6Vx5zKT5jUQeU8eTN6pYfIiymlXwModf3TFBG1CObxzxzrevTXxFfIahFZGMAhDltmmcy7GUWDB7Qav24psONYaH+P69VfTimRzbrLMCfPb3zqp0cS8glMZ5YQuWqpigQRlQBhGq4rN9TGxBE3F1YRByBg+CHelBCHZj+2swHauVRmgy0CKU2/nmKpMrypKguFjjE6+bBur8b2AE28LSfjqxPZJx2BM="
code_bytes = s.encode('UTF-8')
by = base64.b64decode(code_bytes)
private_key = RSA.import_key(private_key_pem)
cipher = PKCS1_v1_5.new(private_key)
sentinel = get_random_bytes(16)
rsadecrypt = cipher.decrypt(by[3: 3 + 256], sentinel)
if rsadecrypt == sentinel:
print('failure')
else:
print(f'success: {rsadecrypt.hex(" ")}')
output is:
success: 48 90 c1 c5 ed fd 67 84 ad 82 df d1 5b 22 40 6f
I don't know what the rest of the bytes of s are all about.
PKCS1 version 1.5 encryption padding is not all that great and is basically deprecated in favor of OAEP padding. One of the weaknesses is the unacceptably high probability that a decryption with the wrong key and/or corrupted ciphertext will succeed. It's unlikely in this case, but not unlikely enough to completely discard the possibility. Although you've provided no additional details about what the payload is supposed to be, the 16 random-looking bytes suggests a key of some sort, perhaps an AES-128 key.
I am trying to encrypt file using pgp module but the public which I am using is throwing flag error.I tried to follow documentation here, and I tried to generate a new key but it's only generating private key as mentioned in document Generating Primary Keys, But we need pair of public and private keys to encrypt and decrypt right? rest of it mentioned in document I am not able to understand(signing and verifying things)
from pgpy import PGPKey, PGPMessage
pub_key, _ = pgpy.PGPKey.from_file('PublicPGP1.asc')
#pub_key._require_usage_flags = False 'This feature have been removed'
with io.open('test.csv', "r",newline='') as csv_file:
input_data = csv_file.read()
msg = pgpy.PGPMessage.new(input_data)
encrypted_message = pub_key.encrypt(msg)
Error is:
Traceback (most recent call last):
File "C:\Users\raia6\Desktop\pgpy\pgpenc.py", line 19, in <module>
encrypted_phrase = key.encrypt(message)
File "C:\Users\raia6\AppData\Local\Programs\Python\Python39\lib\site-packages\pgpy\decorators.py", line 125, in _action
with self.usage(key, kwargs.get('user', None)) as _key:
File "C:\Users\raia6\AppData\Local\Programs\Python\Python39\lib\contextlib.py", line 119, in __enter__
return next(self.gen)
File "C:\Users\raia6\AppData\Local\Programs\Python\Python39\lib\site-packages\pgpy\decorators.py", line 96, in usage
raise PGPError("Key {keyid:s} does not have the required usage flag {flags:s}".format(**em))
pgpy.errors.PGPError: Key 35AF67D132D325C9 does not have the required usage flag EncryptStorage, EncryptCommunications
My requirement is to generate public and private key (with flag), and then encrypt and decrypt data.
You must not use private key for encryption. Export it's public part, and use it instead. Private key is used for decryption and should be kept secret.
In PGP keys and subkeys have some flags, telling for which purpose it may be used. In your case generated primary key seems to be sign-only, so you should generate encrypting subkey and add it to the primary key.
See RFC 4880 for more details on OpenPGP keys/protocol.
To check your key parameters you may use gpg --list-packets PublicPGP1.asc or rnp --list-packets PublicPGP1.asc if you have the later one built/installed.
I have the following JSON object which represents an RSA256 JWK which obtained from a website:
jwk = {
'e': 'AQAB',
'n': 'sAlE_mzYz-2jf_YpxulSJXv_2CGIquflNZWhXUaU1SkJm9P0riLAuzwK7WT5p0Ko3zmQHho70_7D9nqB01rA4ExrMIDKpprE0Qa7NAJN-kgZhd_A25HsdSfpOfpaLvR-mf9fuOTDPLRQCd5HnrjoQKjs3D_XfPmPnT_Ny5erviiky90GSfN9j2DP_5yeDprzWKF-EQ3EDdIWt3snr7AW8rzBcZ1ojyWxckLAeSKDerMXP-zVBUFJE9Kn60HZoGNvmATKaw8LwEbf8DGfrllgSLvhg7mDRMLlbcooQoWAFSfN7t7kFbPSOcvjrpx3Yw_KrEwBZXeUP3260ukmFOx8RQ',
}
Below is the Perl code showing how a public-key object from the Crypt library can be constructed from the above jwk:
use Crypt::OpenSSL::RSA;
use Crypt::OpenSSL::Bignum;
use MIME::Base64 qw/decode_base64url/;
sub public_key {
my $rsa = Crypt::OpenSSL::RSA->new_key_from_parameters(
Crypt::OpenSSL::Bignum->new_from_bin(decode_base64url($jwk->{n})),
Crypt::OpenSSL::Bignum->new_from_bin(decode_base64url($jwk->{e})),
);
return $rsa->get_public_key_x509_string;
}
Two Questions:
How can I translate the above code into Python? The code below failed.
Once I have the public key object in python, how can I use it to verify a JWT signed by the corresponding private key? Please post a snippet showing exactly how it can be done.
>>> from Crypto.PublicKey import RSA
>>> import base64
>>> public_key = RSA.construct((base64.b64decode(jwk['n']), base64.b64decode(jwk['e'])))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "my-virtual-env/lib/python2.7/site-packages/Crypto/PublicKey/RSA.py", line 539, in construct
key = self._math.rsa_construct(*tup)
File "my-virtual-env/lib/python2.7/site-packages/Crypto/PublicKey/_slowmath.py", line 84, in rsa_construct
assert isinstance(n, long)
AssertionError
The error is raised because the RSA contructor is expecting 2 long integers and you are using two strings.
The solution is to convert the base64 decoded string into an hexadecimal integer.
from Crypto.PublicKey import RSA
import base64
n = int(base64.b64decode(jwk['n']).encode('hex'),16)
e = int(base64.b64decode(jwk['e']).encode('hex'),16)
e = long(e)
public_key = RSA.construct((n, e))
print(public_key)
Regarding the second question maybe you can use this method to verify the validity of an RSA signature.
this piece of code
key=RSA.importKey(open("receiver.pem","rb").read())
returns this error
ValueError: Not a valid PEM pre boundary
and
key=RSA.importKey(open("receiver.pem","r").read())
returns
ValueError: Not a valid PEM post boundary
The code worked fine when we used Pycrypto and Python 2.7, now i have shifted to Pycryptodome and Python 3.4.3(using 2to3). But now this code won't work. I can't even try anything because i can't even understand what it means.
To generate the .PEM file this code was used.
random_generator = Random.new().read
rsakey = RSA.generate(1024, random_generator)
f=open(email+'.pem','wb')
f.write(rsakey.exportKey("PEM"))
f.write(rsakey.publickey().exportKey("PEM"))
f.close()
This is what the contents of a .PEM file is like.
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCIQhU/+nPVFgw+T0Tf7NEpHYB12I/qywo5xBdp5kaLxEHD9zOx
2FTOX2OMPiL7fv/PW/AXuSrvD3pZAFzGmkigWdQP6TES5ZM65LUzeUUy8noHkZ00
D4mz+4a4YtBGaFyNL2CCxOAczi9rx5UB6qbY6+5kkBNd7k75XDp28g2bjwIDAQAB
AoGAaFRQ+P/HmSyci0ker2YgcJ7KMXF0II7+cWSXmNpcwb+vq2CoEFBX/vxCuKsL
Fg4TyK3YlBGPsiPjxink35xaZm7eI5sqbmD8Bnw4JZsQ1FN/Si6pbNLZkmOxyZgl
CoQvuvLavKH5GSWQ5wqvLD6OHBGd7w0YyGVOQHNQvOKhLgECQQC6EgYqOOz8ddQ2
qaLHxJl1LwpwvA4nWUWqeP69yl4QrhOmfTyLxLmw8HJFuz8XYiAxKq9fxnrU0j8H
W+QKwxRBAkEAu3eVGHZF5AA+K/Co+y2MTh1uzaSqbPZY/D4+zs1eLxoVM/e0MLYI
SqPciDTHl3HjZqivpJ5SbU3DcfvGSlV7zwJAJUxRogsRLjYsWNy+PY8iN8Q7Mofv
ymFxvo9MeRzkqDFMzRXTmizQEDDSpzm2luhbjZ+B0hAGNT0D12TLHIEoQQI/N6dI
m/qAxS9NRb4sbGUZQhd6zZIVBkQcJsZT3xEY5OLZaJQg6lUgIQiEb+s7Vbp5yABM
JJLb5ZcwbqZQN8EpAkEAt716AEn2qyxONCfLan1tuZVF+3V0KVphdhu6gdXpyHBv
9hLm2Ezb5VXMoU+IoeYGQ3SaSr6Gb1ein/sXGyaZuQ==
-----END RSA PRIVATE KEY----------BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCIQhU/+nPVFgw+T0Tf7NEpHYB1
2I/qywo5xBdp5kaLxEHD9zOx2FTOX2OMPiL7fv/PW/AXuSrvD3pZAFzGmkigWdQP
6TES5ZM65LUzeUUy8noHkZ00D4mz+4a4YtBGaFyNL2CCxOAczi9rx5UB6qbY6+5k
kBNd7k75XDp28g2bjwIDAQAB
-----END PUBLIC KEY-----
You are getting that error because of this function:
def decode(pem_data, passphrase=None):
...
# Verify Pre-Encapsulation Boundary
r = re.compile("\s*-----BEGIN (.*)-----\n")
m = r.match(pem_data)
if not m:
raise ValueError("Not a valid PEM pre boundary")
marker = m.group(1)
# Verify Post-Encapsulation Boundary
r = re.compile("-----END (.*)-----\s*$")
m = r.search(pem_data)
if not m or m.group(1) != marker:
raise ValueError("Not a valid PEM post boundary")
Unfortunately, in non-multiline regular expressions, $ means "end of the string". This implies that PyCryptoDome expects the END boundary at the end of the string, and there are no ways to work around this problem.
You have three options:
split the two keys and import them separately;
fix PyCryptoDome;
switch back to PyCrypto.
As Peter Wood commented previously, the issue is the end-of-line format.
I'm working on a project using python 2.7 in CentOS, and I've got a publickey from a website, which gave me the above error.
The problem was that it came with \r\n end of line sequence, which made the python-jose library jwt.decode call to pyCripto library fail.
My fix was to just remove '\r' character before returning the response this way:
<pre>
response = get(url).content
return response.replace('\r', '')
And this made the thing work at last. Hope this help!
I would like to print out the binary form (not sure if this is how I would refer to it) of a .pem key using python. To clarify, I want to do in python what this unix command would print out:
cat privateKey.pem | openssl rsa -pubout -outform DER
I can't just call this command using subprocess because I want it to work on Windows. I've looked at the M2Crypto and PyCrypto libraries, and with the M2Crypto library I am able to load the key using
from M2Crypto import RSA
rsaKey = RSA.load_key('privateKey.pem')
But I don't see any methods of rsaKey that print out the binary form.
Edit:
Here's what I have so far:
import M2Crypto
key = M2Crypto.RSA.load_key('key.pem')
bio = M2Crypto.BIO.MemoryBuffer()
key.save_key_der_bio(bio)
der = bio.read()
But der isn't the same as what openssl printed out. I piped the output of openssl into hexdump to compare them.
I would do this:
from Crypto.PublicKey import RSA
key = RSA.importKey(open("privatekey.pem").read())
der = key.publickey().exportKey("DER")
I figured it out. So the unix command
cat privateKey.pem | openssl rsa -pubout -outform DER
Is actually printing out the DER form of the public key.
Here is what I had to do, using the M2Crypto library:
import M2Crypto
privatekey = M2Crypto.RSA.load_key('privatekey.pem')
bio = M2Crypto.BIO.MemoryBuffer()
privatekey.save_pub_key_bio(bio)
pubkey = bio.read()
pubkey = ''.join(pubkey.split('\n')[1:-2]) # remove -----BEGIN PUB KEY... lines and concatenate
der = base64.b64decode(pubkey)
This is the form that I wanted. For some reason, if I did
pubkey = M2Crypto.RSA.load_pub_key_bio(bio)
pubkey.save_key_der_bio(bio)
der = bio.read()
It gave me the wrong answer.