pps-platform-account-management

(0 reviews)

Envelope Encryption

The algorithm for Envelope Encryption uses both a symmetric AES key (256bit) and an RSA Public Key, to encrypt the data to be exchanged. The AES key is generated and used for encrypting the data (AES/CBC/PKCS5Padding), and then the AES key itself is encrypted (wrapped) using the Public Key (RSA/ECB/OAEPWithSHA-512AndMGF1Padding), with the encrypted AES key then being passed alongside the encrypted data. In this way, the encrypted data itself can only be decrypted by the holder of the Private Key, which is used to decrypt the AES key.

resources/paytech-envelope-encryption-eaa0d164-d5d8-4742-8e09-5482e685fb49.PNG

Steps for encryption:

  1. Assemble JSON object containing all sensitive data to be encrypted
  2. Generate 256 bit AES Encryption Key
  3. Generate a 16-byte random Initialisation Vector (IV)
  4. Use the AES Key to encrypt the UTF-8 bytes of the Json object, using AES/CBC/PKCS5Padding and the previously generated IV. Hex-encode the result.
  5. Use the recipient Public Key to “wrap” (encrypt) the AES Key using RSA/ECB/OAEPWithSHA-512AndMGF1Padding. Hex-encode the result.
  6. Assemble a new JSON object containing:
    1. encrypted_data - the data encrypted by the AES Key (from Step 4)
    2. encryption_key - the “wrapped” AES, encrypted by the recipient Public Key (from Step 5)
    3. iv - the IV from Step 3, hex-encoded
    4. public_key_fingerprint - a hex-encoded SHA-256 hash of the Public Key, to identify the key used to the recipient. Prefix with the hash algorithm to make it clear what was used to compute the fingerprint (e.g. SHA-256:746573747075626c69636b6579 )
  7. Finally, Base 64-encode the resulting JSON object, and set as a single string value in the request / response body - encrypted_payload

Pseudocode for encryption:

String dataToEncrypt = <assemble data to be encrypted>

PublicKey rsaPublicKey = <received RSA Public Key>
String pubKeyFingerprint = "SHA-256:" + sha256Hash(rsaPublicKey.getEncoded()

SecretKey aesKey = generateKey("AES", 256 bit)
byte[] iv = generateRandomIv(16 bytes)
String ivHex = Hex.encode(iv)

// Encrypt the data with the AES Key and hex encode
byte[] encryptedDataBytes = doEncryption("AES/CBC/PKCS5Padding", aesKey, iv, dataToEncrypt)
String encryptedDataHex = Hex.encode(encryptedDataBytes)

// Encrypt (wrap) the AES Key with the Public Key, and hex-encode
byte[] encryptedKeyBytes = doKeyWrapping("RSA/ECB/OAEPWithSHA-512AndMGF1Padding", rsaPublicKey, aesKey)
String encryptedKeyHex = Hex.encode(encryptedKeyBytes)

// Assemble the encrypted payload envelope
String encryptedPayloadJson = buildJsonString(encryptedDataHex, encryptedKeyHex, ivHex, pubKeyFingerprint)
String base64payload = Base64.encode(encryptedPayloadJson)

Decrypting this then is essentially just the reverse of this process, using the Private Key (corresponding to the Public Key identified by the public_key_fingerprint) to “unwrap” the encrypted symmetric AWS key, and then using that key along with the IV to decrypt the encrypted data.

Pseudocode for decryption:

String base64payload = <received encrypted payload>
Private Key rsaPrivateKey = <corresponding RSA Private Key>

// Base64 decode encrypted payload and extract values
String encryptedPayloadJson = Base64.decode(base64payload)
String encryptedKeyHex = <get from JSON>
String encryptedDataHex = <get from JSON>
String ivHex= <get from JSON>
String pubKeyFingerprint = <get from JSON>

// Decrypt encrypted AES Key with Private Key, and build AES Secret Key
byte[] encryptedKeyBytes = Hex.decode(encryptedKeyHex)
bytep[ aesKeyBytes = doKeyUnwrapping("RSA/ECB/OAEPWithSHA-512AndMGF1Padding", rsaPrivateKey, encryptedKeyBytes)
SecretKey aesKey = createKey("AES", aesKeyBytes )

// Decode IV
byte[] iv = Hex.decode(ivHex)

// Decrypt Encrypted Data to get the original data that was encrypted
byte[] encryptedData = Hex.decode(encryptedDataHex)
String originalData = doDecryption("AES/CBC/PKCS5Padding", aesKey, iv, encryptedData,

Worked Example

In these worked examples, we will encrypt and decrypt some data using the envelope encryption algorithm.

The data to be encrypted / decrypted:

{
  "pan": "1234567890123456",
  "security_data": {
    "security_code_2": "789"
  }
}

Encryption

The first step in the encryption then, is generating the 256-bit AES Encryption Key. The generated key for this example is shown below (Hex-encoded):

AES Key (Hex) = 4821bcb653eb834cea8688d232ae143a6d35b466b6f9201e5bf5cf0d87cc9ee2

To encrypt the data, using AES/CBC/PKCS5Padding we also need to generate a random 16-byte Initialisation Vector (IV). This is shown below (Hex-encoded again):

IV (Hex) = 57a868c9a01351552b25e47fe83e2fdb

The data (the JSON string) encrypted by the AES Key with the IV (AES/CBC/PKCS5Padding), and hex-encoded, should be:

Encrypted Data = 9e064c43fa5bdf8f35a1db98af8bf94f083f0215b04be621f0f686dd8e444b5a9a916b9b77ac6ad56a3294109c99fc85237dbc7e2550141d40e117260d34e9a2342f5632cbe2c25151fa3ae1000500b2e22e3fd6e83e8e40882b619cf361391d

Next, we need to encrypt (wrap) the AES Key used, using the recipients Public Key. For this example, we have generated the following 2048-bit RSA Keypair:

Private Key (will be used later in the decryption flow):

-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDesXXR5vvcff+nWN4UE2xOWBVZ
Z60i77CzX2Z18Cl/oJGoqDwVB5dNe6i8QpHE2DSmtFm0HqgRioH0WhyGSer6S9jMML76+6ny1Qe6
T1j3SQOUE7/tW05HjT1uobbIb4yCcMEEbaN2JMkiTmizZt5uV0E3fH3TPvonwaHEDr3Wn2JwO2jJ
kVAMsqtwrhe4wm2leziO74ceshPvlIiO622RzP14mnBz1QW2zbZc9lCIbewqOqVQZEXHwK8GTf9G
CMw438bRmt+CSbQgOTym1o8XhtD0cE7yTFyg55HPW2HQdFLizjwaVJze34XtQms9b+ZdKtn6NN3K
zytAeyN35qP9AgMBAAECggEAYAGlx86LOq3fb+gyJfmRsdzrp1W5INyxE0PHyoS1f15cLZgJ/w78
vdzYDAGvEmwbltsd6TdmQdmy2QOYVJS9hxb36mABlfa1eAk7gaj/s+x4bHhvQAcjIlnT6EQzpwng
S8QMkropxJ9rTyz6mo14y502cpNrM41tD4K9GOc6FLJoH3tBsgSfv0ND69v30SOy7YtcmSbtzav0
W96bkgBjtS/x/wvh6eQUX4g5j4JLaBSOt++dIwUuvInwM1yaxz/dW6fcXPqnS2EohAy4+XJ1GMT2
BdyuOSuEQyxMaqsZ0WHcZ99jubEmlmLzXvjGscdL+HH2hRHn5AHAwxHanur9CQKBgQDmhwnmZTzJ
QbRwQCIhlMsOUqacs8oQ+gXeP0rRnw+JVA6CGkvT6fzgcWPJs/Ljcaz/anmIrq730j28X4VY7Ir0
3Ih4kz3/2XIiIwFYvMtk3+R0VqeDmiRe4jVtR/rdXXb6HzElf/VFfPpPLjxY25vcguNnRF6Xwa+P
jqJiw/6LDwKBgQD3TM/caDku/wKfvBT60un65upVwhIZaTWyKmYZAQnQSx7psz4qryf8W1EpK3O3
OEdE4v9QCUAnAC3J/ltb2qeRtOg0H4d5dHejDGkwa8LfvuQr2Co2u5J6LYVVFdCl0wR4aAfBCtyX
6qlFTF5l7z9IZEYLQ6/ZABou1RrvRIAQMwKBgQDegQi1MJHzT0/x6HUJJKy5Ll1SctZefHSdh0mX
+obsbg1y1h7Kc9Z4a/l8/gs6LMZl4Udc/f4zhOHBqBD1O5sDrLisaLI2+JKJEHCETfWjb9b504ns
JCNm0b/vssIYV9Y2pVSydUcvwul285Gb0pwlws/rJLqIj5H8ZhembLaLUQKBgQDTIqhgxDb2MdB6
siizUPllNlshZrTvLw0iFOLgPCu1wz0cR+UuqiaYZRpw7OE5r3nhjXBvU8aKxbNSPrCfRppEY1DQ
l46hmlc+RK1WItrZDnBeIos/+XsAhvbMAvkFapjpd+AO54gYx8dHHAGIQ/AijUHkAiETQuwXyefa
GgDAUwKBgHc9uFdXHQETam37I1JMPTMbnV2oZQ6URolxK/nB24SpBfOXrjzjvK0pa/8TYRXjYFLE
koeUMgnEt6YGfGDOB58ZY1YZJemPAvIhHhAj0ZCD8DT0YPui58T8GSU1lvobArkuCVPXp9SXDo7Z
e8KQtmk0DsVlSL5Xq9Ows+/5I/Oq
-----END PRIVATE KEY-----

Public Key:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3rF10eb73H3/p1jeFBNsTlgVWWetIu+w
s19mdfApf6CRqKg8FQeXTXuovEKRxNg0prRZtB6oEYqB9Fochknq+kvYzDC++vup8tUHuk9Y90kD
lBO/7VtOR409bqG2yG+MgnDBBG2jdiTJIk5os2bebldBN3x90z76J8GhxA691p9icDtoyZFQDLKr
cK4XuMJtpXs4ju+HHrIT75SIjuttkcz9eJpwc9UFts22XPZQiG3sKjqlUGRFx8CvBk3/RgjMON/G
0Zrfgkm0IDk8ptaPF4bQ9HBO8kxcoOeRz1th0HRS4s48GlSc3t+F7UJrPW/mXSrZ+jTdys8rQHsj
d+aj/QIDAQAB
-----END PUBLIC KEY-----

Using the Public Key above to wrap the AES key (the bytes, not the hex-encoded value) with RSA/ECB/OAEPWithSHA-512AndMGF1Padding, should result in:

Encrypted AES Key = 9758f6c846e71e4495e3e6ab4c698e988da0627e735722e5971fc9b191bdc64d94f8ef97d9b19c7133a835850d038d58bba7b9c90f99c8dea63aded9d7c5f88662a74911151011617911fd60c7772eb6dcc3eadd1e4798a89ce28b63037d8c50d7ee7dccc537f2e4830bbf14309e1b3756e1df242e7d786e260fc4e4b5be9dde62229b1d62692772d3b6db913b77c8d22f45728ecb76fcdb0db0759208cf2077e6327f3572ae76b32a8a92f9f65f9a88b2f8f9100683cfc2a25a271e04ec328ac82a2f8e1e185a678cbe44daf06bb44cce53c7a9cfeff947511062e99fa28cd39c69e5bf48628b415dd5f042e831a2866a5e5de07983244337a542e120cef294

And the Public Key Fingerprint (SHA-256 Hash of Public Key bytes + Hex-encoded), with the SHA-256: prefix is:

Public Key Fingerprint = SHA-256:c7130979aaeac6cbd2ceb655c61360199a9e9ede78227ef82362832a63420cb8

With these values, we can then assemble the Encrypted Payload envelope as JSON:

{
  "encrypted_data": "9e064c43fa5bdf8f35a1db98af8bf94f083f0215b04be621f0f686dd8e444b5a9a916b9b77ac6ad56a3294109c99fc85237dbc7e2550141d40e117260d34e9a2342f5632cbe2c25151fa3ae1000500b2e22e3fd6e83e8e40882b619cf361391d",
  "encryption_key": "9758f6c846e71e4495e3e6ab4c698e988da0627e735722e5971fc9b191bdc64d94f8ef97d9b19c7133a835850d038d58bba7b9c90f99c8dea63aded9d7c5f88662a74911151011617911fd60c7772eb6dcc3eadd1e4798a89ce28b63037d8c50d7ee7dccc537f2e4830bbf14309e1b3756e1df242e7d786e260fc4e4b5be9dde62229b1d62692772d3b6db913b77c8d22f45728ecb76fcdb0db0759208cf2077e6327f3572ae76b32a8a92f9f65f9a88b2f8f9100683cfc2a25a271e04ec328ac82a2f8e1e185a678cbe44daf06bb44cce53c7a9cfeff947511062e99fa28cd39c69e5bf48628b415dd5f042e831a2866a5e5de07983244337a542e120cef294",
  "public_key_fingerprint": "SHA-256:c7130979aaeac6cbd2ceb655c61360199a9e9ede78227ef82362832a63420cb8",
  "iv": "57a868c9a01351552b25e47fe83e2fdb"
}

... and then Base64 encode the JSON string to return or set as the value of the encrypted_payload field in the API request/response.

Base64-encoded Encrypted Payload = ewogICJlbmNyeXB0ZWRfZGF0YSI6ICI5ZTA2NGM0M2ZhNWJkZjhmMzVhMWRiOThhZjhiZjk0ZjA4M2YwMjE1YjA0YmU2MjFmMGY2ODZkZDhlNDQ0YjVhOWE5MTZiOWI3N2FjNmFkNTZhMzI5NDEwOWM5OWZjODUyMzdkYmM3ZTI1NTAxNDFkNDBlMTE3MjYwZDM0ZTlhMjM0MmY1NjMyY2JlMmMyNTE1MWZhM2FlMTAwMDUwMGIyZTIyZTNmZDZlODNlOGU0MDg4MmI2MTljZjM2MTM5MWQiLAogICJlbmNyeXB0aW9uX2tleSI6ICI5NzU4ZjZjODQ2ZTcxZTQ0OTVlM2U2YWI0YzY5OGU5ODhkYTA2MjdlNzM1NzIyZTU5NzFmYzliMTkxYmRjNjRkOTRmOGVmOTdkOWIxOWM3MTMzYTgzNTg1MGQwMzhkNThiYmE3YjljOTBmOTljOGRlYTYzYWRlZDlkN2M1Zjg4NjYyYTc0OTExMTUxMDExNjE3OTExZmQ2MGM3NzcyZWI2ZGNjM2VhZGQxZTQ3OThhODljZTI4YjYzMDM3ZDhjNTBkN2VlN2RjY2M1MzdmMmU0ODMwYmJmMTQzMDllMWIzNzU2ZTFkZjI0MmU3ZDc4NmUyNjBmYzRlNGI1YmU5ZGRlNjIyMjliMWQ2MjY5Mjc3MmQzYjZkYjkxM2I3N2M4ZDIyZjQ1NzI4ZWNiNzZmY2RiMGRiMDc1OTIwOGNmMjA3N2U2MzI3ZjM1NzJhZTc2YjMyYThhOTJmOWY2NWY5YTg4YjJmOGY5MTAwNjgzY2ZjMmEyNWEyNzFlMDRlYzMyOGFjODJhMmY4ZTFlMTg1YTY3OGNiZTQ0ZGFmMDZiYjQ0Y2NlNTNjN2E5Y2ZlZmY5NDc1MTEwNjJlOTlmYTI4Y2QzOWM2OWU1YmY0ODYyOGI0MTVkZDVmMDQyZTgzMWEyODY2YTVlNWRlMDc5ODMyNDQzMzdhNTQyZTEyMGNlZjI5NCIsCiAgInB1YmxpY19rZXlfZmluZ2VycHJpbnQiOiAiU0hBLTI1NjpjNzEzMDk3OWFhZWFjNmNiZDJjZWI2NTVjNjEzNjAxOTlhOWU5ZWRlNzgyMjdlZjgyMzYyODMyYTYzNDIwY2I4IiwKICAiaXYiOiAiNTdhODY4YzlhMDEzNTE1NTJiMjVlNDdmZTgzZTJmZGIiCn0

Decryption

For the worked example of the decryption, we will work back through the encryption steps in reverse, using the example from the Encryption flow above.

So for the Decryption, we start with the Base64-encoded Encrypted Payload (the envelope):

Base64-encoded Encrypted Payload = ewogICJlbmNyeXB0ZWRfZGF0YSI6ICI5ZTA2NGM0M2ZhNWJkZjhmMzVhMWRiOThhZjhiZjk0ZjA4M2YwMjE1YjA0YmU2MjFmMGY2ODZkZDhlNDQ0YjVhOWE5MTZiOWI3N2FjNmFkNTZhMzI5NDEwOWM5OWZjODUyMzdkYmM3ZTI1NTAxNDFkNDBlMTE3MjYwZDM0ZTlhMjM0MmY1NjMyY2JlMmMyNTE1MWZhM2FlMTAwMDUwMGIyZTIyZTNmZDZlODNlOGU0MDg4MmI2MTljZjM2MTM5MWQiLAogICJlbmNyeXB0aW9uX2tleSI6ICI5NzU4ZjZjODQ2ZTcxZTQ0OTVlM2U2YWI0YzY5OGU5ODhkYTA2MjdlNzM1NzIyZTU5NzFmYzliMTkxYmRjNjRkOTRmOGVmOTdkOWIxOWM3MTMzYTgzNTg1MGQwMzhkNThiYmE3YjljOTBmOTljOGRlYTYzYWRlZDlkN2M1Zjg4NjYyYTc0OTExMTUxMDExNjE3OTExZmQ2MGM3NzcyZWI2ZGNjM2VhZGQxZTQ3OThhODljZTI4YjYzMDM3ZDhjNTBkN2VlN2RjY2M1MzdmMmU0ODMwYmJmMTQzMDllMWIzNzU2ZTFkZjI0MmU3ZDc4NmUyNjBmYzRlNGI1YmU5ZGRlNjIyMjliMWQ2MjY5Mjc3MmQzYjZkYjkxM2I3N2M4ZDIyZjQ1NzI4ZWNiNzZmY2RiMGRiMDc1OTIwOGNmMjA3N2U2MzI3ZjM1NzJhZTc2YjMyYThhOTJmOWY2NWY5YTg4YjJmOGY5MTAwNjgzY2ZjMmEyNWEyNzFlMDRlYzMyOGFjODJhMmY4ZTFlMTg1YTY3OGNiZTQ0ZGFmMDZiYjQ0Y2NlNTNjN2E5Y2ZlZmY5NDc1MTEwNjJlOTlmYTI4Y2QzOWM2OWU1YmY0ODYyOGI0MTVkZDVmMDQyZTgzMWEyODY2YTVlNWRlMDc5ODMyNDQzMzdhNTQyZTEyMGNlZjI5NCIsCiAgInB1YmxpY19rZXlfZmluZ2VycHJpbnQiOiAiU0hBLTI1NjpjNzEzMDk3OWFhZWFjNmNiZDJjZWI2NTVjNjEzNjAxOTlhOWU5ZWRlNzgyMjdlZjgyMzYyODMyYTYzNDIwY2I4IiwKICAiaXYiOiAiNTdhODY4YzlhMDEzNTE1NTJiMjVlNDdmZTgzZTJmZGIiCn0

... and the Private Key corresponding to the Public Key used in the encryption above (fingerprint = SHA-256:c7130979aaeac6cbd2ceb655c61360199a9e9ede78227ef82362832a63420cb8).

-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDesXXR5vvcff+nWN4UE2xOWBVZ
Z60i77CzX2Z18Cl/oJGoqDwVB5dNe6i8QpHE2DSmtFm0HqgRioH0WhyGSer6S9jMML76+6ny1Qe6
T1j3SQOUE7/tW05HjT1uobbIb4yCcMEEbaN2JMkiTmizZt5uV0E3fH3TPvonwaHEDr3Wn2JwO2jJ
kVAMsqtwrhe4wm2leziO74ceshPvlIiO622RzP14mnBz1QW2zbZc9lCIbewqOqVQZEXHwK8GTf9G
CMw438bRmt+CSbQgOTym1o8XhtD0cE7yTFyg55HPW2HQdFLizjwaVJze34XtQms9b+ZdKtn6NN3K
zytAeyN35qP9AgMBAAECggEAYAGlx86LOq3fb+gyJfmRsdzrp1W5INyxE0PHyoS1f15cLZgJ/w78
vdzYDAGvEmwbltsd6TdmQdmy2QOYVJS9hxb36mABlfa1eAk7gaj/s+x4bHhvQAcjIlnT6EQzpwng
S8QMkropxJ9rTyz6mo14y502cpNrM41tD4K9GOc6FLJoH3tBsgSfv0ND69v30SOy7YtcmSbtzav0
W96bkgBjtS/x/wvh6eQUX4g5j4JLaBSOt++dIwUuvInwM1yaxz/dW6fcXPqnS2EohAy4+XJ1GMT2
BdyuOSuEQyxMaqsZ0WHcZ99jubEmlmLzXvjGscdL+HH2hRHn5AHAwxHanur9CQKBgQDmhwnmZTzJ
QbRwQCIhlMsOUqacs8oQ+gXeP0rRnw+JVA6CGkvT6fzgcWPJs/Ljcaz/anmIrq730j28X4VY7Ir0
3Ih4kz3/2XIiIwFYvMtk3+R0VqeDmiRe4jVtR/rdXXb6HzElf/VFfPpPLjxY25vcguNnRF6Xwa+P
jqJiw/6LDwKBgQD3TM/caDku/wKfvBT60un65upVwhIZaTWyKmYZAQnQSx7psz4qryf8W1EpK3O3
OEdE4v9QCUAnAC3J/ltb2qeRtOg0H4d5dHejDGkwa8LfvuQr2Co2u5J6LYVVFdCl0wR4aAfBCtyX
6qlFTF5l7z9IZEYLQ6/ZABou1RrvRIAQMwKBgQDegQi1MJHzT0/x6HUJJKy5Ll1SctZefHSdh0mX
+obsbg1y1h7Kc9Z4a/l8/gs6LMZl4Udc/f4zhOHBqBD1O5sDrLisaLI2+JKJEHCETfWjb9b504ns
JCNm0b/vssIYV9Y2pVSydUcvwul285Gb0pwlws/rJLqIj5H8ZhembLaLUQKBgQDTIqhgxDb2MdB6
siizUPllNlshZrTvLw0iFOLgPCu1wz0cR+UuqiaYZRpw7OE5r3nhjXBvU8aKxbNSPrCfRppEY1DQ
l46hmlc+RK1WItrZDnBeIos/+XsAhvbMAvkFapjpd+AO54gYx8dHHAGIQ/AijUHkAiETQuwXyefa
GgDAUwKBgHc9uFdXHQETam37I1JMPTMbnV2oZQ6URolxK/nB24SpBfOXrjzjvK0pa/8TYRXjYFLE
koeUMgnEt6YGfGDOB58ZY1YZJemPAvIhHhAj0ZCD8DT0YPui58T8GSU1lvobArkuCVPXp9SXDo7Z
e8KQtmk0DsVlSL5Xq9Ows+/5I/Oq
-----END PRIVATE KEY-----

So the first step is to Base64 decode the encrypted payload string, to result in the JSON Envelope:

{
  "encrypted_data": "9e064c43fa5bdf8f35a1db98af8bf94f083f0215b04be621f0f686dd8e444b5a9a916b9b77ac6ad56a3294109c99fc85237dbc7e2550141d40e117260d34e9a2342f5632cbe2c25151fa3ae1000500b2e22e3fd6e83e8e40882b619cf361391d",
  "encryption_key": "9758f6c846e71e4495e3e6ab4c698e988da0627e735722e5971fc9b191bdc64d94f8ef97d9b19c7133a835850d038d58bba7b9c90f99c8dea63aded9d7c5f88662a74911151011617911fd60c7772eb6dcc3eadd1e4798a89ce28b63037d8c50d7ee7dccc537f2e4830bbf14309e1b3756e1df242e7d786e260fc4e4b5be9dde62229b1d62692772d3b6db913b77c8d22f45728ecb76fcdb0db0759208cf2077e6327f3572ae76b32a8a92f9f65f9a88b2f8f9100683cfc2a25a271e04ec328ac82a2f8e1e185a678cbe44daf06bb44cce53c7a9cfeff947511062e99fa28cd39c69e5bf48628b415dd5f042e831a2866a5e5de07983244337a542e120cef294",
  "public_key_fingerprint": "SHA-256:c7130979aaeac6cbd2ceb655c61360199a9e9ede78227ef82362832a63420cb8",
  "iv": "57a868c9a01351552b25e47fe83e2fdb"
}

Next we should verify that the public_key_fingerprint returned matches the Public Key corresponding to the Private Key to be used for Decryption. After that, we can use the Private Key to unwrap the symmettric AES encryption key in encryption_key. This again uses RSA/ECB/OAEPWithSHA-512AndMGF1Padding (but in "unwrap" mode).

This should result in us now having the clear AES symmetric key, shown below Hex-encoded:

AES Key (Hex) = 4821bcb653eb834cea8688d232ae143a6d35b466b6f9201e5bf5cf0d87cc9ee2

Using this AES Key, along with the iv (Initialisation Vector) from the envelope, the encrypted_data can be decrypted using the AES/CBC/PKCS5Padding algorithm, resulting in the original data that was encrypted:

{
  "pan": "1234567890123456",
  "security_data": {
    "security_code_2": "789"
  }
}

Reviews