Crypto-js encryption and Python decryption using HKDF key - javascript

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.

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

CryptoJs decrypt function equivalent in Java

I'm trying to decrypt a token that the server I'm using brings in order to load correctly users access to the page.
I already achieved the goal using CryptoJs using JavaScript. However now I need to transfer that function to my java backend.
Look my code using CryptoJs which works correctly
const token = "U2FsdGVkX1+6YueVRKp6h0dZfk/a8AC9vyFfAjxD4nb7mXsKrM7rI7xZ0OgrF1sShHYNLMJglz4+67n/I7P+fg=="
const key = "p80a0811-47db-2c39-bcdd-4t3g5h2d5d1a"
// Decrypt
const iv = CryptoJS.enc.Utf8.parse(key);
const decryptedToken = CryptoJS.AES.decrypt(token, key, {
keySize: 16,
iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
});
const stringToken = decryptedToken.toString(CryptoJS.enc.Utf8);
const dataToUse = JSON.parse(stringToken)
console.log("decryptedToken => ", stringToken);
console.log("data => ",dataToUse);
And this is my code in Java using javax.crypto
public String decrypt() {
String finalToken = null;
Cipher cipher;
try {
String token = "U2FsdGVkX1+6YueVRKp6h0dZfk/a8AC9vyFfAjxD4nb7mXsKrM7rI7xZ0OgrF1sShHYNLMJglz4+67n/I7P+fg==";
String key = "p80a0811-47db-2c39-bcdd-4t3g5h2d5d1a";
Key skeySpec = new SecretKeySpec(Arrays.copyOf(key.getBytes(), 16), "AES");
byte[] iv = Arrays.copyOf(key.getBytes(StandardCharsets.UTF_8), 16);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
byte[] bytesToDecrypt = Base64.decodeBase64(token.getBytes());
cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec);
byte[] decrypted = cipher.doFinal(bytesToDecrypt);
finalToken = new String(decrypted);
log.info("Decrypt token succesfully {}", finalToken);
} catch (NoSuchAlgorithmException
| InvalidAlgorithmParameterException
| IllegalBlockSizeException
| NoSuchPaddingException
| BadPaddingException
| InvalidKeyException e) {
e.printStackTrace();
log.error("Error decrypting");
}
return finalToken;
}
I'm not sure I'm setting correctly the key and the iv. With the piece of code above I get this error:
javax.crypto.BadPaddingException: pad block corrupted
If I don't "cut" the key and the iv in order to have 16 bytes I get wrong length error.
Can someone help me to figure out what's wrong please!
The expected result is to get this info in java so then I can manipulate the object:
{name: "Burak", surName: "Bayraktaroglu"}
Decryption fails because CryptoJS interprets the key material as password when passed as string (as in the posted code).
In this case, the password and a salt are used to derive the key and IV applying the OpenSSL proprietary key derivation function EVP_BytesToKey().
The random 8 bytes salt is generated during encryption, which returns as result the Base64 encoding of the concatenation of a prefix, the salt and the actual ciphertext. The prefix consists of the ASCII encoding of Salted__.
Therefore, the following steps are necessary in the decryption process:
Determining salt and ciphertext from the CryptoJS return value
Deriving key and IV using password and salt
Decryption of the ciphertext with the derived key and IV
There are several ways to implement the last two steps in Java.
You can find various ports of EVP_BytesToKey() on the web which can be used in conjunction with the native JCA/JCE for decryption. These EVP_BytesToKey() ports are not always reliable, a solid port is this one.
A convenient (and reliable) alternative is BouncyCastle (with its own EVP_BytesToKey() port), described below:
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.ParametersWithIV;
...
// Get salt and ciphertext
String saltCiphertextB64 = "U2FsdGVkX1+6YueVRKp6h0dZfk/a8AC9vyFfAjxD4nb7mXsKrM7rI7xZ0OgrF1sShHYNLMJglz4+67n/I7P+fg==";
byte[] saltCiphertext = Base64.getDecoder().decode(saltCiphertextB64);
ByteBuffer byteBuffer = ByteBuffer.wrap(saltCiphertext);
byte[] prefix = new byte[8];
byte[] salt = new byte[8];
byteBuffer.get(prefix);
byteBuffer.get(salt);
byte[] ciphertext = new byte[byteBuffer.remaining()];
byteBuffer.get(ciphertext);
// Key derivation via EVP_BytesToKey() (using BouncyCastle)
String passwordStr = "p80a0811-47db-2c39-bcdd-4t3g5h2d5d1a";
byte[] password = passwordStr.getBytes(StandardCharsets.UTF_8);
OpenSSLPBEParametersGenerator pbeGenerator = new OpenSSLPBEParametersGenerator(new MD5Digest());
pbeGenerator.init(password, salt);
ParametersWithIV parameters = (ParametersWithIV) pbeGenerator.generateDerivedParameters(256, 128); // keySize, ivSize in bits
// FYI: key: ((KeyParameter)parameters.getParameters()).getKey()
// IV : parameters.getIV()
// Decryption (using BouncyCastle)
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
cipher.init(false, parameters);
byte[] plaintext = new byte[cipher.getOutputSize(ciphertext.length)];
int length = cipher.processBytes(ciphertext, 0, ciphertext.length, plaintext, 0);
length += cipher.doFinal(plaintext, length);
String plaintextSr = new String(plaintext, 0, length, StandardCharsets.UTF_8);
System.out.println(plaintextSr); // {"name":"Burak","surName":"Bayraktaroglu"}

Aes encryption in 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?

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