Aes encryption in javascript - javascript

i am using crypto-js for encryption a text with key and IV (new byte[16]) for some reason it's giving different output from what it gives online or from my python code
my python code:
import base64
import binascii
import hashlib
from Crypto.Cipher import AES
def r_pad(payload, block_size=16):
length = block_size - (len(payload) % block_size)
return payload + chr(length) * length
key = "my-private-key"
body = "my-text-to-encrypt"
iv = binascii.unhexlify(16 * "00")
length = len(body)
encoded_key = key.encode('ascii')
data_from_encryption = r_pad(body).encode('utf-8')
encrypted_data = AES.new(encoded_key, AES.MODE_CBC, iv).encrypt(data_from_encryption)
encrypted_data_to_base64_str = base64.b64encode(encrypted_data).decode('utf-8')
print("Encrypted data: ", encrypted_data_to_base64_str)
my javascript code
import { Buffer } from "buffer";
import CryptoJS from "crypto-js";
const IV = Buffer.alloc(16, 0, "hex");
const key = "my-private-key";
const text = "my-text-to-encrypt"
const ciphertext = CryptoJS.AES.encrypt(text, key, {
iv: IV,
mode: CryptoJS.mode.CBC,
}).toString();
console.log(ciphertext)
i don't know what i am missing here which is causing problem.
if i try to decrypt my encrypted javascript's code cyptherText from the code is used in python to decrypt it shows error
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc7 in position 1: invalid continuation byte
and there is a lot of difference in the output of the both the encryption code:
python outputs: VfCJUymqvQWJWm9Vl93A6Q==
javascript outputs: U2FsdGVkX1+/owRNxuxz16Lq7OeNxYmeBiDQZDHHEAQ=
can anybody help me to fix my javascript code?

Related

Python AES decryption

I have a Typescript code I've been trying to convert to Python (I am interested in the decrypting part) but I kept getting bad output, what I am doing wrong?
Code I want to translate:
Typescript
import CryptoJS from 'crypto-js';
let encrypted = undefined;
let decrypted = undefined;
let key = "d76d312b4647849345285b7c11dbb9e6"
let data = "Data to be encrypted"
encrypted = CryptoJS.AES.encrypt(data, key);
console.log(encrypted.toString()) //results to output like this: U2FsdGVkX198Jnuf6hHeS1bDSP5mrn/+ciFwd5l4Yiw=
decrypted = CryptoJS.AES.decrypt(encrypted, key).toString(CryptoJS.enc.Utf8);
console.log(decrypted) //results to "Data to be encrypted"
Python
import base64
from Crypto.Cipher import AES
key = b'b9730baff3f457f7e98be4351387fcbd'
data = "Some_encrypted_base64_here"
unb64_ciphertext = base64.b64decode(data.encode())
decryption_cipher = AES.new(key, AES.MODE_CBC)#, nonce=nonce)
output_data = decryption_cipher.decrypt(unb64_ciphertext)
print("Decrypted message: ", output_data.decode("utf-8", "ignore"))
I tried to change the AES moode but that didn't help either

Crypto-js encryption and Python decryption using HKDF key

Based on the example provided here on how to establish a shared secret and derived key between JS (Crypto-JS) and Python, I can end up with the same shared secret and derived key on both ends.
However, when I try to encrypt as below, I cannot find a way to properly decrypt from Python. My understanding is that probably I am messing with the padding or salts and hashes.
const payload = "hello"
var iv = CryptoJS.enc.Utf8.parse("1020304050607080");
var test = CryptoJS.AES.encrypt(
payload,
derived_key,
{iv: iv, mode: CryptoJS.mode.CBC}
).toString();
console.log(test)
Output "y+In4kriw0qy4lji6/x14g=="
Python (one of the attempts):
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
iv = "1020304050607080"
test_enc = "y+In4kriw0qy4lji6/x14g=="
enc = base64.b64decode(test_enc)
cipher = AES.new(derived_key, AES.MODE_CBC, iv.encode('utf-8'))
print(base64.b64decode(cipher.decrypt(enc)))
print(unpad(cipher.decrypt(enc),16))
Any guidance here would be greatly appreciated as I am stuck for quite some time.
(I have encryption working using a password, but struggling with HKDF).
EDIT:
Here is the full Python code:
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
def deriveKey():
server_pkcs8 = b'''-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBReGpDVmoVTzxNbJx6
aL4L9z1EdB91eonAmAw7mKDocLfCJITXZPUAmM46c6AipTmhZANiAAR3t96P0ZhU
jtW3rHkHpeGu4e+YT+ufMiMeanE/w8p+d9aCslvIbZyBBzeZ/266yqTUUoiYDzqv
Hb5q8rz7vEgr3DG4XfHYpCqfE2nttQGK3emHKGnvY239AteZkdwMpcs=
-----END PRIVATE KEY-----'''
client_x509 = b'''-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEm0xeyy3nVnYpOpx/CV/FnlNEdWUZaqtB
AGf7flKxXEjmlSUjseYzCd566sLpNg56Gw6hcFx+rWTLGR4eDRWfmwlXhyUasuEg
mb0BQf8XJLBdvadb9eFx2CP1yjBsiy8e
-----END PUBLIC KEY-----'''
client_public_key = serialization.load_pem_public_key(client_x509)
server_private_key = serialization.load_pem_private_key(server_pkcs8, password=None)
shared_secret = server_private_key.exchange(ec.ECDH(), client_public_key)
print('Shared secret: ' + base64.b64encode(shared_secret).decode('utf8')) # Shared secret: xbU6oDHMTYj3O71liM5KEJof3/0P4HlHJ28k7qtdqU/36llCizIlOWXtj8v+IngF
salt_bytes = "12345678".encode('utf-8')
info_bytes = "abc".encode('utf-8')
derived_key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=salt_bytes,
info=info_bytes,
).derive(shared_secret)
print('Derived key: ' + base64.b64encode(derived_key).decode('utf8'))
return derived_key
derived_key = deriveKey()
iv = "1020304050607080"
test_enc = "y+In4kriw0qy4lji6/x14g=="
enc = base64.b64decode(test_enc)
cipher = AES.new(derived_key, AES.MODE_CBC, iv.encode('utf-8'))
print(base64.b64decode(cipher.decrypt(enc)))
print(unpad(cipher.decrypt(enc),16))
The issue is that the key is not passed correctly in the CryptoJS code.
The posted Python code generates LefjQ2pEXmiy/nNZvEJ43i8hJuaAnzbA1Cbn1hOuAgA= as Base64-encoded key. This must be imported in the CryptoJS code using the Base64 encoder:
const payload = "hello"
var derived_key = CryptoJS.enc.Base64.parse("LefjQ2pEXmiy/nNZvEJ43i8hJuaAnzbA1Cbn1hOuAgA=")
var iv = CryptoJS.enc.Utf8.parse("1020304050607080");
var test = CryptoJS.AES.encrypt(payload, derived_key, {iv: iv, mode: CryptoJS.mode.CBC}).toString();
document.getElementById("ct").innerHTML = test; // bLdmGA+HLLyFEVtBEuCzVg==
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<p style="font-family:'Courier New', monospace;" id="ct"></p>
The hereby generated ciphertext bLdmGA+HLLyFEVtBEuCzVg== can be decrypted with the Python code:
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import base64
test_enc = "bLdmGA+HLLyFEVtBEuCzVg=="
enc = base64.b64decode(test_enc)
derived_key = base64.b64decode("LefjQ2pEXmiy/nNZvEJ43i8hJuaAnzbA1Cbn1hOuAgA=")
iv = "1020304050607080"
cipher = AES.new(derived_key, AES.MODE_CBC, iv.encode('utf-8'))
print(unpad(cipher.decrypt(enc),16)) # b'hello'
Note that for security reasons, a static IV should not be used so that key/IV pairs are not repeated.

Encrypt in python - decrypt in Javascript

I have need to simply encrypt some text in python and being able to decrypt in JavaScrypt.
So far I have in python:
from Crypto import Random
from Crypto.Cipher import AES
import base64
BLOCK_SIZE = 16
key = "1234567890123456" # want to be 16 chars
textToEncrypt = "This is text to encrypt"
def encrypt(message, passphrase):
# passphrase MUST be 16, 24 or 32 bytes long, how can I do that ?
IV = Random.new().read(BLOCK_SIZE)
aes = AES.new(passphrase, AES.MODE_CFB, IV)
return base64.b64encode(aes.encrypt(message))
def decrypt(encrypted, passphrase):
IV = Random.new().read(BLOCK_SIZE)
aes = AES.new(passphrase, AES.MODE_CFB, IV)
return aes.decrypt(base64.b64decode(encrypted))
print encrypt( textToEncrypt, key )
this is producing text: ZF9as5JII5TlqcB5tAd4sxPuBXd5TrgE
in JavaScript:
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script>
var decrypted = CryptoJS.AES.decrypt( "ZF9as5JII5TlqcB5tAd4sxPuBXd5TrgE", "1234567890123456");
console.log ( decrypted.toString( CryptoJS.enc.Utf8 ) );
</script>
however it does not produce original string (empty string instead).
What I am doing wrong ?
Is it focusing on AES is a best idea - I will be happy if I have some kind of encryption that will blur data.
There are many problems with your Python code and CryptoJS code:
You use a random IV to encrypt some plaintext in Python. If you want to retrieve that plaintext, you need to use the same IV during decryption. The plaintext cannot be recovered without the IV. Usually the IV is simply prepended to the ciphertext, because it doesn't have to be secret. So you need to read the IV during decryption and not generate a new one.
You use CBC mode in CryptoJS (default) instead of CFB mode. The mode has to be the same. The other tricky part is that CFB mode is parametrized with a segment size. PyCrypto uses by default 8-bit segments (CFB8), but CryptoJS is only implemented for fixed segments of 128-bit (CFB128). Since the PyCrypto version is variable, you need to change that.
The CryptoJS decrypt() function expects as ciphertext either an OpenSSL formatted string or a CipherParams object. Since you don't have an OpenSSL formatted string, you have to convert the ciphertext into an object.
The key for CryptoJS is expected to be a WordArray and not a string.
Use the same padding. PyCrypto doesn't pad the plaintext if CFB8 is used, but padding is needed when CFB128 is used. CryptoJS uses PKCS#7 padding by default, so you only need to implement that padding in python.
Python code (for version 2):
def pad(data):
length = 16 - (len(data) % 16)
return data + chr(length)*length
def unpad(data):
return data[:-ord(data[-1])]
def encrypt(message, passphrase):
IV = Random.new().read(BLOCK_SIZE)
aes = AES.new(passphrase, AES.MODE_CFB, IV, segment_size=128)
return base64.b64encode(IV + aes.encrypt(pad(message)))
def decrypt(encrypted, passphrase):
encrypted = base64.b64decode(encrypted)
IV = encrypted[:BLOCK_SIZE]
aes = AES.new(passphrase, AES.MODE_CFB, IV, segment_size=128)
return unpad(aes.decrypt(encrypted[BLOCK_SIZE:]))
JavaScript code:
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/components/mode-cfb-min.js"></script>
<script>
var base64ciphertextFromPython = "...";
var ciphertext = CryptoJS.enc.Base64.parse(base64ciphertextFromPython);
// 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;
var key = CryptoJS.enc.Utf8.parse("1234567890123456");
// decryption
var decrypted = CryptoJS.AES.decrypt({ciphertext: ciphertext}, key, {
iv: iv,
mode: CryptoJS.mode.CFB
});
console.log ( decrypted.toString(CryptoJS.enc.Utf8));
</script>
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.
(1 Year later but I hope this works for someone)
First of all, thanks Artjom B. your post helps me a lot. And Like OP, I have the same same problem Python server endonding and Javascript client decoding. This was my solution:
Python 3.x (Server)
I used an excplicit PKCS7 encode for padding, why? because I want to be sure Im using the same padding enconding and decoding, this is the link where I found it http://programmerin.blogspot.com.co/2011/08/python-padding-with-pkcs7.html .
Then, like Artjom B. said, be sure about your segment size, IV size and AES mode (CBC for me),
This is the code:
def encrypt_val(clear_text):
master_key = '1234567890123456'
encoder = PKCS7Encoder()
raw = encoder.encode(clear_text)
iv = Random.new().read( 16 )
cipher = AES.new( master_key, AES.MODE_CBC, iv, segment_size=128 )
return base64.b64encode( iv + cipher.encrypt( raw ) )
Note than your are enconding on base64 the concatenation of IV and encryption data.
Javascript (client)
function decryptMsg (data) {
master_key = '1234567890123456';
// Decode the base64 data so we can separate iv and crypt text.
var rawData = atob(data);
// Split by 16 because my IV size
var iv = rawData.substring(0, 16);
var crypttext = rawData.substring(16);
//Parsers
crypttext = CryptoJS.enc.Latin1.parse(crypttext);
iv = CryptoJS.enc.Latin1.parse(iv);
key = CryptoJS.enc.Utf8.parse(master_key);
// Decrypt
var plaintextArray = CryptoJS.AES.decrypt(
{ ciphertext: crypttext},
key,
{iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}
);
// Can be Utf8 too
output_plaintext = CryptoJS.enc.Latin1.stringify(plaintextArray);
console.log("plain text : " + output_plaintext);
}
One of my main problem was keep in mind all kind of encoding and decoding data, for example, I didn't know that the master_key on client side was to be parse with Utf8.
//First pip install pycryptodome -- (pycrypto is obsolete and gives issues)
// pip install pkcs7
from Crypto import Random
from Crypto.Cipher import AES
import base64
from pkcs7 import PKCS7Encoder
from app_settings.views import retrieve_settings # my custom settings
app_secrets = retrieve_settings(file_name='secrets');
def encrypt_data(text_data):
#limit to 16 bytes because my encryption key was too long
#yours could just be 'abcdefghwhatever'
encryption_key = app_secrets['ENCRYPTION_KEY'][:16];
#convert to bytes. same as bytes(encryption_key, 'utf-8')
encryption_key = str.encode(encryption_key);
#pad
encoder = PKCS7Encoder();
raw = encoder.encode(text_data) # Padding
iv = Random.new().read(AES.block_size ) #AES.block_size defaults to 16
# no need to set segment_size=BLAH
cipher = AES.new( encryption_key, AES.MODE_CBC, iv )
encrypted_text = base64.b64encode( iv + cipher.encrypt( str.encode(raw) ) )
return encrypted_text;

AES encrypt in cryptojs and decrypt in python Crypto.Cipher

Getting problem with encrypt using js CryptoJS and decrypt that using python crypto.Cipher
This is my implementation in js,
append iv with encrypted message and encode with base64
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script>
var message='Secreat Message to Encrypt';
var key = CryptoJS.enc.Hex.parse('824601be6c2941fabe7fe256d4d5a2b7');
var iv = CryptoJS.enc.Hex.parse('1011121314151617');
var encrypted = CryptoJS.AES.encrypt(message, key, { iv: iv, mode: CryptoJS.mode.CBC });
encrypted =encrypted.toString();
encrypted = iv+encrypted;
encrypted = btoa(encrypted);
console.log('encrypted',encrypted );
alert(encrypted);
// var decrypted = CryptoJS.AES.decrypt(encrypted, key, { iv: iv, mode: CryptoJS.mode.CBC });
// console.log('decrypted', decrypted);
//alert(decrypted.toString(CryptoJS.enc.Utf8));
</script>
And in the python script for aes encryption and decryption i used
#!/usr/bin/python
import os, random, struct
from Crypto.Cipher import AES
from Crypto import Random
import base64
class AESCipher:
def __init__(self, key):
BS = 16
self.pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
self.unpad = lambda s : s[0:-ord(s[-1])]
self.key = self.pad(key[0:16])
def encrypt(self, raw):
raw = self.pad(raw)
iv = "1011121314151617"
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw))
def decrypt(self, enc):
enc = enc.replace(' ', '+')
enc = base64.b64decode(enc)
iv = enc[:16]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self.unpad(cipher.decrypt( enc[16:]))
def main():
cipher = AESCipher('824601be6c2941fabe7fe256d4d5a2b7')
encrypteddata =cipher.encrypt(''Secreat Message to Encrypt'')
print encrypteddata
decryptdata =cipher.decrypt(encrypteddata)
print decryptdata
main()
but same iv, message and key produce different encrypted message in python and js,
what is the problem with JavaScript to compatible with python decryption?
Both used AES.MODE_CBC and assume both used Pkcs7 padding. hard coded iv for now those are generate randomly
Try with an IV that has actually the same size as the block size of AES, 16 bytes. You are currently specifying 8 bytes in hexadecimals. CBC mode requires an IV of the same size as the block size and the Python API specifies (including final typo):
For all other modes, it must be block_size bytes longs.
It's best to use a method or (predefined) constant like above.

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