Pycryptodome can't decrypt data - python

Why cipher3 can't decrypt cipher data?
cipher2 and cipher3 use same nonce, but cipher3 can't decrypt data
Code:
>>> from Crypto.Cipher import AES
>>> cipher = AES.new(b"M"*16, AES.MODE_EAX)
>>> cipher2 = AES.new(b"M"*16, AES.MODE_EAX, cipher.nonce)
>>> cipher3 = AES.new(b"M"*16, AES.MODE_EAX, cipher.nonce)
>>> data = cipher.encrypt(b"Hello")
>>> data2 = cipher.encrypt(b"World")
>>> cipher2.decrypt(data)
b'Hello'
>>> cipher3.decrypt(data2)
b'S\xa5\x92\xa2\x9a'
>>> cipher2.decrypt(data2)
b'World'

The problem is that cipher objects you use for decryption (cipher2, cipher3 in your case) must be presented the pieces of ciphertext in the same order they were produced (by cipher in your case).
Instead, you are passing data2 as the first piece of ciphertext to cipher3, even though it was produced second.
This is applicable to several other cipher modes, not just EAX.
Note also that EAX is an authenticated cipher mode: you should use the method decrypt_and_verify() unless you have were good reasons not to.

Related

Perfroming AES CBC 128 Cannot get the same result same the ONline tools

I using getting the encrypted message from a third party sever.
I am using the tools in enter link description here
to test and its correct
The Encrypted Text =
"WiI9g5qo+ztSlqHMbpiezHZ2dBkQ2gprGJZyWtwcMTWPoxzLsMmujE9xDeFK4XYMfBdZGh2naMwP3LfbPy/06mazrSs66WRM1oxhz56L2UzTKyCWCl+ld7RlN7aPwfEw2j9VN50YCkMLfQRfIAXTspKQb6o5QQw8ey0cINdtWSHClz/uXiCFqiYJfItxY1rAZkE1Qj0b0izGQFJ9/44Zfw0dJtzCXYgXTPZftPeGTdoX/HnZJpUvfqmLIAdgAyoXi5BxL5bgSs30yaB4bRxJJj7DKpVbAgZmx0ecjmiGDh7t78A16pZ2kz+OIUkuc/hxvUaVehsH1pVdqycpUJfbgy+to0AY/+BBd38GGvv8YdTCa99bSHRGaZuUglLKN/2J0pZmfrIARIdgrV2yDK+IN4hTVKf1jprtfvhvkG+eRyDfoLL9rg8+ZEtdYUdZgDdF3ftmHKTzgxI6leMWX7WFRTHjxVYFVk0yWA9xXk6s/WcG6IFeGYPVF94IcLeC2eAjaMasusF+C6qyFWi6nuyFK2Gr1utvG6kg84Hu0KKYg42MHXIR1AtQW3MWaqosb54y0GutQtnD47l84/PdJvUhuE/a7uyfCjjtyh2sRRLX3WDosyRZsqjLea9EIX6oNmQMZd1WRxM86Ggt6bVOc9KY5Z7HLpLyb0lLF4sdyzfBNJB7u7vqkBzsEss1Yq+sXD0N"
Key = "1234567812345678"
And the result
After AES Decrypted Output (Base64):
And after Decode Plain Text
THe result is :
{"upPacketSN":-1,"upDataSN":-1,"topic":"v1/up/ad","timestamp":1619672797621,"tenantId":"2000034792","serviceId":"","protocol":"lwm2m","productId":"15044315","payload":{"APPdata":"MTIzNDU2NzgsNTM0OCwwNywwOCwyNTUsMTc5LDEyOCwwMDAwLDQyNTksMSw2LDAsMCwwLDAsMCwwLDI0LDMuNzAsMy43MCwxLDEsNiwsMCwwLDAsMCwwLDAsMCwwLDAsMCwtMjU1LC0yNTUsLTI1NSwxLjY5LDEuOCw0OTk4LDYwLDE0NDAsNjA="},"messageType":"dataReport","deviceType":"","deviceId":"523ede8bb7e34dd4a1bd74028d63749e","assocAssetId":"","IMSI":"undefined","IMEI":"864162041961023"}
One of the thing is that IV must be NULL to get the correct answer.
And so if i implement in Python AES CBC using pycryptodome library in the following like:
class AES_CBC:
def add_to_16(self, value):
while len(value) % 16 != 0:
value += '\0'
return str.encode(value) # 返回bytes
#解密方法
def decrypt_oralce(self, key, text):
# 初始化加密器
# 偏移量 16个0
iv = "0000000000000000"
aes = AES.new(self.add_to_16(key), AES.MODE_CBC, self.add_to_16(iv))
#优先逆向解密base64成bytes
base64_decrypted = base64.decodebytes(text.encode(encoding='utf-8'))
#
decrypted_text = str(aes.decrypt(base64_decrypted), encoding='utf-8') # 执行解密密并转码返回str
unpad = lambda s : s[0:-ord(s[-1])]
#PADDING = '\0'
#print decrypted_text.rstrip(PADDING) #zeropadding只见诶去掉结尾\0
# print(unpad(decrypted_text))
return unpad(decrypted_text)
if __name__ == '__main__':
aes = AES_CBC()
#加密
key = "1234567812345678"
enc_msg = "WiI9g5qo+ztSlqHMbpiezHZ2dBkQ2gprGJZyWtwcMTWPoxzLsMmujE9xDeFK4XYMfBdZGh2naMwP3LfbPy/06mazrSs66WRM1oxhz56L2UzTKyCWCl+ld7RlN7aPwfEw2j9VN50YCkMLfQRfIAXTspKQb6o5QQw8ey0cINdtWSHClz/uXiCFqiYJfItxY1rAZkE1Qj0b0izGQFJ9/44Zfw0dJtzCXYgXTPZftPeGTdoX/HnZJpUvfqmLIAdgAyoXi5BxL5bgSs30yaB4bRxJJj7DKpVbAgZmx0ecjmiGDh7t78A16pZ2kz+OIUkuc/hxvUaVehsH1pVdqycpUJfbgy+to0AY/+BBd38GGvv8YdTCa99bSHRGaZuUglLKN/2J0pZmfrIARIdgrV2yDK+IN4hTVKf1jprtfvhvkG+eRyDfoLL9rg8+ZEtdYUdZgDdF3ftmHKTzgxI6leMWX7WFRTHjxVYFVk0yWA9xXk6s/WcG6IFeGYPVF94IcLeC2eAjaMasusF+C6qyFWi6nuyFK2Gr1utvG6kg84Hu0KKYg42MHXIR1AtQW3MWaqosb54y0GutQtnD47l84/PdJvUhuE/a7uyfCjjtyh2sRRLX3WDosyRZsqjLea9EIX6oNmQMZd1WRxM86Ggt6bVOc9KY5Z7HLpLyb0lLF4sdyzfBNJB7u7vqkBzsEss1Yq+sXD0N"
#解密
dec_text = aes.decrypt_oralce(key, enc_msg)
print(key)
print(dec_text)
EDITED:
i got a different result using same IV 16'0'
and
the result from the web is
{"upPacketSN":-1,"upDataSN":-1,"topic":"v1/up/ad","timestamp":1619687373640,"tenantId":"2000034792","serviceId":"","protocol":"lwm2m","productId":"15044315","payload":{"APPdata":"MTIzNDU2NzgsNTM0OCwwNywwOCwyNTUsMTc5LDEyOCwwMDAwLDQ1MDQsMSw2LDAsMCwwLDAsMCwwLDI0LDMuNzAsMy43MCwxLDEsNiwsMCwwLDAsMCwwLDAsMCwwLDAsMCwtMjU1LC0yNTUsLTI1NSwxLjY5LDEuOCw0OTk4LDYwLDE0NDAsNjA="},"messageType":"dataReport","deviceType":"","deviceId":"523ede8bb7e34dd4a1bd74028d63749e","assocAssetId":"","IMSI":"undefined","IMEI":"864162041961023"}
while the result of my code is :
KE#`QS[UDc~
1,"upDataSN":-1,"topic":"v1/up/ad","timestamp":1619687373640,"tenantId":"2000034792","serviceId":"","protocol":"lwm2m","productId":"15044315","payload":{"APPdata":"MTIzNDU2NzgsNTM0OCwwNywwOCwyNTUsMTc5LDEyOCwwMDAwLDQ1MDQsMSw2LDAsMCwwLDAsMCwwLDI0LDMuNzAsMy43MCwxLDEsNiwsMCwwLDAsMCwwLDAsMCwwLDAsMCwtMjU1LC0yNTUsLTI1NSwxLjY5LDEuOCw0OTk4LDYwLDE0NDAsNjA="},"messageType":"dataReport","deviceType":"","deviceId":"523ede8bb7e34dd4a1bd74028d63749e","assocAssetId":"","IMSI":"undefined","IMEI":"864162041961023"}
can anyone help me which part i get wrong?
Thanks
Currently the code is not using a "null-IV", which means an array filled with bytes set to zero. Instead it is using an array filled with '0' characters, which have value 0x30 in hexadecimals or 48 in decimals; distinctly not zero.
To create a null-IV please have a look here on how to indicate byte values within byte arrays in Python. The same trick is used within the add_to_16 loop, where the \0 escape is used to indicate a zero byte.
Note that padding a key or IV is very bad practice. Those need to consist of randomized bytes. Beware that working crypto code is not the same thing as secure crypto code, which should probably be your goal.

PyCryptoDome/cryptography inequality with AES-CFB in python

While running a test to make sure that the two different libraries give the same output, I found out that they don't with CFB. Code to replicate the problem is:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from Crypto.Cipher import AES
KEY = b'legoroojlegorooj'
IV = b'legoroojlegorooj'
aes = Cipher(algorithms.AES(KEY), modes.CFB(IV), default_backend()).encryptor()
output_data = aes.update(b'Feathers fall as fast as bowling balls.') + aes.finalize()
del aes
ctr = AES.new(KEY, AES.MODE_CFB, iv=IV)
output_data2 = ctr.encrypt(b'Feathers fall as fast as bowling balls.')
assert output_data == output_data2 # AssertionError
Any help sorting this out would be appreiciated.
This code works with modes.OFB(IV) and AES.MODE_OFB.
In CFB mode the size of the shift register must be specified, whereby the different libraries often use different default values. For distinction, the size of the shift register in bits is often appended to the CFB, i.e. CFB8 uses a shift register of 8 bits and CFB128 a shift register of 128 bits.
Cryptography has the two variants CFB8 and CFB128, where the latter is simply called CFB. PyCryptodome allows the setting in integer multiples of 8 bit by using the parameter segment_size with the default value 8 bit.
So in the current code Cryptography uses CFB128 and PyCryptodome uses CFB8 (its default value), which causes the different results.
The following combinations work:
PyCryptodome with segment_size=128 and Cryptography with CFB. Both correspond to CFB128:
# CFB with a 128 bit shift-register
# Output as hex-string: 63230751cc1efe25b980d9e707396a1a171cd413e6e77f1cd7a2d3deb2217255a36ae9cbf86c66
...
aes = Cipher(algorithms.AES(KEY), modes.CFB(IV), default_backend()).encryptor()
...
ctr = AES.new(KEY, AES.MODE_CFB, iv=IV, segment_size=128)
...
PyCryptodome with segment_size=8 (the default value) and Cryptography with CFB8. Both correspond to CFB8:
# CFB with a 8 bit shift-register
# Output as hex-string: 63d263889ffe94dd4740580067ee798da474c567b8b54319a5022650085c62674628f7c9e790c3
...
aes = Cipher(algorithms.AES(KEY), modes.CFB8(IV), default_backend()).encryptor()
...
ctr = AES.new(KEY, AES.MODE_CFB, iv=IV, segment_size=8)
...
Note, that (1) both Python libraries provide an identical result for the OFB mode, since both use OFB128. (2) CFB128 is faster than CFB8: In CFB8, AES encryption has to be called 16 times for each block vs. 1 time in CFB128.

How to encrypt with AES in Python with no salt?

I want to encrypt some data using AES in Python such that two encryptions of the same data give the same output (using the same key in both encryptions). I tried to use a static IV but I still get two different outputs given one input.
Here is what I did:
from Crypto.Cipher import AES
iv = 16 * '\x00'
cipher = AES.new(key, AES.MODE_CBC, iv)
I would like to obtain the same cipher if I decide to encrypt the same data multiple times.
Do you know how to prevent the use of a salt?
Thank you!
The cipher object here is the state and is initialized using the provided key data and initialization vector. Once you start to use this initialized keystate it is modified for each block you encrypt when you use CBC mode. That is the cipher block chaining in action. To have two separate encryptions you need to use two separate keystates. Or, as mentioned, you can use a mode (ECB) that does not affect the keystate when encrypting a block.
A demo (python3):
from Crypto.Cipher import AES
key = 16 * b'\0'
iv = 16 * b'\0'
plaintext = 16 * b'\x41'
a = AES.new(key, AES.MODE_CBC, iv)
b = AES.new(key, AES.MODE_CBC, iv)
ar = a.encrypt(plaintext)
br = b.encrypt(plaintext)
>>> ar
b'\xb4\x9c\xbf\x19\xd3W\xe6\xe1\xf6\x84\\0\xfd[c\xe3'
>>> br
b'\xb4\x9c\xbf\x19\xd3W\xe6\xe1\xf6\x84\\0\xfd[c\xe3'
The disadvantage of ECB mode is everytime we encrypt this plaintext with this key and iv we will get this result. With CBC mode, each time we encrypt another block the keystate is changed and we get a different output which is dependent upon what has been encrypted before.
ECB Mode
>>> ecb = AES.new(16 * b'\0', AES.MODE_ECB)
>>> ecb.encrypt(16 * b'\x41')
b'\xb4\x9c\xbf\x19\xd3W\xe6\xe1\xf6\x84\\0\xfd[c\xe3'
>>> ecb.encrypt(16 * b'\x41')
b'\xb4\x9c\xbf\x19\xd3W\xe6\xe1\xf6\x84\\0\xfd[c\xe3'
Each encryption produces the same result. No iv required.
CBC Mode
>>> cbc = AES.new(16 * b'\0', AES.MODE_CBC, 16 * b'\0')
>>> cbc.encrypt(16 * b'\x41')
b'\xb4\x9c\xbf\x19\xd3W\xe6\xe1\xf6\x84\\0\xfd[c\xe3'
>>> cbc.encrypt(16 * b'\x41')
b'o\x9fv\x9a\x9c\xaa\x03\x13\xba\x17\x8c\x1c.\x0f`v'
>>> cbc.encrypt(16 * b'\x41')
b'\n1\x165\xb5?\x04h\xa8\r>k\xdbK\xbc4'
The first block encrypts the same as ECB mode but subsequent blocks are different.
Futher discussion about modes of operation for block ciphers can be found online.

Encrypting message containing public key with another public key

I have two asymmetric key pairs client_key, A and another_key, B.
I want to send a encrypt a dict with B's public key. However, the dict must contain A's public key, like the following example:
message_dict = {
'hostname': socket.getfqdn(),
'request': 'MY_REQUEST',
'client_pub_key': client_key.publickey().exportKey(),
}
I did a json.dumps of the above and encrypted with B's public key. After I decrypt it, I do not get the same message. I noticed that it goes wrong only if I include client_pub_key in the dict.
I have been looking for answers online and this is possibly why it might be going wrong:
The message is too long to encrypt with B's public key
The client_pub_key has special characters which leads to a situation where JSON encoding is messed up
I tried the following:
Used pickle instead of JSON
Used binascii.hexlify to encode client_pub_key
Used base64.b64encode to encode client_pub_key
Used Crypto.Util.RFC1751.key_to_english to convert to some characters client_pub_key
Set B's key 2048 bytes and A's key 1024 bytes
All my above efforts failed. Hence I'm here looking for help.
My Question: How do I encrypt a message of the above dict format so I can send it over to the server?
(I'm using thrift's TTornadoStreamTransport and TBinaryProtocol, if that helps)
My test code snippet:
#!/usr/bin/env python
import base64
import binascii
import json
import pickle
import socket
from Crypto.PublicKey import RSA
from Crypto.Util.RFC1751 import key_to_english
client_key = RSA.generate(1024)
message_dict = {
'hostname': socket.getfqdn(),
'request': 'MY_REQUEST',
'client_pub_key': int(binascii.hexlify(client_key.publickey().exportKey()),16),
}
another_key = RSA.generate(2048)
print '\n\nDICT: {0}'.format(message_dict)
message = json.dumps(message_dict)
print '\n\nMESSAGE: {0}'.format(message)
encrypted = another_key.publickey().encrypt(message, 32)[0]
print '\n\nENCRYPTED: {0}'.format(encrypted)
decrypted = another_key.decrypt(encrypted)
print '\n\nMESSAGE: {0}'.format(decrypted)
result = json.loads(decrypted)
print '\n\nDICT: {0}'.format(result)
# result should be same message_dict
Your problem will be, that the message which you try to encrypt should be never larger than the n-modulo!
Because:
cipher = message ** e (mod N)
If your message is numerically larger than the RSA module (in your case 2048 BITS - not Bytes), you won't get the correct ciphertext, and decription will never work. (mathematically)
In your case this happens.
You should be careful, because I guess, that after the json.dumps() the message should not larger than (in your case of the 2048bit another_key) 2048//8 = 256 bytes long - you can test it after json.dumps() via len(message).
In your abovementioned example the length of your message will be larger than 700 bytes... and 700 > 256.
Just to show the meaning of this:
first try a message with exactly 256bytes of length:
>>> import json
>>> from Crypto.PublicKey import RSA
>>> another_key = RSA.generate(2048)
>>> message_dict = {'t':'a'*248}
>>> message = json.dumps(message_dict)
>>> len(str(message))
256
>>> enc = another_key.publickey().encrypt(message, 32)[0]
>>> dec = another_key.decrypt(enc)
You will see, in this example the length of the message is exactly 256 bytes (not bits, but 256*8 = 2048 bits) long - and it works fine!
But now let us try a length of 257 bytes (more than the length of the n modulo (2048 bits = 256 bytes):
>>> import json
>>> from Crypto.PublicKey import RSA
>>> another_key = RSA.generate(2048)
>>> message_dict = {'t':'a'*249}
>>> message = json.dumps(message_dict)
>>> len(str(message))
257
>>> enc = another_key.publickey().encrypt(message, 32)[0]
>>> dec = another_key.decrypt(enc)
In this case dec != enc - because the message is numerically larger than the n modulo.
If you will mathematically understand the reason for this, take a look in the meaning of "cipher = message ** e (mod N)" ;)

I have a RSA public key exponent and modulus. How can I encrypt a string using Python?

Given a public key exponent and modulus like the following, how can I encrypt a string and send it to a server as text?
publicKey: 10001,
modulus: 'd0eeaf178015d0418170055351711be1e4ed1dbab956603ac04a6e7a0dca1179cf33f90294782e9db4dc24a2b1d1f2717c357f32373fb3d9fd7dce91c40b6602'
I am trying to replicate the functionality provided by the javascript rsa library http://www.ohdave.com/rsa/ in python. In javascript, it looks something like this:
setMaxDigits(67); //sets a max digits for bigInt
var key = new RSAKeyPair('10001', '10001', 'd0eeaf178015d0418170055351711be1e4ed1dbab956603ac04a6e7a0dca1179cf33f90294782e9db4dc24a2b1d1f2717c357f32373fb3d9fd7dce91c40b6602');
var encrypted = encryptedString(key, 'message');
console.log(encrypted); //prints '88d58fec172269e5186592dd20446c594dbeb82c01edad41f841666500c9a530e24a282c6527ec66f4c826719f12478c6535bdc2baef86e4ff26906a26398413'
I imagine there is a way to do this with the PyCrypto library but I couldn't find any examples that use the exponent and modulus.
Edit 1:
Using the solution below, it appears to be working. Since I'm using python 2.7 I modified it to look like this:
from Crypto.PublicKey.RSA import construct
from binascii import unhexlify
from codecs import encode
e = long(10001)
n = int(encode('d0eeaf17801.....5d041817005535171', 'hex'), 16)
key = construct((n, e))
a = key.encrypt('hello', None)
print(a)
('.X?\xdc\x81\xfb\x9b(\x0b\xa1\xc6\xf7\xc0\xa3\xd7}U{Q?\xa6VR\xbdJ\xe9\xc5\x1f\x
f9i+\xb2\xf7\xcc\x8c&_\x9bD\x00\x86}V[z&3\\]_\xde\xed\xdc~\xf2\xe1\xa9^\x96\xc3\
xd5R\xc2*\xcb\xd9\x1d\x88$\x98\xb0\x07\xfaG+>G#\xf7cG\xd8\xa6\xf3y_ 4\x17\x0b\x0
3z\x0cvk7\xf7\xebPyo-\xa1\x81\xf5\x81\xec\x17\x9e\xfe3j\x98\xf2\xd5\x80\x1d\xdd\
xaf\xa4\xc8I\xeeB\xdaP\x85\xa7',)
Now I want to convert this encrypted text to a string to send via a post request. But this doesn't seem to work:
a.decode('utf-8')
With PyCrypto, you can use the Crypto.PublicKey.RSA.construct() function. You'll need to convert the modulus to an int. Here's an example (assuming big-endian):
from Crypto.PublicKey.RSA import construct
e = int('10001', 16)
n = int('d0eeaf...0b6602', 16) #snipped for brevity
pubkey = construct((n, e))
Then you can do the usual things (like encrypt) with the key:
from Crypto.Cipher import PKCS1_OAEP
cipher = PKCS1_OAEP.new(pubkey)
ciphertext = cipher.encrypt(b'abcde')
Edit: Note that your public exponent, 10001, is mostly likely hexadecimal. This would correspond to the common public exponent 65537. I've updated the above to reflect that.
I tried an alternative way using Crypto.Cipher.PKCS1_OAEP motivated by: https://cryptobook.nakov.com/asymmetric-key-ciphers/rsa-encrypt-decrypt-examples and it just worked.
PS: There seems to be something wrong with modulus given, as modulus n must be the product of two large primes, thus should not be an even number. A tiny modification of n has been applied to make the example code runnable.
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import binascii
e = int('10001', 16)
n = int('d0eeaf178015d0418170055351711be1e4ed1dbab956603ac04a6e7a0dca1179cf33f90294782e9db4dc24a2b1d1f2717c357f32373fb3d9fd7dce91c40b6601', 16)
# Construct a `RSAobj` with only ( n, e ), thus with only PublicKey
rsaKey = RSA.construct( ( n, e ) )
pubKey = rsaKey.publickey()
print(f"Public key: (n={hex(pubKey.n)}, e={hex(pubKey.e)})")
# Export if needed
pubKeyPEM = rsaKey.exportKey()
print(pubKeyPEM.decode('ascii'))
# Encrypt message using RSA-OAEP scheme
msg = b'Hello, world.'
encryptor = PKCS1_OAEP.new(pubKey)
encrypted = encryptor.encrypt(msg)
print("Encrypted:", binascii.hexlify(encrypted))

Categories