Card PIN Encryption and Decryption

This documentation shows how

public String encryptPin(String data) throws Exception{ return StringUtils.isBlank(data) ? "" : new String(Hex.encode(encrypt(Hex.encode(data.getBytes())))); } private byte[] encrypt(byte[] data) throws Exception { try { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.ENCRYPT_MODE, publicKey); return cipher.doFinal(data); } catch (GeneralSecurityException e) { throw new Exception("Unable to encrypt data", e); } } public String decryptPin(String data) throws Exception{ return StringUtils.isBlank(data) ? "" : new String(Hex.decode(decrypt(Hex.decode(data)))); } private byte[] decrypt(byte[] data) throws Exception { try { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(data); } catch (GeneralSecurityException e) { throw new Exception("Unable to decrypt data", e); } }

Card PIN Encryption and Decryption is done using Public Private Key Pair.
The above code block should be used for encryption of new card pin and
old card pin (if required) prior to sending the request for pin change.


Sensitive Response Encryption and Decryption

The response data from some of the endpoints contain sensitive information and as such would be returned to the client in an encrypted format. The endpoints currently involved in this encryption include create single card, retry single create card, fetch cards by issuer nr, fetch single card, fetch cards by account and request logs. The encryption algorithm involves the following steps:

  • clear response data is encrypted using a random AES Symmetric Key
  • AES key is then encrypted using the Client's public RSA key
  • Both the encrypted response data and encrypted key are returned as final response. See sample response screenshot below:

Response Message field description

FieldField NameDescription
1encryptionKeyAES Key encrypted using client's public RSA Key
2encryptedResponseAPI Response Data encrypted using AES Key

See below the decryption logic written in Java, as a guideline for implementation.

public String decryptDataByEncryptedAESKey(String encryptedAesKey, String encryptedData, String privateKey) throws GeneralSecurityException { try{ //decrypt aes key using private key String clearKey = this.decryptWithPrivateKey(encryptedAesKey, privateKey); //decrypt data using clear aes key Security.addProvider(new BouncyCastleProvider()); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", BouncyCastleProvider.PROVIDER_NAME); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.decode(clearKey), "AES"), getIvParamSpec()); byte[] decodedData = Base64.decode(encryptedData); byte[] decryptedData = cipher.doFinal(decodedData); return new String(decryptedData, StandardCharsets.UTF_8); } catch(Exception ex){ ex.printStackTrace(); log.error("Exception occurred during encryption", ex); throw ex; } } public String decryptWithPrivateKey(String data, String privateKey) throws GeneralSecurityException { try { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, getPrivateKeyFromString(privateKey)); byte[] decrypted = cipher.doFinal(Hex.decode(data)); return StringUtils.isBlank(data) ? "" : new String(Hex.decode(decrypted)); } catch (GeneralSecurityException e) { throw new GeneralSecurityException(e.getMessage(), e); } } public PrivateKey getPrivateKeyFromString(String privateKeyStr) throws GeneralSecurityException { PrivateKey privateKey = null; try { privateKeyStr = privateKeyStr.replace("-----BEGIN PRIVATE KEY-----\n", ""); privateKeyStr = privateKeyStr.replace("-----END PRIVATE KEY-----", ""); byte[] privateKeyBytes = Base64.decode(privateKeyStr); PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes); privateKey = KeyFactory.getInstance("RSA").generatePrivate(privateKeySpec); } catch (Exception ex) { ex.printStackTrace(); throw new GeneralSecurityException(ex.getMessage(), ex); } return privateKey; }

Database Sensitive Data Encryption and Decryption

public String encrypt(String data) throws Exception { return StringUtils.isBlank(data) ? "" : encryptData(data, encryptionKey); } public String decrypt(String data) throws Exception{ return StringUtils.isBlank(data) ? "" : decryptData(data, encryptionKey); } public static String encryptData(String data, String issuerSecret) throws InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException { Security.addProvider(new BouncyCastleProvider()); Cipher cipher = Cipher.getInstance(SYMMETRIC_KEY_ALG, BouncyCastleProvider.PROVIDER_NAME); cipher.init(Cipher.ENCRYPT_MODE, getAESSecretKey(issuerSecret), getIvParamSpec()); byte[] encryptedData = cipher.doFinal(data.getBytes()); return new String(Base64.encode(encryptedData)); } public static String decryptData(String encryptedData, String issuerSecret) throws InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException { Security.addProvider(new BouncyCastleProvider()); Cipher cipher = Cipher.getInstance(SYMMETRIC_KEY_ALG, BouncyCastleProvider.PROVIDER_NAME); cipher.init(Cipher.DECRYPT_MODE, getAESSecretKey(issuerSecret), getIvParamSpec()); byte[] decodedData = Base64.decode(encryptedData); byte[] decryptedData = cipher.doFinal(decodedData); return new String(decryptedData, StandardCharsets.UTF_8); }

The sensitive data stored on the database is encrypted using AES Symmetric Key.

Decode PIN Block to PIN

HOW TO CALCULATE PIN FROM PIN BLOCK

REQUIREMENTS FOR COMPUTATION:
i. PIN BLOCK
ii. PAN

Format used: Format 0 (ISO-0)

METHOD:

  1. Prepare PAN - take 12 rightmost digits of the primary account number (excluding the check digit)
12345678910111213141516
0000PANPANPANPANPANPANPANPANPANPANPANPAN

  1. XOR both the Prepared PAN in step 1 above and the PIN Block
12345678910111213141516
12345678910111213141516
0LPPPPP/FP/FP/FP/FP/FP/FP/FP/FP/FP/F
XORXORXORXORXORXORXORXORXORXORXORXORXORXORXOR
0000PANPANPANPANPANPANPANPANPANPANPANPAN

  1. Parse the PIN - The first digit is zero, then the next digit L is length of the PIN, and then the next four digits P is PIN digit, after the pin F is padding value "F"
12345678910111213141516
0LPPPPP/FP/FP/FP/FP/FP/FP/FP/FP/FP/F

EXAMPLE


PAN: 43219876543210987
PIN BLOCK: 0412AC89ABCDEF67

XOR: 041234FFFFFFFFFF
PAD: N/A
Format: Format 0 (ISO-0)


Clear PIN: 1234