i want some example for encryption between python and flutter to encrypt request and response body between client and server
i found some example code for AES CRT encryption , but i cant see same result in flutter and python
can anybody help me ?
UPDATE :
Flutter crypt package not have counter parameter but python Crypto.Cipher package have counter parameter
this is sample code for python:
plaintext = '123'.encode('utf-8')
key = '12345678911234567891123456789123'.encode("utf-8")
iv = '12345'.encode('utf')
iv_int = int(binascii.hexlify(iv), 16)
ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)
aes = AES.new(key, AES.MODE_CTR, counter=ctr)
ciphertext = aes.encrypt(plaintext)
print('ctr = ' + str(ctr))
print('iv = ' + str(base64.b64encode(iv)))
print('iv_int = ' + str(iv_int))
print('plaintext = ' + str(plaintext))
print('key = ' + str(base64.b64encode(key)))
print('ciphertext = ' + str(base64.b64encode(ciphertext)))
this is sample code for flutter :
final plainText = '123';
final key = encrypt.Key.fromUtf8('12345678911234567891123456789123');
final iv = encrypt.IV.fromUtf8('12345');
final encrypter = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.ctr));
final encrypted = encrypter.encrypt(plainText, iv: iv);
final decrypted = encrypter.decrypt(encrypted, iv: iv);
print('key = ' + key.base64);
print('iv =' + iv.base64);
print('encrypted = ' + encrypted.base64);
I had the same issue and posted a very similar question. Fortunately, I found the error by myself.
Here is the link: Python AES CTR Mode only encrypts first two bytes
To summarize, the problem: Dart automatically uses the PKCS7 Padding when using AES Encryption, but Python does not anything like that.
So either you set the padding in your Dart code to null e.g.:
aes = AES.new(key, AES.MODE_CTR, counter=ctr, padding: null)
or you add a PKCS7 padding to your python code with like the following:
# The message has to in bytes format
def pkcs7padding(message):
pl = 16 - (len(message) % 16)
return message + bytearray([pl for i in range(pl)])
ciphertext = aes.encrypt(pkcs7padding(plaintext))
Related
I'm trying to figure out the best way to encrypt and decrypt some JSON data that I'm passing from my client (Python) to server (Java). I keep running into a few errors. Originally I had a set IV that I had hardcoded into both client and server and it was working great, it would encrypt and decrypt as I needed. However, that's obviously a bad idea, I set a random IV that I would then slice with my server and that sorta worked. It would decrpyt most of the data, except for the first 50 bytes or so. They would just be random chars (�6OC�Ղ�{�9��aJ, "number": 1243.2,...etc) and I couldn't parse the JSON.
Then when I try to slice OFF the IV (16 chars) from the decoded bytes I get an error saying that the data needs to be in multiples of 16, but I'm not sure why that isn't working.
Any ideas on how to get this working? Thanks!
Python Client
jsonString = json.dumps(data.__dict__, default=str)
key = 'sixteenssixteens'
text = jsonString
plaintext = self.pad(text)
iv = os.urandom(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
encrpyt_btye = cipher.encrypt(plaintext)
encrpyt_text = base64.urlsafe_b64encode(encrpyt_btye).decode("UTF-8")
requests.post('server', data=encrpy_text)
def pad(self, text):
numberBytestoPad = block_size - len(text) % block_size
ascii_string = chr(numberBytestoPad)
padding_str = numberBytestoPad * ascii_string
padded_text = text + padding_str
return padded_text
Java Server
requestBody = exchange.getRequestBody();
String requestData = readString(requestBody);
System.out.println(requestData);
String key = "sixteenssixteens";
String initVector = requestData.substring(0, 16);
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
try {
//Tried to remove the IV from the start of the string
// String removeIV = requestData.substring(16);
Cipher cipherd = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipherd.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipherd.doFinal(Base64.getUrlDecoder().decode(requestData));
String decryptedResult = new String(original);
System.out.println("Decrypted string: " + decryptedResult);
try {
data = gson.fromJson(decryptedResult, Data.class);
} catch (Exception ex){
ex.printStackTrace();
return;
}
I am trying to make a program to decrypt via python. The cipher used is AES-CBC.
But I get this error and I don't know how to fix it. Do you have any ideas?
Thanks in advance
#!/usr/bin/env python3
import base64
import binascii
from Crypto.Cipher import AES
key = "YELLOW SUBMARINE"
iv_dec = "a5f6ba42255643907a5bb3709840ca5b"
block_size = 16
pad_char = u"\00"
pad = lambda s: s + (block_size - len(s) % block_size) * pad_char
ciphertext = "412053617563657266756c206f662053656372657473"
# Decrypt
aes_dec = AES.new(str(key), AES.MODE_CBC, str(iv_dec))
decrypted = aes_dec.decrypt(ciphertext)
print ("Decrypted text: {} ".format(decrypted.rstrip(pad_char)))
i am trying to encode a particular string with python with pycrypto and encode the same string with nodejs with crypto.
i am getting different results in both the cases for the same input string
python code:
from Crypto.Cipher import AES
from hashlib import md5
import base64
password = 'aquickbrownfoxjumpsoverthelazydog'
input = 'hello+world'
BLOCK_SIZE = 16
def pad (data):
pad = BLOCK_SIZE - len(data) % BLOCK_SIZE
return data + pad * chr(pad)
def unpad (padded):
pad = ord(padded[-1])
return padded[:-pad]
def text_encrypt(data, nonce, password):
m = md5()
m.update(password)
key = m.hexdigest()
m = md5()
m.update(password + key)
iv = m.hexdigest()
data = pad(data)
aes = AES.new(key, AES.MODE_CBC, iv[:16])
encrypted = aes.encrypt(data)
return base64.urlsafe_b64encode(encrypted)
output = text_encrypt(input, "", password)
print output
and the nodejs code is as follows:
var crypto = require('crypto');
var password = 'aquickbrownfoxjumpsoverthelazydog';
var input = 'hello+world';
var encrypt = function (input, password, callback) {
var m = crypto.createHash('md5');
m.update(password)
var key = m.digest('hex');
m = crypto.createHash('md5');
m.update(password + key)
var iv = m.digest('hex');
var data = new Buffer(input, 'utf8').toString('binary');
var cipher = crypto.createCipheriv('aes-256-cbc', key, iv.slice(0,16));
var nodev = process.version.match(/^v(\d+)\.(\d+)/);
var encrypted;
if( nodev[1] === '0' && parseInt(nodev[2]) < 10) {
encrypted = cipher.update(data, 'binary') + cipher.final('binary');
} else {
encrypted = cipher.update(data, 'utf8', 'binary') + cipher.final('binary');
}
var encoded = new Buffer(encrypted, 'binary').toString('base64');
callback(encoded);
};
encrypt(input, password, function (encoded) {
console.log(encoded);
});
the results for both the cases is different but after decryption they both tend to give the same correct result.
what might be the issue here?
You didn't specify what different results are you getting but those two should produce same-ish result. The only difference I see is in the base64 alphabet you're using.
In Python you're calling base64.urlsafe_b64encode() which differs from the standard Base64 in what characters it uses for values for 62 and 63 (- and _ instead of + and /). To get the same result, either in Python return:
return base64.b64encode(encrypted)
Or post-process the base64 encoded string in Node.js:
encoded = encoded.replace(/_/g, '/').replace(/-/g, '+');
All this being said, as I've mentioned in my comment, never derive an IV from your password/key (or anything else deterministic and unchanging). Use a cryptographically secure PRNG for it.
In node i am using the following code to get proper decrypted message:
//npm install --save-dev crypto-js
var CryptoJS = require("crypto-js");
var esp8266_msg = 'IqszviDrXw5juapvVrQ2Eh/H3TqBsPkSOYY25hOQzJck+ZWIg2QsgBqYQv6lWHcdOclvVLOSOouk3PmGfIXv//cURM8UBJkKF83fPawwuxg=';
var esp8266_iv = 'Cqkbb7OxPGoXhk70DjGYjw==';
// The AES encryption/decryption key to be used.
var AESKey = '2B7E151628AED2A6ABF7158809CF4F3C';
var plain_iv = new Buffer( esp8266_iv , 'base64').toString('hex');
var iv = CryptoJS.enc.Hex.parse( plain_iv );
var key= CryptoJS.enc.Hex.parse( AESKey );
console.log("Let's ");
// Decrypt
var bytes = CryptoJS.AES.decrypt( esp8266_msg, key , { iv: iv} );
var plaintext = bytes.toString(CryptoJS.enc.Base64);
var decoded_b64msg = new Buffer(plaintext , 'base64').toString('ascii');
var decoded_msg = new Buffer( decoded_b64msg , 'base64').toString('ascii');
console.log("Decryptedage: ", decoded_msg);
But when i try to decrypt it in python i am not getting the proper decoded message.
esp8266_msg = 'IqszviDrXw5juapvVrQ2Eh/H3TqBsPkSOYY25hOQzJck+ZWIg2QsgBqYQv6lWHcdOclvVLOSOouk3PmGfIXv//cURM8UBJkKF83fPawwuxg='
esp8266_iv = 'Cqkbb7OxPGoXhk70DjGYjw=='
key = '2B7E151628AED2A6ABF7158809CF4F3C'
iv = base64.b64decode(esp8266_iv)
message = base64.b64decode(esp8266_msg)
dec = AES.new(key=key, mode=AES.MODE_CBC, IV=iv)
value = dec.decrypt(message)
print(value)
I am getting the decoded message:
"ルᄊ+#ÊZûᆪᄃn*ÿÒá×G1ᄄᄋì;$-#f゚ãᄚk-ìØܳã-トȒ~ヌ8ヘヘ_ᄂ ン?ᄂÑ:ÇäYムü'hユô<`
So i hope someone can show how it is done in python.
You forgot to decode the key from Hex and remove the padding.
Full code:
from Crypto.Cipher import AES
import base64
unpad = lambda s : s[:-ord(s[len(s)-1:])]
esp8266_msg = 'IqszviDrXw5juapvVrQ2Eh/H3TqBsPkSOYY25hOQzJck+ZWIg2QsgBqYQv6lWHcdOclvVLOSOouk3PmGfIXv//cURM8UBJkKF83fPawwuxg='
esp8266_iv = 'Cqkbb7OxPGoXhk70DjGYjw=='
key = '2B7E151628AED2A6ABF7158809CF4F3C'
iv = base64.b64decode(esp8266_iv)
message = base64.b64decode(esp8266_msg)
key = key.decode("hex")
dec = AES.new(key=key, mode=AES.MODE_CBC, IV=iv)
value = unpad(dec.decrypt(message))
print(value)
if len(value) % 4 is not 0:
value += (4 - len(value) % 4) * "="
value = base64.b64decode(value)
print(value)
Output:
eyJkYXRhIjp7InZhbHVlIjozMDB9LCAiU0VRTiI6NzAwICwgIm1zZyI6IklUIFdPUktTISEiIH0
'{"data":{"value":300}, "SEQN":700 , "msg":"IT WORKS!!" }'
Security considerations
The IV must be unpredictable (read: random). Don't use a static IV, because that makes the cipher deterministic and therefore not semantically secure. An attacker who observes ciphertexts can determine when the same message prefix was sent before. The IV is not secret, so you can send it along with the ciphertext. Usually, it is simply prepended to the ciphertext and sliced off before decryption.
It is better to authenticate your ciphertexts so that attacks like a padding oracle attack are not possible. This can be done with authenticated modes like GCM or EAX, or with an encrypt-then-MAC scheme.
Here's my code:
Encrypt:
from Crypto.Cipher import AES
import base64
def encryption (privateInfo):
BLOCK_SIZE = 16
PADDING = '{'
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
secret = 'Fr3#k1nP#ssw0rd.'
print('encryption key:', secret)
cipher = AES.new(secret)
encoded = EncodeAES(cipher, privateInfo)
print('Encrypted string:', encoded)
encryption('secret')
The encrypted string is: b'QuCzNmwiVaq1uendvX7P+g=='
Decrypt:
from Crypto.Cipher import AES
import base64
def decryption(encryptedString):
PADDING = '{'
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
key = 'Fr3#k1nP#ssw0rd.'
cipher = AES.new(key)
decoded = DecodeAES(cipher, encryptedString)
print(decoded)
decryption("b'QuCzNmwiVaq1uendvX7P+g=='")
The result:
ValueError: Input strings must be a multiple of 16 in length
This is PyCrypto 2.6.1 on Python 3.4; I've installed VC++ 2010 Express as well.
What's really confusing me is that it works perfectly on Python 2.7
Any suggestion appreciated, but note that I'm new to Python.
After some research and a lot of coding, testing and improving, I made this code running on Python 3.4.4 and Windows 10:
import base64
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
PAD = "X"
def key_hash(key):
return SHA256.new(key.encode()).digest()
def encrypt(text, key):
while len(text) % 16 != 0:
text += PAD
cipher = AES.new(key_hash(key))
encrypted = cipher.encrypt(text.encode())
return base64.b64encode(encrypted).decode()
def decrypt(text, key):
cipher = AES.new(key_hash(key))
plain = cipher.decrypt(base64.b64decode(text))
return plain.decode().rstrip(PAD)
if __name__ == '__main__':
e = encrypt("This is my string.", "password")
p = decrypt(e, "password")
print("Encrypted:", e)
print("Plain:", p)
Output:
Encrypted: QjkhFlXG2tklZQgHorpAOFSTb2vYZLNb/eEUIvAsT1g=
Plain: This is my string.
If you have any questions/improvements/critics, feel free to comment!
Maybe because you have " around "b'QuCzNmwiVaq1uendvX7P+g=='".
Change
decryption("b'QuCzNmwiVaq1uendvX7P+g=='")
to
decryption(b'QuCzNmwiVaq1uendvX7P+g==')
and you should be all set.