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!
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.
This question already has answers here:
In Python, is it possible to escape newline characters when printing a string?
(3 answers)
Closed 2 years ago.
I have this list in my Python program:
['-----BEGIN RSA PRIVATE KEY-----\n', 'MIIEogIBAAKCAQEArLwCLYoy3F29Ror8+k2qWajpz+bSU8oTEe9UiWE0tH2Pq9fY\n', 'JpaSzmFingQe694xr16eK7OiI4b0YfxoQ9MRpYePHbc2yMQO68nU1q8hXRKgtY3I\n', 'HyO+pvS8jgsT9d1qaCaL2jx5SD9WbsgUADVyE+0Y/BKG38R19aca6QDLnpNHEETz\n', 'CogIZEra2X2Qku1DUcCPC+QH1/VfgSrY1nqxkJiqwx7jMcvTR8ygg5KDWCnXwVtM\n', 'k37LqU+4dqpnZ7d3krtzTu2CThIU+kslwFWG6310zOx6nEoHuYjRbbaGs4vi/4P0\n', 'NXwOTCbDyhI9tWIfkFH28OxhJPgOD9yPUcHejQIDAQABAoIBAErt/4/brDZ1n16T\n', 'eivmNuhP4qUh/skEFoOoKyaw+Rj2grVLm2MZ3DMUgaAEL5mnuCvXwos4kDQTHb3H\n', 'rT8Gt1fz1vxP9r7edY6I7f/21mU2aGp5R39ilRh9f7GqUo1L8BWf6vjPVNknSIoj\n', '1rFEfejw5+6o5sL4ySa3x2+dvbZafBNvDMGnfhU8bn4TXtWMhrmP05Xv5W9UdKH2\n', 'VjaOd4MngPadfBG1Q5MG8rMHh+N5uGBQdbg4JbnIAG94thzETlmylTb93UWOZuDr\n', 'KEhJ+UErodUAWT7TO+TdUG9QE8cObM/4efSSrx0SRpIgIY5VFz4OPEgbkzgTef1b\n', 'CF2cSg0CgYEA4RSeCPgtNt73z+szeFvRC7/nh0wStIMVyXM9trZNF2pES6+SXj3N\n', 'ds6fi77PjindivM1MpTWux2i8SVZlLC0HULY7Lv8Ymnkvcl2JKjsAIOT2/QGCzKC\n', 'Aum0euZxMnV4fA32H6iupMIt//ErzPtkwQiQGWI6pqWFDYpvgdZq5+cCgYEAxHaK\n', 'ZOard/XquTdEK5FILS1iXxub8z9REOwfSfkprzThnvulWUJ33JVSF/vJ0fi/3r9I\n', 'gkiEC6pkntYeViDA9WvWLkaf7yKTLpW2p+4WIoW6i+bLBaiBTAM6PBD++RpQx9d0\n', 'i5Lt0I6dYKw9FEmDSOiCsXB5olT1OJ05yAVzZ2sCgYAGiSG5ovM1J65MdEjvUsM0\n', 'MyNW82eLpE7KpbUCDDMa3fQTuopmgCbqLz/EwAqo1yBeu4cs69N8ZXToyavbPHJO\n', 'czV3UlIWzXpd5G6wy9LmhOuTNHfEdXf++6hI9U4OKAkSmRkvfCduE1FgoDEkZ5xa\n', 'WpRMmGSZmC7P4NgyDfbWuwKBgAUd/U7Rf0fZnOqEGP91gkbC9tv5UR+N16O0MsBv\n', 'f/lzTCki7tSbWZniPcgoI8KXUxAPNWlHUEEpiANnGlUt9zitPV/pMQM+iEzfU2me\n', 'Vu9BQnGgaktUb0G2l862tp1wZQg/gWadRhp3jHvXIKQlogjFHZpXjqN+yQBXgAjI\n', 's4/PAoGAaLJkwQjfcJiBsG2x/eQNbubvSNlZpLlfiS+3QRcDQI6MICBMCtQ5pHEQ\n', 'XcjR2Tp1bjnzoUEZWHNiiA/jC7ZZq05OkUnOiHRa9Oj38zsCzlnXKA1bu4wmGbg8\n', 'CtnXrELOu5QRg2eStWqJsQ4kyUioH6UYebFooZa77au/4NNmLgw=\n', '-----END RSA PRIVATE KEY-----']
I want to join all items on this list leaving the \n newline character visible so that the PEM file looks like this:
-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEArLwCLYoy3F29Ror8+k2qWajpz+bSU8oTEe9UiWE0tH2Pq9fY\nJpaSzmFingQe694xr16eK7OiI4b0YfxoQ9MRpYePHbc2yMQO68nU1q8hXRKgtY3I\nHyO+pvS8jgsT9d1qaCaL2jx5SD9WbsgUADVyE+0Y/BKG38R19aca6QDLnpNHEETz\nCogIZEra2X2Qku1DUcCPC+QH1/VfgSrY1nqxkJiqwx7jMcvTR8ygg5KDWCnXwVtM\nk37LqU+4dqpnZ7d3krtzTu2CThIU+kslwFWG6310zOx6nEoHuYjRbbaGs4vi/4P0\nNXwOTCbDyhI9tWIfkFH28OxhJPgOD9yPUcHejQIDAQABAoIBAErt/4/brDZ1n16T\neivmNuhP4qUh/skEFoOoKyaw+Rj2grVLm2MZ3DMUgaAEL5mnuCvXwos4kDQTHb3H\nrT8Gt1fz1vxP9r7edY6I7f/21mU2aGp5R39ilRh9f7GqUo1L8BWf6vjPVNknSIoj\n1rFEfejw5+6o5sL4ySa3x2+dvbZafBNvDMGnfhU8bn4TXtWMhrmP05Xv5W9UdKH2\nVjaOd4MngPadfBG1Q5MG8rMHh+N5uGBQdbg4JbnIAG94thzETlmylTb93UWOZuDr\nKEhJ+UErodUAWT7TO+TdUG9QE8cObM/4efSSrx0SRpIgIY5VFz4OPEgbkzgTef1b\nCF2cSg0CgYEA4RSeCPgtNt73z+szeFvRC7/nh0wStIMVyXM9trZNF2pES6+SXj3N\nds6fi77PjindivM1MpTWux2i8SVZlLC0HULY7Lv8Ymnkvcl2JKjsAIOT2/QGCzKC\nAum0euZxMnV4fA32H6iupMIt//ErzPtkwQiQGWI6pqWFDYpvgdZq5+cCgYEAxHaK\nZOard/XquTdEK5FILS1iXxub8z9REOwfSfkprzThnvulWUJ33JVSF/vJ0fi/3r9I\ngkiEC6pkntYeViDA9WvWLkaf7yKTLpW2p+4WIoW6i+bLBaiBTAM6PBD++RpQx9d0\ni5Lt0I6dYKw9FEmDSOiCsXB5olT1OJ05yAVzZ2sCgYAGiSG5ovM1J65MdEjvUsM0\nMyNW82eLpE7KpbUCDDMa3fQTuopmgCbqLz/EwAqo1yBeu4cs69N8ZXToyavbPHJO\nczV3UlIWzXpd5G6wy9LmhOuTNHfEdXf++6hI9U4OKAkSmRkvfCduE1FgoDEkZ5xa\nWpRMmGSZmC7P4NgyDfbWuwKBgAUd/U7Rf0fZnOqEGP91gkbC9tv5UR+N16O0MsBv\nf/lzTCki7tSbWZniPcgoI8KXUxAPNWlHUEEpiANnGlUt9zitPV/pMQM+iEzfU2me\nVu9BQnGgaktUb0G2l862tp1wZQg/gWadRhp3jHvXIKQlogjFHZpXjqN+yQBXgAjI\ns4/PAoGAaLJkwQjfcJiBsG2x/eQNbubvSNlZpLlfiS+3QRcDQI6MICBMCtQ5pHEQ\nXcjR2Tp1bjnzoUEZWHNiiA/jC7ZZq05OkUnOiHRa9Oj38zsCzlnXKA1bu4wmGbg8\nCtnXrELOu5QRg2eStWqJsQ4kyUioH6UYebFooZa77au/4NNmLgw=\n-----END RSA PRIVATE KEY-----\n
rather than this:
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEArLwCLYoy3F29Ror8+k2qWajpz+bSU8oTEe9UiWE0tH2Pq9fY
JpaSzmFingQe694xr16eK7OiI4b0YfxoQ9MRpYePHbc2yMQO68nU1q8hXRKgtY3I
HyO+pvS8jgsT9d1qaCaL2jx5SD9WbsgUADVyE+0Y/BKG38R19aca6QDLnpNHEETz
CogIZEra2X2Qku1DUcCPC+QH1/VfgSrY1nqxkJiqwx7jMcvTR8ygg5KDWCnXwVtM
k37LqU+4dqpnZ7d3krtzTu2CThIU+kslwFWG6310zOx6nEoHuYjRbbaGs4vi/4P0
NXwOTCbDyhI9tWIfkFH28OxhJPgOD9yPUcHejQIDAQABAoIBAErt/4/brDZ1n16T
eivmNuhP4qUh/skEFoOoKyaw+Rj2grVLm2MZ3DMUgaAEL5mnuCvXwos4kDQTHb3H
rT8Gt1fz1vxP9r7edY6I7f/21mU2aGp5R39ilRh9f7GqUo1L8BWf6vjPVNknSIoj
1rFEfejw5+6o5sL4ySa3x2+dvbZafBNvDMGnfhU8bn4TXtWMhrmP05Xv5W9UdKH2
VjaOd4MngPadfBG1Q5MG8rMHh+N5uGBQdbg4JbnIAG94thzETlmylTb93UWOZuDr
KEhJ+UErodUAWT7TO+TdUG9QE8cObM/4efSSrx0SRpIgIY5VFz4OPEgbkzgTef1b
CF2cSg0CgYEA4RSeCPgtNt73z+szeFvRC7/nh0wStIMVyXM9trZNF2pES6+SXj3N
ds6fi77PjindivM1MpTWux2i8SVZlLC0HULY7Lv8Ymnkvcl2JKjsAIOT2/QGCzKC
Aum0euZxMnV4fA32H6iupMIt//ErzPtkwQiQGWI6pqWFDYpvgdZq5+cCgYEAxHaK
ZOard/XquTdEK5FILS1iXxub8z9REOwfSfkprzThnvulWUJ33JVSF/vJ0fi/3r9I
gkiEC6pkntYeViDA9WvWLkaf7yKTLpW2p+4WIoW6i+bLBaiBTAM6PBD++RpQx9d0
i5Lt0I6dYKw9FEmDSOiCsXB5olT1OJ05yAVzZ2sCgYAGiSG5ovM1J65MdEjvUsM0
MyNW82eLpE7KpbUCDDMa3fQTuopmgCbqLz/EwAqo1yBeu4cs69N8ZXToyavbPHJO
czV3UlIWzXpd5G6wy9LmhOuTNHfEdXf++6hI9U4OKAkSmRkvfCduE1FgoDEkZ5xa
WpRMmGSZmC7P4NgyDfbWuwKBgAUd/U7Rf0fZnOqEGP91gkbC9tv5UR+N16O0MsBv
f/lzTCki7tSbWZniPcgoI8KXUxAPNWlHUEEpiANnGlUt9zitPV/pMQM+iEzfU2me
Vu9BQnGgaktUb0G2l862tp1wZQg/gWadRhp3jHvXIKQlogjFHZpXjqN+yQBXgAjI
s4/PAoGAaLJkwQjfcJiBsG2x/eQNbubvSNlZpLlfiS+3QRcDQI6MICBMCtQ5pHEQ
XcjR2Tp1bjnzoUEZWHNiiA/jC7ZZq05OkUnOiHRa9Oj38zsCzlnXKA1bu4wmGbg8
CtnXrELOu5QRg2eStWqJsQ4kyUioH6UYebFooZa77au/4NNmLgw=
-----END RSA PRIVATE KEY-----
I tried ''.join() but it left the newline hidden.
Try this:
data_string = repr(''.join(lst))
where lst is your list.
You need to add an escape character before \n to make \n as part of string.
Try this before loading the text to file:
data = [item.replace("\n", "\\n") for item in data]
you can do like this
Lis=['-----BEGIN RSA PRIVATE KEY-----\n', 'MIIEogIBAAKCAQEArLwCLYoy3F29Ror8+k2qWajpz+bSU8oTEe9UiWE0tH2Pq9fY\n', 'JpaSzmFingQe694xr16eK7OiI4b0YfxoQ9MRpYePHbc2yMQO68nU1q8hXRKgtY3I\n', 'HyO+pvS8jgsT9d1qaCaL2jx5SD9WbsgUADVyE+0Y/BKG38R19aca6QDLnpNHEETz\n', 'CogIZEra2X2Qku1DUcCPC+QH1/VfgSrY1nqxkJiqwx7jMcvTR8ygg5KDWCnXwVtM\n', 'k37LqU+4dqpnZ7d3krtzTu2CThIU+kslwFWG6310zOx6nEoHuYjRbbaGs4vi/4P0\n', 'NXwOTCbDyhI9tWIfkFH28OxhJPgOD9yPUcHejQIDAQABAoIBAErt/4/brDZ1n16T\n', 'eivmNuhP4qUh/skEFoOoKyaw+Rj2grVLm2MZ3DMUgaAEL5mnuCvXwos4kDQTHb3H\n', 'rT8Gt1fz1vxP9r7edY6I7f/21mU2aGp5R39ilRh9f7GqUo1L8BWf6vjPVNknSIoj\n', '1rFEfejw5+6o5sL4ySa3x2+dvbZafBNvDMGnfhU8bn4TXtWMhrmP05Xv5W9UdKH2\n', 'VjaOd4MngPadfBG1Q5MG8rMHh+N5uGBQdbg4JbnIAG94thzETlmylTb93UWOZuDr\n', 'KEhJ+UErodUAWT7TO+TdUG9QE8cObM/4efSSrx0SRpIgIY5VFz4OPEgbkzgTef1b\n', 'CF2cSg0CgYEA4RSeCPgtNt73z+szeFvRC7/nh0wStIMVyXM9trZNF2pES6+SXj3N\n', 'ds6fi77PjindivM1MpTWux2i8SVZlLC0HULY7Lv8Ymnkvcl2JKjsAIOT2/QGCzKC\n', 'Aum0euZxMnV4fA32H6iupMIt//ErzPtkwQiQGWI6pqWFDYpvgdZq5+cCgYEAxHaK\n', 'ZOard/XquTdEK5FILS1iXxub8z9REOwfSfkprzThnvulWUJ33JVSF/vJ0fi/3r9I\n', 'gkiEC6pkntYeViDA9WvWLkaf7yKTLpW2p+4WIoW6i+bLBaiBTAM6PBD++RpQx9d0\n', 'i5Lt0I6dYKw9FEmDSOiCsXB5olT1OJ05yAVzZ2sCgYAGiSG5ovM1J65MdEjvUsM0\n', 'MyNW82eLpE7KpbUCDDMa3fQTuopmgCbqLz/EwAqo1yBeu4cs69N8ZXToyavbPHJO\n', 'czV3UlIWzXpd5G6wy9LmhOuTNHfEdXf++6hI9U4OKAkSmRkvfCduE1FgoDEkZ5xa\n', 'WpRMmGSZmC7P4NgyDfbWuwKBgAUd/U7Rf0fZnOqEGP91gkbC9tv5UR+N16O0MsBv\n', 'f/lzTCki7tSbWZniPcgoI8KXUxAPNWlHUEEpiANnGlUt9zitPV/pMQM+iEzfU2me\n', 'Vu9BQnGgaktUb0G2l862tp1wZQg/gWadRhp3jHvXIKQlogjFHZpXjqN+yQBXgAjI\n', 's4/PAoGAaLJkwQjfcJiBsG2x/eQNbubvSNlZpLlfiS+3QRcDQI6MICBMCtQ5pHEQ\n', 'XcjR2Tp1bjnzoUEZWHNiiA/jC7ZZq05OkUnOiHRa9Oj38zsCzlnXKA1bu4wmGbg8\n', 'CtnXrELOu5QRg2eStWqJsQ4kyUioH6UYebFooZa77au/4NNmLgw=\n', '-----END RSA PRIVATE KEY-----']
strip_n=x=[i.strip() for i in Lis]
ouput_string= ''.join(strip_n)
Hope this helps
Do you want this ?
string = ''
for i in lst:
string += i.replace("\n","\\n")
print(str)
I have been trying for a few days to validate some message signed with a private key in python. Note that the message has been signed using Ruby.
When I sign the same message in python I can verify it no problem. Note that I have already validated that the hash are the same.
Python code:
string_to_encrypt = b"aaaaabbbbbaaaaabbbbbaaaaabbbbbCC"
sha1 = SHA.new()
sha1.update(string_to_encrypt)
# load private key
pkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, open('./license.pem', 'rb').read())
sign_ssl = OpenSSL.crypto.sign(pkey, sha1.digest(), 'RSA-SHA1')
b64_ssl = base64.b64encode(sign_ssl)
Ruby:
string_to_encrypt = "aaaaabbbbbaaaaabbbbbaaaaabbbbbCC"
sha1 = Digest::SHA1.digest(string_to_encrypt)
#sign it
private_key_file = File.join(File.dirname(__FILE__), 'license.pem')
rsa = OpenSSL::PKey::RSA.new(File.read(private_key_file))
signed_key = rsa.private_encrypt(sha1)
#update the license string with it
x = Base64.strict_encode64(signed_key)
I would expect b64_ssl and x to contain the same value and they don't. Could someone explain to me what I missing there?
Neither of these code snippets is actually producing the correct signature.
In the Ruby OpenSSL library you want to be using the sign method, not the private_encrypt method, which is a low level operation that doesn’t do everything required to produce a valid signature.
In both libraries the sign operation performs the hashing for you, you don’t need to do this beforehand. In fact your Python code is actually hashing the data twice.
Try the following Python code:
import OpenSSL
import base64
string_to_encrypt = b"aaaaabbbbbaaaaabbbbbaaaaabbbbbCC"
# load private key
pkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, open('./license.pem', 'rb').read())
sign_ssl = OpenSSL.crypto.sign(pkey, string_to_encrypt, 'SHA1')
b64_ssl = base64.b64encode(sign_ssl)
print(b64_ssl.decode())
which produces the same output as this Ruby code:
require 'openssl'
require 'base64'
string_to_encrypt = "aaaaabbbbbaaaaabbbbbaaaaabbbbbCC"
#sign it
private_key_file = File.join(File.dirname(__FILE__), 'license.pem')
rsa = OpenSSL::PKey::RSA.new(File.read(private_key_file))
signed_key = rsa.sign('sha1', string_to_encrypt)
#update the license string with it
x = Base64.strict_encode64(signed_key)
puts x
It is possible to somehow 'pythonically' export a file's digital certificate's subject if the certificate itself is not installed on the workstation but is only used on that specific file?
I need to somehow extract that information from a file and check if it's correct. Preferably using Python/CMD/PowerShell
I'm currently using this python script (which I modified to run on Python 3.6):
http://www.zedwood.com/article/python-openssl-x509-parse-certificate to parse a .cer file that I extract from the original executable file.
I extract the certificate with this little tool I've found (which I also modified to work with Python 3):
https://blog.didierstevens.com/programs/disitool/ and afterwards I convert it from a DER-encoded binary to a base-64 with the Windows certutil.
The problem with the disitool script I use to extract the certificate from the file, though, is that it literally cuts the 'signature' bytearray from the executable itself using the pefile python module, which makes the extracted .cer file invalid, as per the python error that I keep getting when trying to load the certificate with the OpenSSL.crypto module:
[('asn1 encoding routines', 'asn1_check_tlen', 'wrong tag'), ('asn1 encoding routines', 'asn1_item_embed_d2i', 'nested asn1 error'), ('asn1 encoding routines', 'asn1_template_noexp_d2i', 'nested asn1 error'), ('PEM routines', 'PEM_ASN1_read_bio', 'ASN1 lib')]
But parsing a good extracted certificate (with the first script I posted above) works, as you can see here:
TL:DR - I just need a way to export a certificate from a file, I guess. Also, if you've found my solution too complicated, if you have any idea how I could get that "Redmond" text from the certificate's Subject field, I'm very open to other ideas :)
I bumped into this while searching for a similar solution. This is what worked for me. Part of the code is borrowed from disitool.py
import pefile
from OpenSSL import crypto
from OpenSSL.crypto import _lib, _ffi, X509
def get_certificates(self):
certs = _ffi.NULL
if self.type_is_signed():
certs = self._pkcs7.d.sign.cert
elif self.type_is_signedAndEnveloped():
certs = self._pkcs7.d.signed_and_enveloped.cert
pycerts = []
for i in range(_lib.sk_X509_num(certs)):
pycert = X509.__new__(X509)
pycert._x509 = _lib.sk_X509_value(certs, i)
pycerts.append(pycert)
if not pycerts:
return None
return tuple(pycerts)
SignedFile = "/tmp/firefox.exe"
pe = pefile.PE(SignedFile)
address = pe.OPTIONAL_HEADER.DATA_DIRECTORY[
pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_SECURITY"]
].VirtualAddress
size = pe.OPTIONAL_HEADER.DATA_DIRECTORY[
pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_SECURITY"]
].Size
if address == 0:
print("Error: source file not signed")
else:
signature = pe.write()[address + 8 :]
pkcs = crypto.load_pkcs7_data(crypto.FILETYPE_ASN1, bytes(signature))
certs = get_certificates(pkcs)
for cert in certs:
c = crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
a = crypto.load_certificate(crypto.FILETYPE_PEM, c)
# get data from parsed cert
print(a.get_subject())
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).