AES Encryption in PHP and Decryption in Javascript - javascript

I have an application in which I am encrypting a json encoded array using AES CBC 128 algorithm and then Decrypting it in javascript(React/Next Js Project). My Encryption in php is as shown in the below code
ENCRYPTION PHP
$plaintext = "message to be encrypted";
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
$ciphertext = base64_encode( $iv.$hmac.$ciphertext_raw );
I am facing problems in decryption of this in Javascript
my code so far is as shown below
const baseenc = CryptoJS.enc.Base64.parse(cipher).toString();
var encrypted = CryptoJS.AES.decrypt(cipher, key, { iv: iv }).toString();
var plaintext = CryptoJS.enc.Latin1.stringify(encrypted);
Can any body please show what is the error or help me in getting the correct output

The following steps must be implemented in the CryptoJS code:
Separate IV, HMAC and ciphertext (after Base64 decoding)
Calculate the HMAC for the ciphertext
Check the authenticity of the ciphertext. The ciphertext is authentic if the received and calculated HMAC are identical.
Perform decryption, only if the ciphertext is authentic
The following code is a possible implementation. As key 0123456789012345 was applied and with the PHP code the used ciphertext was generated:
var ciphertext = 'WqfMfCxKg7U7h5S1mbx7mSHOkkkIrUUpg++mX4ZdWt0I26VfKn7bsi60Oo/SIsWQGyC4dF5z081NvjTXwZGjIpguA0k/QqIM/GDEpCojaro=';
var key = '0123456789012345';
// Convert key and ciphertext into WordArrays
var ciphertextWA = CryptoJS.enc.Base64.parse(ciphertext);
var keyWA = CryptoJS.enc.Utf8.parse(key);
// Separate IV, HMAC and ciphertext
var ivWA = CryptoJS.lib.WordArray.create(ciphertextWA.words.slice(0, 4));
var hmacWA = CryptoJS.lib.WordArray.create(ciphertextWA.words.slice(4, 4 + 8));
var actualCiphertextWA = CryptoJS.lib.WordArray.create(ciphertextWA.words.slice(4 + 8));
// Authenticate
var hmacCalculatedWA = CryptoJS.HmacSHA256(actualCiphertextWA, keyWA);
if(CryptoJS.enc.Base64.stringify(hmacCalculatedWA) === CryptoJS.enc.Base64.stringify(hmacWA)) {
// Decrypt if authentication is successfull
var decryptedMessageWA = CryptoJS.AES.decrypt({ciphertext: actualCiphertextWA}, keyWA, {iv: ivWA});
var decryptedMessage = CryptoJS.enc.Utf8.stringify(decryptedMessageWA);
console.log(decryptedMessage);
} else {
console.log('Authentication failed!');
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
Please note that it is better to use different keys for encryption and authentication, see here.

Related

Decrypt AES in JavaScript

I am encrypting a text with AES256 in swift language and outputting it as hex. I want to decrypt this code I received with JS, but I could not reach the result. I tried the CryptoJS library but still couldn't get the result I wanted. All I want is the js code that will give me the decoded version when I enter the IV, password and ciphertext.
const crypto = require("crypto");
var key = "";
const iv = "";
const token = "";
function decrypt(token, iv, key) {
const decrypter = crypto.createDecipheriv("aes-256-cbc", key, iv);
let decrypted = decrypter.update(token, "hex", "utf8");
decrypted += decrypter.final("utf8");
return decrypted
}
console.log(decrypt(token, iv, key));
With the Node.js code above, I achieve what I want, but I want to do it with normal JS code, not using node. I don't want to mess with the server. I would be very happy if you help.
EDIT:
I am using CryptoSwift library in Swift language.
func encryption(uuid: String, token: String) -> String {
do {
let aes = try AES(key: String(uuid.prefix(32)), iv: String(uuid.prefix(16)))
let ciphertext = try aes.encrypt(Array(token.utf8))
let encrypttext = ciphertext.toHexString()
return encrypttext
}
catch {
return "error"
}
}
I tried to do something with CryptoJS with the codes from the site below, but it didn't work like the codes in Node.js.
https://embed.plnkr.co/0VPU1zmmWC5wmTKPKnhg/
EDIT2:
I've been trying different things but couldn't quite figure it out. I get an error when I add PBKDF2. I don't fully understand the problem.
var password = "6268890F-9B58-484C-8CDC-34F9C6A9";
var iv = "6268890F-9B58-48";
var cipher = "79a247e48ac27ed33ca3f1919067fa64";
/*
var key = CryptoJS.PBKDF2(password, {
keySize: 32
});
*/
var dec= CryptoJS.enc.Hex.parse(cipher);
const decrypted = CryptoJS.AES.decrypt({
ciphertext: dec
},
password, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
console.log(decrypted.toString(CryptoJS.enc.Utf8));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/pbkdf2.js"></script>
CryptoJS uses WordArrays, so that key, IV and ciphertext have to be converted accordingly. For this purpose the appropriate encoders have to be applied. Furthermore decrypt() expects the ciphertext as CipherParams object.
This results in the following possible CryptoJS implementation:
var ciphertext = "79a247e48ac27ed33ca3f1919067fa64";
var key = "6268890F-9B58-484C-8CDC-34F9C6A9";
var iv = "6268890F-9B58-48";
var ciphertextWA = CryptoJS.enc.Hex.parse(ciphertext);
var keyWA = CryptoJS.enc.Utf8.parse(key);
var ivWA = CryptoJS.enc.Utf8.parse(iv);
var ciphertextCP = { ciphertext: ciphertextWA };
var decrypted = CryptoJS.AES.decrypt(
ciphertextCP,
keyWA,
{ iv: ivWA }
);
console.log(decrypted.toString(CryptoJS.enc.Utf8)); // Apple
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
which is functionally identical to the posted NodeJS code that also successfully decrypts the test data.
Regarding the question asked in the comment about the encodings:
In general, the decryption side must have knowledge of the encodings used for encryption. However, in this case the encodings can be derived from the posted NodeJS code:
For decryption, the input encoding of the ciphertext is specified as 'hex', see decipher.update().
key and iv are defined as strings which are UTF-8 encoded, see crypto.createDecipheriv().
Also, the data used is consistent with these conclusions.
Note that for security reasons a static IV may not be used. Instead, a random IV must be generated for each encryption.
Also, no password may be applied as key, even if it has the right length. If a password is to be used, a key derivation is necessary, e.g. with PBKDF2.
For test purposes, the data is of course enough.

CryptoJS and openssl_decrypt not produce same result

I am trying to implement AES 256 bit encryption with string on php and JavaScript. For jasvascript I a musing CryptoJS and php I use openssl_decrypt/enecrypt.
Below is the code in JS for encryption and decryption.
JavaScript
function aes_encrypt(str_to_encrypt){
if(str_to_encrypt==null)
return "";
var key = CryptoJS.enc.Hex.parse("0123456789abcdef0123456789abcdef");
var iv = CryptoJS.enc.Hex.parse("abcdef9876543210abcdef9876543210");
var encrypted = CryptoJS.AES.encrypt(str_to_encrypt,key, {'mode': CryptoJS.mode.CBC, iv: iv});
var encryptedString = encrypted.toString();
return encryptedString;
}
function aes_decrypt(str_to_decrypt){
if(str_to_decrypt==null)
return "";
var key = CryptoJS.enc.Hex.parse("0123456789abcdef0123456789abcdef");
var iv = CryptoJS.enc.Hex.parse("abcdef9876543210abcdef9876543210");
var decrypted = CryptoJS.AES.decrypt(str_to_decrypt,key, {'mode': CryptoJS.mode.CBC, iv: iv });
var decryptedString = decrypted.toString(CryptoJS.enc.Utf8);
return decryptedString;
}
And in php the code is
PHP
class Crypto_AES256
{
public $key = "0123456789abcdef0123456789abcdef";
public $iv = "abcdef9876543210abcdef9876543210";
public $encrypt_method = 'AES-256-CBC';
function __construct ()
{
$this->key = hex2bin($this->key);
$this->iv = hex2bin($this->iv);
}
public function encrypt ( $string )
{
if ( $encrypted = base64_encode( openssl_encrypt ( $string, $this->encrypt_method, $this->key, 0, $this->iv ) ) )
{
return $encrypted;
}
else
{
return false;
}
}
public function decrypt ($string)
{
if ( $decrypted = openssl_decrypt ( base64_decode ( $string ), $this->encrypt_method, $this->key, 0, $this->iv ) )
{
return $decrypted;
}
else
{
return false;
}
}
}
But the result of encryption at JavaScript side is not same as php, I need to produce same encrypted and encrypted result at both JavaScript and php. What could be the problem.
Both codes differ in two ways:
The PHP code applies AES-256, but since only a 16 bytes key is used (because of the hex decoding), PHP automatically pads it with 0 values to a length of 32 bytes. In the CryptoJS code, the key length determines the mode, thus AES-128 is applied. So that both codes produce the same result, the key must be extended in the CryptoJS code analogously to the PHP code, or AES-128 must be used in the PHP code.
In the PHP code, openssl_encrypt() returns the ciphertext Base64 encoded by default, so the ciphertext is currently Base64 encoded twice. Therefore remove the explicit base64_encode() or use OPENSSL_RAW_DATA as the 4th parameter so that the raw data is returned. Similarly for openssl_decrypt().
When these issues are fixed, both codes provide the same ciphertext on my machine. Note that a static IV is insecure (see also the comment), but you probably only do this for testing purposes.
Example: The following code uses your unmodified CryptoJS code, i.e. AES-128:
function aes_encrypt(str_to_encrypt){
if(str_to_encrypt==null)
return "";
var key = CryptoJS.enc.Hex.parse("0123456789abcdef0123456789abcdef");
var iv = CryptoJS.enc.Hex.parse("abcdef9876543210abcdef9876543210");
var encrypted = CryptoJS.AES.encrypt(str_to_encrypt,key, {'mode': CryptoJS.mode.CBC, iv: iv});
var encryptedString = encrypted.toString();
return encryptedString;
}
function aes_decrypt(str_to_decrypt){
if(str_to_decrypt==null)
return "";
var key = CryptoJS.enc.Hex.parse("0123456789abcdef0123456789abcdef");
var iv = CryptoJS.enc.Hex.parse("abcdef9876543210abcdef9876543210");
var decrypted = CryptoJS.AES.decrypt(str_to_decrypt,key, {'mode': CryptoJS.mode.CBC, iv: iv });
var decryptedString = decrypted.toString(CryptoJS.enc.Utf8);
return decryptedString;
}
var ciphertext = aes_encrypt('The quick brown fox jumps over the lazy dog');
var decrypted = aes_decrypt(ciphertext);
console.log(ciphertext.replace(/(.{56})/g,'$1\n'));
console.log(decrypted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
The PHP code returns the same ciphertext if AES-128-CBC is applied and if the OPENSSL_RAW_DATA flag is set as the 4th parameter in openssl_encrypt() and openssl_decrypt().

Encrypt in PHP and decrypt in JS - AES256

The function will receive a file uploaded, and will encrypt this one to save in the server, i'm think about using openssl_encrypt.
The encryption type will be AES256.
After when a web-service is requested will return a base 64 encrypted document to be decrypted in the JS side using the crypto-js.
For know, my question is how can i do the encryption process using openssl_encrypt php function?
Encrypt process:
fopen
encrypt
encode base64
fwrite
fclose
Decrypt process:
Decode base 64
Decrypt
Open the pdf document
The processes above, is the idea i have in mind, please correct me if i'm wrong or there is a mistake.
Phase 1:
PHP Code:
After handling the file:
$encryptionMethod = "AES-256-CBC";
$secret = "1234567890##########123456789012"; //must be 32 char length
$iv = substr($secret, 0, 16);
$encryptedMessage = openssl_encrypt($textToEncrypt, $encryptionMethod, $secret,0,$iv);
On jquery to decrypt doesn't work:
var ckey = "1234567890##########123456789012";
var decrypted = CryptoJS.AES.decrypt(data.content, ckey, { iv: "1234567890######" });
The pdf is generated again but i'm not able to open, shows an error message "Acrobat cannot open the file"..
Why am i getting this?
Encrypt/Decrypt working!
PHP SIDE TO ENCRYPT
$encryptionMethod = "AES-256-CBC";
$secret = "1234567890##########123456789012"; //must be 32 char length
$iv = substr($secret, 0, 16);
$encryptedMessage = openssl_encrypt($textToEncrypt, $encryptionMethod, $secret,0,$iv);`
CRYPTOJS TO DECRYPT
var cipherParams = CryptoJS.lib.CipherParams.create({ciphertext: CryptoJS.enc.Hex.parse(data.toString())});
var decrypted = CryptoJS.AES.decrypt(cipherParams, CryptoJS.enc.Hex.parse(key), { iv: CryptoJS.enc.Hex.parse(iv) });
window.open("data:application/pdf;base64, " + btoa(decrypted.toString(CryptoJS.enc.Utf8)));

Encrypt iOS and Decrypt Node.js AES

I have searched high and low for a solution which and encrypt on Node.js server and Objective-C client, and vise versa using AES (or other if appropriate)
I am relatively new to cryptography, and understanding why my encrypted text is different in each language is beyond my knowledge.
This is what I have so far:
Node.js crypto methods Using this CryptoJS Library - node-cryptojs-aes
var node_cryptojs = require("node-cryptojs-aes");
var CryptoJS = node_cryptojs.CryptoJS;
var textToEncrypt = 'Hello';
var key_clear = 'a16byteslongkey!';
//encrypted + decrypted
var encrypted = CryptoJS.AES.encrypt(clearText, key_clear, { iv: null });
var decrypted = CryptoJS.AES.decrypt(encrypted, key_clear, { iv: null });
//Outputs
console.log("encrypted: " + encrypted); //encrypted: U2FsdGVkX1/ILXOjqIw2Vvz6DzRh1LMHgEQhDm3OunY=
console.log("decrypted: " + decrypted.toString(CryptoJS.enc.Utf8)); // decrypted: Hello
Objective-C crypto methods Using AESCrypt library
NSString* textToEncrypt = #"Hello";
// encrypt
NSString* encryptedText = [AESCrypt encrypt:textToEncrypt password:#"a16byteslongkey!"];
// decrypt
NSString* decryptedText = [AESCrypt decrypt:encryptedText password:#"a16byteslongkey!"];
// output
NSLog(#"Text to encrypt: %#", textToEncrypt); // Text to encrypt: Hello
NSLog(#"Encrypted text: %#", encryptedText); // Encrypted text: wY80MJyxRRJdE+eKw6kaIA==
NSLog(#"Decrypted text: %#", decryptedText); // Decrypted text: Hello
I've been scratching my head for ages and tried everything I can think of. Can show underlying crypto methods from the libraries if required. There is SHAR256 hash applied to the key in AESCrypt library but I have removed this, and think there is some missmatch with the string encoding.
I'm posting this here because there are bound to be others trying to interop Node.js and iOS. Everyone seems to get hung up on keeping everything in the correct structures, buffers, strings etc. I know I did. So here is a step-by-step process to creating a key, creating an iv, encrypting, decrypting and placing in base64 for easy transfer:
JavaScript (Node.js using the CryptoJS module)
// Generate key from password and salt using SHA256 to hash and PDKDF2 to harden
var password = "1234567890123456";
var salt = "gettingsaltyfoo!";
var hash = CryptoJS.SHA256(salt);
var key = CryptoJS.PBKDF2(password, hash, { keySize: 256/32, iterations: 1000 });
console.log("Hash :",hash.toString(CryptoJS.enc.Base64));
console.log("Key :",key.toString(CryptoJS.enc.Base64));
// Generate a random IV
var iv = CryptoJS.lib.WordArray.random(128/8);
console.log("IV :",iv.toString(CryptoJS.enc.Base64));
// Encrypt message into base64
var message = "Hello World!";
var encrypted = CryptoJS.AES.encrypt(message, key, { iv: iv });
var encrypted64 = encrypted.ciphertext.toString(CryptoJS.enc.Base64);
console.log("Encrypted :",encrypted64);
// Decrypt base64 into message again
var cipherParams = CryptoJS.lib.CipherParams.create({ ciphertext: CryptoJS.enc.Base64.parse(encrypted64) });
var decrypted = CryptoJS.AES.decrypt(cipherParams, key, { iv: iv }).toString(CryptoJS.enc.Utf8);
console.log("Decrypted :",decrypted);
iOS SDK using CommonCrypto
// Generate key from password and salt using SHA256 to hash and PDKDF2 to harden
NSString* password = #"1234567890123456";
NSString* salt = #"gettingsaltyfoo!";
NSMutableData* hash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
NSMutableData* key = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
CC_SHA256(salt.UTF8String, (CC_LONG)strlen(salt.UTF8String), hash.mutableBytes);
CCKeyDerivationPBKDF(kCCPBKDF2, password.UTF8String, strlen(password.UTF8String), hash.bytes, hash.length, kCCPRFHmacAlgSHA1, 1000, key.mutableBytes, key.length);
NSLog(#"Hash : %#",[hash base64EncodedStringWithOptions:0]);
NSLog(#"Key : %#",[key base64EncodedStringWithOptions:0]);
// Generate a random IV (or use the base64 version from node.js)
NSString* iv64 = #"ludWXFqwWeLOkmhutxiwHw=="; // Taken from node.js CryptoJS IV output
NSData* iv = [[NSData alloc] initWithBase64EncodedString:iv64 options:0];
NSLog(#"IV : %#",[iv base64EncodedStringWithOptions:0]);
// Encrypt message into base64
NSData* message = [#"Hello World!" dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData* encrypted = [NSMutableData dataWithLength:message.length + kCCBlockSizeAES128];
size_t bytesEncrypted = 0;
CCCrypt(kCCEncrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
key.bytes,
key.length,
iv.bytes,
message.bytes, message.length,
encrypted.mutableBytes, encrypted.length, &bytesEncrypted);
NSString* encrypted64 = [[NSMutableData dataWithBytes:encrypted.mutableBytes length:bytesEncrypted] base64EncodedStringWithOptions:0];
NSLog(#"Encrypted : %#",encrypted64);
// Decrypt base 64 into message again
NSData* encryptedWithout64 = [[NSData alloc] initWithBase64EncodedString:encrypted64 options:0];
NSMutableData* decrypted = [NSMutableData dataWithLength:encryptedWithout64.length + kCCBlockSizeAES128];
size_t bytesDecrypted = 0;
CCCrypt(kCCDecrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
key.bytes,
key.length,
iv.bytes,
encryptedWithout64.bytes, encryptedWithout64.length,
decrypted.mutableBytes, decrypted.length, &bytesDecrypted);
NSData* outputMessage = [NSMutableData dataWithBytes:decrypted.mutableBytes length:bytesDecrypted];
NSString* outputString = [[NSString alloc] initWithData:outputMessage encoding:NSUTF8StringEncoding];
NSLog(#"Decrypted : %#",outputString);
The output should be the same
/*
Hash : AEIHWLT/cTUfXdYJ+oai6sZ4tXlc4QQcYTbI9If/Moc=
Key : WdHhJ19dSBURBA25HZSpbCJ4KnNEEgwzqjgyTBqa3eg=
IV : ludWXFqwWeLOkmhutxiwHw==
Encrypted : D3JpubesPMgQTiXbaoxAIw==
Decrypted : Hello World!
*/
Hopefully this saves someone else the time I've wasted :)
Are you sure the same key is being used in both libraries? You say you took out the SHA-256 part in AESCrypt, how is the library using the password parameter now? The AES algorithm can only use keys of 16, 24, or 32 bytes in length. Your password is 16 bytes long, but did you change the corresponding parameter to 128 (instead of 256) in the encrypt function?
Do you know how CryptoJS is using the key parameter? Are you sure it's being used directly, or might there be some processing (for example, hashing) before it's passed to the underlying primitive AES encryption function?
What mode of encryption is the CryptoJS library using? Its documentation doesn't say. Given that it asks for an IV, it's probably CBC, but you would have to look at the source to know for sure.
AESCrypt's documentation claims to use CBC mode, but you don't give it an IV anywhere. That must mean that it generates it own somewhere, or always uses a fixed one. (Which half defeats the purpose of CBC mode, but that's another story). So you need to figure out what the IV actually is.
TL;DR: unless you make sure that the same key and key length, the same mode, and the same IV are used across both libraries, then you will have different cipher text.

CryptoJS and Pycrypto working together

I'm encrypting a string in a web application using CryptoJS (v 2.3), and I need to decrypt it on the server in Python, so I'm using PyCrypto. I feel like I'm missing something because I can't can it working.
Here's the JS:
Crypto.AES.encrypt('1234567890123456', '1234567890123456',
{mode: new Crypto.mode.CBC(Crypto.pad.ZeroPadding)})
// output: "wRbCMWcWbDTmgXKCjQ3Pd//aRasZ4mQr57DgTfIvRYE="
The python:
from Crypto.Cipher import AES
import base64
decryptor = AES.new('1234567890123456', AES.MODE_CBC)
decryptor.decrypt(base64.b64decode("wRbCMWcWbDTmgXKCjQ3Pd//aRasZ4mQr57DgTfIvRYE="))
# output: '\xd0\xc2\x1ew\xbb\xf1\xf2\x9a\xb9\xb6\xdc\x15l\xe7\xf3\xfa\xed\xe4\xf5j\x826\xde(m\xdf\xdc_\x9e\xd3\xb1'
Here is a version with CryptoJS 3.1.2. Always beware of the following things (use the same in both languages):
Mode of operation (CBC in this case)
Padding (Zero Padding in this case; better use PKCS#7 padding)
Key (the same derivation function or clear key)
Encoding (same encoding for key, plaintext, ciphertext, ...)
IV (generated during encryption, passed for decryption)
If a string is passed as the key argument to the CryptoJS encrypt() function, the string is used to derive the actual key to be used for encryption. If you wish to use a key (valid sizes are 16, 24 and 32 byte), then you need to pass it as a WordArray.
The result of the CryptoJS encryption is an OpenSSL formatted ciphertext string. To get the actual ciphertext from it, you need to access the ciphertext property on it.
The IV must be random for each encryption so that it is semantically secure. That way attackers cannot say whether the same plaintext that was encrypted multiple times is actually the same plaintext when only looking at the ciphertext.
Below is an example that I have made.
JavaScript:
var key = CryptoJS.enc.Utf8.parse('1234567890123456'); // TODO change to something with more entropy
function encrypt(msgString, key) {
// msgString is expected to be Utf8 encoded
var iv = CryptoJS.lib.WordArray.random(16);
var encrypted = CryptoJS.AES.encrypt(msgString, key, {
iv: iv
});
return iv.concat(encrypted.ciphertext).toString(CryptoJS.enc.Base64);
}
function decrypt(ciphertextStr, key) {
var ciphertext = CryptoJS.enc.Base64.parse(ciphertextStr);
// split IV and ciphertext
var iv = ciphertext.clone();
iv.sigBytes = 16;
iv.clamp();
ciphertext.words.splice(0, 4); // delete 4 words = 16 bytes
ciphertext.sigBytes -= 16;
// decryption
var decrypted = CryptoJS.AES.decrypt({ciphertext: ciphertext}, key, {
iv: iv
});
return decrypted.toString(CryptoJS.enc.Utf8);
}
Python 2 code with pycrypto:
BLOCK_SIZE = 16
key = b"1234567890123456" # TODO change to something with more entropy
def pad(data):
length = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
return data + chr(length)*length
def unpad(data):
return data[:-ord(data[-1])]
def encrypt(message, key):
IV = Random.new().read(BLOCK_SIZE)
aes = AES.new(key, AES.MODE_CBC, IV)
return base64.b64encode(IV + aes.encrypt(pad(message)))
def decrypt(encrypted, key):
encrypted = base64.b64decode(encrypted)
IV = encrypted[:BLOCK_SIZE]
aes = AES.new(key, AES.MODE_CBC, IV)
return unpad(aes.decrypt(encrypted[BLOCK_SIZE:]))
Warning: Keep in mind that both python2 and pycrypto are obsolete, so the code has to be adjusted to fit python3 and pycryptodome.
Other considerations:
It seems that you want to use a passphrase as a key. Passphrases are usually human readable, but keys are not. You can derive a key from a passphrase with functions such as PBKDF2, bcrypt or scrypt.
The code above is not fully secure, because it lacks authentication. Unauthenticated ciphertexts may lead to viable attacks and unnoticed data manipulation. Usually the an encrypt-then-MAC scheme is employed with a good MAC function such as HMAC-SHA256.
I had to port a Javascript implementation of AES encryption/decryption which was using crypto-js library, to Python3.
Basically, my approach was to run the debugger on the existing JS code and look at variables getting filled in each step. I was able to figure out the equivalent methods to do the same in python as well.
Here is how I ported it using pycryptodome library which has some useful features.
AES.js
var CryptoJS = require("crypto-js");
var Base64 = require("js-base64");
function decrypt(str, secret) {
try {
var _strkey = Base64.decode(secret);
var reb64 = CryptoJS.enc.Hex.parse(str);
var text = reb64.toString(CryptoJS.enc.Base64);
var Key = CryptoJS.enc.Base64.parse(_strkey.split(",")[1]); //secret key
var IV = CryptoJS.enc.Base64.parse(_strkey.split(",")[0]); //16 digit
var decryptedText = CryptoJS.AES.decrypt(text, Key, { keySize: 128 / 8, iv: IV, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
return decryptedText.toString(CryptoJS.enc.Utf8); //binascii.unhexlify(decryptedText)
} catch (e) {
console.log("Error", e)
}
}
function encrypt(str, secret) {
str = Math.random().toString(36).substring(2, 10) + str;
var _strkey = Base64.decode(secret);
_strkey.split(",");
var text = CryptoJS.enc.Utf8.parse(str);
var Key = CryptoJS.enc.Base64.parse(_strkey.split(",")[1]); //secret key
var IV = CryptoJS.enc.Base64.parse(_strkey.split(",")[0]); //16 digit
var encryptedText = CryptoJS.AES.encrypt(text, Key, { keySize: 128 / 8, iv: IV, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
var b64 = encryptedText.toString();
var e64 = CryptoJS.enc.Base64.parse(b64);
var eHex = e64.toLocaleString(CryptoJS.enc.Hex);
return eHex.toUpperCase();
}
const secret = "V1VWTVRFOVhJRk5WUWsxQlVrbE9SUT09LFRrOUNUMFJaSUZkSlRFd2dTMDVQVnc9PQ=="
const data = "THIS IS MY SECRET MESSAGE!"
encData = EncryptText2(data, secret)
decData = DecryptText2(encData, secret)
console.log("encryptedData", encData)
console.log("decryptedData", decData)
AESify.py
import string
import random
import base64
import binascii
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
class AESify:
def __init__(self, key=None, iv=None,secret = None, block_len=16, salt_len= 8):
self.key = key
self.iv = iv
self.salt_len = salt_len
self.block_len = block_len
self.mode = AES.MODE_CBC
if(secret):
self.useSecret(secret)
if(self.key is None and self.iv is None):
raise Exception("No key , IV pair or secret provided")
#staticmethod
def makeSecret(key, iv):
if(len(key) % 8 != 0):
raise Exception("Key length must be a mutliple of 8")
if(len(iv) % 8 != 0):
raise Exception("Initial vector must be a multiple of 8")
key64 = base64.b64encode(key.encode()).decode()
iv64 = base64.b64encode(iv.encode()).decode()
secret = iv64 + "," + key64
secret64 = base64.b64encode(secret.encode()).decode()
return secret64
def useSecret(self, secret):
iv64, key64 = base64.b64decode(secret).decode().split(",") # decode and convert to string
self.iv = base64.b64decode(iv64)
self.key = base64.b64decode(key64)
return self
def encrypt(self, text):
text = self.add_salt(text, self.salt_len)
cipher = AES.new(self.key, self.mode, self.iv)
text = cipher.encrypt(pad(text.encode('utf-8'), self.block_len))
return binascii.hexlify(text).decode()
def decrypt(self, data):
text = binascii.unhexlify(data) # UNHEX and convert the encrypted data to text
cipher = AES.new(self.key, self.mode, self.iv)
return unpad(cipher.decrypt(text), self.block_len).decode('utf-8')[self.salt_len:]
def add_salt(self, text, salt_len):
# pre-pad with random salt
salt = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(salt_len))
text = salt + text
return text
main.py
from AESify import AESify
key , iv = "NOBODY WILL KNOW", "YELLOW SUBMARINE"
# contains IV and key
secret = AESify.makeSecret(key, iv)
aes = AESify(secret= secret, block_len=16, salt_len=4)
msg = "THIS IS MY SECRET MESSAGE"
encrypted = aes.encrypt(msg)
decrypted = aes.decrypt(encrypted)
print(f"{secret=}")
print(f"{encrypted=}")
print(f"{decrypted=}")
Note : salt , iv , padding should be same in js and python
generate salt and iv value and convert it into a byte string uisng CryptoJS.enc.Utf8.parse()
js file
var encrypted = CryptoJS.AES.encrypt(JSON.stringify(json_data), CryptoJS.enc.Utf8.parse(data['salt']) , { iv: CryptoJS.enc.Utf8.parse(data['iv']) , mode: CryptoJS.mode.CBC , padding: CryptoJS.pad.Pkcs7});
en_data = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
send this encrypted data to the python file
python file
from Crypto.Util.Padding import pad, unpad
ct = request.POST['encrypted_data']
data = base64.b64decode(ct)
cipher1 = AES.new(salt, AES.MODE_CBC, iv)
pt = unpad(cipher2.decrypt(data), 16)
data = json.loads(pt.decode('utf-8'))
pad and upad in pycrypto by default uses pkcs#7
salt and iv value should in byte string

Categories