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.
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:

Field | Field Name | Description |
---|
1 | encryptionKey | AES Key encrypted using client's public RSA Key |
2 | encryptedResponse | API 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;
}
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.
REQUIREMENTS FOR COMPUTATION:
i. PIN BLOCK
ii. PAN
Format used: Format 0 (ISO-0)
METHOD:
- Prepare PAN - take 12 rightmost digits of the primary account number (excluding the check digit)
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|
0 | 0 | 0 | 0 | PAN | PAN | PAN | PAN | PAN | PAN | PAN | PAN | PAN | PAN | PAN | PAN |
- XOR both the Prepared PAN in step 1 above and the PIN Block
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
0 | L | P | P | P | P | P/F | P/F | P/F | P/F | P/F | P/F | P/F | P/F | P/F | P/F |
XOR | XOR | XOR | XOR | XOR | XOR | XOR | XOR | XOR | XOR | XOR | XOR | XOR | XOR | XOR | |
0 | 0 | 0 | 0 | PAN | PAN | PAN | PAN | PAN | PAN | PAN | PAN | PAN | PAN | PAN | PAN |
- 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"
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|
0 | L | P | P | P | P | P/F | P/F | P/F | P/F | P/F | P/F | P/F | P/F | P/F | P/F |
PAN: 43219876543210987
PIN BLOCK: 0412AC89ABCDEF67
XOR: 041234FFFFFFFFFF
PAD: N/A
Format: Format 0 (ISO-0)
Clear PIN: 1234