I've tried md5 and sha256 when converting key to 16 bit but after encrypting, the result doesnt work if I'm going to validate it via third party decryptor https://www.browserling.com/tools/aes-decrypt
My goal is to decrypt the js version using python.
Added another link for js version.
https://jsfiddle.net/korvacs/4obfkxm7/17/
Python code:
from Crypto.Cipher import AES
from Crypto import Random
import hashlib
from base64 import b64encode
key = "lazydog".encode("utf-8")
key = hashlib.sha256(key).digest()
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
msg = iv + cipher.encrypt('Attack at dawn')
print(b64encode(msg).decode('utf-8'))
Can someone help me? I'm not really good in encryptions.
I am using PBKDF2 to generate IV and key. This is good practice. We don't need to transfer IV:
Javascript:
let password = "lazydog";
let salt = "salt";
let iterations = 128;
let bytes = CryptoJS.PBKDF2(password, salt, { keySize: 48, iterations: iterations });
let iv = CryptoJS.enc.Hex.parse(bytes.toString().slice(0, 32));
let key = CryptoJS.enc.Hex.parse(bytes.toString().slice(32, 96));
let ciphertext = CryptoJS.AES.encrypt("Attack at dawn", key, { iv: iv });
console.log(ciphertext.toString());
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.js"></script>
Python:
from base64 import b64decode
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
data = b64decode("ibirgCQu8TwtJOaKKtMLxw==")
bytes = PBKDF2("lazydog".encode("utf-8"), "salt".encode("utf-8"), 48, 128)
iv = bytes[0:16]
key = bytes[16:48]
cipher = AES.new(key, AES.MODE_CBC, iv)
text = cipher.decrypt(data)
text = text[:-text[-1]].decode("utf-8")
Related
I have a specific AES key that I have to work with which is 10h,10h,10h,"A",10h,10h,10h,"AAA",10h,10h,10h,10h,10h,10h,10h,10h,10h,"A",10h,"A",10h,10h,"AA",10h,"A",10h,"A",10h,"A"
I have to use this key to encrypt a message. In python I covert it to "\x10,\x10,\x10,A,\x10,\x10,\x10,AAA,\x10,\x10,\x10,\x10,\x10,\x10,\x10,\x10,\x10,A,\x10,A,\x10,\x10,AA,\x10,A,\x10,A,\x10,A" but I get ValueError: Incorrect AES key length (60 bytes).
I tried encoding the key in a different way like key = bytearray("\x10,\x10,\x10,A,\x10,\x10,\x10,AAA,\x10,\x10,\x10,\x10,\x10,\x10,\x10,\x10,\x10,A,\x10,A,\x10,\x10,AA,\x10,A,\x10,A,\x10,A".encode('utf-8')) but I still get a same key length error.
What can I do in this case?
This is my code:
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
msg = b"<input><type=\"update\"/></input>"
key = bytearray("\x10,\x10,\x10,A,\x10,\x10,\x10,AAA,\x10,\x10,\x10,\x10,\x10,\x10,\x10,\x10,\x10,A,\x10,A,\x10,\x10,AA,\x10,A,\x10,A,\x10,A".encode('utf-8'))
print(key)
cipher = AES.new(key, AES.MODE_GCM)
encrypted_msg = cipher.encrypt(msg)
print(encrypted_msg)
How about using bytes instead?
#pip install pycryptodome
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
msg = b"<input><type=\"update\"/></input>"
key = bytearray(b"\x10\x10\x10A\x10\x10\x10AAA\x10\x10\x10\x10\x10\x10\x10\x10\x10A\x10A\x10\x10AA\x10A\x10A\x10A")
print(key)
cipher = AES.new(key, AES.MODE_GCM)
encrypted_msg = cipher.encrypt(msg)
print(encrypted_msg)
This seems to be working.
Please note that you can also use bytes.fromhex, which is more readable
#pip install pycryptodome
from Crypto.Cipher import AES
msg = b"<input><type=\"update\"/></input>"
key = bytearray(bytes.fromhex("1010104110101041414110101010101010101041104110104141104110411041"))
print(key)
cipher = AES.new(key, AES.MODE_GCM)
encrypted_msg = cipher.encrypt(msg)
print(encrypted_msg)
Now, the good question: why does this happen?
Well, utf-8 encodes the non ascii characters differently, pseudocode:
'\x80 \x81 \x82 \x83 \x84 \x85 \x86 \x87 \x88 \x89 \x8a \x8b \x8c \x8d \x8e \x8f \x90 \x91 \x92 \x93 \x94 \x95 \x96 \x97 \x98 \x99 \x9a \x9b \x9c \x9d \x9e \x9f \xa0 \xa1 \xa2 \xa3 \xa4 \xa5 \xa6 \xa7 \xa8 \xa9 \xaa \xab \xac \xad \xae \xaf \xb0 \xb1 \xb2 \xb3 \xb4 \xb5 \xb6 \xb7 \xb8 \xb9 \xba \xbb \xbc \xbd \xbe \xbf '.encode()
b'\xc2\x80\xc2\x81\xc2\x82\xc2\x83\xc2\x84\xc2\x85\xc2\x86\xc2\x87\xc2\x88\xc2\x89\xc2\x8a\xc2\x8b\xc2\x8c\xc2\x8d\xc2\x8e\xc2\x8f\xc2\x90\xc2\x91\xc2\x92\xc2\x93\xc2\x94\xc2\x95\xc2\x96\xc2\x97\xc2\x98\xc2\x99\xc2\x9a\xc2\x9b\xc2\x9c\xc2\x9d\xc2\x9e\xc2\x9f\xc2\xa0\xc2\xa1\xc2\xa2\xc2\xa3\xc2\xa4\xc2\xa5\xc2\xa6\xc2\xa7\xc2\xa8\xc2\xa9\xc2\xaa\xc2\xab\xc2\xac\xc2\xad\xc2\xae\xc2\xaf\xc2\xb0\xc2\xb1\xc2\xb2\xc2\xb3\xc2\xb4\xc2\xb5\xc2\xb6\xc2\xb7\xc2\xb8\xc2\xb9\xc2\xba\xc2\xbb\xc2\xbc\xc2\xbd\xc2\xbe\xc2\xbf'
I am currently trying to use AES cryptography to encrypt and decrypt a string that always has a length of 9 characters. What I am trying to do is to encrypt the string in swift and then decrypt that encrypted string in python. I am using AES encryption with CryptoSwift and decrypting with PyCryptodome.
This is what my function in swift looks like:
import CryptoSwift
func crypto_testing() {
print("Cryptography!")
let ivString = "0000000000000000"
let keyString = "This is a key123"
let key = [UInt8](keyString.utf8)
let iv = [UInt8](ivString.utf8)
let stringToEncrypt = "123456789"
let enc = try! aesEncrypt(stringToEncrypt: stringToEncrypt, key: key, iv: iv)
print("ENCRYPT:",enc)
}
func aesEncrypt(stringToEncrypt: String, key: Array<UInt8>, iv: Array<UInt8>) throws -> String {
let data = stringToEncrypt.data(using: String.Encoding.utf8)
let encrypted = try AES(key: key, blockMode: CFB(iv: iv), padding: .noPadding).encrypt((data?.bytes)!)
return encrypted.toHexString() //result
}
The result I get from running the crypto_testing function is:
Cryptography!
ENCRYPT: 5d02105a49e55d2ff7
Furthermore, this is what my decryption function looks like in python:
import binascii
from Crypto.Cipher import AES
KEY = b'This is a key123'
IV = b'0000000000000000'
MODE = AES.MODE_CFB
def decrypt(key, iv, encrypted_text):
aes = AES.new(key, MODE, iv)
encrypted_text_bytes = binascii.a2b_hex(encrypted_text)
decrypted_text = aes.decrypt(encrypted_text_bytes)
return decrypted_text
decrypted_text = decrypt(KEY, IV, encrypted_text)
print(decrypted_text)
And the result from plugging in the encrypted message into the decrypt function like so:
>>> decrypt(b'This is a key123', b'0000000000000000', '5d02105a49e55d2ff7')
b'1%\xdc\xc8\xa0\r\xbd\xb8\xf0'
If anyone has any clue as to what is going wrong here that would be a great help.
Try this:
let stringToEncrypt = "123456789"
var aes: AES
var encrypted: [UInt8]
do {
aes = try AES(key: key, blockMode: CBC(iv: iv), padding: . noPadding)
encrypted = try aes.encrypt(stringToEncrypt.bytes)
}
let base64Encypted = encrypted.toBase64()```
This is my code:
from Crypto.Cipher import AES
from Crypto import Random
import base64
Plain_text = "Text"
random = Random.new()
IV = random.read(AES.block_size)
KEY = base64.b64encode(random.read(AES.key_size[0]))
Cipher = AES.AESCipher(KEY, AES.MODE_CFB, IV)
print "Key:", KEY
Encrypting = Cipher.encrypt(Plain_text)
print "Encrypting:\n",Encrypting
#KEY2 = base64.b64decode(KEY)
#IV2 = random.read(AES.block_size)
#print "KEY2:", KEY2
Cipher2 = AES.AESCipher(base64.b64decode(KEY), AES.MODE_CFB, IV)
Decrypting = Cipher2.decrypt(Encrypting)
print "Decrypting:\n", Decrypting
Script output:
Output is:
Key: VYy9unePPuKiQHwVcqkJzA==
Encrypting:
�F!C
Decrypting:
���
Why script can not decrypt?
OS = Ubuntu 16.04
Python Version = 2.7.12
The key supplied to the AES function should be in binary format. In your case you are encoding the key to base-64 first. In the encryption, it seems that you accidentally use this encoded string as the key.
Fixed code snippet:
from Crypto.Cipher import AES
from Crypto import Random
import base64
plaintext = "Text"
random = Random.new()
iv = random.read(AES.block_size)
key = random.read(AES.key_size[0])
cipher = AES.AESCipher(key, AES.MODE_CFB, iv)
key_b64 = base64.b64encode(key)
print ("Key: {}".format(key_b64))
ciphertext = cipher.encrypt(plaintext)
print("Encrypting: {}".format(ciphertext))
Having a bit of trouble getting a AES cipher text to decrypt.
In this particular scenario, I am encrypting data on the client side with Crypto-JS and decrypting it back on a python server with PyCrypto.
encrypt.js:
var password = 'BJhtfRjKnTDTtPXUBnErKDxfkiMCOLyP';
var data = 'mytext';
var masterKey = CryptoJS.SHA256(password).toString();
// Derive keys for AES and HMAC
var length = masterKey.toString().length / 2
var encryptionKey = masterKey.substr(0, length);
var hmacKey = masterKey.substr(length);
var iv = CryptoJS.lib.WordArray.random(64/8);
var encrypted = CryptoJS.AES.encrypt(
data,
encryptionKey,
{
iv: iv,
mode: CryptoJS.mode.CFB
}
);
var concat = iv + encrypted;
// Calculate HMAC using iv and cipher text
var hash = CryptoJS.HmacSHA256(concat, hmacKey);
// Put it all together
var registrationKey = iv + encrypted + hash;
// Encode in Base64
var basemessage = btoa(registrationKey);
decrypt.py:
class AESCipher:
def __init__(self, key):
key_hash = SHA256.new(key).hexdigest()
# Derive keys
encryption_key = key_hash[:len(key_hash)/2]
self.key = encryption_key
self.hmac_key = key_hash[len(key_hash)/2:]
def verify_hmac(self, input_cipher, hmac_key):
# Calculate hash using inputted key
new_hash = HMAC.new(hmac_key, digestmod=SHA256)
new_hash.update(input_cipher)
digest = new_hash.hexdigest()
# Calculate hash using derived key from local password
local_hash = HMAC.new(self.hmac_key, digestmod=SHA256)
local_hash.update(input_cipher)
local_digest = local_hash.hexdigest()
return True if digest == local_digest else False
def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = enc[:16]
hmac = enc[60:]
cipher_text = enc[16:60]
# Verify HMAC using concatenation of iv + cipher like in js
verified_hmac = self.verify_hmac((iv+cipher_text), self.hmac_key)
if verified_hmac:
cipher = AES.new(self.key, AES.MODE_CFB, iv)
return cipher.decrypt(cipher_text)
password = 'BJhtfRjKnTDTtPXUBnErKDxfkiMCOLyP'
input = 'long base64 registrationKey...'
cipher = AESCipher(password)
decrypted = cipher.decrypt(input)
I'm successful in re-calculating the HMAC but when I try and then decrypt the cipher I get something that seems encrypted with �'s in the result.
I was getting errors about input length of cipher text but when I switched to CFB mode that fixed it so I don't think it's a padding issue.
There are many problems with your code.
Client (JavaScript):
AES has a block size of 128 bit and CFB mode expects a full block for the IV. Use
var iv = CryptoJS.lib.WordArray.random(128/8);
The iv and hash variables are WordArray objects, but encrypted is not. When you force them to be converted to strings by concatenating them (+), iv and hash are Hex-encoded, but encrypted is formatted in an OpenSSL compatible format and Base64-encoded. You need to access the ciphertext property to get the encrypted WordArray:
var concat = iv + encrypted.ciphertext;
and
var registrationKey = iv + encrypted.ciphertext + hash;
registrationKey is hex-encoded. There is no need to encode it again with Base64 and bloat it even more:
var basemessage = registrationKey;
If you want to convert the hex encoded registrationKey to base64 encoding, use:
var basemessage = CryptoJS.enc.Hex.parse(registrationKey).toString(CryptoJS.enc.Base64);
concat is a hex-encoded string of the IV and ciphertext, because you forced the stringification by "adding" (+) iv and encrypted. The HmacSHA256() function takes either a WordArray object or a string. When you pass a string in, as you do, it will assume that the data is UTF-8 encoded and try to decode it as UTF-8. You need to parse the data yourself into a WordArray:
var hash = CryptoJS.HmacSHA256(CryptoJS.enc.Hex.parse(concat), hmacKey);
The CryptoJS.AES.encrypt() and CryptoJS.HmacSHA256() expect the key either as a WordArray object or as a string. As before, if the key is supplied as a string, a UTF-8 encoding is assumed which is not the case here. You better parse the strings into WordArrays yourself:
var encryptionKey = CryptoJS.enc.Hex.parse(masterKey.substr(0, length));
var hmacKey = CryptoJS.enc.Hex.parse(masterKey.substr(length));
Server (Python):
You're not verifying anything in verify_hmac(). You hash the same data with the same key twice. What you need to do is hash the IV+ciphertext and compare the result with the hash (called tag or HMAC-tag) that you slice off the full ciphertext.
def verify_hmac(self, input_cipher, mac):
# Calculate hash using derived key from local password
local_hash = HMAC.new(self.hmac_key, digestmod=SHA256)
local_hash.update(input_cipher)
local_digest = local_hash.digest()
return mac == local_digest
And later in decrypt():
verified_hmac = self.verify_hmac((iv+cipher_text), hmac)
You need to correctly slice off the MAC. The 60 that is hardcoded is a bad idea. Since you're using SHA-256 the MAC is 32 bytes long, so you do this
hmac = enc[-32:]
cipher_text = enc[16:-32]
The CFB mode is actually a set of similar modes. The actual mode is determined by the segment size. CryptoJS only supports segments of 128 bit. So you need tell pycrypto to use the same mode as in CryptoJS:
cipher = AES.new(self.key, AES.MODE_CFB, iv, segment_size=128)
If you want to use CFB mode with a segment size of 8 bit (default of pycrypto), you can use a modified version of CFB in CryptoJS from my project: Extension for CryptoJS
Full client code:
var password = 'BJhtfRjKnTDTtPXUBnErKDxfkiMCOLyP';
var data = 'mytext';
var masterKey = CryptoJS.SHA256(password).toString();
var length = masterKey.length / 2
var encryptionKey = CryptoJS.enc.Hex.parse(masterKey.substr(0, length));
var hmacKey = CryptoJS.enc.Hex.parse(masterKey.substr(length));
var iv = CryptoJS.lib.WordArray.random(128/8);
var encrypted = CryptoJS.AES.encrypt(
data,
encryptionKey,
{
iv: iv,
mode: CryptoJS.mode.CFB
}
);
var concat = iv + encrypted.ciphertext;
var hash = CryptoJS.HmacSHA256(CryptoJS.enc.Hex.parse(concat), hmacKey);
var registrationKey = iv + encrypted.ciphertext + hash;
console.log(CryptoJS.enc.Hex.parse(registrationKey).toString(CryptoJS.enc.Base64));
Full server code:
from Crypto.Cipher import AES
from Crypto.Hash import HMAC, SHA256
import base64
import binascii
class AESCipher:
def __init__(self, key):
key_hash = SHA256.new(key).hexdigest()
self.hmac_key = binascii.unhexlify(key_hash[len(key_hash)/2:])
self.key = binascii.unhexlify(key_hash[:len(key_hash)/2])
def verify_hmac(self, input_cipher, mac):
local_hash = HMAC.new(self.hmac_key, digestmod=SHA256)
local_hash.update(input_cipher)
local_digest = local_hash.digest()
return SHA256.new(mac).digest() == SHA256.new(local_digest).digest() # more or less constant-time comparison
def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = enc[:16]
hmac = enc[-32:]
cipher_text = enc[16:-32]
verified_hmac = self.verify_hmac((iv+cipher_text), hmac)
if verified_hmac:
cipher = AES.new(self.key, AES.MODE_CFB, iv, segment_size=128)
return cipher.decrypt(cipher_text)
else:
return 'Bad Verify'
password = 'BJhtfRjKnTDTtPXUBnErKDxfkiMCOLyP'
input = "btu0CCFbvdYV4B/j7hezAra6Q6u6KB8n5QcyA32JFLU8QRd+jLGW0GxMQsTqxaNaNkcU2I9r1ls4QUPUpaLPQg=="
obj = AESCipher(password)
decryption = obj.decrypt(input)
print 'Decrypted message:', decryption
I'm trying to user CryptoJS under node to decrypt messages. I've got working Python code for decrypting the messages, but I need to run this under nodejs and would rather not call out to python for every message.
from Crypto.Cipher import AES
from Crypto import Random
import base64
encrypted='tBIFLLdvl/Bp8XAwXBYatbJSYkNTl9/dXkHZd4OjbZ0I9Jg6xrAx/bxuQHuZrNSzYZOBEKbyMlTTT8nQEDza8wQ22mrRaZlQqT3aWpdZe6aiWAEIvTHoQPglgVbz1HnYOHfZtGmu3a3cwfpFMK+ouczTWM545nWvG/I4zV4uFgna1rW9sznxumN/3RKSbC1USZ2TM9PrG967M5Mu+riQfh9i/yt6ubwj3kln2+C0WsRRr44ELyDKGdS69YExa535z42bfXTORjvaiMvizvkz55c343s0G4ziT6tLfDCGELsrAu/2NViKxJZZRg8Dmm0FnchB9OQ4ujVCBoDUXvfx3iHjzquC+OftbOovQUecoXb7UfuwIxMekgSJnonLC45S'
key = '22<\\09\\8e.==\\4#{{+!%i=]%Y/upi8!Z'
iv = '{+!%i=]%Y/upi8!Z'
cipher = AES.new(key, AES.MODE_CBC, iv)
print cipher.decrypt(base64.b64decode(encrypted))
This prints out my decrypted string from python. I'm sure my CryptoJS version is completely wrong at this point.
var node_cryptojs = require('node-cryptojs-aes');
var CryptoJS = node_cryptojs.CryptoJS;
var key = CryptoJS.enc.Latin1.parse('22<\\09\\8e.==\\4#{{+!%i=]%Y/upi8!Z');
var iv = CryptoJS.enc.Latin1.parse('{+!%i=]%Y/upi8!Z');
var encrypted = 'tBIFLLdvl/Bp8XAwXBYatbJSYkNTl9/dXkHZd4OjbZ0I9Jg6xrAx/bxuQHuZrNSzYZOBEKbyMlTTT8nQEDza8wQ22mrRaZlQqT3aWpdZe6aiWAEIvTHoQPglgVbz1HnYOHfZtGmu3a3cwfpFMK+ouczTWM545nWvG/I4zV4uFgna1rW9sznxumN/3RKSbC1USZ2TM9PrG967M5Mu+riQfh9i/yt6ubwj3kln2+C0WsRRr44ELyDKGdS69YExa535z42bfXTORjvaiMvizvkz55c343s0G4ziT6tLfDCGELsrAu/2NViKxJZZRg8Dmm0FnchB9OQ4ujVCBoDUXvfx3iHjzquC+OftbOovQUecoXb7UfuwIxMekgSJnonLC45S';
var plaintextArray = CryptoJS.AES.decrypt({ ciphertext: encrypted }, key, { iv: iv } );
console.log(CryptoJS.enc.Latin1.stringify(plaintextArray));
All I get out of this version is a bunch of garbled text such as
{)¬L¶u[?®º[ «)þd0³(Á¨ÕßgÙä Þ¨Þêâí99dáb*¦ÿßqf pr£Æ(> þ?C×$ÀM#<o¬_±À¥s=ê,)u<¯XÚîDÊP¢q|f̽^IiaJÂ__NîjbÉâïðp8å.º}ÜucósLÈqÁè&ô£LYLüâÙháë
Turns out I was one encoding away from correct. The Latin1 parses are correct. It was just the decode from base64 on the input that was missing. Must have missed that combination earlier.
var node_cryptojs = require('node-cryptojs-aes');
var CryptoJS = node_cryptojs.CryptoJS;
var key = CryptoJS.enc.Latin1.parse('22<\\09\\8e.==\\4#{{+!%i=]%Y/upi8!Z');
var iv = CryptoJS.enc.Latin1.parse('{+!%i=]%Y/upi8!Z');
var encrypted = 'tBIFLLdvl/Bp8XAwXBYatbJSYkNTl9/dXkHZd4OjbZ0I9Jg6xrAx/bxuQHuZrNSzYZOBEKbyMlTTT8nQEDza8wQ22mrRaZlQqT3aWpdZe6aiWAEIvTHoQPglgVbz1HnYOHfZtGmu3a3cwfpFMK+ouczTWM545nWvG/I4zV4uFgna1rW9sznxumN/3RKSbC1USZ2TM9PrG967M5Mu+riQfh9i/yt6ubwj3kln2+C0WsRRr44ELyDKGdS69YExa535z42bfXTORjvaiMvizvkz55c343s0G4ziT6tLfDCGELsrAu/2NViKxJZZRg8Dmm0FnchB9OQ4ujVCBoDUXvfx3iHjzquC+OftbOovQUecoXb7UfuwIxMekgSJnonLC45S';
var plaintextArray = CryptoJS.AES.decrypt({ ciphertext: CryptoJS.enc.Base64.parse(encrypted) }, key, { iv: iv } );
console.log(CryptoJS.enc.Latin1.stringify(plaintextArray));