getting different results when encoding with python and when encoding with nodejs - python

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.

Related

How to successfully pass an IV from Python to Java (Encryption)?

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;
}

Recover Encrypted String With Python - Passphrase, Salt, and Source Code Known

I have some source code in Kotlin that I only partially understand. I know a little bit of Python so I'm looking to recreate the decrypt function.
I have an encrypted string that I want to recover, and the original passphrase and salt too.
Here is the source (from here, with constants, encrypt function etc):
fun decrypt(passPhrase: String, salt: ByteArray, encryptedData: String): String? {
val iv: ByteArray?
val encrypted: ByteArray?
return try {
lastException = null
val byteBuffer = ByteBuffer.wrap(Base64.decode(encryptedData))
val ivLength = byteBuffer.get().toInt()
iv = ByteArray(ivLength)
byteBuffer[iv]
encrypted = ByteArray(byteBuffer.remaining())
byteBuffer[encrypted]
val cipherDec: Cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipherDec.init(Cipher.DECRYPT_MODE, prepCipherKey(passPhrase, salt), GCMParameterSpec(TAG_LENGTH_BIT, iv))
val dec = cipherDec.doFinal(encrypted)
String(dec)
} catch (e: Exception) {
lastException = e
aapsLogger.error("Decryption failed due to technical exception: $e")
null
}
}
Here is my attempt simplified (I get incorrect padding when using the inputs I have):
def generate_key(password, salt, iterations):
assert iterations > 0
key = base64.b64decode(password + salt)
for i in range(iterations):
key = hashlib.sha256(key).digest()
return key
def decrypt(ciphertext, password):
salt = 'XXXXXXXetc'
key = generate_key(password, salt, NUMBER_OF_ITERATIONS)
cipher = AES.new(key, AES.MODE_GCM)
plaintext = decrypt(ciphertext, cipher)
return plaintext
Edit:
Here is some test data, with passphrase 1234
{
"format": "aaps_encrypted",
"metadata": {
"device_name": "Galaxy S8 SE",
"created_at": "2022-04-19T19:58:17.338Z",
"aaps_version": "3.0.0.1",
"aaps_flavour": "full",
"device_model": "samsung SM-G950F (dreamlte)"
},
"security": {
"file_hash": "96cf809f37eb46d0eb837511bae539b5fadd424e74d3e6eedaa4ceb2fdac07d8",
"algorithm": "v1",
"salt": "4030cc7f15e5f7bf830543270ced2b7c8b24237c53ce95382b232df69c594353",
"content_hash": "197510b071e3e592c43301e77c997400048641e77275a02ebd41a58213e90c61"
},
"content": "DKQDMzcfTQdd59fKSN5s3pmcbs+nK4fS6VUt0+sfWDolkrUqfq33HT82GOpUwCpCtHH0zwY2plw8ZEgv1a4I\/OjDQeueAyVL8NZUiNKWPdtUwSxbfiAuP6P61H1X9iGIAvhuvR+uyy2l\/DNyWqIox2tvcNGXqt3og2mFojiqryB+tKfia8dO7bpph\/8q3\/NGtJajijqTPNCcjLid8N0yivEXKSXaceL9qdGGZNoMGmIGMhrlrS8JBKOfRvFkE7hdat6D8uaF4iyxfuLGY210oDenf2V+GcT5XBWv8nr1NKWaQTJOLx6A2QKZM\/9xKck0fl04PCFFz+d0x+Y3HgiecFrU3uCWKSL901OCUIlDdOiR7U1Ik1BJFfRzZMaB8qLU7uyc9SNzXs55u3Xm2yD14Fa44jhQKkXI\/DplqYl4M8wgSL7M2oCqNIMI4VpRpRF9apGRcdzVCjwnOWk3mBQkOUn\/etITu2kRMB8QZqvxXJtW40ZRjmqNXH7\/xWV+jDr+3QnmdvqVl7YvityxfPmTTnJdQpUYvV8SbKR2kclidhZ9pTF14yndejNrZru5IE5D7a8vuiydkL6vcAchM9ts3MBv\/lg0Bu5y8H5+DCp8lwVBZDgPnjH1CpfNtk\/nYTJnlkkmRdjozZeqjrC8J\/+saRqizgXFqblVXY2A3Z0gF9iVjbGZywsabns1vUuYalqaXKW9U8sNZvs3S0\/ngRC25Rgo6AQdloqKr4PLeCEO18jq\/Vh+Vky8MoAZXS\/xmCyFdAn+EDLCdhKEYTA9Ynl+pRTGAwtS9HLgNq36AohlKQm2vuRP1dchaelzL5tycHuNQq6zqUqh+4j0dw2vd7P8ZRvE3m5wNnOLC7lugpddxnbrwI\/j1Pu9aPlHHZWTy9dKW01IThVC9FWJqnhEhGao19FDXBEAjOUh70o2YIDb3d8oEDLMJX5Weaaoh9itMVLkafwmYx3YhqZqZ+38RykuL7x36aCksefL\/YtphPJkxwPgny2IDM6948uNUg3jUCem7ZwxHTWhgsIxI00PNo2D1ZWAOdk8HPy+gRaJ\/rXsNvo0ErW2CD4gkKT8cvwkRJ2WH+ovcVrcMFO1Ns7a8VdksjuMZhCWa8rzl6efRznT7FkXrKJSmUG5WkLQbPlSirMN2YK2R111qm7hbxvcTMFvGIh6ZZDHdDaaWMi7M3nHRT8AzlTiap1arnTCpSqtMVndB4Gu6i\/fNOFiV+3iXFfDUJsgvcGBDHsOQZ3lCdkHcCz5H1UB8D0tqqPHKt\/TAAwz\/Ew5axshAmiCuKqC+EiTTHkHKd1e5uCF5kRXJWxlzlGNWYJmVZOkPfDpV0mX81Nc9RGgAKnzZ9v\/kHvzKsCjXyPbgzXBuYB+FqaaP8CNNlyrKuTObqndUnCPIbIpBEtMf7auI2n5ME78KXd+fPX1PvCYx3z4Z6FwZVBNtfTiIQgK4obNaPG40ysdcSGonKSDEVNEUDlZOZWTdNxGfXa5XfmsYN+qw3v1+Sd2FDKjlP1BZPOmsCNEWXBrnqLsuoHnGv20che\/spEsS8aTQ4pWCerb4f7o13b303cTbSyQaC0AohH9UfjdXS19pkdHQySV2EXTLfcvfPPX+E39aZG4BiKZ86jbmUQNjhgpoFupK9VUa7ZEEwQfhUOrTvZr+zcwV0fJcD9FaaEcVLs1kez7TRhS\/p8rBUpSvoLa5KSEfquHbQq0WUr\/bSiX0hXVGY0h6MooZ8z\/HCFzzBHhHFZfRfsEbr1F9cJp7cn8MuqDuIH3GJ+6wW8JAb\/ftk\/BHMtfSvT\/TWxmldn+Z5GQwGODJ5nGQTzsFmOJszSoVubVfuEJQ94SMHAB0tULDHhIFDknDgDjEY3VuNQ51ZC8nGas4XuohLTMeZ6cmJJmTnvPYDFb0RlbVrG7TVq7VaoZNIx9tTUhmAaSvbiiDc9pHGbiWAb5oe9dpGWjPDi5kayrckDjWkImy3jJLEV+s9Dtz5R621+GzkQYw0KTKDEaqQtVkWXPP+8do2PyRn+zh5MOpV7CxVKbjs19vR2gcC2DE9Ra\/naK+cvKiwLnzmZEleBCCKsB5+9LJzHvhPrpbaFczKxDi\/A9LAwCFawGjsS6hF58tI2FpZoOw\/FacP1wb8V6sp97dCZjRH14ECMp24AuTfnmbwXmabT5\/zJUyDZoGCd7NONM0lOFBDFmKn+kwo\/gyB5Sy\/YFFVEeTEfzL+49tPfphA\/SmbeyCop51ln8BeIOTTpdAwhSNpyU2YzUB845nN6I+\/9QUO7ErkHBEhbAbwbVAiLev0dkw9J+2V4tZEMVzhoJrV5QjBiu+wvGj0pBUhmSXOUhfyqnlp3XK6FU7M63MWzRlkZarW7FFHcGdhnArqQ9st\/ETdVXGpVR3ZGPxeh0C9ycjUpPKtRxEuNf93JGf99UPxou83zoL3sfEkxfWAbDB0eW2WoNZveuPaLwN3fGEgQaxglEXlM\/L9CaihvCZocseWHtpoBS1HjdHj7gGrRF\/DPOudS8dvu\/mWmK4IaUKIErYZoBnjopDmtpzqoNENjTNkhRvp3ojxg\/DgXsos3t7RxORC07T0SC\/SjUo6veEk5COlLENb49PozZbVIL6YECXp1xJxbf\/QCFiXosA+S34ERwSwkJGYGhaPY42XJUODplYecy22FKjg2HN\/bvR213IQt9ctmZN5JB3wC8Fmyi5faxf2sl9VV3aM7ekpr3tyj7ATAvyRMl1UlnAz30ZO7GGhGhQbxYpe5SU20dpcVfC+U3VQzrsGHW5f6OaI3GJpM5PVL98bK+CfXEvC9acmPyH0AVHPWvYPEih81r2Ek4n2T6evtgrvixbRBGDTr24hB+27u29GEsZnqoL9vPx7xJeJ1WYYKYerVg+t2GiVgEmn3XOFt3CkrSrp144HrWo0ZgAipTQ8BHVJ45r5WsojRLKrazpJMYLnFBSx4iokhtODb9dv0Tum+wZ2zUIhsXDMBbAsjSDhJtrQLwuCoLzaz7o5BGinq9fIJks7uVZ5dUGe5dMJux8zNwujPVP9fuwsmmP6ObbGsG4MEhgyVoSpV39v8l7gyrulLp3D1fxbtNmZ9JolHFNtGTrkbouFjEnKn8B4iE4ofLGcuWGBnw\/zVSerFdMO8\/I4FaofLFQCLmx92s\/ROBXuycYJ4Isa4flJAwWK8SKIFpGWQg\/FfLkij611I+1i5m8wHX2XHcm5veA4ZcdcfhzqtywP8onLia0KZdZMrRRwa4oz6wumV3m56tyS\/zeEiPri9iqitZnlVwEtHCh\/jTQwfUykbMiidVficpqPGi5k8D0iUTJ5FtFSQkVOpK9KOX7CgO\/z6958d\/ZA9SJ1XZumuTBDfFmOwI3nkOIbThjGfvs7OlHyxP6K6k3Jkygt8bLUIshJHwLOPRlFlNhZLN9mcIYRhdVMJr0ZODLjdTLB94gLYUTffsLc27JHVeBEWWtDBr8IEc3Oq7cqPZpbSTM1\/5pjlxRXYIUIlgeAbUMeg\/yHof6NyPpzjO1bxhj1YM4qmnWL34f82eO\/3sim5GcT56hm5pkFeF2vKs+JKbVNYwaMOYhcuQBCXT+Ta+hqCjColVQrJf9twy1k+aGkJrMBaKelIYaWprKGennWL\/hVQwEgOR28426euRTHdgWPsQqe46hy1FabEjiWgRYsSiIwKHszpKXEyy760BvJqeJhRC4FFDooD8grnjuoDpnK8brUhlwWlmpPDSbm02eZSPxT54zBkti5u\/EONG2Dj3EtQ6jaQ29CvxVkIHPwapyTWBMO3d4dGJ0pfhYVHLwxtd02I8eVBU471jv57cU\/P4ypYGLMt3q14MMfKVTKqSbE0lM0mVt1wPgRTolIsAbRJYdDuwrQXxFM0mo1SXjkAwdoXxlYwBnLT6rsLwQpI0VrkESygdFjuWrc\/1IDbUkoy99nlC7YmQ9drvR6h7Jhol2LuQPche+fnIy4rzx6XD6K1GFwa5Rii\/pBCvJ4AH9z3EwKhvL1Q0U1SjUiKTv69OSWfQKmjFLehtRrXx8odVFiYafxhfsskstPDkbdElT5SvLUOZOrqqdHq78LJuLC7GBRd1OPTG0VpokmSGNigRAwV+ZvlY8D\/Xqlyc92FCnl9auQGsfwmcvcgiM74YcRTKFfH4sHmIPgWxBH3\/oX5S9fn40hOORHMpQwqpdD+IvMiVS1dgKfThNT39JaMu441THkPH9ijYLJdlUv+SVtiMqnk+rZXgNsSeWSkhduGKs22wXceFM1CWIZtlvM6VL\/n8tHaOF0hk1F5GJ1kSbAG97YG8dRTU9XvcOzyStCYw1yDGH0Wo0V8iLyPefBNbueFpPcnkQ9sAt9fhDx+v4XJ6X9Vq0iNvGbZQWo8wZghAOjMPNiopJUuL4hky4rXxrp0o5uCtNJSvmgs8FUUD8Wjn5\/9ZMA4EXNvmNBxSrpifiwWx+Hrq9tUD22uFb\/8Y5BWHjSNUOeed1QG9upxKf6FXPohfDVd1Y7ECNh7J12yvrFkxo+PiUDlyC47OKzc4NlYZMZLLA4udHln4aAXEnwRm6tmK0tKbbTWkjsgWrQChT\/Soxv4a\/vqjqvwLzm1grr2iaYSYAOMRDMwRse\/h9L913RKI9+IbmRfToxgTcrSqIhBDM99H+PARRIBZtf75dEOn4yGBqEMyroPgR0mjs2dPslMOntIPYbLAUoAKydpc0FNbvmpmaX5n0N1dFB3UJZOFYKP+A5nx8kYWsKhG+nfjvMVPF7eFW7YksQfWqT8xka33ySfWhdPbtbyNWXJxKUCZSzzorc4qHQuo5GQCO3Tu\/V4TTfNENcxgE3VZDQ0qUPix4ShIx5WDn7uYtj8oP+jWuqbwu+blV45w\/u8\/yAbR5HWDfY5n3ce3yeqPlaX50jZ5iBWdY27LtYwy+R79cgxL+x\/hMMmftKoz1e5UL33JJ+Aq+clbjUSKkSK7lpuJDhCUb2MXi0jy2KkyHfP5vPcdcuqLCwc\/UPRrxjsYfIQDzryGHXbX2RoFPINnZU0uqOm2YuHz9+9zEX1ahpTKTx0y2j7OdsD2PBAJs1a8ip9j4Xx+7ZNM6Ngqvsm2cAjurtcp52yCkIuDOoVU6t5OBwCbm6SRQswT1oayjEgbUn\/Kn6+oXDrx9Vc4G\/hZk95kK1Ygu6Sh1PEeftTNXVfYkpfjRgb0UQDPpupDL83cEKC+WhSp\/3S2sMo8tUtENPX9EXO+nLEy6XZEA1FpalN6vT4ThMYUximQEGXb\/fYerhaC353Ht\/f5Dr56KGuwDPCSpL4yJJ6BloU2qgxwZiIHp8IM86k1bnMuCnY3KZltvPcXXJ6E5sDQB0Z1BLnnSnLKuAKeX6ulJ5noBkTzZe4rQwHF5cXNQx9pX6TFKxjSiu9qAB9fQYrm6Faj8FqbTVhACKSXHw7kj3DOeavgE28wyj9pO\/jfGfwV8sP3xOH1r4pM6CbuJavgXticH0X9ho6VyrRrsy74XZp1AI6FBHP6iIdA\/b7w7il8Vl7TsaZyf+xbYtCM1hIcsgCUVo50tYG3CoL+5kOjfzViZnxlJZhEGqZssp+\/kOb7Ty2ZoS0ZewjqRwOesRdEFM2ABy43qLuT7C1ulOJgYxKkAh4leb3vd0HYIXXTEkbfxrhHQ9j2dB3by6OyPbPgw8dVojeSAL\/7\/zaJoJdApKfeiV8BCHgsNdrdoVonOH4an0SD3YVuB5Yz81Z2C43WEJIUBITL\/RVnVLIGOxVmEWHD4FkwQPgmUS770Z8rqq2sZRUIGgYgKSeAGkslE4itJUQ+\/LeFwbK3WYaVa8iT4Rd74goc7Dm39h54qZ6dgel0CqgvzAngw2NmTP3aETa\/Co+eMwNyCLP+QxGEzcnqgcs63YQyqrtG4pVhQg5cMkQbRpqsytHPjg2mS+fBSAD1sXF5lrThlg6skL70o4H36abbAjOST\/ThvJ\/hViFb6lCD7w+iXDuOa0+p3sn4yhgMIqcRwKjcIxx5GsGNNSJDKWWJn7zxdT525gwSh7rf\/aGVI9PNUhlXC0bdcHQ4UjIkqSa5MQbapiDDhg1PEqDlBa0v6vqSIdDAKaxWVZNYk0Msr3cNwaAYyQfdYVCrd6CN0bFIeOWOng6klQhTYxkhmrsogCXCsMLDFaBr\/IbfQSMfwtsYiM9Jtcn18QjfumXucfKL\/F0rP0D3FKOD72idANPCy+73lYAxGSDLVmT4wmsGCEcmAtrJHCauHn65T+lSZ5d8LGFbU4P3fzP2i0+vDfPh74\/BOpx3rPgJ24chryugVZ3LRgCYrphkiiz7u+cAAsUUmcb3\/9Oy8hRdqMYIW+d8bvLygwdt\/5BjGALaF4l6kr+BuC3Zdfm3VpNgSd0yneWc81sHJVY+NTGGH6r0UEGlNOaXfv3EgCVKFIVVxQ7sdQZMlVsh\/st7NET6aPvEALHnfcC1LxmnBq4eDHNPD9Dkf2BPedenQM+nl1kzvFAn3wmRknOkW5R1yNn\/ytxx0HGmmR8Ice5wgZ4O1iiu6gYQIgfNh7iqZa6f9+uRR7OPx3G3F3FqU494Fz966O311NQWt0v9OsZcTCjdHJacQQAlrhj6ueuAjvzwPEONSf9K2l6TNYcFqw8GzppCc10C+R\/q72nSInBhsDSTw3uxgP+acqC6vthAUi6ddQSsn1n8go5+hhZ\/+BG1253M3k646Lzf3auMRmIpsm7+S1ILBZ0yoDTTh4c2+MeN2WhTaoyX2aA2SN3uNtLVEKeiZS6abtkp1VnoMP0j2IXlCkP+mii\/oqche0vGbbTphq2UUANX7KnHbbO1cOZs9kQW7JKmCzXIb9qvqF3agB8R8x++FlXXvzaE0uEX2OLHI5n\/jUgTEu+jktNbX\/fXGZmPg+T0Fcy10N1LGumnMIf4DZ3LQpBMosI\/Q7O1HfZslxjibux6HTQHMQq\/3rJ4COnsYgLhHveFY05pCAKJIX+VXbW89zVgx\/7FMBse7XVNAxZM4s+cIsPT8C4k12hqFoxBlMshjSOrF2IXLo3ha7jWl9+U4pK6wlCS3jxkIRjItzHss0Wsin9GuIAwsHo9mSpjVV88g93JOWNda7m9ClSSIBf6XVmcEVnWnRASOltW+8XKt66XviOlDQ9kc+s5j9UVKrh4EJ5y1+aVitzn95F\/7YRZqwC3WKWLAnZcYkEw2Gowg9AUg5WCbpaKoWt\/BLP+Rvklq+eWl8B9IthFtiWVVNR+j8yeJur\/5jwgrEqmRysRi2gFWhrEtA+bnx9R1llfuYjHxxPjMjrtmPRwL5\/ixnEajtuXEURjd227mnRXXuj7sYPcEb2bR18PgE4YTCkSNPlKN7tEg03TLMSk5BGtNJ4vRbfuGCWDdza7AXu8jBlF7Iz4Fr1XXwRRH1Cd4p1L6ChT\/ODh1Co4nlsbj+ttjX4g8KcHBeZJnpCySC3crd5gRRSgl1Q+Ipj3Jn9RI04uSFYW+5ju9A3ssgn9Vc5IaxH++U2R4271sbTJ3hl95flFkPeqQLFBhl2BUs7MBzuaFnhzuD5aRiYln8bHzR0Xj2jGjUtTIenvf\/ooVaYMENUwkY8hu8BYJQe183rSy7Y6dSvRZLvU7pOak2u1X0j3eDXEcFqbMpoNtJUSpGy9X4SoqD4KOcbOlP6b0y63tfZ2Y+tBQx\/MF7RbuMJQN+sgD8iBltpJsYXgatTsvUw2l\/G197UjG3FrZDROIZgnFCY\/v4E4MUm8h2Vb7c7mU2WijpNMf2XL8Lk5533OltGoenaAU68qxrcSU2+xs\/tNcLql+wwxNG1p7qC1lMRKzjfI88HdFIsaOmWPG62jzmkrZqeRp0Bp6ppeJu\/IFdmus2ELqrmewgk4GSk7cm4qK\/tE9xxYg0xD50exelnOuCzJyT5EyMa89pCHKKcr1M6rn90AD6fBq7AIRvb2GP44ADTNJrDG4ZjC+e13Flp7cf5LLWByYxe\/ku4woR1g1DeJvWZnXOwuL5Z6OwmjM05MV9GvJIeIu0f469ameZlZp9n9EOE9qhqaS1hHqbLppPSnf5TEP5ElNjbv2jIWwPJbANa0CHFf1DqDM+Lh95CYgZTPozesNUasWH6\/5drnxGhiJgqVTHM0aFFO4pEVfV49hltrzoxlOy3ZfDCDw\/qYfpN9Ej9Nw8M+zjm4yWCbpjXuHa3BnZ2yBBT\/Tsb2rqrOWSffaRMVak80px9OhdW\/MSsYqIxu2F\/1l1QlzyksAQfZREfJ5mDYMGIyD2uVjK66asosDtx2qCy3vsVdatB7sT24yiZHAfh1XfWqIv6pkJNuVYCQQNMj7Yl3fhMcDKn4sKox1W\/1XPyGPYud+rZuAVzYLXgDihHRVRy3FnOgIylhSAXyQrnS3kDFIFreD3yC\/vo98BHr\/53\/UgyJtVQEtqX2MKgZYx0qrCxGTqODZ2KL6t54bN9MTZP0kLD+uwqFx1aPwudrNffCU7\/AVwte+4HjULcubQ3bsQcYYS9jDPo1upDYvvXmOlYj4dMCiO0Hvk1X1Wd6yS6Vz9SXFud3iHsfKdKcYMzSMTXX07tAnHBU0uYS5Cipbb3sKJHhmPouj+EjNpWr6D6XwPKKL4J\/N5UzMWvjFXmeu7i7AzozgrMPhRrjyAv8hIDI9+4JiJpWQKbwWek0y4uUU9TnxvRqrvv5wgX+HFMAm36UqfUzFFbfAiULEgKPIF8jDwVp3fzZyWRtZYpc8xDOLAqM2H+rQOre7\/6LNHNwHdDceY4vQ9rJ16UB+GwShFyHRmKqP2DPV7yJG11MOOAl6l1ugKGXWz22c3ulRowRP\/qBeBarbgY="
}
When encrypting with the Kotlin code, the following data is concatenated at the end:
Size of the nonce
nonce
ciphertext/tag
When decrypting with the Python code, this data must be separated.
Note that Kotlin/Java returns as ciphertext the concatenation of actual ciphertext and tag. These two parts must also be separated, since Python processes both separately.
In addition, PBKDF2 with HMAC/SHA1 is used as the key derivation in the Kotlin code. This algorithm must also be used for decryption with the Python code.
A possible implementation in Python is:
import base64
from Crypto.Hash import SHA1
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
AES_KEY_SIZE_BIT = 256
TAG_LENGTH_BIT = 128
PBKDF2_ITERATIONS = 50000
def decrypt(passphrase, salt, ciphertext):
# Separation
ciphertextBytes = base64.b64decode(ciphertext)
ivSize = int.from_bytes(ciphertextBytes[:1], "big")
iv = ciphertextBytes[1:1+ivSize]
actualCiphertext = ciphertextBytes[1+ivSize:-TAG_LENGTH_BIT//8]
tag = ciphertextBytes[-TAG_LENGTH_BIT//8:]
# Key derivation
key = PBKDF2(passphrase, salt, AES_KEY_SIZE_BIT//8, count=PBKDF2_ITERATIONS, hmac_hash_module=SHA1)
# Decryption
cipher = AES.new(key, AES.MODE_GCM, nonce=iv)
decrypted = cipher.decrypt_and_verify(actualCiphertext, tag)
return decrypted
passphrase = "1234".encode('utf-8')
salt = bytes.fromhex("4030cc7f15e5f7bf830543270ced2b7c8b24237c53ce95382b232df69c594353")
ciphertext = "DKQDMzcfTQdd59fKSN5s3pmcbs+nK4fS6VUt0+sfWDolkrUqfq33HT82GOpUwCpCtHH0zwY2plw8ZEgv1a4I/OjDQeueAyVL8NZUiNKWPdtUwSxbfiAuP6P61H1X9iGIAvhuvR+uyy2l/DNyWqIox2tvcNGXqt3og2mFojiqryB+tKfia8dO7bpph/8q3/NGtJajijqTPNCcjLid8N0yivEXKSXaceL9qdGGZNoMGmIGMhrlrS8JBKOfRvFkE7hdat6D8uaF4iyxfuLGY210oDenf2V+GcT5XBWv8nr1NKWaQTJOLx6A2QKZM/9xKck0fl04PCFFz+d0x+Y3HgiecFrU3uCWKSL901OCUIlDdOiR7U1Ik1BJFfRzZMaB8qLU7uyc9SNzXs55u3Xm2yD14Fa44jhQKkXI/DplqYl4M8wgSL7M2oCqNIMI4VpRpRF9apGRcdzVCjwnOWk3mBQkOUn/etITu2kRMB8QZqvxXJtW40ZRjmqNXH7/xWV+jDr+3QnmdvqVl7YvityxfPmTTnJdQpUYvV8SbKR2kclidhZ9pTF14yndejNrZru5IE5D7a8vuiydkL6vcAchM9ts3MBv/lg0Bu5y8H5+DCp8lwVBZDgPnjH1CpfNtk/nYTJnlkkmRdjozZeqjrC8J/+saRqizgXFqblVXY2A3Z0gF9iVjbGZywsabns1vUuYalqaXKW9U8sNZvs3S0/ngRC25Rgo6AQdloqKr4PLeCEO18jq/Vh+Vky8MoAZXS/xmCyFdAn+EDLCdhKEYTA9Ynl+pRTGAwtS9HLgNq36AohlKQm2vuRP1dchaelzL5tycHuNQq6zqUqh+4j0dw2vd7P8ZRvE3m5wNnOLC7lugpddxnbrwI/j1Pu9aPlHHZWTy9dKW01IThVC9FWJqnhEhGao19FDXBEAjOUh70o2YIDb3d8oEDLMJX5Weaaoh9itMVLkafwmYx3YhqZqZ+38RykuL7x36aCksefL/YtphPJkxwPgny2IDM6948uNUg3jUCem7ZwxHTWhgsIxI00PNo2D1ZWAOdk8HPy+gRaJ/rXsNvo0ErW2CD4gkKT8cvwkRJ2WH+ovcVrcMFO1Ns7a8VdksjuMZhCWa8rzl6efRznT7FkXrKJSmUG5WkLQbPlSirMN2YK2R111qm7hbxvcTMFvGIh6ZZDHdDaaWMi7M3nHRT8AzlTiap1arnTCpSqtMVndB4Gu6i/fNOFiV+3iXFfDUJsgvcGBDHsOQZ3lCdkHcCz5H1UB8D0tqqPHKt/TAAwz/Ew5axshAmiCuKqC+EiTTHkHKd1e5uCF5kRXJWxlzlGNWYJmVZOkPfDpV0mX81Nc9RGgAKnzZ9v/kHvzKsCjXyPbgzXBuYB+FqaaP8CNNlyrKuTObqndUnCPIbIpBEtMf7auI2n5ME78KXd+fPX1PvCYx3z4Z6FwZVBNtfTiIQgK4obNaPG40ysdcSGonKSDEVNEUDlZOZWTdNxGfXa5XfmsYN+qw3v1+Sd2FDKjlP1BZPOmsCNEWXBrnqLsuoHnGv20che/spEsS8aTQ4pWCerb4f7o13b303cTbSyQaC0AohH9UfjdXS19pkdHQySV2EXTLfcvfPPX+E39aZG4BiKZ86jbmUQNjhgpoFupK9VUa7ZEEwQfhUOrTvZr+zcwV0fJcD9FaaEcVLs1kez7TRhS/p8rBUpSvoLa5KSEfquHbQq0WUr/bSiX0hXVGY0h6MooZ8z/HCFzzBHhHFZfRfsEbr1F9cJp7cn8MuqDuIH3GJ+6wW8JAb/ftk/BHMtfSvT/TWxmldn+Z5GQwGODJ5nGQTzsFmOJszSoVubVfuEJQ94SMHAB0tULDHhIFDknDgDjEY3VuNQ51ZC8nGas4XuohLTMeZ6cmJJmTnvPYDFb0RlbVrG7TVq7VaoZNIx9tTUhmAaSvbiiDc9pHGbiWAb5oe9dpGWjPDi5kayrckDjWkImy3jJLEV+s9Dtz5R621+GzkQYw0KTKDEaqQtVkWXPP+8do2PyRn+zh5MOpV7CxVKbjs19vR2gcC2DE9Ra/naK+cvKiwLnzmZEleBCCKsB5+9LJzHvhPrpbaFczKxDi/A9LAwCFawGjsS6hF58tI2FpZoOw/FacP1wb8V6sp97dCZjRH14ECMp24AuTfnmbwXmabT5/zJUyDZoGCd7NONM0lOFBDFmKn+kwo/gyB5Sy/YFFVEeTEfzL+49tPfphA/SmbeyCop51ln8BeIOTTpdAwhSNpyU2YzUB845nN6I+/9QUO7ErkHBEhbAbwbVAiLev0dkw9J+2V4tZEMVzhoJrV5QjBiu+wvGj0pBUhmSXOUhfyqnlp3XK6FU7M63MWzRlkZarW7FFHcGdhnArqQ9st/ETdVXGpVR3ZGPxeh0C9ycjUpPKtRxEuNf93JGf99UPxou83zoL3sfEkxfWAbDB0eW2WoNZveuPaLwN3fGEgQaxglEXlM/L9CaihvCZocseWHtpoBS1HjdHj7gGrRF/DPOudS8dvu/mWmK4IaUKIErYZoBnjopDmtpzqoNENjTNkhRvp3ojxg/DgXsos3t7RxORC07T0SC/SjUo6veEk5COlLENb49PozZbVIL6YECXp1xJxbf/QCFiXosA+S34ERwSwkJGYGhaPY42XJUODplYecy22FKjg2HN/bvR213IQt9ctmZN5JB3wC8Fmyi5faxf2sl9VV3aM7ekpr3tyj7ATAvyRMl1UlnAz30ZO7GGhGhQbxYpe5SU20dpcVfC+U3VQzrsGHW5f6OaI3GJpM5PVL98bK+CfXEvC9acmPyH0AVHPWvYPEih81r2Ek4n2T6evtgrvixbRBGDTr24hB+27u29GEsZnqoL9vPx7xJeJ1WYYKYerVg+t2GiVgEmn3XOFt3CkrSrp144HrWo0ZgAipTQ8BHVJ45r5WsojRLKrazpJMYLnFBSx4iokhtODb9dv0Tum+wZ2zUIhsXDMBbAsjSDhJtrQLwuCoLzaz7o5BGinq9fIJks7uVZ5dUGe5dMJux8zNwujPVP9fuwsmmP6ObbGsG4MEhgyVoSpV39v8l7gyrulLp3D1fxbtNmZ9JolHFNtGTrkbouFjEnKn8B4iE4ofLGcuWGBnw/zVSerFdMO8/I4FaofLFQCLmx92s/ROBXuycYJ4Isa4flJAwWK8SKIFpGWQg/FfLkij611I+1i5m8wHX2XHcm5veA4ZcdcfhzqtywP8onLia0KZdZMrRRwa4oz6wumV3m56tyS/zeEiPri9iqitZnlVwEtHCh/jTQwfUykbMiidVficpqPGi5k8D0iUTJ5FtFSQkVOpK9KOX7CgO/z6958d/ZA9SJ1XZumuTBDfFmOwI3nkOIbThjGfvs7OlHyxP6K6k3Jkygt8bLUIshJHwLOPRlFlNhZLN9mcIYRhdVMJr0ZODLjdTLB94gLYUTffsLc27JHVeBEWWtDBr8IEc3Oq7cqPZpbSTM1/5pjlxRXYIUIlgeAbUMeg/yHof6NyPpzjO1bxhj1YM4qmnWL34f82eO/3sim5GcT56hm5pkFeF2vKs+JKbVNYwaMOYhcuQBCXT+Ta+hqCjColVQrJf9twy1k+aGkJrMBaKelIYaWprKGennWL/hVQwEgOR28426euRTHdgWPsQqe46hy1FabEjiWgRYsSiIwKHszpKXEyy760BvJqeJhRC4FFDooD8grnjuoDpnK8brUhlwWlmpPDSbm02eZSPxT54zBkti5u/EONG2Dj3EtQ6jaQ29CvxVkIHPwapyTWBMO3d4dGJ0pfhYVHLwxtd02I8eVBU471jv57cU/P4ypYGLMt3q14MMfKVTKqSbE0lM0mVt1wPgRTolIsAbRJYdDuwrQXxFM0mo1SXjkAwdoXxlYwBnLT6rsLwQpI0VrkESygdFjuWrc/1IDbUkoy99nlC7YmQ9drvR6h7Jhol2LuQPche+fnIy4rzx6XD6K1GFwa5Rii/pBCvJ4AH9z3EwKhvL1Q0U1SjUiKTv69OSWfQKmjFLehtRrXx8odVFiYafxhfsskstPDkbdElT5SvLUOZOrqqdHq78LJuLC7GBRd1OPTG0VpokmSGNigRAwV+ZvlY8D/Xqlyc92FCnl9auQGsfwmcvcgiM74YcRTKFfH4sHmIPgWxBH3/oX5S9fn40hOORHMpQwqpdD+IvMiVS1dgKfThNT39JaMu441THkPH9ijYLJdlUv+SVtiMqnk+rZXgNsSeWSkhduGKs22wXceFM1CWIZtlvM6VL/n8tHaOF0hk1F5GJ1kSbAG97YG8dRTU9XvcOzyStCYw1yDGH0Wo0V8iLyPefBNbueFpPcnkQ9sAt9fhDx+v4XJ6X9Vq0iNvGbZQWo8wZghAOjMPNiopJUuL4hky4rXxrp0o5uCtNJSvmgs8FUUD8Wjn5/9ZMA4EXNvmNBxSrpifiwWx+Hrq9tUD22uFb/8Y5BWHjSNUOeed1QG9upxKf6FXPohfDVd1Y7ECNh7J12yvrFkxo+PiUDlyC47OKzc4NlYZMZLLA4udHln4aAXEnwRm6tmK0tKbbTWkjsgWrQChT/Soxv4a/vqjqvwLzm1grr2iaYSYAOMRDMwRse/h9L913RKI9+IbmRfToxgTcrSqIhBDM99H+PARRIBZtf75dEOn4yGBqEMyroPgR0mjs2dPslMOntIPYbLAUoAKydpc0FNbvmpmaX5n0N1dFB3UJZOFYKP+A5nx8kYWsKhG+nfjvMVPF7eFW7YksQfWqT8xka33ySfWhdPbtbyNWXJxKUCZSzzorc4qHQuo5GQCO3Tu/V4TTfNENcxgE3VZDQ0qUPix4ShIx5WDn7uYtj8oP+jWuqbwu+blV45w/u8/yAbR5HWDfY5n3ce3yeqPlaX50jZ5iBWdY27LtYwy+R79cgxL+x/hMMmftKoz1e5UL33JJ+Aq+clbjUSKkSK7lpuJDhCUb2MXi0jy2KkyHfP5vPcdcuqLCwc/UPRrxjsYfIQDzryGHXbX2RoFPINnZU0uqOm2YuHz9+9zEX1ahpTKTx0y2j7OdsD2PBAJs1a8ip9j4Xx+7ZNM6Ngqvsm2cAjurtcp52yCkIuDOoVU6t5OBwCbm6SRQswT1oayjEgbUn/Kn6+oXDrx9Vc4G/hZk95kK1Ygu6Sh1PEeftTNXVfYkpfjRgb0UQDPpupDL83cEKC+WhSp/3S2sMo8tUtENPX9EXO+nLEy6XZEA1FpalN6vT4ThMYUximQEGXb/fYerhaC353Ht/f5Dr56KGuwDPCSpL4yJJ6BloU2qgxwZiIHp8IM86k1bnMuCnY3KZltvPcXXJ6E5sDQB0Z1BLnnSnLKuAKeX6ulJ5noBkTzZe4rQwHF5cXNQx9pX6TFKxjSiu9qAB9fQYrm6Faj8FqbTVhACKSXHw7kj3DOeavgE28wyj9pO/jfGfwV8sP3xOH1r4pM6CbuJavgXticH0X9ho6VyrRrsy74XZp1AI6FBHP6iIdA/b7w7il8Vl7TsaZyf+xbYtCM1hIcsgCUVo50tYG3CoL+5kOjfzViZnxlJZhEGqZssp+/kOb7Ty2ZoS0ZewjqRwOesRdEFM2ABy43qLuT7C1ulOJgYxKkAh4leb3vd0HYIXXTEkbfxrhHQ9j2dB3by6OyPbPgw8dVojeSAL/7/zaJoJdApKfeiV8BCHgsNdrdoVonOH4an0SD3YVuB5Yz81Z2C43WEJIUBITL/RVnVLIGOxVmEWHD4FkwQPgmUS770Z8rqq2sZRUIGgYgKSeAGkslE4itJUQ+/LeFwbK3WYaVa8iT4Rd74goc7Dm39h54qZ6dgel0CqgvzAngw2NmTP3aETa/Co+eMwNyCLP+QxGEzcnqgcs63YQyqrtG4pVhQg5cMkQbRpqsytHPjg2mS+fBSAD1sXF5lrThlg6skL70o4H36abbAjOST/ThvJ/hViFb6lCD7w+iXDuOa0+p3sn4yhgMIqcRwKjcIxx5GsGNNSJDKWWJn7zxdT525gwSh7rf/aGVI9PNUhlXC0bdcHQ4UjIkqSa5MQbapiDDhg1PEqDlBa0v6vqSIdDAKaxWVZNYk0Msr3cNwaAYyQfdYVCrd6CN0bFIeOWOng6klQhTYxkhmrsogCXCsMLDFaBr/IbfQSMfwtsYiM9Jtcn18QjfumXucfKL/F0rP0D3FKOD72idANPCy+73lYAxGSDLVmT4wmsGCEcmAtrJHCauHn65T+lSZ5d8LGFbU4P3fzP2i0+vDfPh74/BOpx3rPgJ24chryugVZ3LRgCYrphkiiz7u+cAAsUUmcb3/9Oy8hRdqMYIW+d8bvLygwdt/5BjGALaF4l6kr+BuC3Zdfm3VpNgSd0yneWc81sHJVY+NTGGH6r0UEGlNOaXfv3EgCVKFIVVxQ7sdQZMlVsh/st7NET6aPvEALHnfcC1LxmnBq4eDHNPD9Dkf2BPedenQM+nl1kzvFAn3wmRknOkW5R1yNn/ytxx0HGmmR8Ice5wgZ4O1iiu6gYQIgfNh7iqZa6f9+uRR7OPx3G3F3FqU494Fz966O311NQWt0v9OsZcTCjdHJacQQAlrhj6ueuAjvzwPEONSf9K2l6TNYcFqw8GzppCc10C+R/q72nSInBhsDSTw3uxgP+acqC6vthAUi6ddQSsn1n8go5+hhZ/+BG1253M3k646Lzf3auMRmIpsm7+S1ILBZ0yoDTTh4c2+MeN2WhTaoyX2aA2SN3uNtLVEKeiZS6abtkp1VnoMP0j2IXlCkP+mii/oqche0vGbbTphq2UUANX7KnHbbO1cOZs9kQW7JKmCzXIb9qvqF3agB8R8x++FlXXvzaE0uEX2OLHI5n/jUgTEu+jktNbX/fXGZmPg+T0Fcy10N1LGumnMIf4DZ3LQpBMosI/Q7O1HfZslxjibux6HTQHMQq/3rJ4COnsYgLhHveFY05pCAKJIX+VXbW89zVgx/7FMBse7XVNAxZM4s+cIsPT8C4k12hqFoxBlMshjSOrF2IXLo3ha7jWl9+U4pK6wlCS3jxkIRjItzHss0Wsin9GuIAwsHo9mSpjVV88g93JOWNda7m9ClSSIBf6XVmcEVnWnRASOltW+8XKt66XviOlDQ9kc+s5j9UVKrh4EJ5y1+aVitzn95F/7YRZqwC3WKWLAnZcYkEw2Gowg9AUg5WCbpaKoWt/BLP+Rvklq+eWl8B9IthFtiWVVNR+j8yeJur/5jwgrEqmRysRi2gFWhrEtA+bnx9R1llfuYjHxxPjMjrtmPRwL5/ixnEajtuXEURjd227mnRXXuj7sYPcEb2bR18PgE4YTCkSNPlKN7tEg03TLMSk5BGtNJ4vRbfuGCWDdza7AXu8jBlF7Iz4Fr1XXwRRH1Cd4p1L6ChT/ODh1Co4nlsbj+ttjX4g8KcHBeZJnpCySC3crd5gRRSgl1Q+Ipj3Jn9RI04uSFYW+5ju9A3ssgn9Vc5IaxH++U2R4271sbTJ3hl95flFkPeqQLFBhl2BUs7MBzuaFnhzuD5aRiYln8bHzR0Xj2jGjUtTIenvf/ooVaYMENUwkY8hu8BYJQe183rSy7Y6dSvRZLvU7pOak2u1X0j3eDXEcFqbMpoNtJUSpGy9X4SoqD4KOcbOlP6b0y63tfZ2Y+tBQx/MF7RbuMJQN+sgD8iBltpJsYXgatTsvUw2l/G197UjG3FrZDROIZgnFCY/v4E4MUm8h2Vb7c7mU2WijpNMf2XL8Lk5533OltGoenaAU68qxrcSU2+xs/tNcLql+wwxNG1p7qC1lMRKzjfI88HdFIsaOmWPG62jzmkrZqeRp0Bp6ppeJu/IFdmus2ELqrmewgk4GSk7cm4qK/tE9xxYg0xD50exelnOuCzJyT5EyMa89pCHKKcr1M6rn90AD6fBq7AIRvb2GP44ADTNJrDG4ZjC+e13Flp7cf5LLWByYxe/ku4woR1g1DeJvWZnXOwuL5Z6OwmjM05MV9GvJIeIu0f469ameZlZp9n9EOE9qhqaS1hHqbLppPSnf5TEP5ElNjbv2jIWwPJbANa0CHFf1DqDM+Lh95CYgZTPozesNUasWH6/5drnxGhiJgqVTHM0aFFO4pEVfV49hltrzoxlOy3ZfDCDw/qYfpN9Ej9Nw8M+zjm4yWCbpjXuHa3BnZ2yBBT/Tsb2rqrOWSffaRMVak80px9OhdW/MSsYqIxu2F/1l1QlzyksAQfZREfJ5mDYMGIyD2uVjK66asosDtx2qCy3vsVdatB7sT24yiZHAfh1XfWqIv6pkJNuVYCQQNMj7Yl3fhMcDKn4sKox1W/1XPyGPYud+rZuAVzYLXgDihHRVRy3FnOgIylhSAXyQrnS3kDFIFreD3yC/vo98BHr/53/UgyJtVQEtqX2MKgZYx0qrCxGTqODZ2KL6t54bN9MTZP0kLD+uwqFx1aPwudrNffCU7/AVwte+4HjULcubQ3bsQcYYS9jDPo1upDYvvXmOlYj4dMCiO0Hvk1X1Wd6yS6Vz9SXFud3iHsfKdKcYMzSMTXX07tAnHBU0uYS5Cipbb3sKJHhmPouj+EjNpWr6D6XwPKKL4J/N5UzMWvjFXmeu7i7AzozgrMPhRrjyAv8hIDI9+4JiJpWQKbwWek0y4uUU9TnxvRqrvv5wgX+HFMAm36UqfUzFFbfAiULEgKPIF8jDwVp3fzZyWRtZYpc8xDOLAqM2H+rQOre7/6LNHNwHdDceY4vQ9rJ16UB+GwShFyHRmKqP2DPV7yJG11MOOAl6l1ugKGXWz22c3ulRowRP/qBeBarbgY="
print(decrypt(passphrase, salt, ciphertext).decode('utf-8')) # {"DisabledTo_basaltest":"0","DisabledTo_breadgrams":"0","DisabledTo_dia":"0","DisabledTo_exercise":"0","DisabledTo_exercise2":"0","DisabledTo_extendedcarbs":"0","DisabledTo_hypott":"0","DisabledTo_ic":"0","DisabledTo_insulin":"0","DisabledTo_iob":"0","DisabledTo_isf":"0","DisabledTo_noisycgm":"0","DisabledTo_nsclient":"0","DisabledTo_objectives":"0","DisabledTo_objectives2":"0","DisabledTo_otherMedicationWarning":"0","DisabledTo_prerequisites":"0","DisabledTo_prerequisites2":"0","DisabledTo_profileswitch":"0","DisabledTo_profileswitch2":"0","DisabledTo_profileswitch4":"0","DisabledTo_profileswitchtime":"0","DisabledTo_pumpdisconnect":"0","DisabledTo_sensitivity":"0","DisabledTo_troubleshooting":"0","DisabledTo_update":"0","DisabledTo_wrongcarbs":"0","DisabledTo_wronginsulin":"0","ExamTask_basaltest":"false","ExamTask_breadgrams":"false","ExamTask_dia":"false","ExamTask_exercise":"false","ExamTask_exercise2":"false","ExamTask_extendedcarbs":"false","ExamTask_hypott":"false","ExamTask_ic":"false","ExamTask_insulin":"false","ExamTask_iob":"false","ExamTask_isf":"false","ExamTask_noisycgm":"false","ExamTask_nsclient":"false","ExamTask_objectives":"false","ExamTask_objectives2":"false","ExamTask_otherMedicationWarning":"false","ExamTask_prerequisites":"false","ExamTask_prerequisites2":"false","ExamTask_profileswitch":"false","ExamTask_profileswitch2":"false","ExamTask_profileswitch4":"false","ExamTask_profileswitchtime":"false","ExamTask_pumpdisconnect":"false","ExamTask_sensitivity":"false","ExamTask_troubleshooting":"false","ExamTask_update":"false","ExamTask_wrongcarbs":"false","ExamTask_wronginsulin":"false","Monitor_MainActivity_resumed":"1650398254754","Monitor_MainActivity_start":"1650398048449","Monitor_MainActivity_total":"44111","Monitor_PreferencesActivity_resumed":"1650398069260","Monitor_PreferencesActivity_start":"1650398091955","Monitor_PreferencesActivity_total":"22695","Monitor_RequestDexcomPermissionActivity_resumed":"1650398253908","Monitor_RequestDexcomPermissionActivity_start":"1650398048510","Monitor_RequestDexcomPermissionActivity_total":"505","Monitor_SetupWizardActivity_resumed":"1650398049172","Monitor_SetupWizardActivity_start":"1650398061115","Monitor_SetupWizardActivity_total":"11943","Monitor_SingleFragmentActivity_resumed":"1650398286784","Monitor_SingleFragmentActivity_start":"1650398099736","Monitor_SingleFragmentActivity_total":"19515","ObjectivesActionsUsed":"true","Objectives_auto_accomplished":"0","Objectives_auto_started":"0","Objectives_autosens_accomplished":"0","Objectives_autosens_started":"0","Objectives_config_accomplished":"0","Objectives_config_started":"0","Objectives_exam_accomplished":"0","Objectives_exam_started":"0","Objectives_maxbasal_accomplished":"0","Objectives_maxbasal_started":"0","Objectives_maxiob_accomplished":"0","Objectives_maxiob_started":"0","Objectives_maxiobzero_accomplished":"0","Objectives_maxiobzero_started":"0","Objectives_openloop_accomplished":"0","Objectives_openloop_started":"0","Objectives_smb_accomplished":"0","Objectives_smb_started":"0","Objectives_usage_accomplished":"0","Objectives_usage_started":"0","absorption_cutoff":"6","activity_duration":"0","activity_target":"0","age":"adult","always_use_shortavg":"false","application_protection":"0","aps_mode":"open","autosens_max":"1.2","autosens_min":"0.7","bolus_protection":"0","boluswizard_percentage":"100","bt_watchdog":"false","carbsReqThreshold":"1","carbs_button_increment_1":"5","carbs_button_increment_2":"10","carbs_button_increment_3":"20","dexcom_lognssensorchange":"false","dexcomg5_nsupload":"false","dexcomg5_xdripupload":"false","eatingsoon_duration":"0","eatingsoon_target":"0","enableSMB_after_carbs":"false","enableSMB_always":"false","enableSMB_with_COB":"false","enableSMB_with_high_temptarget":"false","enableSMB_with_temptarget":"false","enable_carbs_required_alert_local":"true","enable_fabric2":"true","enable_missed_bg_readings":"false","enable_pump_unreachable_alert":"true","fill_button1":"0.3","fill_button2":"0","fill_button3":"0","gradually_increase_notification_volume":"false","high_mark":"0","high_temptarget_raises_sensitivity":"false","hypo_duration":"0","hypo_target":"0","insulin_button_increment_1":"0.5","insulin_button_increment_2":"1.0","insulin_button_increment_3":"2.0","keep_screen_on":"false","key_usersuperbolus":"false","language":"default","last_revoked_certs_check":"1650398043316","last_time_this_version_detected":"1650398061747","last_versionchecker_plugin_waring":"1650398048275","loop_openmode_min_change":"20","low_mark":"0","low_temptarget_lowers_sensitivity":"false","maintenance_logs_amount":"2","maintenance_logs_email":"logs#androidaps.org","master_password":"hmac:1067c3e86cb042d799eb25e9988280703563366195f38dee2d6f1f5cc05f57e6:7b29002999b3af7dd1e5fb0b2f3f4ea96ad90a410549bf0c6e34bc9ff5e5039c","missed_bg_readings_threshold":"30","nextMissedReadingsAlarm":"1650398347880","nextPumpDisconnectedAlarm":"1650398347880","ns_alarms":"false","ns_announcements":"false","openapsama_current_basal_safety_multiplier":"4","openapsama_max_daily_safety_multiplier":"3","openapsama_min_5m_carbimpact":"8.0","openapsama_useautosens":"false","openapsma_max_basal":"1","openapsmb_max_iob":"3","pump_unreachable_threshold":"30","raise_urgent_alarms_as_android_notification":"true","resistance_lowers_target":"false","sensitivity_raises_target":"true","settings_protection":"0","short_tabtitles":"false","show_calibration_button":"true","show_carbs_button":"true","show_cgm_button":"false","show_insulin_button":"true","show_notes_entry_dialogs":"false","show_statuslights":"true","show_treatment_button":"false","show_wizard_button":"true","smbinterval":"3","smbmaxminutes":"30","smscommunicator_otp_secret":"Qz12f1m7s5Tt8Q37ZB3FYFQS9ef1p3oxtYy+t9oTQ90","startupwizard_processed":"true","statuslights_bage_critical":"240","statuslights_bage_warning":"216","statuslights_bat_critical":"5","statuslights_bat_warning":"25","statuslights_cage_critical":"72","statuslights_cage_warning":"48","statuslights_iage_critical":"144","statuslights_iage_warning":"72","statuslights_res_critical":"10","statuslights_res_warning":"80","statuslights_sage_critical":"240","statuslights_sage_warning":"216","statuslights_sbat_critical":"5","statuslights_sbat_warning":"25","treatmentssafety_maxbolus":"3","treatmentssafety_maxcarbs":"48","uamsmbmaxminutes":"30","units":"mg\/dl","use_bolus_advisor":"false","use_bolus_reminder":"false","use_smb":"false","use_uam":"false","virtualpump_type":"1","virtualpump_uploadstatus":"false"}
using the posted test data.
Note that the size of the nonce should be known on both sides so that the size doesn't need to be stored.
Also, for security reasons, the (non-secret) salt should be randomly generated like the nonce and passed to the decryption side concatenated, e.g. salt|nonce|ciphertext|tag.

How to write the equivalent Python encryption/decryption functions as the C# ones so that Python/C# could decrypt each other encrypted string?

Have two programs, one is developed in C#.NET, having the below C# encryption/decryption functions:
public static string Encrypt(string plainText, string keyString)
{
byte[] cipherData;
Aes aes = Aes.Create();
aes.Key = Encoding.UTF8.GetBytes(keyString);
aes.GenerateIV();
aes.Mode = CipherMode.CBC;
ICryptoTransform cipher = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, cipher, CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs))
{
sw.Write(plainText);
}
}
cipherData = ms.ToArray();
}
byte[] combinedData = new byte[aes.IV.Length + cipherData.Length];
Array.Copy(aes.IV, 0, combinedData, 0, aes.IV.Length);
Array.Copy(cipherData, 0, combinedData, aes.IV.Length, cipherData.Length);
return Convert.ToBase64String(combinedData);
}
public static string Decrypt(string combinedString, string keyString)
{
string plainText;
byte[] combinedData = Convert.FromBase64String(combinedString);
Aes aes = Aes.Create();
aes.Key = Encoding.UTF8.GetBytes(keyString);
byte[] iv = new byte[aes.BlockSize / 8];
byte[] cipherText = new byte[combinedData.Length - iv.Length];
Array.Copy(combinedData, iv, iv.Length);
Array.Copy(combinedData, iv.Length, cipherText, 0, cipherText.Length);
aes.IV = iv;
aes.Mode = CipherMode.CBC;
ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream ms = new MemoryStream(cipherText))
{
using (CryptoStream cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
plainText = sr.ReadToEnd();
}
}
return plainText;
}
}
Another program is using Python, need to decrypt the encrypted string from the C# program.
What are the Python encryption/decryption functions so that Python's decryption function could decrypt the encrypted string by the above C# encryption function, also C#'s decryption function could decrypt the encrypted string by Python's encryption function?
The below are the Python functions worked for me:
import Crypto.Random
from Crypto.Cipher import AES
import base64
from Crypto.Util.Padding import unpad, pad
def encrypt(plain_text, key_string):
raw = pad(plain_text.encode(), AES.block_size)
iv = Crypto.Random.get_random_bytes(AES.block_size)
cipher = AES.new(key_string, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw))
def decrypt(combined_string, key_string):
enc = base64.b64decode(combined_string)
iv = enc[:AES.block_size]
cipher = AES.new(key_string, AES.MODE_CBC, iv)
msg = unpad(cipher.decrypt(enc[AES.block_size:]), AES.block_size)
return msg.decode()

same encryption between flutter and python [ AES ]

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))

AES Decryption using Pycrypto(python) not working. Getting correct decrypted code in crypto(Nodejs).

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.

Categories

Resources