Python Crypto decoding - python

i try to encrypt and then decrypt text with Crypto and AWS KMS on Python, i have this code :
import base64
import boto3
from Crypto.Cipher import AES
PAD = lambda s: s + (256 - len(s) % 256) * ' '
def get_arn(aws_data):
return 'arn:aws:kms:{region}:{account_number}:key/{key_id}'.format(**aws_data)
def encrypt_data(aws_data, plaintext_message):
kms_client = boto3.client(
'kms',
region_name=aws_data['region'])
data_key = kms_client.generate_data_key(
KeyId=aws_data['key_id'],
KeySpec='AES_256')
cipher_text_blob = data_key.get('CiphertextBlob')
plaintext_key = data_key.get('Plaintext')
# Note, does not use IV or specify mode... for demo purposes only.
cypher = AES.new(plaintext_key, AES.MODE_CBC)
encrypted_data = base64.b64encode(cypher.encrypt(PAD(plaintext_message).encode("utf-8")))
# Need to preserve both of these data elements
return encrypted_data, cipher_text_blob
def decrypt_data(aws_data, encrypted_data, cipher_text_blob):
kms_client = boto3.client(
'kms',
region_name=aws_data['region'])
decrypted_key = kms_client.decrypt(CiphertextBlob=cipher_text_blob).get('Plaintext')
cypher = AES.new(decrypted_key, AES.MODE_CBC)
return cypher.decrypt(base64.b64decode(encrypted_data)).rstrip()
def main():
# Add your account number / region / KMS Key ID here.
aws_data = {
'region': 'eu-west-1',
'account_number': '7011777xxxxx',
'key_id': 'xxxxxx-83ac-xxxxxx-93d4-xxxxxx',
}
# And your super secret message to envelope encrypt...
plaintext = 'Hello, Worldas!'
# Store encrypted_data & cipher_text_blob in your persistent storage. You will need them both later.
encrypted_data, cipher_text_blob = encrypt_data(aws_data, plaintext)
print(encrypted_data)
decrypted_data = decrypt_data(aws_data, encrypted_data, cipher_text_blob)
print(decrypted_data)
if __name__ == '__main__':
main()
I encrypting for test message 'Hello, Worldas!' my encrypted_data in output looks like : b'ESsdSQv6JxpQptBmj321eX/bVj3gyGJ7AHtrH5qeIfTWbqSzIP7i6URrZFme1PGSNRGzl12B/NBFbK0nHBcCcaj9Wb9Qh+YMYJjeSTnGWOKFWmcIKYAAut9d040xiWG0KKBwHJTdl+41+g8F2ueSWqO1zR9Uuw1qyekF9s/141W7t+Le8IRe60tQKhgMAW5qxDVGluWZGJXLYDLIqFXszN9OhLmjwbMnF4g0ryMq41xbAXH77x0EJODhF1GQ+peHnKuexlhuzRjq1XVAvIgxQ1kYvBSE9AkqqCsO5BwIJuAlwfOWA93gSyTgLmWOg8bPTan4UnQNtTQ3vaRScffPgg=='
But then i try to decrypt i get output : b'-94\xc1\xee\xecF\xfbw9\x81o;\x9d\x1a\x10' instead of 'Hello, Worldas!'
Maybe whom know where is a problem? Why it happen? and how can i encrypt and decrypt my file properly ? please suggest!

Related

What is openssl_decrypt in Python?

I can't seem to do this PHP code in python:
openssl_decrypt( $acipher, "aes-256-gcm", $secret, OPENSSL_RAW_DATA, $bnonce, $tag);
This is my Python code:
from crypto.Cipher import AES
cipher = AES.new(acipher, AES.MODE_GCM, acipher)
plaintext = cipher.decrypt(secret)
# i don't even know where to put the "$bnonce, $tag", haha.
I'm quite stuck, please help.
I tried this but i got MAC check failed
secret = "b34f09fc80e7974a918b50cfda6f48ce" # after i use open-ssl on some string, this is what i received. Just for Extra Info.
content = "EZrjXFQ4o3oCVzy28q/IWYLgg9aP+VlmhQZ+wKs4C20D7k+lZMKlmi9UgZgINz6t/ucWmOzRFLo5DAZ3b6dYrMOz1lV2gJs9v9K6MDFeweREKRDupnTDKgx8AS7OaimofNK8wKxNp5QDnYBZNpY6BQ02mU5586LXGfwJJrAc3S7D85cZZofnUBpNcUztrHYOjCEtvB24p6j8W9ju3ALkAfd1Dk+UdKMVzp9sLa/qbzxPqQeZhPPS43A5b9Wa10DeKvMnMl4Z46M6j+AeudKmS46xCOM84zMbOonxIkCA+kS0BCPaDlpwOyiL31yeJqHw/dKhHuY0qVyHWgrlf3mdHizGKakeAoYsy4Wo1WjeTteSRnw713s="
# Content
bcontent = str( base64.b64decode(content) )
bcontent_total_characters = len(bcontent)
bcontent_total_characters_minus_12 = bcontent_total_characters - 12
bnonce = bcontent[ bcontent_total_characters_minus_12 : bcontent_total_characters - 12 + bcontent_total_characters ]
bcipher = bcontent[ 0 : 0 + bcontent_total_characters_minus_12 ]
# default tag
bcipher_total_characters = len(bcipher)
taglength = 16
tag = bcipher[ bcipher_total_characters - taglength : bcipher_total_characters - taglength + bcipher_total_characters ]
acipher = bcipher[ 0 : 0 + bcipher_total_characters - taglength]
cipher = AES.new(str.encode(secret), AES.MODE_GCM, str.encode(bnonce) )
decrypted = cipher.decrypt_and_verify(str.encode(content), str.encode(tag))
From php documentation i modified your function vars you can rename them if you like
openssl_decrypt( $acipher,
"aes-256-gcm",
$passphrase, //This is your secret
OPENSSL_RAW_DATA,
$iv, //This is your nonce
$tag //This is your tag
);
This is how it would look in python
#!/usr/bin/env python3
# pip install pycryptodome
import json
from base64 import b64encode,b64decode
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Random import get_random_bytes
def python_encrypt(data, passphrase, iv):
"""
Encrypt using AES-256-GCM
"""
cipher = AES.new(passphrase, AES.MODE_GCM,iv)
ciphertext, tag = cipher.encrypt_and_digest(data)
json_k = [ 'nonce', 'ciphertext', 'tag' ]
json_v = [ b64encode(x).decode('utf-8') for x in [cipher.nonce, ciphertext, tag ]]
result = json.dumps(dict(zip(json_k, json_v)))
return result
def python_decrypt(data, passphrase, iv, tag):
"""
Decrypt using AES-256-GCM
"""
cipher = AES.new(passphrase, AES.MODE_GCM, iv)
decrypted = cipher.decrypt_and_verify(data, tag)
return decrypted
#Testing code out
data = b"secret"
passphrase = get_random_bytes(16)
nonce = get_random_bytes(16)
enc_json = python_encrypt(data,passphrase,nonce)
print(enc_json)
b64 = json.loads(enc_json)
json_k = [ 'nonce', 'ciphertext', 'tag' ]
jv = {k:b64decode(b64[k]) for k in json_k}
print(python_decrypt(jv['ciphertext'], passphrase, jv['nonce'], jv['tag']))
Modified from the docs

How to convert SQLite Blob field to string with Python

I want to open the Chrome (Login Data) file and use its password field. But this field is stored in byte/blob mode and can not be converted to text.
I also tried codecs and pickle and bytes.encode and str.decode but it didn't work.
Please look at the code below and help :
import sqlite3
connection_obj = sqlite3.connect('C:/Users/{username}/AppData/Local/Google/Chrome/User
Data/Default/Login Data')
cursor_obj = connection_obj.cursor()
statement = '''SELECT action_url, username_value, password_value FROM logins'''
cursor_obj.execute(statement)
output = cursor_obj.fetchmany(5)
for url,usr,psw in output:
# convert psw blob -> ascii text
# ....
# ....
# for example psw filed:
# b'v10\x7f\xa3\x1a\xd1\x83g\x8c\xc4\x14]\xb6n\xf85\xba\xca\xf5r\x17\xb6D\xed\xf5\x11rM\xbe\xbf\xb1\xc2y\xc5Vr\xc3\xb3NB\xc7J\x14\x95'
#
# convert to below text :
# zarfilm-136342
print(url, usr, psw,sep='------')
print('*'*10)
connection_obj.commit()
connection_obj.close()
That data is encrypted in AES, and further the key is encrypted with CryptProtectData to lock the encryption key to user data. You can decrypt the data with something like this:
import base64, json, os, sqlite3, win32crypt
from Crypto.Cipher import AES
def chrome_key():
local_state_fn = os.path.join(os.environ["USERPROFILE"],"AppData","Local","Google","Chrome","User Data","Local State")
with open(local_state_fn, "r") as f:
local_state = json.load(f)
key = base64.b64decode(local_state["os_crypt"]["encrypted_key"])
key = key[5:]
return win32crypt.CryptUnprotectData(key, None, None, None, 0)[1]
def decrypt_password(password, key):
iv, password = password[3:15], password[15:]
aes = AES.new(key, AES.MODE_GCM, iv)
return aes.decrypt(password)[:-16].decode("utf-8")
def main():
key = chrome_key()
db_fn = os.path.join(os.environ["USERPROFILE"],"AppData","Local","Google","Chrome","User Data","default","Login Data")
db = sqlite3.connect(db_fn)
for origin_url, username, password_crypt in db.execute("SELECT origin_url, username_value, password_value FROM logins;"):
password = decrypt_password(password_crypt, key)
print(f"{origin_url}, {username}, {password}")
db.close()
if __name__ == "__main__":
main()
Are you surprised to learn that this field is encrypted? Google would be in for a world of trouble if it wasn't. Even Chrome doesn't know how to decrypt this. It's done with the Windows cryptography APIs, and involved your Windows login password. You can't get them.

how would I generate a repeatable encryption key?

I need to sent my friend Bob the number 42, my code will use a pre-shared key generate a random number to encrypt the message (42) and decrypt it on Bobs end. the only problem is, I have no clue how to generate a repeatable encryption key in python.
You can use Crypto to encrypt a message using AES.
import hashlib, base64
from Crypto import Random
from Crypto.Cipher import AES
class AESCipher():
def __init__(self, key):
self.private_key = hashlib.sha256(key.encode()).digest()
self.bs = AES.block_size
def encrypt(self, data):
# generate public key
public_key = Random.new().read(self.bs)
# setup AES Cipher using public key and private key
cipher = AES.new(self.private_key, AES.MODE_CBC, public_key)
# enrpyt the data and convert to base64
return base64.b64encode(public_key + cipher.encrypt(self.pad(data).encode()))
def decrypt(self, enc):
# convert encrypted data to base 64
enc = base64.b64decode(enc)
# get public key
public_key = enc[:AES.block_size]
# setup AES Cipher using public and private key
cipher = AES.new(self.private_key, AES.MODE_CBC, public_key)
# decrypt data using the public key
return self.unpad(cipher.decrypt(enc[AES.block_size:])).decode("utf-8")
def pad(self, s):
# pads data so that it's a multiple of 16
return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)
def unpad(self, s):
# removes padding
return s[:-ord(s[len(s)-1:])]
cipher = AESCipher("your secret key")
Here I encrypt some text, the public key with the encrypted text is joined and returned from the encrypt method, you can then send the returned text to Bob, to decrypt the text bob then just needs to run the decrypt method.
>>> cipher.encrypt("your message")
b'HYfUkcd//CaRquG9AhReR8bJYdVQdcGWRAjcp9AstLs='
>>> output = cipher.encrypt("your message")
>>> output
b'RVTK7L7ZDw9DzvuXuj8zYPZJjBO/A0N3l5N1hp9LY6U='
>>> cipher.decrypt(output)
'your message'
>>>

How to decrypt an AES/CBC encrypted string in Kotlin?

I have this Python method on the server to encrypt a string into bytes (AES/CBC).
class AESCipher(object, key):
def __init__(self, key):
self.bs = AES.block_size
self.key = hashlib.sha256(key.encode()).digest()
def encrypt(self, raw):
raw = self._pad(raw)
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw.encode()))
def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')
def _pad(self, s):
return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)
The output of encrypt() is in bytes like this: b'PMgMOkBkciIKfWy/DfntVMyAcKtVsM8LwEwnTYE5IXY='
I would like to store this into database, and send it as string via API to Kotlin. And in there I would like to decrypt it via the same shared secret key.
In what format do I save the bytes above into database?
Once arrived in Kotlin client, how do I convert that string into ByteArray?
My theory is that I have to store the bytes as base64 string in the database.
And on the other side I have to decode the string as base64 into bytes. Is this approach correct? Will the encryption/decryption work like this end-to-end with the code below?
fun decrypt(context:Context, dataToDecrypt: ByteArray): ByteArray {
val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
val ivSpec = IvParameterSpec(getSavedInitializationVector(context))
cipher.init(Cipher.DECRYPT_MODE, getSavedSecretKey(context), ivSpec)
val cipherText = cipher.doFinal(dataToDecrypt)
val sb = StringBuilder()
for (b in cipherText) {
sb.append(b.toChar())
}
return cipherText
}
fun getSavedSecretKey(context: Context): SecretKey {
val sharedPref = PreferenceManager.getDefaultSharedPreferences(context)
val strSecretKey = sharedPref.getString("secret_key", "")
val bytes = android.util.Base64.decode(strSecretKey, android.util.Base64.DEFAULT)
val ois = ObjectInputStream(ByteArrayInputStream(bytes))
val secretKey = ois.readObject() as SecretKey
return secretKey
}
fun getSavedInitializationVector(context: Context) : ByteArray {
val sharedPref = PreferenceManager.getDefaultSharedPreferences(context)
val strInitializationVector = sharedPref.getString("initialization_vector", "")
val bytes = android.util.Base64.decode(strInitializationVector, android.util.Base64.DEFAULT)
val ois = ObjectInputStream(ByteArrayInputStream(bytes))
val initializationVector = ois.readObject() as ByteArray
return initializationVector
}
UPDATE
I have tried to remove the Base64 to remove the memory overhead as suggested.
Python:
def encrypt(self, raw):
raw = self._pad(raw)
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return iv + cipher.encrypt(raw.encode())
So this is no longer possible.
enc = AESCipher('abc').encrypt("myLife")
value_to_save_in_db = enc.decode("utf8")
So I need to find a way to store the byte array directly in the database. I think I should be able to do this as blob. But some challenges remain as how to send the bytearray as part of JSON over the API to the android device. I think I have to convert it to Base64 string again. Not sure if I have gained anything in that case...
The following Kotlin code:
val decrypted = decrypt("blEOKMQtUbNOzJbvEkL2gNhjF+qQ/ZK84f2ADu8xyUFme6uBhNYqvEherF/RRO9YRImz5Y04/ll+T07kqv+ExQ==");
println(decrypted);
decrypts a ciphertext of the Python code. Here decrypt() is:
fun decrypt(dataToDecryptB64 : String) : String {
// Base64 decode Python data
val dataToDecrypt = Base64.getDecoder().decode(dataToDecryptB64)
// Separate IV and Ciphertext
val ivBytes = ByteArray(16)
val cipherBytes = ByteArray(dataToDecrypt.size - ivBytes.size)
System.arraycopy(dataToDecrypt, 0, ivBytes, 0, ivBytes.size)
System.arraycopy(dataToDecrypt, ivBytes.size, cipherBytes, 0, cipherBytes.size)
// Derive key
val keyBytes = MessageDigest.getInstance("SHA256").digest("abc".toByteArray(Charsets.UTF_8))
// Decrypt
val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(keyBytes, "AES"), IvParameterSpec(ivBytes))
val cipherText = cipher.doFinal(cipherBytes)
return String(cipherText, Charsets.ISO_8859_1)
}
For this, the ciphertext was generated using the posted Python class AESCipher as follows:
plaintext = 'The quick brown fox jumps over the lazy dog'
cipher = AESCipher('abc')
ciphertext = cipher.encrypt(plaintext)
print(ciphertext.decode('utf8')) # Base64 string, which can be stored e.g. in a DB
I applied the originally posted Python implementation that derives the key using SHA256. However, if the key is derived from a password, for security reasons not SHA256 but a reliable key derivation function, e.g. Argon2 or PBKDF2, should be used.
The Kotlin code first Base64 decodes the Python data and then separates IV and the actual ciphertext. Then, the key is derived by generating the SHA256 hash of the password. Finally the data is decrypted.
The current Python code Base64 encodes the data so that it can be stored as a string in the DB. Alternatively, the Python code could be modified so that no Base64 encoding is performed, and the raw data can be stored (which requires less memory, Base64 overhead: 33%).
Depending on the solution chosen, the Kotlin code may or may not need to Base64 decode the data.

AWS encrypt TypeError: can't concat str to bytes

i try to write a code , whom encrypt my data , then i try to execute me code i get an error:
import base64
import boto3
from Crypto.Cipher import AES
PAD = lambda s: s + (32 - len(s) % 32) * ' '
def get_arn(aws_data):
return 'arn:aws:kms:{region}:{account_number}:key/{key_id}'.format(**aws_data)
def encrypt_data(aws_data, plaintext_message):
kms_client = boto3.client(
'kms',
region_name=aws_data['region'])
data_key = kms_client.generate_data_key(
KeyId=aws_data['key_id'],
KeySpec='AES_256')
cipher_text_blob = data_key.get('CiphertextBlob')
plaintext_key = data_key.get('Plaintext')
# Note, does not use IV or specify mode... for demo purposes only.
cypher = AES.new(plaintext_key, AES.MODE_EAX)
encrypted_data = base64.b64encode(cypher.encrypt(PAD(plaintext_message)))
# Need to preserve both of these data elements
return encrypted_data, cipher_text_blob
def main():
# Add your account number / region / KMS Key ID here.
aws_data = {
'region': 'eu-west-1',
'account_number': '70117777xxxx',
'key_id': 'xxxxxxx-83ac-4b5e-93d4-xxxxxxxx',
}
# And your super secret message to envelope encrypt...
plaintext = b'Hello, World!'
# Store encrypted_data & cipher_text_blob in your persistent storage. You will need them both later.
encrypted_data, cipher_text_blob = encrypt_data(aws_data, plaintext)
print(encrypted_data)
if __name__ == '__main__':
main()
this is an error:
PAD = lambda s: s + (32 - len(s) % 32) * ' '
TypeError: can't concat str to bytes
maybe whom know where is a problem ? please suggest
Your function PAD is intended to work with a string input and you call it with a bytes input (b'Hello, World!' in your example).
PAD('Hello, World!') (without the leading b) works.
One solution would be to pad the plaintext as a stringand convert it to bytesafterwards, e.g.:
plaintext = PAD('Hello, world!')
plaintext_bytes = plaintext.encode('utf-8')
See this StackOverflow question for how to convert a stringto bytes.

Categories