Following program in python:
from Crypto.Cipher import AES
key = '11223344556677889900aabbccddeeff'.decode("hex")
aesECB = AES.new(key, AES.MODE_ECB)
ciphertext = aesECB.encrypt('1234567890abcdef')
print ciphertext.encode('base64')
Gets me this result:
$ python example_cipher.py
r9yD3EmmAIpxncxZSldsKg==
Following command line from openssl, gets me the same result:
$ echo -n "1234567890abcdef" | openssl aes-128-ecb -K 11223344556677889900aabbccddeeff -nopad | openssl base64
r9yD3EmmAIpxncxZSldsKg==
But this code in Node:
var crypto = require('crypto');
var key = new Buffer('11223344556677889900aabbccddeeff', 'hex');
var plaintext = new Buffer('1234567890abcdef', 'utf8');
var cipher = crypto.createCipher("aes-128-ecb", key);
cipher.setAutoPadding(false);
var ciphertext = cipher.update(plaintext, 'utf8');
console.log(ciphertext.toString('base64'));
Doesn'g gets me the same result:
$ node cipher
tOunZRvle8B6HYuBSzblqw==
Where is the mistake?
First off, DO NOT use ECB if you are in control of the crypto parameters. It is incredibly insecure and should not be used to encrypt data.
Now for ECB, which technically does not use an IV, you still need to use crypto.createCipheriv() with a zero-length IV Buffer to match the output of Python:
var cipher = crypto.createCipheriv("aes-128-ecb", key, Buffer.alloc(0));
Also (in general), you are missing cipher.final() to include any leftover data, so use this instead:
var ciphertext = Buffer.concat([
cipher.update(plaintext, 'utf8'),
cipher.final()
]);
Related
I need to have a python code and a swift code exchange encrypted message.
Here's what I tried:
Fernet
After a review of the options, I thought that a symetric key algorithm could work well.
In python (as usual), it is straightforward to encrypt and decrypt:
Fernet(key).encrypt(b"mdg") # encrypt
Fernet(key).decrypt(encryptedMsg) # decrypt
In swift, it seemed initially straightforward with something along the lines of:
func encrypt(key: String, msg: String) throws -> String {
let data = Data(base64URL: key)!
let symetricKey = try! SymmetricKey(data: d)
let msgUtf8 = msg.data(using: .utf8)!
let sealBox = try! AES.GCM.seal(msgUtf8, using: symetricKey, nonce: nil)
return sealBox.combined.base64EncodedString();
}
However, I have been unable to find the algorithm in swift matching python's Fernet.
ChaCha
While searching for the problem, I landed on this amazing answer from Bram. Very unfortunately it only solves one side of my problem : encrypting messages in python and decoding them in swift. I also need the reverse process.
How to solve this?
To start, we first need a way to create secure random values to generate the IV and keys. You can also generate the keys using CryptoKit's SymmetricKey and extract the data from them, but for now, I'll use this function.
extension Data {
static func secureRandom(ofSize size: Int) -> Data {
var output = [UInt8](repeating: 0, count: size)
_ = SecRandomCopyBytes(kSecRandomDefault, size, &output)
return Data(output)
}
}
We then require the possibility to compute the AES CBC ciphertext, which can be done using CommonCrypto.
func encrypt(plaintext: Data, key: Data, iv: Data) -> Data {
var encryptor: CCCryptorRef?
defer {
CCCryptorRelease(encryptor)
}
var key = Array(key)
var iv = Array(iv)
var plaintext = Array(plaintext)
CCCryptorCreate(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES), CCOperation(kCCOptionPKCS7Padding), &key, key.count, &iv, &encryptor)
var outputBytes = [UInt8](repeating: 0, count: CCCryptorGetOutputLength(encryptor, plaintext.count, false))
CCCryptorUpdate(encryptor, &plaintext, plaintext.count, &outputBytes, outputBytes.count, nil)
var movedBytes = 0
var finalBytes = [UInt8](repeating: 0, count: CCCryptorGetOutputLength(encryptor, 0, true))
CCCryptorFinal(encryptor, &finalBytes, finalBytes.count, &movedBytes)
return Data(outputBytes + finalBytes[0 ..< movedBytes])
}
and the HMAC with the SHA-256 hash function. I recommend using CryptoKit's HMAC implementation here, but to keep things simple, I went with the CommonCrypto implementation.
func computeHMAC(_ data: Data, using key: Data) -> Data {
var data = Array(data)
var key = Array(key)
var macOut = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), &key, key.count, &data, data.count, &macOut)
return Data(macOut)
}
This brings all of this together into the following
let plaintext = Data("Hello world!".utf8)
let signingKey = Data.secureRandom(ofSize: kCCKeySizeAES128)
let cryptoKey = Data.secureRandom(ofSize: kCCKeySizeAES128)
let fernetKey = (signingKey + cryptoKey).base64EncodedString()
let version: [UInt8] = [0x80]
let timestamp: [UInt8] = {
let timestamp = Int(Date().timeIntervalSince1970).bigEndian
return withUnsafeBytes(of: timestamp, Array.init)
}()
let iv = Data.secureRandom(ofSize: kCCBlockSizeAES128)
let ciphertext = encrypt(plaintext: plaintext, key: cryptoKey, iv: iv)
let hmac = computeHMAC(version + timestamp + iv + ciphertext, using: signingKey)
let fernetToken = (version + timestamp + iv + ciphertext + hmac).base64EncodedString()
print("Fernet key: \(fernetKey)")
print("Fernet token: \(fernetToken)")
An example output can be
Fernet key: 7EwFlYNKTGfj+2fSgL3AUqtrRqRs4D1TWNK7t2XbGJQ=
Fernet token: gAAAAABivCLM0y0poDtGOohT1yK4XTDJppYPJdu4fuDTZ5tb9P9KP5ACgX8aJq4imsSdbzOCcvY3Tueo4FYbwyG+ZugozILL+Q==
We can use this in python using cryptography.io's implementation
from cryptography.fernet import Fernet
key = b'7EwFlYNKTGfj+2fSgL3AUqtrRqRs4D1TWNK7t2XbGJQ='
token = b'gAAAAABivCLM0y0poDtGOohT1yK4XTDJppYPJdu4fuDTZ5tb9P9KP5ACgX8aJq4imsSdbzOCcvY3Tueo4FYbwyG+ZugozILL+Q=='
Fernet(key).decrypt(token)
# b'Hello world!'
I have this encrypt with AES function in python:
def encrypt_text(self):
raw = self.PKCS7_padding()
iv = Random.new().read(self.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
encrypted = base64.b64encode(iv + cipher.encrypt(raw))
return encrypted
And then padding it with:
def PKCS7_padding(self):
return self.text+chr(16-len(self.text)%16)*(16-len(self.text)%16)
But when I send it to this c# function
public static string DecryptString(string key, string cipherText)
{
byte[] iv = new byte[16];
byte[] buffer = Convert.FromBase64String(cipherText);
using (Aes aes = Aes.Create())
{
aes.Key = Convert.FromBase64String(key);
aes.IV = iv;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream(buffer))
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader((Stream)cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
Say i send "hello world"
I get a weird mix of the string i sent plus random non pritable characters. EX: "�Ȯ�Ŗf�"��Xhello world"
The key is generated as follows:
key = base64.b64encode(os.urandom(32))
return key.decode()
I do not get any errors but weird text any ideas Thanks! If you have any questions please feel free to ask
In the Python code, a random IV is generated and concatenated with the ciphertext. However, in the C# code, the separation of IV and ciphertext is missing. Instead, a zero IV is applied and the concatenation of IV and ciphertext is decrypted, which causes the problem.
The separation of IV and ciphertext can be added to the C# code e.g. as follows:
byte[] buffer = ...
byte[] ct = new byte[buffer.Length - iv.Length];
Buffer.BlockCopy(buffer, 0, iv, 0, iv.Length);
Buffer.BlockCopy(buffer, iv.Length, ct, 0, ct.Length);
buffer = ct;
using ...
Note that PyCryptodome supports padding in a dedicated module: Crypto.Util.Padding.
Also, in the Python code the Base64 encoded key is nowhere Base64 decoded (but maybe the corresponding code was just not posted).
I have a Kamstrup WMbus water meter sending out frames like this:
21442D2C529027581B168D2814900939201F0775C6452FBBAC155B46A546035219D51AB8
I am trying to decrypt this in Python.
Breaking the frame up I have the following values:
Key 16ce383ebc0790e928215525cd4f7abf
Input IV 2D2C529027581B162890093920000000
Input Data 1F0775C6452FBBAC155B46A546035219D5
Pasting this into http://www.cryptogrium.com/aes-ctr.html results in a valid decrypted frame:
bbe57934ddc46a71004400000044000000
I have tried both PyCrypto and PyCryptodome but neither gives the same correct answer than cryptogrium.com.
from Cryptodome.Cipher import AES
# ctr = Crypto.Util.Counter.new(128, initial_value=int("0000002039099028161B582790522C2D", 16))
# cipher = Crypto.Cipher.AES.new(ecryptionKey, Crypto.Cipher.AES.MODE_CTR, counter=ctr)
# secret = Secret()
spec = AES.new(ecryptionKey, AES.MODE_CTR, iv=inputIV)
dataDec = cipher.decrypt(data)
The first commented out approach runs but gives the wrong result.
The second one stops with the error:
TypeError: Invalid parameters for CTR mode: {'iv': bytearray(b"-,R\x90\'X\x1b\x16(\x90\t9 \x00\x00\x00")}
In C# we are using the following which works:
IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CTR/NoPadding");
ParametersWithIV ivAndKey = new ParametersWithIV(new KeyParameter(keyBytes), inputIVBytes);
cipher.Init(false, ivAndKey);
...
int length1 = cipher.ProcessBytes(data, 0, data.Length, outBuf, 0);
int length2 = cipher.DoFinal(outBuf, length1);
...
I am confused because C# uses the parameters I have: key, data, IV
But Python expects: key, data, counter
Does anyone have an example how I can decrypt this in Python3? Or maybe explain how I should use the IV to set up the counter for AES-CNT?
In the end one of the examples from https://www.programcreek.com/python/example/87998/Crypto.Cipher.AES.MODE_CTR put me on the right track. After fixing a couple of typos in my code it worked correctly.
def kamstrupDecrypt(meterSerial, frame, payload):
inputIV = getInputIV(frame)
print("IV:", binascii.hexlify(inputIV))
ecryptionKey = getEncryptionKey(meterSerial)
print("Key:", binascii.hexlify(ecryptionKey))
counter = Counter.new(128, initial_value = bytes_to_long(inputIV))
cipher = AES.new(ecryptionKey, AES.MODE_CTR, counter=counter)
payloadDec = cipher.decrypt(payload)
print("Decrypted: ", binascii.hexlify(payloadDec))
return payloadDec
I am trying to rewrite a node.js decoding function in python
this is the function I am trying to rewrite
module.exports.decrypt = function (data, key, iv, hmacKey) {
iv = iv.slice(0, 16)
var decipherer = crypto.createDecipheriv('aes-128-ctr', key, iv)
var hmac
if (hmacKey) {
hmac = crypto.createHmac('sha1', hmacKey).update(data).digest()
}
return {
data: decipherer.update(data),
hmac: hmac
}
}
the key parameter is generated by crypto.pbkdf2Sync function of node's standard library.
What I implemented in python is
from Crypto.Cipher import AES
from Crypto.Util import Counter
def decrypt(data, key, iv, hmac=None):
iv = iv[:16]
ctr = Counter.new(128)
cipher = AES.new(key, mode=AES.MODE_CTR, IV=iv, counter=ctr)
return cipher.decrypt(data)
I provided the python decrypt function the same same data that the js function receives.
since the data and key, iv params that the js function expects are binary buffers, I copied them to the python code as hex strings and then used binascii.unhexlify in the python code before passing them to the ecrypt python function.
so the arguments that I am providing to both functions are (hex)
data 972acf88c5d7
key 129b6e542600889a75ec7659d9dc23df
iv 31323331323331323331323331323331323331323331313233313233313233313233
The python function returns gibberish.
can anyone help?
thanks!
Recently, I have a task to make HMAC to communicate API server.
I got a sample code of node.js version which makes HMAC of message. Using concept and sample, I've got to make a python code which is equivalent with node.js version but result is different, but I have no idea why.
Please review both code and help finding the difference.
Python 3.0
import hmac
import string
import hashlib
import base64
secret = 'PYPd1Hv4J6'
message = '1515928475.417'
key = base64.b64encode(secret.encode('utf-8'))
hmac_result = hmac.new(key, message.encode('utf-8'), hashlib.sha512)
print(base64.b64encode(hmac_result.digest()))
Result (Python 3.6)
b'7ohDRJGMGYjfHojnrvNpM3YM9jb+GLJjbQvblzrE17h2yoKfIRGEBSjfOqQFO4iKD7owk+gSciFxFkNB+yPP4g=='
Node.JS
var crypto = require('crypto');
var secret = 'PYPd1Hv4J6';
var message = '1515928475.417'
var key = Buffer(secret, 'base64');
var hmac = crypto.createHmac('sha512', key);
var hmac_result = hmac.update(message).digest('base64');
console.log(hmac_result)
Result (Node.JS 6.11)
m6Z/FxI492VXKDc16tO5XDNvty0Tmv0b1uksSbiwh87+4rmg43hEXM0WmWzkTP3aXB1s5rhm05Hu3g70GTrdEQ==
Your input keys are different, so the outputs will be different.
Node:
var secret = 'PYPd1Hv4J6';
var message = '1515928475.417'
var key = Buffer(secret, 'base64'); // buffer of bytes from the base64-encoded string 'PYPd1Hv4J6'
// <Buffer 3d 83 dd d4 7b f8 27>
Python:
secret = 'PYPd1Hv4J6'
message = '1515928475.417'
key = base64.b64encode(secret.encode('utf-8')) # did you mean b64decode here?
I was able to get them to match by stripping out the base64ing of everything:
Python:
import hmac
import string
import hashlib
import base64
secret = 'PYPd1Hv4J6'
message = '1515928475.417'
key = secret.encode('utf-8')
hmac_result = hmac.new(key, message.encode('utf-8'), hashlib.sha512)
print(base64.b64encode(hmac_result.digest()))
Output:
b'jezLNuBz37FoACm4LdLSqOQ5C93cuGID9a8MQmOZntXklDV3SvWdNfqndzK0a54awKeHY+behFiv4FYyILRoGQ=='
Javascript:
var crypto = require('crypto');
var secret = 'PYPd1Hv4J6';
var message = '1515928475.417'
var hmac = crypto.createHmac('sha512', secret);
var hmac_result = hmac.update(message).digest('base64');
console.log(hmac_result)
Output:
jezLNuBz37FoACm4LdLSqOQ5C93cuGID9a8MQmOZntXklDV3SvWdNfqndzK0a54awKeHY+behFiv4FYyILRoGQ==
Equivalent/Expected python code's is below.
import hmac
import string
import hashlib
import base64
secret = 'PYPd1Hv4J6=='
message = '1515928475.417'
key = base64.b64decode (secret.encode('utf-8'))
hmac_result = hmac.new(key, message.encode('utf-8'), hashlib.sha512)
print(base64.b64encode(hmac_result.digest()))
Padding '=' to targeted and decoding part was important.
Thank you.