Reputation: 1013
I need to access some resources protected by passwords. A colleague of mine gave me access to a database table that contains these passwords, but they are encrypted. (and I think the result of the encryption is encoded in base64, since it's 44 characters long and it always ends with an equal "=" character).
My colleague gave me the name of the algorithm he used to encrypt the passwords: AES-256-CBC, and he also gave me the encryption key.
I thought that with all this information it would be easy to decipher the passwords, but it was not.
I used the functions suggested on this page: https://paperbun.org/encrypt-and-decrypt-using-pycrypto-aes-256-python/
that are the following:
# Define the encryption function
def encrypt_AES_CBC_256(key, message):
key_bytes = key.encode('utf-8')
message_bytes = message.encode('utf-8')
iv = get_random_bytes(AES.block_size)
cipher = AES.new(key_bytes, AES.MODE_CBC, iv)
padded_message = pad(message_bytes, AES.block_size)
ciphertext_bytes = cipher.encrypt(padded_message)
ciphertext = b64encode(iv + ciphertext_bytes).decode('utf-8')
return ciphertext
# Define the decryption function
def decrypt_AES_CBC_256(key, ciphertext):
key_bytes = key.encode('utf-8')
ciphertext_bytes = b64decode(ciphertext)
iv = ciphertext_bytes[:AES.block_size]
cipher = AES.new(key_bytes, AES.MODE_CBC, iv)
ciphertext_bytes = ciphertext_bytes[AES.block_size:]
decrypted_bytes = cipher.decrypt(ciphertext_bytes)
plaintext_bytes = unpad(decrypted_bytes, AES.block_size)
plaintext = plaintext_bytes.decode('utf-8')
return plaintext
Then, I tried these functions by encrypting a message, decrypting it back and ensuring that the decrypted result was identical to the initial string. Here is my code:
key = "XXXXXXXXXXXXXXXXXXXXXXXXXXX" # actual key is 32 characters long
print('-----------')
raw_input = "HERE IS MY TEST STRING"
print(f"Entrée : {raw_input}")
test4 = encrypt_AES_CBC_256(key, raw_input)
print(f"Entrée chiffrée : {test4}")
test5 = decrypt_AES_CBC_256(key, test4)
print(f"Message déchiffré : {test5}")
The output is:
-----------
Entrée : HERE IS MY TEST STRING
Entrée chiffrée : sZ1iZks9+qGVDHzt9WEO1hU3YrXKHvJj5sQzVd64HscPT8fiFBNbeihGFxpFyGC3
Message déchiffré : HERE IS MY TEST STRING
We can see that everything seems to work fine, the decrypted message equals the input.
Now, I tried to decrypt (with the same key) an encrypted password given by my colleague:
print('-----------')
raw = "e46mK0OmmYMBa2qkaALx5aUea4y/pT43OapQ6lmnDOA="
print(f"Mot de passe chiffré : {raw}")
FINAL_TEST = decrypt_AES_CBC_256(key, raw)
print(f"Mot de passe déchiffré : {FINAL_TEST}")
print(f"Longueur du résultat : {len(FINAL_TEST)} caractères")
The output was:
-----------
Mot de passe chiffré : e46mK0OmmYMBa2qkaALx5aUea4y/pT43OapQ6lmnDOA=
Mot de passe déchiffré :
Longueur du résultat : 0 caractères
We can see that the result is 0 character long. And it was the same with every encrypted password he gave me. What am I missing?
And why is the encrypted password only 44 characters long and not 64? I know that this is somewhat related to 256/6 = 42.66 and then you pad the missing characters with = signs, and since you want the length to be a multiple of 4 you pad up to 44, but if my coworker indeed use the AES-CBC-256 algorithm, why would his encrypted passwords not end up being 64 characters long like my encrypted test message?
Upvotes: 0
Views: 4289
Reputation: 1013
I found a solution! In fact, to encrypt the passwords, my coworker used the PHP function openssl_encrypt
: https://www.php.net/manual/en/function.openssl-encrypt
openssl_encrypt(
string $data,
string $cipher_algo,
string $passphrase,
int $options = 0,
string $iv = "",
string &$tag = null,
string $aad = "",
int $tag_length = 16
): string|false
You can notice that the IV used in the encryption has a default value of an empty string ""
. It means that the first 16 bytes are the null character \x00
. And in fact, my coworker kept this default value (which is not very secure...) so, having this information, I was abled to decipher the passwords using an IV whose all 16 characters are \x00
.
Here is my code:
from base64 import b64decode
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
def decipher(encrypted, passphrase):
nulls = b'\x00' * 16
my_cipher = AES.new(
key=passphrase.encode('utf-8'),
mode=AES.MODE_CBC,
iv=nulls
)
result = unpad(
my_cipher.decrypt(b64decode(encrypted)),
AES.block_size
).decode('utf-8')
return result
Upvotes: 0