# PIN Block Formation and Encryption Process
source: https://developer.mastercard.com/mastercard-processing-core/documentation/tutorials-and-guides/pin-encryption-process-tutorial/index.md

This tutorial explains the steps of the PIN block formation and encryption process using `setPIN` or `verifyPIN` operations.

## Step 1 - PIN Block Formation {#step-1---pin-block-formation}

For secure transmission of the card PIN, Mastercard Processing (MP) supports ISO 9564 **format 0** and ISO 9564 **format 1** standard PIN-block formats. ISO 9564 is an international standard for Personal Identification Number (PIN) management and security in retail banking.

* **ISO-0 format** - Format 0 PIN block is constructed by XOR-ing two 64-bit fields: the plain text PIN and the card Primary Account Number (PAN, referred to as `cardContractNumber` in the Mastercard Processing API). This format is offered only to PCI-compliant issuers.

* **ISO-1 format** - Format 1 PIN block is constructed by concatenating the PIN with a transaction number, and it should be used where the card PAN is not available. This format is opted by non-PCI-compliant issuers.


Note: The card must have a PIN defined before a physical card is produced. Mastercard Processing CMS does not validate the old PIN. The following encryption options are allowed:

<br />

* **Symmetric** - a PIN block is secured with a Zone Pin Key (ZPK) exchanged with Mastercard Processing during the onboarding process. When the ZPK encryption is used, the PIN block must be in ISO format 0.
* **Asymmetric** - a PIN block is secured with a dynamically chosen Public RSA key received from Mastercard Processing offered Security API (operation: `getPublicRsaKey`). When the Public RSA key encryption is used, the PIN block may be in ISO format 0 or format 1 (depending on the institution configuration in the Mastercard Processing API). In such a case, the PIN block length is up to 2048 characters. A permitted value format is an ASCII/UTF-8 string of characters 0-9, A-F (ASN.1 DER cryptogram hex unpacked to string).

Note: Currently, Mastercard Processing supports only 4-digit or 6-digit PINs. Any request for PIN verification and PIN translation must adhere to the given format.

### PIN block ISO-0 (FORMAT 0) formation using card PIN and PAN {#pin-block-iso-0-format-0-formation-using-card-pin-and-pan}

An **ISO-0 PIN block format** is equivalent to the ANSI X9.8, VISA-1, and ECI-1 PIN block
formats and is similar to a VISA-4 PIN block format. The ISO-0 PIN block format supports a PIN from 4 to 12 digits in length. A PIN that is longer than 12 digits is truncated on the right. The first nibble (which identifies the block format) has the value 0.

#### 1. Prepare a PIN hexadecimal (base 16) value {#1-prepare-a-pinhexadecimal-base-16value}

The first digit '0' represents ISO format 0. L is the length of the PIN, P is the PIN digit, and F is the 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 | F | F  | F  | F  | F  | F  | F  | F  |

**Sample hexadecimal (base 16) value for PIN 1234**

|   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 4 | 1 | 2 | 3 | 4 | F | F | F | F | F | F | F | F | F | F |

#### 2. Prepare PAN hexadecimal (base 16) value {#2-prepare-pan-hexadecimal-base-16-value}

Take 12 rightmost digits of the Primary Account Number (excluding the last checksum/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 |

**Sample hexadecimal (base 16) value for Card Number (PAN) - 5432 1012 3456 7891**

|   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 2 | 1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

#### 3. XOR both values {#3-xor-both-values}

|  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 | F   | F   | F   | F   | F   | F   | F   | F   |
| XOR | 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 |

The hexadecimal (or **base 16** ) digits start with 0, 1, 2, 3, 4, 5, 6, 7, 8, and 9 (just like in **base 10** ). The remaining **base-16** digits are A, B, C, D, E, and F, corresponding to the remaining **base-10** numbers less than **16** (namely, 10, 11, 12, 13, 14, 15).

**Sample hexadecimal (base 16) post XOR calculation for PIN 1234 and Card Number (PAN) - 4329 8765 4321 0987**

|        |        |        |        |        |        |        |        |        |        |        |        |        |        |     |     |
|--------|--------|--------|--------|--------|--------|--------|--------|--------|--------|--------|--------|--------|--------|-----|-----|
| 0      | 4      | 1      | 2      | 3      | 4      | F      | F      | F      | F      | F      | F      | F      | F      | F   | F   |
| XOR    | XOR    | XOR    | XOR    | XOR    | XOR    | XOR    | XOR    | XOR    | XOR    | XOR    | XOR    | XOR    | XOR    | XOR | XOR |
| 0      | 0      | 0      | 0      | 2      | 1      | 0      | 1      | 2      | 3      | 4      | 5      | 6      | 7      | 8   | 9   |
| ------ | ------ | ------ | ------ | ------ | ------ | ------ | ------ | ------ | ------ | ------ | ------ | ------ | ------ |     |     |
| 0      | 4      | 1      | 2      | 1      | 5      | F      | E      | D      | C      | B      | A      | 9      | 8      | 7   | 6   |

The detailed steps to form a PIN block and encrypt it programmatically are as follows:

#### Implementation: PIN block format 0 formation {#implementation-pin-block-format-0-formation}

This padding mechanism is known as the PIN block format. The HSM supports several PIN block formats identified by a 2-digit PIN block format code.

A 16-digit block is made from the digit 0, the PIN's length, the PIN, and a pad character (hexadecimal F).

Another 16-digit block is made from four zeros and the 12 right-most digits of the card number, excluding the last checksum or the check digit.

Then the two blocks are exclusive-OR added to generate the final PIN block.

**Methods related to PIN block generation**

```java
public String generatePinBlockFormat0(char[] pin, String cardNumber) throws Exception {
    if(pin.length < 4 || pin.length > 6) { 
	// Throw exception here
    }
    StringBuilder pinBlockBuffer = new StringBuilder("0");
    pinBlockBuffer.append(pin.length);
    pinBlockBuffer.append(String.valueOf(pin));

    /** Prefix pin with zero and suffix with F to make it 16 characters long */
    String pinBlock = StringUtils.rightPad(pinBlockBuffer.toString(), 16, 'F');

    int cardLen = cardNumber.length();
/** Take 12 rightmost digits of the primary account number (excluding the last checksum/check digit). */
    String accountNumber = "0000" + cardNumber.substring(cardLen - 13, cardLen - 1);
    

    String pinBlock0 = String.format("%016x", new BigInteger(pinBlock, 16).xor(new BigInteger(accountNumber, 16))).toLowerCase(); 
    return pinBlock0;  
}
```

**Output - PIN block format 0**

```java
// Example ->
String pinBlock = generatePinBlockFormat0("7580", "5141576351968605");
 
// Sample Output -> 
04759589cae6979f
```

### PIN block ISO-1 (FORMAT 1) formation using card PIN {#pin-block-iso-1-format-1-formation-using-card-pin}

#### Prepare a PIN hexadecimal (base 16) value {#prepare-a-pinhexadecimal-base-16value}

The first digit '1' represents ISO format 1. L is the length of the PIN, P is the PIN digit, and F is the random hex value used for padding. It is recommended to generate a random value for padding to prevent an exhaustive attack.

| 1 | 2 | 3 | 4 | 5 | 6 |  7  |  8  | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
|---|---|---|---|---|---|-----|-----|---|----|----|----|----|----|----|----|
| 1 | L | P | P | P | P | P/F | P/F | F | F  | F  | F  | F  | F  | F  | F  |

**Sample hexadecimal (base 16) value for PIN 1234**

|   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 4 | 1 | 2 | 3 | 4 | F | F | F | F | F | F | F | F | F | F |

#### Implementation: PIN block formation {#implementation-pin-block-formation}

This padding mechanism is known as the PIN block format. The HSM supports several PIN block formats identified by a 2-digit PIN block format code.

A 16-digit block is made from the digit 1 (represents ISO 1 format), the PIN's length, the PIN, and a pad character (random hexadecimal characters).

**Methods to generate PIN block**

```java
private String generatePinBlockFormat1(char[] pin) throws ReferenceAppGenericException {
        StringBuilder pinBlock = new StringBuilder("1");
        pinBlock.append(pin.length);
        pinBlock.append(String.valueOf(pin));

        /** Generate random hex value for padding to prevent an exhaustive attack */
        String randomString = org.apache.commons.lang3.RandomStringUtils.random(16 - pinBlock.length(), 0, 0, true, false, HEX_CHAR_SET, new SecureRandom());

        pinBlock.append(randomString.toUpperCase());

        return pinBlock.toString();
    }
private static final char[] HEX_CHAR_SET = new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
```

**Output - PIN block format 1**

```java
// Example ->
String pinBlock = generatePinBlockFormat1("7580");

// Sample Output -> 
147580DBBBAABACB
```

## Step 2 - PIN Block Asymmetric Encryption {#step-2---pin-block-asymmetric-encryption}

The PIN block is secured with a Public RSA key received from Mastercard Processing CMS in a `GET` request sent to the `/public-keys` response (API operation: `getPublicRsaKey`). When Public RSA key encryption is used, the PIN block may be in **ISO format 0** or **format 1** (depending on the Mastercard Processing API configuration).

### Load RSA Public key {#load-rsa-public-key}

This step is applicable only for asymmetric encryption in `setPin` and `verifyPin` operations.

In addition to the below-described PIN block encryption, the payload JWE encryption must be applied before calling the operations, such as `setPIN` and `verifyPIN`.

Load the RSA Public key by calling the `getPublicRsaKey` operation of Core Security API.

Mastercard Processing frequently changes the active RSA Public key as per Mastercard Information Security guidelines.

The key index provides the unique identifier of the RSA Public key.

#### Implementation: Load RSA Public key {#implementation-load-rsa-public-key}

The Core Security API must be called whenever you wish to encrypt the PIN block.

##### Methods to fetch RSA Public key {#methods-to-fetch-rsa-public-key}

```java
public PublicRsaKeyData getPublicRsaKey() {
        PublicRsaKeyData response = null;
            try {
                SecurityApi securityApi = new SecurityApi();
                response = securityApi.getPublicRsaKey("PIN_BLOCK");
                if (response != null) {
                    String keyIndex = response.getKeyIndex();
                    String publicKey = response.getPublicRsaKey();
                    log.debug("getPublicRsaKey KeyIndex={}", keyIndex);
                    log.debug("getPublicRsaKey getPublicRsaKey={}", publicKey);
                }
            } catch (Throwable exception) { 
                log.error("Exception occurred while calling getPublicRsaKey API: {}", exception.getMessage(), exception);
            } 

        return response;
    }
```

### PIN block encryption under RSA Public key {#pin-block-encryption-under-rsa-public-key}

Encrypt the PIN block using `RSA/ECB/PKCS1Padding` encryption transformation mechanism to encrypt the PIN block using RSA Public key.

```java
PublicRsaKeyData publicRsaKeyData = getPublicRsaKey();

String publicKeyIndex = publicRsaKeyData.getKeyIndex();
String publicRsaKeyString = publicRsaKeyData.getPublicRsaKey();

X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(org.bouncycastle.util.encoders.Hex.decode Hex(publicRsaKeyString.toCharArray()));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(publicSpec);
```

#### Encrypt PIN block {#encrypt-pin-block}

Encrypt the PIN block using `RSA/ECB/PKCS1Padding` encryption transformation mechanism to encrypt the PIN block using RSA Public key.

```java
/** Step 1 - Create Cipher for encryption transformation */
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

/** Step 2 - Initializes 'Cipher' with a key. */
cipher.init(Cipher.ENCRYPT_MODE, publicKey);

/** Step 3 - Hex.decodeHex - Converts an array of characters representing hexadecimal values into an array of bytes of those same values. */
byte[] pinBlockBytes = org.bouncycastle.util.encoders.Hex.decode Hex(pinBlock.toCharArray());

/** Step 4 - Encrypts the 'PIN Block' */
 byte[] encryptedKeyBytes = cipher.doFinal(pinBlockBytes);
```

#### Encode encrypted PIN (created in the above step) in hex format {#encode-encrypted-pin-created-in-the-above-step-in-hex-format}

Hex encode the encrypted PIN block.

```java
/** Convert the encrypted 'PIN Block' bytes to HEX string */
String hexEncodedPinBlock = org.bouncycastle.util.encoders. Hex.toHexString(encryptedKeyBytes);
```

#### Output - encrypted PIN block {#output---encrypted-pin-block}

```java
// Example ->
 "04759589cae6979f";
String pinBlockFormat0 = generatePinBlockFormat0("7580", "5141576351968605");
// pinBlockFormat0 = "04759589cae6979f";
String encryptedPinBlock = asymmetricEncryption(pinBlockFormat0);
 
// Sample Output -> 
570db924136ae4cdcf0ee7e7ecd72fa6d4bb08e1a998eca5f72d853d6978e9a017ba5ca2fbc0646303ab40aa548684fc6f501d8e025957d8a836f35c269d14377e0094b43a39b01ca1b6d0be4ab928d209695cb562942c97d774ad17340d259cfae852f303ac12f51c84418a0d01804bd901648f42e6eee792cb75073e684041cfdb64c52c1cb5189ee2f1144ad26106a82d0850f2ffdd40bb5eaf898d0699271543d986ec7df99504fb0aa659752e72b3657d81608a059efdde5e3ad6149ac35e38221431713bb47742f4d9269e052d6819e5ed1153e64496ef2be89cd1fcac5f62ed863025c301f5d3f9bb389596aa40c13de605d7a72effe7e39f6fb64c12
```

Note: Apart from the above encryption (symmetric or asymmetric), the payload JWE encryption is used.

## Step 3 - PIN Block Symmetric Encryption {#step-3---pin-block-symmetric-encryption}

The PIN block is secured with Zone Pin Key (ZPK) exchanged with Mastercard Processing. When the ZPK encryption is used, the PIN block must be in **ISO format 0**.

### Load ZPK {#load-zpk}

Load the hex-encoded ZPK from the secret vault.

```java
String hexEncodedZPK = "7C933777139EAA6F2940664B299DFFC2";
```

### Build TDEA key from the ZPK {#build-tdea-key-from-the-zpk}

Hex decode the Mastercard shared ZPK and build the TDEA key supporting triple length keys.

```java
public static byte[] buildTDEAKey(String hexEncodedZPK) {
        byte[] key = new byte[24];
        /**
         * Decode Hex ZPK - Converts an array of characters representing hexadecimal values into an array of bytes of those same values.
         */
        byte[] tmp = Hex.decode(hexEncodedZPK);

        /** Copy key to new byte array based on the length of ZPK */
        if (tmp.length == 24) {
            /** Support triple length 3DES keys */
            System.arraycopy(tmp, 0, key, 0, 24);
        } else {
            /** Support double length 3DES keys */
            System.arraycopy(tmp, 0, key, 0, 16);
            System.arraycopy(tmp, 0, key, 16, 8);
        }
        return key;
    }
```

### PIN block encryption under ZPK {#pin-block-encryption-under-zpk}

Encrypt the PIN block by using the Mastercard shared ZPK, and hex encode it.

```java
public static String symmetricEncryption(String hexEncodedZPK, String pinBlock) {
        /**
         * Step 1 - decode Hex ZPK and build TDEA key supporting 2 keys and 3 keys
         */
        byte[] key = buildTDEAKey(hexEncodedZPK);

        /**
         * Step 2 - Create Cipher for encryption transformation.
         */
        Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");

        /** Step 3 - Initializes 'Cipher' with a key. */
        cipher.init(1, new SecretKeySpec(key,"DESede"));

        /** Step 4 - Encrypts the 'PIN Block' */
        byte[] ciphertext = cipher.doFinal(org.bouncycastle.util.encoders.Hex.decode(pinBlock));

        /** Step 5 - Convert the encrypted 'PIN Block' bytes to HEX string */
        String hexEncodedPinBlock =  org.bouncycastle.util.encoders.Hex.toHexString(ciphertext);

        /** Step 6 - Convert the encrypted 'PIN Block' to Upper case */
        return hexEncodedPinBlock.toUpperCase();
```

### Output - encrypted PIN block {#output---encrypted-pin-block-1}

```java
// Example ->
 "04759589cae6979f";
String pinBlockFormat0 = generatePinBlockFormat0("7580", "5141576351968605");

// pinBlockFormat0 = "04759589cae6979f";
String encryptedPinBlock = symmetricEncryption("7C933777139EAA6F2940664B299DFFC2", pinBlockFormat0);
 
// Sample Output -> 
0b8315c81719e9ca
```

Note: Apart from the above encryption (symmetric or asymmetric), the payload JWE encryption is used.
