I am working on a little sideproject in Python 3.
My current problem is around AES-based decryption of a file. The content of the file (text) is symmetrical encrypted with AES.
I have imported PyCrypto: https://www.dlitz.net/software/pycrypto/api/current/
The docs specificy only little regarding the symmetrical key:
key (byte string) - The secret key to use in the symmetric cipher. It must be 16 (AES-128), 24 (AES-192), or 32 (AES-256) bytes long.
I have the key and looks like:
0xB0,0x0D,0xDF,0x9D,... (for security reasons I don't report the complete key here)
Anyway, my first question:
What kind of string is that? It looks like ASCII, but I lack deep knowledge about Encodings. Do I need any kind of transformation / decoding?
I wrote a little program to open a file and decrypt it. But PyCrypto throws an error and I spent now 5 hours with trial and error without any progress:
ValueError: AES key must be either 16, 24, or 32 bytes long
So I tried both:
initializing as string:
key = "0xB0,0x0D,0xDF,0x9D,..."
and 2. as byte-string:
key = b"0xB0,0x0D,0xDF,0x9D,..."
No effect.
Any comments or ideas?
Best Regards,
AFX
What you have is a hex string. For example, if you had this:
0x0F, 0x10, 0x1A
Then this, condensed down to be an actual hex string, is:
0F101A
Which, as raw bytes, is:
15, 16, 26
You just need to convert it to a byte array first. Have a look at binascii.unhexlify.
I wanted to provide the solution that worked out for me:
First, get the basics:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
key = 'insert-key-here'
Now, I build a method to decrypt:
def decrypt_file(key, in_filename, out_filename=None):
""" Decrypts a file using AES (CBC mode) with the
given key.
"""
backend = default_backend()
with open(in_filename, mode="r+b") as infile:
iv = infile.read(16) #Calling .read() will move the iterator exactly as many positions
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend)
decryptor = cipher.decryptor()
with open(out_filename, mode="w+b") as outfile:
cipher_text = infile.read() #Only the first 16byte are the iv, the rest is the cipher-text
outfile.write(decryptor.update(cipher_text) + decryptor.finalize())
Here is the key: I had to transform the key to byte-string first
#Transform key to byte-string
key_int = [int(x,0) for x in key.split(',')]
decrypt_byte_key = b''
for x in key_int:
decrypt_byte_key += x.to_bytes(1, 'little')
Finally, you can run it on your file:
decrypt_file(decrypt_byte_key, "input.enc", "output.txt")
Have fun.
Related
I'm trying to encrypt and decrypt values, using python pycryptodome, saved to my Postgresql database on the client side based on example 2 found here; SymmetricEncryptionClientSide. However, I keep running into UnicodeDecodeErrors, such as the following one;
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xbf in position 0: invalid start byte
This is my code;
nonce = uuid.uuid4().bytes
str_key = "wq5RdHHfzCW1/2eE" # example key
def sym_encrypt(data):
key = str_key.encode("utf-8")
cipher = AES.new(key, AES.MODE_EAX, nonce=nonce)
data = data + (" " * (16 - (len(data) % 16)))
return cipher.encrypt(data.encode("utf-8")).hex()
def sym_decrypt(data):
key = str_key.encode("utf-8")
cipher = AES.new(key, AES.MODE_EAX, nonce=nonce)
return cipher.decrypt(binascii.unhexlify(data)).decode("utf8").rstrip()
nounce is used to ensure that all the same values have the same encryption string for querying purposes. It's the same reason why the values get Hex.
When performing the encryption on a string value such as; test#test.com using the encryption function above I get the following string saved to my database;
938174e09f8411a4957b7b91e07162b4 which is a string
However, when I perform the decrypt part as shown above I get the UnicodeDecodeError error.
For more information;
this is what the value looks like after encoding and before encryption and hex:
b'test#test.com '
This is what the value looks like after encoding and encryption and before hex:
b'\x92\xa1\xe1\x06\xd1\xd8\x8c\xbdv0\xb2\x13p!$#'
When encoding the value before decryption the value looks like:
b'938174e09f8411a4957b7b91e07162b4'
But after unhexlify the value the byte looks different to the byte after encryption:
b'\x93\x81t\xe0\x9f\x84\x11\xa4\x95{{\x91\xe0qb\xb4'
I'm not sure what is happening here. I expected the byte to look the same after encryption and when unhexlify, but this is not the case. When I perform the decrypt I get the 'UnicodeDecodeError error.
Does anyone know why this is happening and how to solve this issue?
Thanks in advance.
I have a column ID in oracle which was encrypted like this :
select CAST(DBMS_CRYPTO.encrypt(UTL_RAW.CAST_TO_RAW('SECRETSTRING'), 4356 , 'SOMEKEY') AS VARCHAR2(100 char)) as temp from dual;
Now i am reading this table in python using pandas. Now I want to decrypt this in python.
I tried several ways, but I am unable decrypt it
Following are the things which I tried:
1)
from Cryptodome.Cipher import AES
from Cryptodome.Random import get_random_bytes
def decrypty(enc):
unpad = lambda s: s[:-ord(s[-1:])]
enc = base64.b64decode(enc)
iv = enc[:AES.block_size]
cipher = AES.new(__key__, AES.MODE_CFB, iv)
return unpad(base64.b64decode(cipher.decrypt(enc[AES.block_size:])).decode('utf8'))
this threw an error :
binascii.Error: Invalid base64-encoded string: number of data characters (1) cannot be 1 more than a multiple of 4
2)
from Crypto.Cipher import AES
from Crypto import Random
def decrypt(key, enc):
enc = base64.b64decode(enc)
iv = enc[:16]
cipher = AES.new(key, AES.MODE_CBC, iv)
return unpad(cipher.decrypt(enc[16:]), block_size=16)
This threw an error related to padding.
Basically if something is encrypted in DB with a key, using the same key I am unable to decrypt it in python. any pointers ?
If the database did the encryption I for sure would advice to use the database again for the decryption too, if possible at all.
If it is not possible to use the database for both the encryption and decryption, put them both in the python code.
Keep the code for en/de-cryption close and make the use the same bugs. Also: what is the reason for the encryption? Maybe that data is not meant to be readable for your application.
I'm trying to get some help regarding encoding with Triple DES in python.
I want to have 2 functions: encode/decode
and I want it to operate as it does on the site: https://www.devglan.com/online-tools/triple-des-encrypt-decrypt, with the base64 output format.
because when I decode that base64 msg I get some random chars and that's what I want.
I've tried doing that with pycryptodome but it gives me a byte output.
what I've tried:
from Crypto.Cipher import DES3
from Crypto.Random import get_random_bytes
# Avoid Option 3
while True:
try:
key = DES3.adjust_key_parity(get_random_bytes(24))
break
except ValueError:
pass
cipher = DES3.new(key, DES3.MODE_ECB)
plaintext = b'We are no longer the knights who say ni!'
msg = cipher.iv + cipher.encrypt(plaintext)
How would I be able to do that? any help would be appreciated!
Or maybe do you know what most programs use to encrypt their files? for example word.
Hy, i was trying the cryptography module for python. So, i made a code that "encrypts" (i'ts very newbie code) text and save it encrypted, but when i try to save the encrypted bytes i get a "TypeError: a bytes-like object is required, not 'int'". This is very weird, because I look the type of the "encrypted_message" and its a class bytes.
def encrypt(paswd, formated_data):
#generates the salt
digest = hashes.Hash(hashes.SHA256(), default_backend())
digest.update(bytes(paswd, "utf-8"))
salt = digest.finalize()
kdf = PBKDF2HMAC(
algorithm = hashes.SHA256,
length = 32,
salt = salt,
iterations = 1000000,
backend = default_backend()
)
key = kdf.derive(bytes(paswd, 'utf-8'))
iv = os.urandom(16)
encryptor = Cipher(algorithms.AES(key), modes.CTR(iv), default_backend()).encryptor()
#untill this line of code everything works fine
encrypted_msg = encryptor.update(bytes(formated_data, "utf-8")) + encryptor.finalize()
print(type(encripted_msg)) #this prints <class 'bytes'>
with open ("./vault/locked.aes", mode = "wb") as f:
f.writelines( encripted_msg ) #and here i get the error
btw, i'm using python 3.6.8
Ok, I figured out how to solve this. First of all, the byte class in python storage the data as a list, you could read each byte of data in a loop if you want to. So, the method "writelines()" in a binary data file don't use "\n" to end each line, instead will write each byte in the bytes class variable, just have to transform it into a list and voilá!, the method will read each bite and write it into a file.
I have a function written in c# that you can pass an ascii string as the key, and the encrypted string from a database and decode the data.
I've verified that the code works by writing a simple c# program to decode the data. The code snippet converts the key string to bytes and MD5 hashes it.
C# Code Snippet, omitting some code that converted the byteHash to an ascii string for output in the compiled program
key = "joetest"
byte[] byteHash = cryptoServiceProvider.ComputeHash(Encoding.ASCII.GetBytes(key));
byteHash = "f2fc0f481787cc4cbb15f7ded4412fe4"
I run the following commands and Python3 and get the same byteHash
key = "joetest"
encoded_key = key.encode("ascii")
m = hashlib.md5()
m.update(encoded_key)
hex_key = m.hexdigest()
print(hex_key)
hex_key = "f2fc0f481787cc4cbb15f7ded4412fe4"
I've tried encoding 'hex_key' as binary.
My issue is I'm trying to pass hex_key into 2 different python3 crypto programs. Cryptodome and pyDes. Both tell me that i'm passing in an invalid key.
The C# code that uses byteHash is as follows
tripleDesCryptoServiceProvider.Key = byteHash;
tripleDesCryptoServiceProvider.Mode = CipherMode.ECB;
byte[] byteBuff = Convert.FromBase64String(encryptedString);
string strDecrypted = Encoding.UTF8.GetString(tripleDesCryptoServiceProvider.CreateDecryptor().TransformFinalBlock(byteBuff, 0, byteBuff.Length));
This all works, i was able to decrypt data when i passed in the encrypted string into this function.
Using pyDes i'm using this code
from pyDes import *
import base64
import hashlib
my_data = "fK/jw6/25y0="
#my_data is the word 'test' encrypted with the key of 'joetest'
#This code takes the key string and converts it to an MD5 hash
my_key = "joetest"
encoded_key = my_key.encode("ascii") #Encode the data as binary data
m = hashlib.md5()
m.update(encoded_key)
hex_key = m.hexdigest() #Convert the key to an MD5 hash
encoded_hex_key = hex_key.encode() #Make the MD5 key a binary key
#Convert the Base64 encoded string to the format that the decoder wants
decoded_data = base64.b64decode(my_data)
k = triple_des(encoded_hex_key, ECB, padmode=PAD_PKCS5)
my_out = k.decrypt(decoded_data)
print("my_out")
print(my_out)
exit()
The error i'm getting is:
(3destest) c:\3des-test\3destest>joe3des_test3.py
Traceback (most recent call last):
File "C:\3des-test\3destest\joe3des_test3.py", line 20, in <module>
k = triple_des(encoded_hex_key, ECB, padmode=PAD_PKCS5)
File "c:\3des-test\3destest\lib\site-packages\pyDes.py", line 710, in __init__
self.setKey(key)
File "c:\3des-test\3destest\lib\site-packages\pyDes.py", line 719, in setKey
raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long")
ValueError: Invalid triple DES key size. Key must be either 16 or 24 bytes long
Using pyCryptodome, i've tried this code
from Cryptodome.Cipher import DES3
import base64
import hashlib
# Converts the key string to an MD5 hash
key = "joetest"
encoded_key = key.encode("ascii")
m = hashlib.md5()
m.update(encoded_key)
hex_key = m.hexdigest()
#Decodes the string to binary digits
encryptedString = base64.b64decode("fK/jw6/25y0=")
#Create the cipher to decrypt the data
cipher = DES3.new(hex_key, DES3.MODE_ECB)
decryptedString = cipher.decrypt(encryptedString)
And i get this error
Traceback (most recent call last):
File "C:\3des-test\3destest\joe3des_test2.py", line 16, in <module>
cipher = DES3.new(hex_key, DES3.MODE_ECB)
File "c:\3des-test\3destest\lib\site-packages\Cryptodome\Cipher\DES3.py", line 174, in new
return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs)
File "c:\3des-test\3destest\lib\site-packages\Cryptodome\Cipher\__init__.py", line 55, in _create_cipher
return modes[mode](factory, **kwargs)
File "c:\3des-test\3destest\lib\site-packages\Cryptodome\Cipher\_mode_ecb.py", line 175, in _create_ecb_cipher
cipher_state = factory._create_base_cipher(kwargs)
File "c:\3des-test\3destest\lib\site-packages\Cryptodome\Cipher\DES3.py", line 99, in _create_base_cipher
key = adjust_key_parity(key_in)
File "c:\3des-test\3destest\lib\site-packages\Cryptodome\Cipher\DES3.py", line 80, in adjust_key_parity
raise ValueError("Not a valid TDES key")
ValueError: Not a valid TDES key
My python MD5 hash is 32 hex characters long. Assuming my math is right, 32 * 4 is 128 bits. And the error is saying it must be 16 or 24 bytes long. 16 * 8 is also 128 bits. So the byte string value i'm passing it should be correct. I think I'm missing something, but can't seem to figure it out.
Update 2-Jan-2018
Based on answer below here's a copy of the code that I used to confirm this will decrypt the data from the DB.
from pyDes import *
import base64
import hashlib
#my_data is the word 'test' encrypted with the key of 'joetest'
my_data = "fK/jw6/25y0="
#This code takes the key string and converts it to an MD5 hash
my_key = "joetest"
encoded_key = my_key.encode("ascii")
m = hashlib.md5()
m.update(encoded_key)
digest_key = m.digest()
#Convert the Base64 encoded string to the format that the decoder wants
decoded_data = base64.b64decode(my_data)
k = triple_des(digest_key, ECB)
my_out = k.decrypt(decoded_data)
print("my_out")
print(my_out.decode("ascii"))
The disconnect here is that pyDes.triple_des() is looking for a binary key, but what you are giving it is an encoded string with the hex representation of that key. Since pyDes doesn't expect the hex string, try just giving it the raw digest instead (i.e. m.digest() instead of m.hexdigest()). No need to .encode() it either.
TripleDES, by definition, is meant to use a 24 byte key, e.g. 192 bits. Implementations that accept less than that actually reuse key data.
In C#, TripleDES with a 128-bit key reuses the first 64 bits to create a key that is 192 bits in length.
With that in mind, try using the following 192-bit key instead:
f2fc0f481787cc4cbb15f7ded4412fe4f2fc0f481787cc4c
If this works, which I expect it will, you'll just need to modify the code to copy the first 64 bits to the end.
The Error
line 80, in adjust_key_parity
raise ValueError("Not a valid TDES key")
Comes from the following code in pyCryptodome:
79 if len(key_in) not in key_size:
80 raise ValueError("Not a valid TDES key")
..
186 # Size of a key (in bytes)
187 key_size = (16, 24)
Your key is 16 bytes long, but in hex form the key you pass have size 32.