AES encrypt in python decrypt in php - python

I am asking this because I don't really know php but must to somehow manage with.
I have encrypted data in python and need to decrypt in php (serversite).
python encryption:
import hashlib
from base64 import b64encode, b64decode, urlsafe_b64encode, urlsafe_b64decode
from Crypto.Cipher import AES
text = "secret"
secret_key = 'This is my secret key'
secret_iv = 'This is my secret iv'
key = hashlib.sha256(secret_key.encode('utf-8')).hexdigest()[:32].encode("utf-8")
iv = hashlib.sha256(secret_iv.encode('utf-8')).hexdigest()[:16].encode("utf-8")
_pad = lambda s: s + (AES.block_size - len(s) % AES.block_size) * chr(AES.block_size - len(s) % AES.block_size)
txt = _pad(text)
cipher = AES.new(key, AES.MODE_CBC, iv)
output = urlsafe_b64encode(cipher.encrypt(str.encode(txt))).rstrip(b'=')
this gives 'rtVabOaDdf528T63xOhhww' output, which is correctly AES encrypted.
and php which encrypts and decrypts in other way:
<?php
$string="secret";
class CryptService{
private static $encryptMethod = 'AES-256-CBC';
private $key;
private $iv;
public function __construct(){
echo '<br>: '.$this->key = substr(hash('sha256', 'This is my secret key'), 0, 32);
echo '<br>: '.$this->iv = substr(hash('sha256', 'This is my secret iv'), 0, 16).'<br>';
}
public function decrypt($string){
// $string = strtr($data, '-_', '+/');
$string = base64_decode($string);
return openssl_decrypt($string, self::$encryptMethod, $this->key, 0, $this->iv);
}
public function encrypt($string){
$output = openssl_encrypt($string, self::$encryptMethod, $this->key, 0, $this->iv);
$output = base64_encode($output);
return $output;
}
}
$a = new CryptService;
echo $ok=$a->encrypt('secret');
echo "\n";
echo 'TEST: '.$a->decrypt($string);
echo 'BACK ok: '.$a->decrypt($ok);
echo "\n\n";
There is some issue with openssl_decrypt() function because of "iv". Can someone help me figure this out...

You're assigning additional 4 characters <br> to your $this->iv. This will fix it:
echo '<br>: ' . ($this->iv = substr(hash('sha256', 'This is my secret iv'), 0, 16)) . '<br>';
Basically, your . '<br>' is concatenating the <br> to your substr(). I added () around the variable value assignment. Now it works
cnRWYWJPYURkZjUyOFQ2M3hPaGh3dz09 TEST: BACK ok: secret
I am not an expert on encryption, but... I think there's something in your code that doesn't quite belong in there. When I remove these two lines:
$string = base64_decode($string);
$output = base64_encode($output);
I get this output:
rtVabOaDdf528T63xOhhww==
Which, after a rtrim($ok, '=');, would give you
rtVabOaDdf528T63xOhhww

Related

Implementing PKCS7 padding in python for a C# script

I have this encrypt with AES function in python:
def encrypt_text(self):
raw = self.PKCS7_padding()
iv = Random.new().read(self.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
encrypted = base64.b64encode(iv + cipher.encrypt(raw))
return encrypted
And then padding it with:
def PKCS7_padding(self):
return self.text+chr(16-len(self.text)%16)*(16-len(self.text)%16)
But when I send it to this c# function
public static string DecryptString(string key, string cipherText)
{
byte[] iv = new byte[16];
byte[] buffer = Convert.FromBase64String(cipherText);
using (Aes aes = Aes.Create())
{
aes.Key = Convert.FromBase64String(key);
aes.IV = iv;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream(buffer))
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader((Stream)cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
Say i send "hello world"
I get a weird mix of the string i sent plus random non pritable characters. EX: "�Ȯ�Ŗf�"��Xhello world"
The key is generated as follows:
key = base64.b64encode(os.urandom(32))
return key.decode()
I do not get any errors but weird text any ideas Thanks! If you have any questions please feel free to ask
In the Python code, a random IV is generated and concatenated with the ciphertext. However, in the C# code, the separation of IV and ciphertext is missing. Instead, a zero IV is applied and the concatenation of IV and ciphertext is decrypted, which causes the problem.
The separation of IV and ciphertext can be added to the C# code e.g. as follows:
byte[] buffer = ...
byte[] ct = new byte[buffer.Length - iv.Length];
Buffer.BlockCopy(buffer, 0, iv, 0, iv.Length);
Buffer.BlockCopy(buffer, iv.Length, ct, 0, ct.Length);
buffer = ct;
using ...
Note that PyCryptodome supports padding in a dedicated module: Crypto.Util.Padding.
Also, in the Python code the Base64 encoded key is nowhere Base64 decoded (but maybe the corresponding code was just not posted).

Decrypting AES CBC data from PyCrypto with Powershell

I am working on a project where I will be encrypting a string of data using the AES module from PyCrypto and then decrypting it using Powershell.
I've written a simple encryption function to do what I need here:
import base64
from Crypto import Random
from Crypto.Cipher import AES
key = "SuperSecret" #Insecure and just for testing
plaintext = "Secret message please don't look"
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
def padKey(s): #Pad key to 32 bytes for AES256
return (s * (int(32/len(s))+1))[:32]
class AESCipher:
def __init__(self, key):
self.key = key
def encrypt(self, raw):
raw = 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 ) )
paddedKey = padKey(key)
cipher = AESCipher(paddedKey)
encrypted = str(cipher.encrypt(plaintext))
encrypted = encrypted[2:-1]
print("Key:", paddedKey)
print("Plaintext:",plaintext)
print("Encrypted and B64:",encrypted)
I am having some issues with decrypting and decoding the output with Powershell and could use some help. I was able to find a simple decryption script that I've been working with online, but the output is all garbage:
function Create-AesManagedObject($key, $IV) {
$aesManaged = New-Object "System.Security.Cryptography.AesManaged"
$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
$aesManaged.BlockSize = 128
$aesManaged.KeySize = 256
if ($IV) {
if ($IV.getType().Name -eq "String") {
$aesManaged.IV = [System.Convert]::FromBase64String($IV)
}
else {
$aesManaged.IV = $IV
}
}
if ($key) {
if ($key.getType().Name -eq "String") {
$aesManaged.Key = [System.Convert]::FromBase64String($key)
}
else {
$aesManaged.Key = $key
}
}
$aesManaged
}
function Decrypt-String($key, $encryptedStringWithIV) {
$bytes = [System.Convert]::FromBase64String($encryptedStringWithIV)
$IV = $bytes[0..15]
$aesManaged = Create-AesManagedObject $key $IV
$decryptor = $aesManaged.CreateDecryptor();
$unencryptedData = $decryptor.TransformFinalBlock($bytes, 16, $bytes.Length - 16);
$aesManaged.Dispose()
[System.Text.Encoding]::UTF8.GetString($unencryptedData).Trim([char]0)
}
Sample output:
PS C:\> Decrypt-String 'SuperSecretSuperSecretSuperSecre' $encryptedString
���H�'G zM۞� �i�ZtCI���H~N�GG��A�Pc��aF��`)��GS�N�2{�[.
Related: Using PowerShell to decrypt a Python encrypted String
Closing this out with the solution (Thanks #Maarten and #t.m.adam). The issue was twofold. First, the key needs to be passed to Powershell in Base64 format, and the padding needed to me moved to PKCS7. The final code is as follows:
Python Encryption:
import base64
from Crypto import Random
from Crypto.Cipher import AES
key = "SuperSecret" #Insecure and just for testing
plaintext = "Secret message please don't look"
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
def padKey(s): #Pad key to 32 bytes for AES256
return (s * (int(32/len(s))+1))[:32]
class AESCipher:
def __init__(self, key):
self.key = key
def encrypt(self, raw):
raw = 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 ) )
paddedKey = padKey(key)
cipher = AESCipher(paddedKey)
encrypted = str(cipher.encrypt(plaintext))
encrypted = encrypted[2:-1]
print("Key:", base64.b64encode(paddedKey))
print("Plaintext:",plaintext)
print("Encrypted and B64:",encrypted)
Powershell Decryption:
function Create-AesManagedObject($key, $IV) {
$aesManaged = New-Object "System.Security.Cryptography.AesManaged"
$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7
$aesManaged.BlockSize = 128
$aesManaged.KeySize = 256
if ($IV) {
if ($IV.getType().Name -eq "String") {
$aesManaged.IV = [System.Convert]::FromBase64String($IV)
}
else {
$aesManaged.IV = $IV
}
}
if ($key) {
if ($key.getType().Name -eq "String") {
$aesManaged.Key = [System.Convert]::FromBase64String($key)
}
else {
$aesManaged.Key = $key
}
}
$aesManaged
}
function Decrypt-String($key, $encryptedStringWithIV) {
$bytes = [System.Convert]::FromBase64String($encryptedStringWithIV)
$IV = $bytes[0..15]
$aesManaged = Create-AesManagedObject $key $IV
$decryptor = $aesManaged.CreateDecryptor();
$unencryptedData = $decryptor.TransformFinalBlock($bytes, 16, $bytes.Length - 16);
$aesManaged.Dispose()
[System.Text.Encoding]::UTF8.GetString($unencryptedData).Trim([char]0)
}
Usage:
PS C:> $key = 'U3VwZXJTZWNyZXRTdXBlclNlY3JldFN1cGVyU2VjcmU='
PS C:> $encryptedString = 'Opgtr8XEcvkcYT5UzsFjZR4Wt5DI++fU4Gm0dTM/22m+xyObjP162rFphIS/xkS4I7ErJfshwI7T4X1MNz
wMog=='
PS C:> Decrypt-String $key $encryptedString
Secret message please don't look
In the Python code, the following is entirely unnecessary and should be removed:
encrypted = str(cipher.encrypt(plaintext))
encrypted = encrypted[2:-1]
In your PowerShell code, you need to use PKCS7 instead of Zeros for the padding.
In the PowerShell code you don't implement the key padding with zeros anywhere. This is required (I'm not sure how you get this to work at all without it).

Node Crypto aes-128-ecb encrypt doesnt get me right results

Following program in python:
from Crypto.Cipher import AES
key = '11223344556677889900aabbccddeeff'.decode("hex")
aesECB = AES.new(key, AES.MODE_ECB)
ciphertext = aesECB.encrypt('1234567890abcdef')
print ciphertext.encode('base64')
Gets me this result:
$ python example_cipher.py
r9yD3EmmAIpxncxZSldsKg==
Following command line from openssl, gets me the same result:
$ echo -n "1234567890abcdef" | openssl aes-128-ecb -K 11223344556677889900aabbccddeeff -nopad | openssl base64
r9yD3EmmAIpxncxZSldsKg==
But this code in Node:
var crypto = require('crypto');
var key = new Buffer('11223344556677889900aabbccddeeff', 'hex');
var plaintext = new Buffer('1234567890abcdef', 'utf8');
var cipher = crypto.createCipher("aes-128-ecb", key);
cipher.setAutoPadding(false);
var ciphertext = cipher.update(plaintext, 'utf8');
console.log(ciphertext.toString('base64'));
Doesn'g gets me the same result:
$ node cipher
tOunZRvle8B6HYuBSzblqw==
Where is the mistake?
First off, DO NOT use ECB if you are in control of the crypto parameters. It is incredibly insecure and should not be used to encrypt data.
Now for ECB, which technically does not use an IV, you still need to use crypto.createCipheriv() with a zero-length IV Buffer to match the output of Python:
var cipher = crypto.createCipheriv("aes-128-ecb", key, Buffer.alloc(0));
Also (in general), you are missing cipher.final() to include any leftover data, so use this instead:
var ciphertext = Buffer.concat([
cipher.update(plaintext, 'utf8'),
cipher.final()
]);

Implement new SHA256 signature for Redsys/Sermepa in Python

I have a django-based application that uses Redsys (earlier Sermepa) as a credit card payment platform. Recently we have received a message that urge us to migrate from old SHA1 signatures to SHA256 in order to keep using the platform securely.
Platform provide several examples of how to make the new requests generating a new signature, but only using PHP and Java. Now I am trying to port the provided solutions to Python without success.
This is a sample working code in PHP (with fake key/data):
<?php
// Init form with default data
$order = "ABCDEFGHI";
$merchant_parameters = array(
"DS_MERCHANT_AMOUNT" => "1000",
"DS_MERCHANT_ORDER" => strval($order) ,
"DS_MERCHANT_MERCHANTCODE" => "012345678",
"DS_MERCHANT_CURRENCY" => "978",
"DS_MERCHANT_TRANSACTIONTYPE" => "0",
"DS_MERCHANT_TERMINAL" => "1",
);
// Key definition, decoding, and encrypt using $order
$key = 'Bg8HYhVT79PDvOxbI/Newcm31QY/9999';
echo 'key --> ' , $key , '<br/><br/>';
$key_decoded = base64_decode($key);
echo 'key-decoded --> ' , $key_decoded , '<br/><br/>';
$ciphertext = mcrypt_encrypt(MCRYPT_3DES, $key_decoded, $order, MCRYPT_MODE_CBC, implode(array_map("chr", array(0,0,0,0,0,0,0,0))) );
echo 'ciphertext --> ' , $ciphertext , '<br/><br/>';
// Transform array in encoded json
$parameters = json_encode($merchant_parameters);
echo 'parameters --> ' , $parameters , '<br/><br/>';
$parameters_encoded = base64_encode($parameters);
echo 'parameters_encoded --> ' , $parameters_encoded , '<br/><br/>';
// Calculate MAC256 of encoded array
$mac_256 = hash_hmac('sha256', $parameters_encoded, $ciphertext, true);
echo 'mac_256 --> ' , $mac_256 , '<br/><br/>';
// Encode MAC256 in base64 to get the signature
$mac_256_encoded = base64_encode($mac_256);
echo 'mac_256_encoded --> ' , $mac_256_encoded , '<br/><br/>';
?>
And this is my equivalent code in Python, with the same fake data:
# -*- encoding: utf-8 -*-
from pyDes import triple_des, CBC, PAD_PKCS5
import hashlib, json, base64, hmac
from json import JSONEncoder
# Init form with default data
order = "ABCDEFGHI"
merchant_parameters = {}
merchant_parameters['DS_MERCHANT_AMOUNT'] = "1000"
merchant_parameters['DS_MERCHANT_ORDER'] = order
merchant_parameters['DS_MERCHANT_MERCHANTCODE'] = "012345678"
merchant_parameters['DS_MERCHANT_CURRENCY'] = "978"
merchant_parameters['DS_MERCHANT_TRANSACTIONTYPE'] = "0"
merchant_parameters['DS_MERCHANT_TERMINAL'] = "1"
# Key definition, decoding, and encrypt using order
key = 'Bg8HYhVT79PDvOxbI/Newcm31QY/9999'
print '\n\nkey --> %s' % key
key_decoded = base64.b64decode(key)
print '\n\nkey_decoded --> %s' % key_decoded
k = triple_des(key_decoded, CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
ciphertext = k.encrypt(order)
print '\n\nciphertext --> %s' % ciphertext
# Transform array in encoded json (with no lines
parameters = (json.dumps(merchant_parameters)).encode()
print '\n\nparameters --> %s' % parameters
parameters_encoded = ''.join(unicode(base64.encodestring(parameters), 'utf-8').splitlines())
print '\n\nparameters_encoded --> %s' % parameters_encoded
# Calculate MAC256 of encoded array
mac_256 = hmac.new(ciphertext, parameters_encoded, digestmod=hashlib.sha256).hexdigest()
print '\n\nmac_256 --> %s' % mac_256
# Encode MAC256 in base64 to get the signature
mac_256_encoded = base64.b64encode(mac_256)
print '\n\nmac_256_encoded --> %s' % mac_256_encoded
As can be seen if running the codes, resultant signatures are not equivalent, thus the payment platform rejects my Python request.
Can anybody notice where am I wrong regarding the signature generation?
Thanks in advance,
Álvaro.
Got the same problem.
Actually, after JSON encoding I have had to clean the blank spaces between key/value pairs up.
But I am getting different signatures for the ciphertext. That causes that final signature is different from the examples.
Notice that in example code they do a trailing pad for order number in the 3DES encryption.
iv = b'\0\0\0\0\0\0\0\0'
k = DES3.new(key, DES3.MODE_CBC, iv)
ceros = b'\0'*(len(DS_MERCHANT_ORDER)%8)
clave = k.encrypt(DS_MERCHANT_ORDER+ceros.encode('UTF-8'))
Thanks
EDITED:
Finally I fixed it:
To get the specific per operation key:
iv = b'\0\0\0\0\0\0\0\0'
k = DES3.new(key, DES3.MODE_CBC, iv)
ceros = b'\0'*(len(DS_MERCHANT_ORDER)%8)
claveOp = k.encrypt(DS_MERCHANT_ORDER+ceros.encode('UTF-8'))
To get the hmac.sha256:
from Crypto.Hash import HMAC, SHA256
# Realizo la codificacion SHA256
dig = HMAC.new(claveOp , msg=DS_MERCHANT_PARAMETERS, digestmod=SHA256).digest()
signatureEnc = base64.b64encode(dig)
Hope it helps you
Hie,
finally I publish in pypi new version about redsys client. https://pypi.python.org/pypi/redsys/
Thanks Álvaro about to do sha256 signature.

Python AES encryption(PyCrypto) -> AS3 decryption (as3Crypto) using AES

I've a django app which serves encrypted media files to Flash apps. Encryption in python is done with PyCrypto as follows (I include description too in case useful):
def encrypt_aes(key, text):
try:
raw = _pad(text)
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw))
except Exception as e:
print e.message
return text
def decrypt_aes(key, text):
try:
enc = base64.b64decode(text)
iv = enc[:AES.block_size]
cipher = AES.new(key, AES.MODE_CBC, iv)
return _unpad(cipher.decrypt(enc[AES.block_size:]))
except Exception as e:
print e.message
return text
def _pad(s):
bs = 16
return s + (bs - len(s) % bs) * chr(bs - len(s) % bs)
def _unpad(s):
return s[:-ord(s[len(s) - 1:])]
I cannot yet decrypt the Python provided media files (downloaded with LoaderMax by GreenSock, using 'DataLoader'). My AS3 code (using AS3Crypto) is as follows:
public static function decipher(_key:String):void{
key=_key;
cipher = Crypto.getCipher("simple-aes-cbc", Hex.toArray(key));
cipher.decrypt(ByteArray(_content));
}
I get
RangeError: Error #2006
One suspicion is that in Python I have 64bit base but I think that AS3 ByteArray is 32bit base. I have tried the below, but get the same Error.
cipher.decrypt(ByteArray(com.hurlant.util.Base64.decodeToByteArray(_content)));
Another suspicion is that I have not appropriately removed 'padding' from the _content / set up my IV appropriately (which is specified by the padding I must remove from the _content). This should be done via that "simple" statement however. I have been trying this, but with no success:
var pad:IPad = new PKCS5
cipher = Crypto.getCipher("simple-aes", Hex.toArray(key),pad);
pad.setBlockSize(cipher.getBlockSize());
Could anyone advise on how I can fix this ? :)
Many thanks!
OK finally figured out what was going wrong. Besides some AS3 tweaks, we wrongly were transmitting files as MP3/image (should have been text/html).
Our Python remains as above. Our AS3 is tweaked to the below.
Here's the AS3 class we used:
package com.xperiment.preloader
{
import com.greensock.loading.DataLoader;
import com.hurlant.crypto.Crypto;
import com.hurlant.crypto.symmetric.ICipher;
import com.hurlant.util.Base64;
import flash.events.Event;
import flash.utils.ByteArray;
public class EncryptedDataLoader extends DataLoader
{
private static var backlog:Vector.<EncryptedDataLoader>;
private static var cipher:ICipher;
private var decrypted:Boolean = true;
public function EncryptedDataLoader(urlOrRequest:*, vars:Object=null)
{
this.addEventListener(Event.COMPLETE,decryptL);
super(urlOrRequest, vars);
}
public function decryptL(e:Event):void {
trace("start decrypt");
e.stopImmediatePropagation();
this.removeEventListener(Event.COMPLETE,decryptL);
backlog ||= new Vector.<EncryptedDataLoader>;
backlog.push(this);
if(cipher) pingBacklog();
}
public function decipher():void
{
_content = Base64.decodeToByteArray( _content );
cipher.decrypt( _content );
decrypted=true;
this.dispatchEvent(new Event(Event.COMPLETE));
}
public static function setCipher(_key:String):void{
var keyBA:ByteArray = new ByteArray;
keyBA.writeMultiByte(_key, "iso-8859-1");
cipher = Crypto.getCipher("simple-aes", keyBA);
pingBacklog();
}
public static function kill():void{
cipher.dispose();
cipher = null;
}
public static function pingBacklog():void{
if(backlog){
var encrypted:EncryptedDataLoader;
while(backlog.length>0){
encrypted=backlog.shift();
encrypted.decipher();
}
backlog=null;
}
}
}
}

Categories