Skip to main content

Card Data Encryption

Mea Token Platform provides multiple ways to digitize a card. One of those methods is digitization with encrypted card data. In this case, an issuer encrypts the data at its back-end, and then sends encrypted card data to its own mobile app (proprietary delivery mechanism), and mobile app provides it to the SDK or sends directly to Mea backend API for decryption.

Card Data Encryption

This approach is often used in cases where the issuer does not want any backend integrations with the MTP. Therefore, encryption with the right keys in those cases also act as approval for the digitization.

Setup

  1. MeaWallet generates Public Private key pair;
  2. MeaWallet shares public key with the issuer (in a format of CSR);
  3. Issuer stores the key for card digitization in future.

High Level Steps

  1. When card needs to sent, the issuer generates AES-256 bit key one-time/secret key (SK).
  2. Encrypts the JSON-formatted card data with this one-time/secret key.
  3. Encrypt (wrap) the one-time/secret key (SK) with MeaWallet’s Public Key.
  4. Send encrypted payload with encrypted secret key to:
    1. the mobile device where it is forwarded to the MeaWallet SDK.
    2. send directly to MeaWallet backend API.
  5. MTP decrypts the payload and uses received card data.

Building Payload

Card Data Payload

Configuration

Sensitive Data Encryption

Key Length = 256 bits
Algorithm = AES
Block Cipher Mode = CBC
Padding = PKCS#5/PKCS#7

Key Encryption

Key length = 4096 bit
Algorithm = RSA
Block Cipher Mode: ECB
Padding = PKCS#1 v2.2 OAEP method
OAEP Mask Generation Function: MGF1
OAEP Mask Generation Function Hash Algorithm = SHA-512
OAEP Parameters = none

info

Below is a sample of Java implementation with real data. However, it's expected from the PCI-DSS compliant issuer to use Hardware Security Module when encrypting the card data.

Input JSON:

{
"encryptedData": {
"accountNumber": "5123456789012345",
"expiryMonth": "12",
"expiryYear": "21",
"cardholderName": "Sample Bank",
"securityCode": "123",
"validToUTC": "2018-04-05T13:21:10"
}
}
info

It's important to convert from UTF-8 to ASCII string before converting to bytes for encryption. UTF-8 may use 2 bytes or more for a single character in some languages.

Sample Public Key:
modulus: C93CD9E6FC2317DB8977DE7007411A7DFCD57A863E5CE16E4A5F96E7D9CC04579E1FA1853CC88E70BDFAA7EBC36EE2E35A4390477ED8944403CDBBB859A7FB930C70BD46CD15D96EA4D9630526A60BD61854ED04E9DFF021EFF8A44A77E38DB4F3F17CF9BFDE38EF52DCB599908724BFCF643F515728776247B68339D59B504B7650646C1B49D291CE25544E729E981698C53B11279DA0856E5C59AB1144459E05C580BFE2B500BAC36091B77C3379EFFB4F16DD17C1965377A92952E6249C0869B2D10E655C7AA0F70021A172178F37CC50603E778A7820E71D65A7DC9E19922D7158C06FBF37505AE5617C51CB1364F7DF48190E82DB2F536124D0C9AA6B24896C854DF6D47538FB15A0F5827B0992778B9ECEA5B7F07EC6D4EDD124A594F2C2930799B7F926C90622EC0D7CF131141549BC1A1E34D50C9FE4E46582FE36287D304DE8DB846B1B545919CA3BF8ABFADDC07975FB9CBBBC6E678B904848653902970EB61CDB45D3EFF16241700DDEB8A675B1A79C41F01FC18D9981F3E0D3B2274788ACB26BE2B982C1B0EA7CD6D4BE26898ACCCE94F1C61513113A6B80D3B8E9AA3FBAFA7F82A8640EE90C7D0D1E45FC81FA23DA0DD572FBF3D9313EE903CCC6565AFEA4D0A4B1CDEA12C7FFED5E9A41C31DAB6D7A1B5E43883FBDDE9E32B4690FF5BC9470716E2135FE3B92D3F077D6E36AC0F5188E21EF1CC52829994289
exponent: 010001
fingerprint: 18e8997d14d0166ccd46297f58cd49bead508afe6da7f0acc6d716bca2e76e30

Importing Public Key

Java method to generate Java type Public Key:

public static PublicKey buildRsaPublicKey(byte[] modulus, byte[] exponent) throws NoSuchAlgorithmException, InvalidKeySpecException {
RSAPublicKeySpec spec = new RSAPublicKeySpec(new BigInteger(1,modulus), new BigInteger(1,exponent));
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePublic(spec);
}

Generating Secret Key and IV

public static SecretKey generateSecretKey(String algorithm, int size, String digestAlgorithm ) throws NoSuchAlgorithmException, NoSuchProviderException {
KeyGenerator keyGen = KeyGenerator.getInstance(algorithm, "SunJCE");
keyGen.init(size);
MessageDigest messageDigest = MessageDigest.getInstance(digestAlgorithm, "SUN");
messageDigest.reset();
byte[] keyValue = messageDigest.digest(keyGen.generateKey().getEncoded());
keyValue = Arrays.copyOf(keyValue, size/8);
return new SecretKeySpec(keyValue, algorithm);
}

public static IvParameterSpec generateIv() throws NoSuchAlgorithmException, NoSuchProviderException {
SecureRandom randomSecureRandom = SecureRandom.getInstance("SHA1PRNG", "SUN");
byte[] ivBytes = new byte[16]; //cipher.getBlockSize()
randomSecureRandom.nextBytes(ivBytes);
return new IvParameterSpec(ivBytes);
}

iv = 5bcd1e34f226c7f406aea1c054fd0da0
secretKey = E9AE358D1EE507A10BB7F71FAE6BCF704F2FCB961DB22011A13C64807B60CEA5

Encrypting the Data

crypt(Cipher.ENCRYPT_MODE, "AES/CBC/PKCS5Padding", "SunJCE", secretKey, iv, payload.getBytes("UTF8"))

public static byte[] crypt(int operation, String algorithm, String provider, Key key, AlgorithmParameterSpec iv, byte[] clearText)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException,
NoSuchProviderException {

if (operation == Cipher.UNWRAP_MODE || operation == Cipher.WRAP_MODE) {
throw new InvalidAlgorithmParameterException("Cannot use Wrap/UnWrap in a crypt method");
}

Cipher currentCipher = null;

if (provider != null) {
currentCipher = Cipher.getInstance(algorithm, provider);
} else {
currentCipher = Cipher.getInstance(algorithm);
}

if (iv == null) {
currentCipher.init(operation, key);
} else {
currentCipher.init(operation, key, iv);
}

return currentCipher.doFinal(clearText);
}

encryptedData = 6d7896692c66cfb97a20947978772c1ae66a92a4a1b58943b303a61ce1ba8654ddf7d26d8c058f1497d34fc5b4ecf9e089964cf5e650397f7e87a8a0c5bc4fb5a1b123525e50a403680c22aee365e5918a58a8c4141148ee4189f471f453c64d829b8a16d702df204751e52e7bcd8968bb83424ce7a81d55a88b518f0241443e

Wrapping the Key

Encrypt the key: wrap("RSA/ECB/OAEPWithSHA-512AndMGF1Padding", "SunJCE", pubKey, secretkey);

public static byte[] wrap(String algorithm, String provider, Key publicKey, Key secretKey)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException,
NoSuchProviderException {

Cipher currentCipher = null;

if (provider != null) {
currentCipher = Cipher.getInstance(algorithm, provider);
} else {
currentCipher = Cipher.getInstance(algorithm);
}

if (algorithm.contains("OAEPWith")) {
currentCipher.init(Cipher.WRAP_MODE, publicKey, getOAEPParameterSpec(algorithm, null));
} else {
currentCipher.init(Cipher.WRAP_MODE, publicKey);
}

return currentCipher.wrap(secretKey);
}

private static OAEPParameterSpec getOAEPParameterSpec(String algorithm, String oaepHashingAlgorithm) {
if (algorithm.contains("OAEPWith")) {
//OAEPWith<digest>And<mgf>Padding
// String template = "OAEPWith<digest>And<mgf>Padding";
int startDigest = algorithm.indexOf("OAEPWith") + 8;
int endDigest = algorithm.indexOf("And");

int startPadding = endDigest + 3;
int endPadding = algorithm.indexOf("Padding");

String digest = algorithm.substring(startDigest, endDigest);
String padding = algorithm.substring(startPadding, endPadding);

if (oaepHashingAlgorithm != null) {
return new OAEPParameterSpec(digest, padding, new MGF1ParameterSpec(oaepHashingAlgorithm), PSource.PSpecified.DEFAULT);
} else {
return new OAEPParameterSpec(digest, padding, new MGF1ParameterSpec(digest), PSource.PSpecified.DEFAULT);
}
}
return null;
}

Encrypted key looks like this:
1782c5bccbf312d67a4e3980633fa8e914cc338aec715ab52a8c871e962502b64c75ef448f998c14ab293bea303aec422b4becdbd3060c6f103c8dbbea089b5f1a8175cdaceacb454bd88ae398075511daa432e6a4059a6650810a790ffe263c7c1ded866831325fd78080b8b3b2d68d73f19b6ea39d59407b85eb7d790ed339d2f81726498ffddb58d2278a37ba0587cf5638ffd67673e82828e0e0d8cc9edef41cd5de87d5afcc7b52b9cfd69d0f1fc449ef68653a296b2906cb51405aeab6f806b8e24fa5a7223c2bad911d6b2e9b1bdf4286847d186a261c57704999365b48940d137165a2ffa4e148ce9e793aa2b94e0034ac9393b6f47fc0a8f4a20acaac035aacc9a9649c4c11b4e706bc2da4ff4ab10f286e117f4ec6ce7a62e03041f18234b8f129f100693d4ba1176cb72e67dc1a6fe367f8b14c0ba3c615ae75a1d97dbb4acc34d7923a7635d65479ca6dfa723eb4b0270204778a53331cedbe50ef12da8fd8a94a45adf9c7910a4c6bfdef121b8b17ed2e9f6aa2ba89e03cd96c7d0ebf00fa1efb7828ebcce04b124237669dce899d069d2722043946e104de20c782c08671f868f551db13e3f1984bc2ab72927f4d6ed670760c63472c137a5ce0e50160e5263ac8cc19c6c8f1ab3573ff0c376dcd0370ebf7ff6827815c0e99dc0475a3cfe83f786987b8eb9d99ed20f2c5a8daff5736fd8b7cfd420b52ee8a

Calculating Fingerprint

Calculate fingerprint by using SHA-256 from PublicKey, or store it static as provided by MeaWallet:

Hex.bytesToHex(Crypto.calculateSha2(publicKey.getEncoded()));

Result

Output JSON:

{
"publicKeyFingerprint" : "18e8997d14d0166ccd46297f58cd49bead508afe6da7f0acc6d716bca2e76e30",
"encryptedKey" : "1782c5bccbf312d67a4e3980633fa8e914cc338aec715ab52a8c871e962502b64c75ef448f998c14ab293bea303aec422b4becdbd3060c6f103c8dbbea089b5f1a8175cdaceacb454bd88ae398075511daa432e6a4059a6650810a790ffe263c7c1ded866831325fd78080b8b3b2d68d73f19b6ea39d59407b85eb7d790ed339d2f81726498ffddb58d2278a37ba0587cf5638ffd67673e82828e0e0d8cc9edef41cd5de87d5afcc7b52b9cfd69d0f1fc449ef68653a296b2906cb51405aeab6f806b8e24fa5a7223c2bad911d6b2e9b1bdf4286847d186a261c57704999365b48940d137165a2ffa4e148ce9e793aa2b94e0034ac9393b6f47fc0a8f4a20acaac035aacc9a9649c4c11b4e706bc2da4ff4ab10f286e117f4ec6ce7a62e03041f18234b8f129f100693d4ba1176cb72e67dc1a6fe367f8b14c0ba3c615ae75a1d97dbb4acc34d7923a7635d65479ca6dfa723eb4b0270204778a53331cedbe50ef12da8fd8a94a45adf9c7910a4c6bfdef121b8b17ed2e9f6aa2ba89e03cd96c7d0ebf00fa1efb7828ebcce04b124237669dce899d069d2722043946e104de20c782c08671f868f551db13e3f1984bc2ab72927f4d6ed670760c63472c137a5ce0e50160e5263ac8cc19c6c8f1ab3573ff0c376dcd0370ebf7ff6827815c0e99dc0475a3cfe83f786987b8eb9d99ed20f2c5a8daff5736fd8b7cfd420b52ee8a",
"encryptedData" : "6d7896692c66cfb97a20947978772c1ae66a92a4a1b58943b303a61ce1ba8654ddf7d26d8c058f1497d34fc5b4ecf9e089964cf5e650397f7e87a8a0c5bc4fb5a1b123525e50a403680c22aee365e5918a58a8c4141148ee4189f471f453c64d829b8a16d702df204751e52e7bcd8968bb83424ce7a81d55a88b518f0241443e",
"oaepHashingAlgorithm" : "SHA512",
"iv" : "5bcd1e34f226c7f406aea1c054fd0da0"
}
info
  • In RSA encryption random values are involved therefore the output is always different.
  • Common mistake is to use PCKS1.5 without OAEP padding. Another common error is to not handle public key correctly, but samples should help you out.

Python Example

import binascii
import json
import secrets
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

# -- Define functions

def jsonToUtf8String(jsonObj):
return json.dumps(jsonObj, ensure_ascii=False).replace("'", '"')


def bytes2str(data):
return data.decode("utf-8")


def getRandomBytes(size):
return secrets.token_bytes(size)


def hex2bytes(hexData):
return bytes(bytearray.fromhex(hexData.replace(" ", "")))


def bytes2hex(byteArray, addSpace=False):
hexVal = binascii.hexlify(byteArray).upper().decode("utf-8")
res = ''
if addSpace:
for i in range(int(len(hexVal) / 2)):
res += hexVal[i * 2:i * 2 + 2] + ' '
# print('['+res+']')
res = res[:-1]
else:
res = hexVal
return res


def addPkcs5Padding(dataHex, blockSize):
diff = blockSize - int(len(dataHex) / 2) % blockSize
paddingByteStr = ""
if diff == 0:
diff = blockSize
paddingByteStr = bytes2hex(bytes([blockSize]))
else:
paddingByteStr = bytes2hex(bytes([diff]))
for i in range(diff):
dataHex += paddingByteStr
return dataHex


def encryptAesCbc(key, iv, dataHex):
cipher = Cipher(algorithms.AES(hex2bytes(key)), modes.CBC(
hex2bytes(iv)), backend=default_backend())
en = cipher.encryptor()
ct = en.update(hex2bytes(dataHex)) + en.finalize()
return bytes2hex(ct)


def encryptWithPublicKeyOaep(publicKeyDerHex, dataToEncryptHex, hashSize):
publicKeyDer = hex2bytes(publicKeyDerHex)
public_key = serialization.load_der_public_key(
publicKeyDer, backend=default_backend())
h = hashes.SHA256()
if hashSize == 1 or hashSize == 128:
h = hashes.SHA1()
elif hashSize == 224:
h = hashes.SHA224()
elif hashSize == 256:
h = hashes.SHA256()
elif hashSize == 384:
h = hashes.SHA384()
elif hashSize == 512:
h = hashes.SHA512()
encrypted = public_key.encrypt(
hex2bytes(dataToEncryptHex),
padding.OAEP(mgf=padding.MGF1(algorithm=h), algorithm=h, label=None)
)
return bytes2hex(encrypted)


def rsaWrapingInPublic(dataHex, publicKeyHex, oaepHashSize=512):
# print('dataHex', dataHex)
dataWithPaddingHex = addPkcs5Padding(dataHex, 16)
# print('dataWithPaddingHex', dataWithPaddingHex)
aesSessionKey = bytes2hex(getRandomBytes(32))
# print('aesSessionKey', aesSessionKey)
iv = bytes2hex(getRandomBytes(16))
# print('iv', iv)
encData = encryptAesCbc(aesSessionKey, iv, dataWithPaddingHex)
# print('encData', encData)
encKey = encryptWithPublicKeyOaep(publicKeyHex, aesSessionKey, oaepHashSize)
return encData, encKey, iv, 'SHA' + str(oaepHashSize)


def decryptWithPrivateKeyOaep(privateKeyDerHex, dataToDecryptHex, hashSize):
privateKeyDer = hex2bytes(privateKeyDerHex)
private_key = serialization.load_der_private_key(
privateKeyDer, password=None, backend=default_backend())
h = hashes.SHA256()
if hashSize == 1 or hashSize == 128:
h = hashes.SHA1()
elif hashSize == 224:
h = hashes.SHA224()
elif hashSize == 256:
h = hashes.SHA256()
elif hashSize == 384:
h = hashes.SHA384()
elif hashSize == 512:
h = hashes.SHA512()
decrypted = private_key.decrypt(
hex2bytes(dataToDecryptHex),
padding.OAEP(mgf=padding.MGF1(algorithm=h), algorithm=h, label=None)
)
return bytes2hex(decrypted)


def removePkcs5Padding(dataHex):
paddingLen = hex2bytes(dataHex[-2:])[0]
return dataHex[:-paddingLen * 2]


def decryptAesCbc(key, iv, dataHex):
cipher = Cipher(algorithms.AES(hex2bytes(key)), modes.CBC(
hex2bytes(iv)), backend=default_backend())
de = cipher.decryptor()
ct = de.update(hex2bytes(dataHex)) + de.finalize()
return bytes2hex(ct)


def rsaUnwrapingWithPrivateKey(ivHex, encryptedKeyHex, encryptedDataHex, privateKeyHex, oaepHashingAlgorithm='SHA512'):
hashLenInt = 512
if oaepHashingAlgorithm == 'SHA128':
hashLenInt = 128
elif oaepHashingAlgorithm == 'SHA256':
hashLenInt = 256
elif oaepHashingAlgorithm == 'SHA384':
hashLenInt = 384
elif oaepHashingAlgorithm == 'SHA512':
hashLenInt = 512
else:
print('ERROR: Unknown OAEP hashing (', oaepHashingAlgorithm, ')')
decKey = decryptWithPrivateKeyOaep(privateKeyHex, encryptedKeyHex, hashLenInt)
# print('decKey', decKey)
decData = decryptAesCbc(decKey, ivHex, encryptedDataHex)
# print('decData:', decData)
dataWithoutPaddingHex = removePkcs5Padding(decData)
# print('dataWithoutPaddingHex', dataWithoutPaddingHex)
return dataWithoutPaddingHex


if __name__ == "__main__":

# -- Keys
KEYS = {
"FINGERPRINT": "262B7A32129A0A2B86657F31B51A7AEA79CF464B",
"PUBLIC_KEY": "3082010A0282010100EBA0EDFE32BDB2F2A685B9871AFDC293AD05033A148DE1F56CFD73E7D974107511071D11D2B0A889C93BF980571715126925F928BE8861970843510CCACFF0ECCC824607F6F2AA3E58B0843625D825AB4F4E016DEEFD75AAB1578301C6366C30BA5B3A40B0C0DC0B72A531FFEC8326CFE8E796143D95A8657CF55409EF25BA85F9FA83DDB2DBAC07A503C087FBA54161181E23BB423334747BCC17C80F9C3C2D5389118C5B8902B4F75ECF68585564CBC0C338652281BBAAE97378D3C12B773AC59E43E465E04CAFB4B3B1DFD96A06D42C6B666A65F2E6E7450A952F2D95CCB1560869A88E2B78D768BF734EFC5535FCA8BEEBE3FEA196A10AD9382D6BECEDCB0203010001",
"PRIVATE_KEY": "308204A50201000282010100EBA0EDFE32BDB2F2A685B9871AFDC293AD05033A148DE1F56CFD73E7D974107511071D11D2B0A889C93BF980571715126925F928BE8861970843510CCACFF0ECCC824607F6F2AA3E58B0843625D825AB4F4E016DEEFD75AAB1578301C6366C30BA5B3A40B0C0DC0B72A531FFEC8326CFE8E796143D95A8657CF55409EF25BA85F9FA83DDB2DBAC07A503C087FBA54161181E23BB423334747BCC17C80F9C3C2D5389118C5B8902B4F75ECF68585564CBC0C338652281BBAAE97378D3C12B773AC59E43E465E04CAFB4B3B1DFD96A06D42C6B666A65F2E6E7450A952F2D95CCB1560869A88E2B78D768BF734EFC5535FCA8BEEBE3FEA196A10AD9382D6BECEDCB0203010001028201006CF6DFF854B8BB1F5178B7C24DEE19E436F80175037A558B2C9ACCD5E40EB3699FB53D713790B06FFF9A77982D2E24757E20AC67762D158C45AAF3B949B4FDC25414CE93F25467FC2B017E32982FABC3DC09C3C975E2CA5E646286D7F434C8B584089A18BE64819DDDA79D46AFB63AE59CDC33E02A56A22844B022EBF126EE691173D6D635E76BA3AEDFC7ED04F92760B0C00CADF8E39E93F13509E3DA90209865FA17D8B1A9EF4CBF5A4B700A79F78461D68B8CE93DDB7E48A97E0CD6B373D8FEF795DDC5EB78E95FF81E07408BF8C48A1105FA624F90F3A8706AA6E452FBBA0FEE1D8F51DB5F8110E32A62E2260F62FD07CE9F720B362E85EAD218C9C2C0D902818100F878FD86F8E979E17F0127D26EADD4E5691988020CCC439449FF65F4C88317E5BA15BCE29D49917DCFDE0E5C3E1A74443B4885C5BEBAEEE361A6A467B93325DDA98F4B3F332DF4DE9CDE74E2199807B460393A433A8128E6829A0F70CB4D38ADCB7C0ED61630C8D00B4D87BA78A971506DFEC1DAE0C67D643E2796402637771702818100F2C4542C18D99C60538B1DED4467FE178D9888FCF2A88073891706C6D09251CBA969367BEE6C706C934D45A4CE5E5DAB32B5737843F7AF305241D4831A091263699CEA0DF323E1102CEDDC6803CA621E136C83C93CE10D9AC6E1C93B54A03D570202DC4D079211DD8CB1E95DDBC172687709B05B215D3CEB5EE5F3D4CB5E2F6D028181008008884B4673B85413C79AA77A54A48159F8C7F69675A2D50494500B244F38BF2D760A10938FD722873C1B16390214AB1C0930B546A4B877FE578A1BD078A4A865B8EAC33432181DA5A39382F6413AA71D761FA988BA09434E44E873ECAE6BE8521428E8F4224B37B8BB48973399CC43EF89F8863A90CD081C66F5AB34861C1D028181009A6B85BB130780AFC91469B1DDDC6444E91D88AD9550D77C264C8E53F2A1D003C33C189B165AC5DC5A8A416D869381F37EEAEA8B2A78A7680E3AAF89C10ADA36801C7EC8E6CB00C3404100FCECC4544B1B41E0462642655BF97BEF7B9CE5DC7EB6D9C8F5FA85D07563A45B74076FF26D3EA2DC19EB8C1A4F016B36013A672BB502818100F717827BE3D5F5E1454E7BE18690F99AC1C95EA0F0802A34D1BC796D159E22AA1E253C190046529D4E9D3B962C3D675A4C8249750FA8E3CA00336CB9B4827AE3E2FC762FD9A18F0021F294D3076855849159840E7B466367FEC545BB0E7A2173B38FB6E344A065B7E2248E92974C2716560569B9B52106C8A4D0F82821817DDF",
"SHA": 512
}

# -- ENCRYPT using public key

dataToEncrypt = {
"accountNumber": "5541146445656057",
"expiryMonth": "12",
"expiryYear": "21",
"cardholder": "John Doe",
}
dataToEncryptUtf8 = jsonToUtf8String(dataToEncrypt)

print('\n\nDATA IN PLAIN-TEXT to encrypt using RSA public key:\n\n\t' + dataToEncryptUtf8)

encDataHex, encKey, iv, sha = rsaWrapingInPublic(bytes2hex(dataToEncryptUtf8.encode('utf8')), KEYS["PUBLIC_KEY"], KEYS["SHA"])

request = {
"requestId": "123456",
"encryptedPayload": {
"encryptedData": hex2bytes(encDataHex),
"publicKeyFingerprint": KEYS["FINGERPRINT"],
"encryptedKey": encKey,
"oaepHashingAlgorithm": sha,
"iv": iv
}
}
responseString = jsonToUtf8String(request)

# -- SEND REQUEST
print('\n\nJSON REQUEST example to be sent:\n\n\t', responseString)

# -- DECRYPT (on MeaWallet side using private key)

decryptedDataHex = rsaUnwrapingWithPrivateKey(iv, encKey, encDataHex, KEYS["PRIVATE_KEY"], sha)

decryptedData = hex2bytes(decryptedDataHex).decode()

print('\n\nDECRYPTED DATA using RSA private key:\n\n\t', decryptedData)