Python AES 256 CBC Encryption - python

I am trying to transfer a hash key generated encryption via PHP to Python. I couldn't figure out how to edit openssl_encrypt. I would be glad if you help.
PHP Code
function generateSaveCardCreateHashKey(
$merchant_key,
$customer_number,
$card_number,
$card_holder_name,
$expiry_month,
$expiry_year,
$app_secret
){
$data = $merchant_key.'|'. $customer_number .'|'.$card_holder_name.'|'.$card_number.'|'.$expiry_month.'|'.$expiry_year;
$iv = substr(sha1(mt_rand()), 0, 16);
$password = sha1($app_secret);
$salt = substr(sha1(mt_rand()), 0, 4);
$saltWithPassword = hash('sha256', $password . $salt);
$encrypted = openssl_encrypt("$data", 'aes-256-cbc', "$saltWithPassword", null, $iv);
$msg_encrypted_bundle = "$iv:$salt:$encrypted";
$msg_encrypted_bundle = str_replace('/', '__', $msg_encrypted_bundle);
return $msg_encrypted_bundle;
}
Python Code
from Crypto.Hash import SHA1
from Crypto.Hash import SHA256
def generateSaveCardCreateHashKey(
merchant_key ,
customer_number ,
card_number ,
card_holder_name ,
expiry_month ,
expiry_year ,
app_secret
) :
data = merchant_key + ":" + customer_number + "|" + card_holder_name + "|" + card_number + "|" + expiry_month + "|" + expiry_year
randNumIv = str(random.randint(10000000000000000,99999999999999999))
hashNumIv = SHA1.new()
hashNumIv.update(randNumIv.encode("UTF-8"))
hashNumber = hashNumIv.hexdigest()
iv = hashNumber[:16]
hashAppSec = SHA1.new()
hashAppSec.update(app_secret.encode("UTF-8"))
password = hashAppSec.hexdigest()
randNumSalt = str(random.randint(10000000000000000,99999999999999999))
hashNumSalt = SHA1.new()
hashNumSalt.update(randNumSalt.encode("UTF-8"))
hashSalt = hashNumSalt.hexdigest()
salt = hashSalt[:4]
strPassSalt = password + salt
hashStr = SHA256.new()
hashStr.update(strPassSalt.encode("UTF-8"))
saltWithPassword = hashStr.hexdigest()
encrypted = ""
msg_encrypted_bundle = iv + ":" + salt + ":" + encrypted
msg_encrypted_bundle = msg_encrypted_bundle.replace("/" , "_")
return msg_encrypted_bundle
PHP code is working. I need to write the python code correctly because it is used in the payment system. The bank system checks the generated hash key and provides the transaction. I couldn't figure out how to edit the encrypted value. So I couldn't run the code properly. I would appreciate it if you could guide me to edit it.

I found the way to get the code working with more tries. I wanted to share in case it might be helpful. Or there may be a better way :)
def encrypt(plain_text: bytes, app_secret: str):
randNumIv = str(random.randint(100000000,900000000))
hashNumber = hashlib.sha1(randNumIv.encode("utf-8")).hexdigest()
iv = hashNumber[:16]
password = hashlib.sha1(app_secret.encode("utf-8")).hexdigest()
randNumSalt = str(random.randint(100000000,900000000))
hashSalt = hashlib.sha1(randNumSalt.encode("utf-8")).hexdigest()
salt = hashSalt[:4]
strPassSalt = password + salt
saltWithPassword = hashlib.sha256(strPassSalt.encode("utf-8")).hexdigest()
padded_text = pad(plain_text)
cipher_config = AES.new(bytes(saltWithPassword[:32], "utf-8"), AES.MODE_CBC, bytes(iv, "utf-8"))
cipher = cipher_config.encrypt(padded_text)
encrypted = base64.b64encode(cipher).decode('utf-8')
msg_encrypted_bundle = iv + ":" + salt + ":" + encrypted
msg_encrypted_bundle = msg_encrypted_bundle.replace("/" , "__")
return msg_encrypted_bundle
I was able to create a function and send the necessary information and get results.

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

AES Encryption Method Returning Different Encrypted Text Each Time

I have been working on a purely PROOF OF CONCEPT code stemming from the Diffie Hellman keypair exchange through SHA-256 encryption of those keys to then using that as a key for AES-256 encryption of text. I am only writing it for personal use. I know the prime numbers and secrets would need to be much longer..
The only code I have an issue with is the AES-256 encryption. It appears to work as it shows the encrypted text and is able to decrypt it but upon closer inspection, the encrypted text changes every time you run the program? I find this strange as the program is able to encrypt the given plain text, generate encrypted text and then decrypt this using the key given. However, this encrypted text changes every time the program is run using the same plaintext and same key.
Please see below the code...
from __future__ import print_function
import hashlib
from Crypto.Cipher import AES
from Crypto import Random
from base64 import b64encode, b64decode
# Variables
sharedPrime = 3721728827 # p
sharedBase = 2097383831 # g
p1Secret = 927391 # a
p2Secret = 193749 # b
# Begin
print( "\n--------------------------------------------\n" )
print( "Publicly Shared Variables:")
print( " Publicly Shared Prime (p): " , sharedPrime )
print( " Publicly Shared Base (g): " , sharedBase )
print( "\nPrivate Variables:")
print( " Private key a (a): " , p1Secret )
print( " Private key b (b): " , p2Secret )
print( "\n--------------------------------------------\n" )
# Person1 Sends Person2 A = g^a mod p
A = (sharedBase**p1Secret) % sharedPrime
print("Public Keys Sent Over Public Chanel: ")
print(" A = g^b mod p")
print(" Person1 Sends Over Public Chanel: " , A )
# Person2 Sends Person1 B = g^b mod p
B = (sharedBase ** p2Secret) % sharedPrime
print(" B = g^a mod p")
print(" Person2 Sends Over Public Chanel: ", B )
print( "\n--------------------------------------------\n" )
print( "Privately Calculated Shared Secret: " )
# P1 Computes Shared Secret: s = B^a mod p
p1SharedSecret = (B ** p1Secret) % sharedPrime
print( " s = B^a mod p")
print( " Person1 Shared Secret: ", p1SharedSecret )
# P2 Computes Shared Secret: s = A^b mod p
p2SharedSecret = (A**p2Secret) % sharedPrime
print( " s = A^b mod p")
print( " Person2 Shared Secret: ", p2SharedSecret )
print( "\n--------------------------------------------\n" )
# Converts DH secret to SHA256
print( "Converting shared key to SHA-256 key")
secretbyte = str(p1SharedSecret).encode()
print( " Shared secret to bytes: ", secretbyte)
shaV = hashlib.sha256(secretbyte).hexdigest()
print( " SHA-256 key: ", shaV)
key32 = shaV[0:32]
print( " SHA-256 key to AES-256 32bit secret key: ", key32)
print( "\n--------------------------------------------\n" )
#Begin AES-256 encryption
print( "Using SHA-256 32bit key for AES-256 encryption")
class AESCipher(object):
def __init__(self, key):
self.block_size = AES.block_size
self.key = hashlib.sha256(secretbyte).digest()
def encrypt(self, plain_text):
plain_text = self.__pad(plain_text)
iv = Random.new().read(self.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
encrypted_text = cipher.encrypt(plain_text.encode())
return b64encode(iv + encrypted_text).decode("utf-8")
def decrypt(self, encrypted_text):
encrypted_text = b64decode(encrypted_text)
iv = encrypted_text[:self.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
plain_text = cipher.decrypt(encrypted_text[self.block_size:]).decode("utf-8")
return self.__unpad(plain_text)
def __pad(self, plain_text):
number_of_bytes_to_pad = self.block_size - len(plain_text) % self.block_size
ascii_string = chr(number_of_bytes_to_pad)
padding_str = number_of_bytes_to_pad * ascii_string
padded_plain_text = plain_text + padding_str
return padded_plain_text
#staticmethod
def __unpad(plain_text):
last_character = plain_text[len(plain_text) - 1:]
return plain_text[:-ord(last_character)]
#Accept user input for encryption
message = input("Please enter message you want to be encrypted: ")
encMessage = AESCipher(key32)
encrypted = encMessage.encrypt(message)
print("Your encrypted message is: \n", encrypted)
print("Person 2 will now decrypt this message using the shared public key")
decrypted = encMessage.decrypt(encrypted)
print("Your decrypted message is: \n", decrypted)
The code produces the SHA-256 key of 72538667a2065257993b531746b9d92527cfe3caecc1457c4842e6a6caffe472 which I then truncate down to 32 characters to produce the AES-256 key of 72538667a2065257993b531746b9d925. Any plaintext I then enter will be encoded and decoded successfully, however the encrypted text changes each time even though the same key and plaintext are entered. Am I doing something wrong?
As long as your ciphertext can be decrypted successfully every time, you don't need to worry about the ciphertext being different.
Your code uses CBC chaining mode, which is common that a different Initialization Vector is generated each time you encrypt something, to avoid identical plaintext looking identical after encryption (which would partially defeat the point of encryption since an attacker now knows whether it's the same plaintext).
Image source: Block cipher mode of operation - Wikipedia (public domain)

Hash using a key in Powershell

I'm trying to create a hash in Powershell using a key but I seemed to be getting a different result compared when I'm doing it with Python.
Its a signature that I'm generating for an API call.
The Python code I use is as follows:
nonce = "20"
client_id = "mdfgfgkjl3456"
api_key = "asdkjasdkljsomekey"
message = nonce + client_id + api_key
secret = "dsdklfjsdfkljsomesecret"
secret_bytes = bytes(secret , 'latin-1')
message_bytes = bytes(message , 'latin-1')
signature = hmac.new(
secret_bytes,
msg=message_bytes,
digestmod=hashlib.sha256
).hexdigest().upper()
The Powershell Code that I have is as follows:
$nonce = "20"
$clientid = "mdfgfgkjl3456"
$apikey = "asdkjasdkljsomekey"
$message = $nonce + $clientid + $apikey
$secret = "dsdklfjsdfkljsomesecret"
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Text.Encoding]::ASCII.GetBytes($secret)
$signature =
$hmacsha.ComputeHash([Text.Encoding]::ASCII.GetBytes($message))
$signature = ([Convert]::ToBase64String($signature)).ToUpper()
$signature
The Powershell runs but it produces a different Signature then the Python code.
In the PowerShell version you perform an additional operation - converting the byte array to a Base64 string - before converting it to upper case:
$signature = ([Convert]::ToBase64String($signature)).ToUpper()
Python on the other hand converts the array to a hexadecimal string.
Change the powershell version to:
$nonce = "20"
$clientid = "mdfgfgkjl3456"
$apikey = "asdkjasdkljsomekey"
$message = $nonce + $clientid + $apikey
$secret = "dsdklfjsdfkljsomesecret"
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Text.Encoding]::ASCII.GetBytes($secret)
$signature = $hmacsha.ComputeHash([Text.Encoding]::ASCII.GetBytes($message))
$signature = -join($signature |ForEach-Object ToString X2).ToUpper()
$signature
-join($signature |ForEach-Object ToString X2).ToUpper() will produce the exact same format as .hexdigest().upper()

Generating bitcoin key pair in python 3.6 from public key to public address

I have a question about a script I am trying to write that generates a key pair for Bitcoin adreses. I came as far as generating a random private key and generate a public key. I know (or think really much?) that my first part of code is correct. When I go to bitaddress.org and check my generated private keys for details, I always get the correct generated public key.
This is what I have now
import os
import ecdsa
import binascii
private_key = binascii.hexlify(os.urandom(32)).decode()
print("private key = " + private_key)
Private_key = bytes.fromhex(private_key)
signing_key = ecdsa.SigningKey.from_string(Private_key, curve = ecdsa.SECP256k1)
verifying_key = signing_key.get_verifying_key()
public_key = bytes.fromhex("04") + verifying_key.to_string()
print ("public key = " + public_key.hex())
The problem is that for now Im getting the 130 characters public key and I want to transform this to a bitcoin address. I don't understand how to do this. I need to do some encoding/decoding but can't wrap my head around it.
This is the explanation from the internet I found but fail to understand:
Bitcoin address explanation png
Could someone maybe help me with this
This code runs both in Python 2 and Python 3.
It prints not only the bitcoin address, but also some intermediate values.
The public key is the 130 hex char string in pubkey variable.
Please note that there are two possible and valid bitcoin addresses for each public key: the uncompressed and the compressed form. Change compress_key boolean variable to extract each one.
#!/usr/bin/env python
# https://en.bitcoin.it/wiki/Protocol_documentation#Addresses
import hashlib
import base58
# ECDSA bitcoin Public Key
pubkey = '0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6'
# See 'compressed form' at https://en.bitcoin.it/wiki/Protocol_documentation#Signatures
compress_pubkey = False
def hash160(hex_str):
sha = hashlib.sha256()
rip = hashlib.new('ripemd160')
sha.update(hex_str)
rip.update( sha.digest() )
print ( "key_hash = \t" + rip.hexdigest() )
return rip.hexdigest() # .hexdigest() is hex ASCII
if (compress_pubkey):
if (ord(bytearray.fromhex(pubkey[-2:])) % 2 == 0):
pubkey_compressed = '02'
else:
pubkey_compressed = '03'
pubkey_compressed += pubkey[2:66]
hex_str = bytearray.fromhex(pubkey_compressed)
else:
hex_str = bytearray.fromhex(pubkey)
# Obtain key:
key_hash = '00' + hash160(hex_str)
# Obtain signature:
sha = hashlib.sha256()
sha.update( bytearray.fromhex(key_hash) )
checksum = sha.digest()
sha = hashlib.sha256()
sha.update(checksum)
checksum = sha.hexdigest()[0:8]
print ( "checksum = \t" + sha.hexdigest() )
print ( "key_hash + checksum = \t" + key_hash + ' ' + checksum )
print ( "bitcoin address = \t" + base58.b58encode( bytes(bytearray.fromhex(key_hash + checksum)) ) )

Setting Variables in Module from different program

I am struggling with converting this piece of code into a module in where I can use an outside program to set the variables in this piece of code. How would I go about setting variables in one program and set them in another(to be used as a module) and get the results from said program into the first program.
Here is the code, any advice/help would be greatly appreciated.
import Crypto.Random
from Crypto.Cipher import AES
import hashlib
SALT_SIZE = 16
iterations = 64000
salt = 'h5eE0b814M'
password = 'fortytwo'
text = 'What do you mean'
padded_text = ''
ciphertext = ''
key = ''
ciphertext_with_salt = ''
def key_generation(password, salt, iterations):
global key
assert iterations > 0
key = password + salt
for i in range(iterations):
key = hashlib.sha256(key).digest()
print '\nKey: ' + key #Debug Print
return key
def pad_text(text, SALT_SIZE):
print '\nUnpadded Text: ' + text #Debug Print
global padded_text
extra_bytes = len(text) % SALT_SIZE
pad_size = SALT_SIZE - extra_bytes
pad = chr(pad_size) * pad_size
padded_text = text + pad
print '\nPadded Text: ' + padded_text #Debug Print
return padded_text
def encryption(text, password):
global ciphertext
global key
salt1 = Crypto.Random.get_random_bytes(SALT_SIZE)
cipher = AES.new(key, AES.MODE_ECB)
padded_plaintext = pad_text(text, SALT_SIZE)
ciphertext = cipher.encrypt(padded_text)
ciphertext_with_salt = salt + ciphertext
#debug script
print '\nSalt: ' + salt #Debug Print
print '\nEncrypted Text: ' + ciphertext #Debug Print
print '\nEncrypted Text with Salt: ' + ciphertext_with_salt #Debug Print
return ciphertext_with_salt
def decryption(ciphertext, password):
salt = ciphertext[0:SALT_SIZE]
ciphertext_sans_salt = ciphertext[SALT_SIZE:]
key = key_generation(password, salt, iterations)
cipher = AES.new(key, AES.MODE_ECB)
padded_plaintext = cipher.decrypt(ciphertext_sans_salt)
print '\nUnencrypted Text: ' + text #Debug Print
return text
key_generation(password, salt, iterations)
encryption(text, password)
decryption(ciphertext, password)
Simply put
if __name__ == '__main__':
Before your last 3 lines. Then you can import it like any other module and call functions with e.g. yourmodulename.encryption(text, password).

Categories