I am currently working in a application where RSA Encryption is being used for Encrypting sensitive data. Is there a way to use RSA Encryption in Jmeter so that this value appears encrypted in the previous response? Below is an example,
The actual value,
"emailAddressInfo" : ("PerfTestA#gmail.com | PerftestB#gmail.com").
I want the above value to appear encrypted like below in Jmeter.
"emailAddressInfo": "(as15114677sfh0hfu4scr460hdhsb24sjj324j689hsakw42cjjftks4wefe4sd)"
The mix of characters represent the encrypted value.
It doesn't look like an RSA-encrypted message to me so I would recommend double-checking the algorithm.
Whatever, in order to RSA-encrypt a string you need to use Cipher class from JSR223 Test Elements using Groovy language
Example code:
import javax.crypto.Cipher
import java.security.KeyFactory
import java.security.spec.X509EncodedKeySpec
//replace with your own RSA public key
def publicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsvX9P58JFxEs5C+L+H7W\n' +
'duFSWL5EPzber7C2m94klrSV6q0bAcrYQnGwFOlveThsY200hRbadKaKjHD7qIKH\n' +
'DEe0IY2PSRht33Jye52AwhkRw+M3xuQH/7R8LydnsNFk2KHpr5X2SBv42e37LjkE\n' +
'slKSaMRgJW+v0KZ30piY8QsdFRKKaVg5/Ajt1YToM1YVsdHXJ3vmXFMtypLdxwUD\n' +
'dIaLEX6pFUkU75KSuEQ/E2luT61Q3ta9kOWm9+0zvi7OMcbdekJT7mzcVnh93R1c\n' +
'13ZhQCLbh9A7si8jKFtaMWevjayrvqQABEcTN9N4Hoxcyg6l4neZtRDk75OMYcqm\n' +
'DQIDAQAB'
def x509PublicKey = new X509EncodedKeySpec(publicKey.decodeBase64())
def keyFactory = KeyFactory.getInstance('RSA')
def key = keyFactory.generatePublic(x509PublicKey)
def string2Encrypt = '("PerfTestA#gmail.com | PerftestB#gmail.com").'
def encryptCipher = Cipher.getInstance('RSA')
encryptCipher.init(Cipher.ENCRYPT_MODE, key)
def secretMessage = string2Encrypt.getBytes('UTF-8')
def encryptedMessage = encryptCipher.doFinal(secretMessage)
def encodedMessage = encryptedMessage.encodeBase64().toString()
vars.put('encodedMessage', encodedMessage)
Once done you will be able to refer the encrypted value as ${encodedMessage} where required.
Related
I am trying to generate encrypted text in Node using Crypto.js using its AES algorithm. It works in Js for encryption and decryption both. Similarly I tried implementing same in Python using pycrypto and it does encryption and decryption both in python. But the problem arises when I want to use encrypted cipher text from JS to decrypt in Python. The problem is that the encrypted text generated in JS is different from what is generated in Python.
Here is the JS based usage of AES:
import CryptoJS from "crypto-js";
let str = "lol";
let salt = "ABCDEFGHIJKLMNOP";
let key = "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP";
salt = CryptoJS.enc.Hex.parse(salt);
key = CryptoJS.enc.Hex.parse(key);
const options = {
iv: salt,
padding: CryptoJS.pad.ZeroPadding,
mode: CryptoJS.mode.CFB,
};
// function to encrypt the string
function encrypt(str, salt, key) {
const cipher = CryptoJS.AES.encrypt(str, key, options);
console.log("cipher", CryptoJS.enc.Hex.stringify(cipher.ciphertext));
return cipher.ciphertext.toString(CryptoJS.enc.Base64);
}
// function to decrypt the string
function decrypt(str, salt, key) {
const cipher = CryptoJS.AES.decrypt(str, key, options);
return cipher.toString(CryptoJS.enc.Utf8);
}
const encrypted = encrypt(str, salt, key);
console.log("encrypted", encrypted);
const decrypted = decrypt(encrypted, salt, key);
console.log("decrypted", decrypted);
// OUTPUT
// encrypted sd4Xpz/ws8x2j+cgF17t6A==
// decrypted lol
Here is the Python based usage of AES:
from Crypto.Cipher import AES
from base64 import b64encode, b64decode
str = "lol";
salt = b"ABCDEFGHIJKLMNOP";
key = "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP";
enc_dec_method = 'utf-8'
def encrypt(str_to_enc, str_key, salt):
aes_obj = AES.new(str_key.encode('utf-8'), AES.MODE_CFB, salt)
hx_enc = aes_obj.encrypt(str_to_enc.encode('utf8'))
df = b64encode(hx_enc)
mret = b64encode(hx_enc).decode(enc_dec_method)
return mret
def decrypt(str_to_dec, str_key, salt):
aes_obj = AES.new(str_key.encode('utf-8'), AES.MODE_CFB, salt)
str_tmp = b64decode(str_to_dec.encode(enc_dec_method))
str_dec = aes_obj.decrypt(str_tmp)
mret = str_dec.decode(enc_dec_method)
return mret
test_enc_text = encrypt(str, key, salt)
test_dec_text = decrypt(test_enc_text, key, salt)
print(f"Encrypted Text: {test_enc_text}")
print(f"Decrypted Text: {test_dec_text}")
# OUTPUT
# Encrypted Text: o9XB
# Decrypted Text: lol
I tired encrypting and decrypting in both the languages and check the the documentation and source code on both the libraries.
Did also trying checking if I am missing encoding or decoding, but had no luck.
What key concept am I missing here which can help bridge the gap of compatibility here?
The codes are incompatible because:
Key and IV are not hex encoded, so the hex encoder must not be used in the CryptoJS code. If Utf-8 encoding is to be applied as in the Python code, the Utf-8 encoder must be used instead.
salt = CryptoJS.enc.Utf8.parse(salt);
key = CryptoJS.enc.Utf8.parse(key);
CFB is a stream cipher mode that does not require padding. However, in the current CryptoJS code, Zero padding is used (CryptoJS.pad.ZeroPadding). Instead, the padding has to be disabled (which does not happen implicitly, unlike in the Python code):
padding: CryptoJS.pad.NoPadding,
CFB is configured with an additional parameter, the segment size, which specifies the number of bits encrypted per encryption step. The CryptoJS code supports only one segment size, 128 bits. With PyCryptodome the segment size can be configured, by default 8 bits are used. For compatibility it must be changed to 128 bits:
aes_obj = AES.new(str_key.encode('utf-8'), AES.MODE_CFB, salt, segment_size=128)
With these changes both codes are compatible.
Test: Both codes provide the following ciphertext for the following plaintext:
plaintext: The quick brown fox jumps over the lazy dog
ciphertext: m0T/7e04eV49RTcgd7KtwHxSOavNzHNwlrvjt1YmmHidBy4rHS0oovclKQ==
Note that what you call salt is actually the initialization vector (IV). The term salt is more commonly used in the context of key derivation functions (like PBKDF2).
Regarding security: The values used for salt and IV are OK for testing, but in a real world scenario a random byte sequence has to be applied for the IV for each encryption, which has to be passed along with the ciphertext to the decrypting side (generally concatenated).
Also, as key no passphrase has to be used, but a random byte sequence. A passphrase (a strong one of course) may only be applied in combination with a key derivation function.
encrypted=b'$\x940\xc6\xef3s\x01|e\xe8\xdf\xaa00\xcc\x8c\x98i\x84\xcc[e\xd2\xa4]\xb37\xab\xbc#} \xaeC\xdd\xd7\x89\xdd[\xab)\xf0t\xa8\x95\xfe\xf9>\x15\x02:\x85-s*\x0cf\xe1\xa5\xc3\xa8]\x1d\x8f\xe2f\x1aje\x9f\xc3^\xe0\xc6i\xd6\x02$I\xce0_\x97\x8c\x06Rj\x1e\xe0F\x1fE\xfb\xf4\x0be:\x01\xd1m\xe4\xfaVv\xa0\xdf\xa8\x1e\xec\x03\xc1\x9a\x89\x9a\xa1m\xb4uL\x97\x81\xdc\xaaG5\xf7Q\xba\xc6\xf33\x05\x94l#pL!\x886\x9a5\xa3\xa2Z)\xd3\x80&p\xa4)\x1e\x9ei\xcc\xa9\xcc\xe9j\xe9\xb4\x14\xe1<\x8c\xa50\xd4\xf9\xb5\xb2\xd4#3\nwW#m\x14\xb67\xf3!a\x19:\x8af\x1c\xf1\x13\xbfM\x1a\xfe\xaf\xb0\x95Y~\xe2}\x06\xf0\xc1\xff\xde\x04\x06\xc9\xb4\xfe\xc8\xbb\x8eS\xff\xe29Q\xa0\xf6\xd2\xc78\xf5\x97\x07OW\xbb\xfd\x08q\x8f4C\xbb\x00\xa0\xa1_g\x0e3&x\\\x1a\xa4/a\xa2\x1bp|\x84\xdfr[.\xe5\xbd\xb1\xfc\xc3\t_0l\xddU\xc8HL#&LD\x17g=\xb5\xb0\xcf'
mk123=b'$\x940\xc6\xef3s\x01|e\xe8\xdf\xaa00\xcc\x8c\x98i\x84\xcc[e\xd2\xa4]\xb37\xab\xbc#} \xaeC\xdd\xd7\x89\xdd[\xab)\xf0t\xa8\x95\xfe\xf9>\x15\x02:\x85-s*\x0cf\xe1\xa5\xc3\xa8]\x1d\x8f\xe2f\x1aje\x9f\xc3^\xe0\xc6i\xd6\x02$I\xce0_\x97\x8c\x06Rj\x1e\xe0F\x1fE\xfb\xf4\x0be:\x01\xd1m\xe4\xfaVv\xa0\xdf\xa8\x1e\xec\x03\xc1\x9a\x89\x9a\xa1m\xb4uL\x97\x81\xdc\xaaG5\xf7Q\xba\xc6\xf33\x05\x94l#pL!\x886\x9a5\xa3\xa2Z)\xd3\x80&p\xa4)\x1e\x9ek\xcc\xa9\xcc\xe9j\xe9\xb4\x14\xe1<\x8c\xa50\xd4\xf9\xb5\xb2\xd4#3\nwW#m\x14\xb67\xf3!a\x19:\x8af\x1cq\x13\xbfM\x1a\xfe\x8f\xb0\x95Y~\xe2}\x06\xf0\xc1\xff\xde\x14\x06\xc9\xb4\xfe\xc8\xb3\x8eS\xff\xe29Q\xa0\xf6\xd2\xc78\xf5\x97\x07OW\xbb\xfd\x08q\x8f4C\xbb\x00\xa0\xa1_g\x0es&x\\\x1a\xa4/a\xa2\x1bp|\x84\xdbr[.\xe5\xbd\xb1\xbc\xe3\t_0L\xd9U\xc8HL#&LD\x17g=\xb5\xb0\xcf'
note:ive done some process in between so the bytestrings mk123 and encrypted are not the same
the first byte string is the one ive got after encryption,but when i try to decode its throwing the error"padding is incoreect"
this is the code ive used
from hashlib import md5
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
class AESCipher:
def __init__(self, key):
password = key.encode('utf-8')
self.key = md5(password).digest()
def encrypt(self, data):
vector = get_random_bytes(AES.block_size)
encryption_cipher = AES.new(self.key, AES.MODE_CBC, vector)
return vector + encryption_cipher.encrypt(pad(data, AES.block_size))
def decrypt(self, data):
file_vector = data[:AES.block_size]
decryption_cipher = AES.new(self.key, AES.MODE_CBC, file_vector)
return unpad(decryption_cipher.decrypt(data[AES.block_size:]), AES.block_size)
the driver code is
message='0000000000000000011111111111111001000010001011100101101011101110010000100010111001011010111011100101101000100010011111111111111001111111111111100101111011100010010111101110101001011110111010100101111011101010010001100010001001111111111111100000000000000000'
msg = message.encode('utf-8')
pwd = "password"
encrypted = AESCipher(pwd).encrypt(msg)
print('Ciphertext:', encrypted)
decrypted = AESCipher(pwd).decrypt(mk123).decode('utf-8')
If you change the last block or the previous to last block of ciphertext then unpadding may indeed fail for CBC mode. This is because both of these blocks change the resulting plaintext. Generally, for CBC mode, you'll change the bits of one block with a chance of 0.5 for the block that you changed, and the same bits of the plaintext at the next block are flipped. How changes affect the decryption routine is called "error propagation".
You can figure most of this out yourself by looking at the decryption diagram from Wikipedia (by WhiteTimberwolf):
Knowing that, if you want to investigate the original plaintext you simply need to skip unpadding. Then you get the above changes made to the plaintext (including padding). You can then try to fix or guess the plaintext and/or correct the padding. If you cannot correct the padding then the size of the plaintext can differ by one block - as far as the cipher is concerned anyway.
I am writing Python Decryption logic to decrypt the message (client-side it is encrypted using Java).
Python code:
def decrypt(self, text):
decode = base64.b64decode(text)
cryptor = AES.new(InKey, MODE_CBC, InIV)
plain_text = cryptor.decrypt(decode)
return unpad(plain_text)
I have received below key & IV values (sample values) from the client team:
InKey =
"6asd6587daIs8g2qvi3rJbM9sdasd6cb2kdYC0TOy5zEgTo+8LrQn0UJZAmJCtmX......"(it
is length of 684 chars) InIV =
"7as76cascsagoKtID7z1nUakJqzj+Dwl9cL9Q2/zBFbs0Sg3Kw6US8yvvzbkyg2bnjGHWofIWrhMQ/Bcde...."
(it is length of 684 chars)
Question: While running the above python code, getting the below error. How to convert the above IV value into 16bytes in size and also key-value length?
ValueError: Incorrect IV length (it must be 16 bytes long)
existing Decryption Java Client follows Hybrid pattern(first using RSA private key to pull two params(IV & Key)...and then AES cipher to decrypt actual message):
Pseudo code:
................................
//Step-1) Generate RSA-PrivateKey
................................
RSAPrivateKey serverPrivateKey = KeyUtils.getPrivateKey("lib", PrivKey);
/* here PrivKey refers to .der certificate
and FYI- KeyUtils actually refers to a class, that generate RSA PrivateKey with below code:
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
KeySpec ks = new PKCS8EncodedKeySpec(privKeyBytes);
return (RSAPrivateKey)keyFactory.generatePrivate(ks);
*/
................................
//Step-2) use RSA-PrivateKey to decrypt the message
................................
MessageEncryption encryptor = new MessageEncryption(serverPrivateKey);
/* initializes Cipher(RSA instance) with passed RSA PrivateKey
this.m_asymmetricalKey = serverPrivateKey;
this.m_asymmetricalCipher = Cipher.getInstance(this.m_asymmetricalKey.getAlgorithm());
*/
encryptor.decryptMessage(JSONresponseBody); // invokes below mentioned method
...............
decryptMessage(JSONObject jsonMessage)
{
.............................
IvParameterSpec initializationVector = new IvParameterSpec(this.m_asymmetricalCipher.doFinal(StringCodec.decodeBase64(jsonMessage.getString("iv").getBytes())));
this.m_asymmetricalCipher.init(2, this.m_asymmetricalKey);
Key secretKey = new SecretKeySpec(this.m_asymmetricalCipher.doFinal(StringCodec.decodeBase64(jsonMessage.getString("key").getBytes())), "AES");
Cipher symmetricalCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
symmetricalCipher.init(2, secretKey, initializationVector);
return new String(symmetricalCipher.doFinal(StringCodec.decodeBase64(jsonMessage.getString("message").getBytes())));
.............................
}
..............
So at Python how to get right key-size & IV, is there way to implement similar hybrid pattern at java or does it any other approach to deal with at python ?
Thanks.
Your Java-side code does the following things:
Read the PKCS8-encoded RSA key
Decode the base64-encoded IV
Decrypt the IV using the private key (a side-note: encrypting IV makes very little sense to me)
Decode the base64-encoded AES key
Decrypt the AES key using the private key
Decode the base64-encoded cyphertext
Decrypt the ciphertext using the obtained IV and AES key
You need to re-implement each of the above steps in Python. Some are trivial, like base64-decoding. The others depend on the cryptography libraries you're using.
I am trying to validate the digital signature with given certificate files for the offline aadhaar KYC verification application.
This instruction is given in the documentation for the verification.
Read the entire XML and separate the s=”xxxx” tag from it.
Use a signature validation algorithm leveraging “SHA256withRSA” based hashing and encryption technique
Signature value present in “s” tag, remaining XML (without "s" tag) and UIDAI public key (available here.) is to be fed to the algorithm to validate the digital signature.
Sample C# code snippets provided by the organization. (PS :which is also not working)
using System;
using System.Security.Cryptography.X509Certificates;
using System.Xml;
namespace test
{
class MainClass
{
public static void Main(string[] args)
{
// link -> https://drive.google.com/file/d/1aSv3HJUFf5_42Z-FqpdVHEk5b3VA3T3D/view
string XMLFilePath = "offlineaadhaar.xml"; //Get the XML file
// link -> https://drive.google.com/file/d/1FW4ciIhZqJuelOcGF2x6VaBCSDO9J-gM/view
string KeyFilePath = "okyc-publickey.cer"; //Get the public key certificate file
XmlDocument ObjXmlDocument = new XmlDocument();
ObjXmlDocument.Load(XMLFilePath); //Load the XML
XmlAttributeCollection SignatureElement = ObjXmlDocument.DocumentElement.Attributes; //Get the all XML attribute
string SignatureValue = SignatureElement.GetNamedItem("s").InnerXml; // Get Signature value
SignatureElement.RemoveNamedItem("s");//Remove the signature "s" attribute from XML and get the new XML to validate
/*----------------Read and parse the public key as string-----------------------*/
X509Certificate2 ObjX509Certificate2 = new X509Certificate2(KeyFilePath, "public"); //Initialize the public ket certificate file
Org.BouncyCastle.X509.X509Certificate objX509Certificate;
Org.BouncyCastle.X509.X509CertificateParser objX509CertificateParser = new Org.BouncyCastle.X509.X509CertificateParser();
objX509Certificate = objX509CertificateParser.ReadCertificate(ObjX509Certificate2.GetRawCertData());
/*----------------End-----------------------*/
/* Init alg */
Org.BouncyCastle.Crypto.ISigner signer = Org.BouncyCastle.Security.SignerUtilities.GetSigner("SHA256withRSA");
/* Populate key */
signer.Init(false, objX509Certificate.GetPublicKey());
/* Get the signature into bytes */
var expectedSig = Convert.FromBase64String(SignatureValue);
/* Get the bytes to be signed from the string */
var msgBytes = System.Text.Encoding.UTF8.GetBytes(ObjXmlDocument.InnerXml);
/* Calculate the signature and see if it matches */
signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
bool Flag = signer.VerifySignature(expectedSig);
if (Flag)
{
Console.WriteLine("XML Validate Successfully");
}
else
{
Console.WriteLine("XML Validation Failed");
}
}
}
}
I am trying to implement in Python and getting the XML validation failed. I am not sure if the certificate file is wrong or there is some bug on my code.
Here is my Python Code :
import xml
import xml.etree.cElementTree as etree
from xml.etree import ElementTree
import OpenSSL
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from Crypto.PublicKey import RSA
from base64 import b64encode, b64decode
from M2Crypto import BIO, RSA, EVP
xmlDoc = open('adhar.xml', 'r').read()
Tr = etree.XML(xmlDoc)
Tr.keys()
# ['s', 'r', 'a', 'g', 'm', 'e', 'd', 'i', 'n', 'v']
sign = Tr.get('s')
len(sign)
# 344
del Tr.attrib['s']
from M2Crypto import X509
x509 =X509.load_cert('ekyc_public_key.cer')
#x509 =X509.load_cert(cert4)
rsa = x509.get_pubkey().get_rsa()
pubkey = EVP.PKey()
pubkey.assign_rsa(rsa)
xmlstr = etree.tostring(Tr, encoding='utf8', method='xml')
#rstr=str(xmlstr)[45:][:-1]
#rstr = rstr.encode(encoding='utf-8')
# if you need a different digest than the default 'sha1':
pubkey.reset_context(md='sha256')
pubkey.verify_init()
# hashlib.sha256(message_without_sign).digest()
pubkey.verify_update(xmlstr)
if(pubkey.verify_final(b64decode(sign)) != 1):
print('Digital Signeture not validated')
else:
print('Digital Signeture validated')
In cryptography, the devil is in the details, like what really is meant by:
remaining XML (without "s" tag)
We are lucky: this ridiculously vague specification comes came with a sample XML file and matching certificate, allowing to:
extract the signature signature field (converted from Base64 to binary) from the former to get S;
extract N and e from the later;
compute V = S e mod N
check that V is indeed formatted per SHA256withRSA (also known as RSASSA-PKCS1-v1_5 with SHA-256 hash),
on the right of V after a fixed DER prefix 3031300d060960864801650304020105000420 characteristic of a Digestinfo with SHA-256 we extract the 32-byte value f4efef8c788058df45385ec65a49e92f806b9ffd6fc6d11b4f3c2cf89a81fe2f, which thus is the expected hash for that example's signed data.
So with a little trial and error we find what that signed data really is. We want to start from XML file and remove
the s tag
its = (and, should there be any, whitespace between former s tag and =, and after = as allowed by the XML syntax)
its value field including leading and closing " (which contains the base64-encoded signature)
precisely two nearby space characters, like the one before the s tag and the one after the closing " (alternatively, we might want to leave a single space where there are now several, probably three after the removals in bullet points 1-3).
then hash what remains including < /> delimiters (as UTF-8), that is in the sample code pass it to BlockUpdate or verify_update. For that example:
<OKV v="1" n="Ravi Krishna Nalagatla" i="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" d="10121993" e="ac342bb947d5aa49847a0a05f87c388759a6cb156e04eac6ae995e9dca5ba1d9" m="254618fd06a2e7308247c7f0fb547f58174ab3b99e7b042d6705a5457235f32a" g="male" a="S/O Narasimham, 45, 74, 4, 7, Secunderabad, Hyderabad, Andhra Pradesh - 500003" r="328020181115111938451" />
Update: sample XML, certificate, and the above are in this zip archive.
For this project, all the instructions can be found on this page in Steps to validate signature step.
Sample data, sample public key and sample c# code can be found on this page in Offline ekyc Sample data tab.
For your own data please refer to this link and download the zip file and extract it.
Here is the ekyc public key for the other xml validation ekyc_public_key.cer
Please find the complete answer in below code snippet which is implemented in python :
from M2Crypto import BIO, RSA, EVP
from M2Crypto import X509
# 'ekyc_public_key.cer' for own your own data. as 'okyc_public_key.cer' only work for sample data only
x509 =X509.load_cert('okyc_public_key.cer')
rsa = x509.get_pubkey().get_rsa()
pubkey = EVP.PKey()
pubkey.assign_rsa(rsa)
import lxml.etree as le
with open(xml_path,'r') as f:
doc=le.parse(f)
for elem in doc.xpath('//*[attribute::s]'):
sign = elem.attrib['s']
elem.attrib.pop('s')
data_str = str(le.tostring(doc))[2:][:-1]
data = data_str[:-2] + ' />'
pubkey.reset_context(md='sha256')
pubkey.verify_init()
pubkey.verify_update((data_str[:-2] + ' />').encode())
is_valid_signeture = ""
if(pubkey.verify_final(b64decode(sign)) != 1):
print('Digital Signeture not validated')
is_valid_signeture = 'Invalid'
else:
print('Digital Signeture validated')
is_valid_signeture = 'Valid'
More about XML signing here
Python Code for Digital Signature verification for new Aadhar XML:
certificate_file = "uidai_offline_publickey_19062019.cer"
aadhar_file = "offlineaadhaar20200120032019978.xml"
certificate = open(certificate_file, "rb").read()
aadhar_xml = open(aadhar_file, "rb").read()
from signxml import XMLSigner, XMLVerifier
try:
verify_result = XMLVerifier().verify(aadhar_xml, x509_cert=certificate)
# return True
except Exception as e:
verify_result = None
# return False
print(e)
You can download the certificate from here.
More about signxml here.
I have the following code in python:
from Cryptodome.Cipher import AES
key = b'\x19\x3c\x04\x27\xf6\xa2\x1a\x08\x7f\xb0\x1c\xe9\xa7\x92\x3d\xd8\xe5\xc7\xae\x98\xf5\x0b\xf4\xa3\x7b\x4f\xd6\x76\x95\xd3\xe0\xd9'
data = b'\x47\x56\x64\xc1\x54\xe7\xa1\xed\x92\x4b\x26\x06\x6f\x4e\x95\x30\xea\xc4\x08\xf7\xdc\x69\x1c\x38\x38\xff\x38\x58\x50\x78\x9f\xd9\x32\xea\x86\xcd\x8c\x6d\x8f\x17\x30\x2a\xae\x6e\xa1\xc6\x6d'
header = b'\xdb\xd2\xed\x76\x3c\x50\xf2\x7c\xe3\xbe\x78\xa9\xc3\x6b'
cipher = AES.new(key, AES.MODE_GCM)
cipher.update(header)
cipherText, tag = cipher.encrypt_and_digest(data)
print(cipherText)
The ciphertext seemed to be changing everytime.
Any idea why that is?