# PIN Display Process
source: https://developer.mastercard.com/mastercard-processing-core/documentation/tutorials-and-guides/pin-display-process-tutorial/index.md

This tutorial explains the steps to display PIN using the `getPIN` operation.

The `getPin` operation is used to retrieve the PIN set for a given card contract. For secure transmission of the PIN, Mastercard Processing (MP) supports ISO 9564 **format 0** and ISO 9564 **format 1** standard PIN block format. ISO 9564 is an international standard for Personal Identification Number (PIN) management and security in retail banking. Based on the API configuration, the `getPin` operation supports either format 0 or format 1 for a given institution.

## PIN Block Format {#pin-block-format}

* **ISO-0 format** - The PIN block is constructed by XOR-ing two 64-bit fields: the plain text PIN and the Primary Account Number (PAN, referred to as card contract number in the Mastercard Processing API).
* **ISO-1 format** - The PIN block is constructed by concatenating the PIN with a random number, and it should be used where the PAN is unavailable. This format is opted by PAN-less issuers.

## Security {#security}

This PIN block is secured with a Zone PIN Key (ZPK) shared by Mastercard in response to every API call. The ZPK is secured with the RSA Public key shared by you in the `getPin` request.

## Step 1 - Generate and Share RSA Public Key {#step-1---generate-and-share-rsa-public-key}

### Generate an RSA key pair {#generate-an-rsa-key-pair}

Generate a new RSA key (key size: 2048) pair before calling the `getPin` operation.

**Sample code**

```java
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
// key size 2048
generator.initialize(2048);
KeyPair pair = generator.generateKeyPair();
PrivateKey privateKey = pair.getPrivate();
PublicKey publicKey = pair.getPublic();

String privateKeyString = new String(Hex.encode(privateKey.getEncoded()));
String publicKeyString = new String(Hex.encode(publicKey.getEncoded())); 
```

### Share the RSA Public key {#share-the-rsa-public-key}

Share the RSA Public key (Hex encoded with uppercase characters) in the HTTP header `Customer-Public-Rsa-Key` while calling the `getPin` operation.

**Sample code**

```java
// Lets assume that you are using RestTemplate
// Set the HTTP headers
final HttpHeaders headers = new HttpHeaders();
headers.set("Customer-Public-Rsa-Key", publicKeyString);

// Create a new HttpEntity
final HttpEntity<String> entity = new HttpEntity<String>(headers);
```

## Step 2 -- Request Processing by Mastercard Processing {#step-2--request-processing-by-mastercard-processing}

**Form the PIN block** - The Mastercard Processing (MP) system retrieves the card PAN based on the API configuration and forms the PIN block.

**Generate the ZPK** -- The Mastercard Processing system generates a unique ZPK.

**Encrypt the (symmetric) PIN block** - The Mastercard Processing system encrypts the PIN block using the newly generated ZPK.

**Encrypt the (asymmetric) ZPK** - The Mastercard Processing system encrypts the ZPK using the RSA Public key from the HTTP header `Customer-Public-Rsa-Key` from Step 1.

**Encrypt the (asymmetric) card contract number** - The Mastercard Processing system encrypts the card contract number (PAN) using the RSA Public key from Step 1.

## Step 3 -- Response Processing by the Issuer {#step-3--response-processing-by-the-issuer}

Note: Before proceeding with the following steps, the JWE decryption must be applied to the complete `getPin` response payload.

### Decrypt the (asymmetric) card contract number {#decrypt-the-asymmetric-card-contract-number}

Decrypt the card contract number (PAN) using the RSA Private key generated by itself in Step 1.

**Sample code**

```java
// Input encryptedCardContractNumber fetched from API response
// Output clearCardContractNumber
/**
 * Step 1 - Create Cipher for encryption transformation.
 */
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

/** Step 2 - Initializes 'Cipher' with a private key (part of RSA key pair generated earlier. */
cipher.init(Cipher.DECRYPT_MODE, privateKey);

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

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

/** Step 5 - Convert the encrypted bytes to HEX string */
String hexEncodedCardContractNumber = Hex.toHexString(hexEncodedCardContractNumberBytes);

/** Step 6 - Convert HEX string to string */
String clearCardContractNumber = new String (Hex.decode(hexEncodeCardContractNumber));
```

**Sample input**

```java
String encryptedCardContractNumber= "854D2479C72C18102F4950EC70378AF239E6F9A88B39C68E59FFCF641255A4841ADEB1061B78B75159AD795ACBAFF5FB33636D4219602F32887BBCBB00D6E322D32C0276A46A2059E11029497EB74F682D5891C36DAD8AF7692EAA216BB007EA50C78E8E16D136AC2CD5BEBA69660732CD9F2A36211C6907FE3EC31F80DC7224C15A63E278278B14161702861AA1F8328959F380AA8EDF803B7EE2709F8A16C495A067F162AEF85210218A936F484AEC0770A43942346C1EA8193DFD8E066FD3996D42074F70C3F91CB506F5738DB8BE2CA1B4133061A60435A354D2D1A9466306DAF2ED8BA07D9B49AC310BBC3AF6BE7A57F4471C550F9A904772B8FB005534"
```

**Sample output**

```java
String clearCardContractNumber = "5290848056552490"
```

### Decrypt the (asymmetric) ZPK {#decrypt-the-asymmetric-zpk}

Decrypt the ZPK using the RSA Private key generated in Step 1 before calling the API. This is the hex-encoded ZPK. It is the same as the card number decryption process using the RSA Private key. The output plain text ZPK is in the Standard Key Block Type.

**Sample code**

```java
// Input encryptedZpk fetched from API response
// Output encodedZPK
/**
 * Step 1 - Create Cipher for encryption transformation.
 */
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

/** Step 2 - Initializes 'Cipher' with a private key (part of RSA key pair generated earlier. */
cipher.init(Cipher.DECRYPT_MODE, privateKey);

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

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

/** Step 5 - Convert the encrypted bytes to HEX string */
String tlvEncodedZPK = Hex.toHexString(decryptedKeyBytes);
```

**Sample input**

```java
String encryptedZpk = "80B50F4F7A07C30F90CAD796FF07D9B10DCED69B186D8D1DC6ABD51B7459139DA4E71F05BEE76353E778F6314E7F79412EE1C3AF9BFE4BFCA7C8968D2754EAB7EFBABD59065D709F84355F030D8AE951C5CCE9DBF46415BDECC5C336EDFB67E093914F810AF2C56F3F353064C926A0165D503D731C748C98B099B8E80E778D419A8E64F89C8FFEFC4338DE8CF25C685B751C2FC4F9070D80C851ADE7722EC4C194707C50C8CA366E79C4D3B226163AFB8253C5EDC6F94B8FF8B479CFB88FF6C1DF09AC5FBBBE67BF8EF1A2EA17704A11A9772164A4F6B4C6A214101BCB94177778553EC25D4A64ABEA7615AACE730D5EB1CFD9FDB0D0EC1E100EED04FAFD6768"
```

**Sample output**

```java
String tlvEncodedZPK = "301c041052b94057a42f62ab45d6d9c16def86f40408b5f0bed099f271dd"
```

### Extract the clear ZPK {#extract-the-clear-zpk}

Extract the clear ZPK from the above plain text ZPK (Standard Key Block Type). The standard key block format is a type-length-value or tag-length-value (TLV) encoding scheme with length as represented in Hexadecimal.

**Sample code**

```java
SEQUENCE {
    key OCTET STRING,
    iv octet string
}
```

Note: For a DES/3DES key, the 'key' may be 8, 16, or 24 bytes and the 'iv' is 8 bytes; for an AES key, the 'key' may be 16, 24, or 32 bytes and the 'iv' is 16 bytes.

Example:

```java
Plain text ZPK (Standard Key Block Type) = 301C04102083FB37F7EF3DB63DB6A12F4CC4F4DA040852C44094B1A2AC50
30 1C   SEQUENCE {
04 10 2083FB37F7EF3DB63DB6A12F4CC4  OCTET STRING
2083FB37F7EF3DB63DB6A12F4CC4F4DA
04 08 52C44094B1A2AC50              OCTET STRING 52C44094B1A2AC50
}
```

**Sample code**

```java
// Input tlvEncodedZPK
// Output hexEncodedClearZPK

String clearZPKLengthHex = tlvEncodedZPK.substring(6, 8);
int clearZPKLength = Integer.parseInt(clearZPKLengthHex, 16) * 2;
String hexEncodedClearZPK = tlvEncodedZPK.substring(8, 8 + clearZPKLength);
```

**Sample input**

```java
String tlvEncodedZPK = "301c041052b94057a42f62ab45d6d9c16def86f40408b5f0bed099f271dd"
```

**Sample output**

```java
String hexEncodedClearZPK = "52b94057a42f62ab45d6d9c16def86f4"
```

### Build the TDEA key {#build-the-tdea-key}

**Sample code**

```java
// Input hexEncodedZPK
// Output clearZPK bytes

public static byte[] buildTDEAKey(String hexEncodedZPK) {
    byte[] key = null;
    /**
     * 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);
    /** log.debug("hexEncodedZPK={}, key length={}", hexEncodedZPK, tmp.length); */

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

### Decrypt the (symmetric) PIN block {#decrypt-the-symmetric-pin-block}

Decrypt the PIN block (ISO-0 or ISO-1 format) using the clear ZPK retrieved in the earlier step.

**Sample code**

```java
// Input encryptedPinBlock fetched from API response and hexEncodedZPK extracted from encodedZPK
// Output clearPinBlock

/**
 * 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/CBC/NoPadding");

SecretKey secretKey = new SecretKeySpec(key, "DESede");

IvParameterSpec iv = new IvParameterSpec(new byte[8]);

/** Step 3 - Initializes 'Cipher' with a key. */
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);

/** Step 4 - Encrypts the 'PIN Block' */
byte[] ciphertext = cipher.doFinal(Hex.decode(encryptedPinBlock));

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

Simple input
String encryptedPinBlock = "AEC21ADFBE5BA6C9"

Sample output (ISO-0 format)
String iso0PinBlock = "04420db7fa9aadb6"

Sample output (ISO-1 format)
String iso1PinBlock = "144205AAFFBBDBBD"
```

### Extract PIN from clear PIN block {#extract-pin-from-clear-pin-block}

The first character of a clear PIN block indicates the PIN block format. For example, `04420db7fa9aadb6` indicates ISO-0 format and `144205AAFFBBDBBD` indicates ISO-1 format.

**Option 1 - ISO format 0 PIN block** -- If the PIN block format is set as ISO-0 in the API configuration, decrypt the PIN block using the following steps:

```java
// Input ISO-0 formatted PinBlock, clear CardContractNumber (PAN) and reference to earlier generated RSA Private Key
// Output clearPin

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

BigInteger clearPinBlock = new BigInteger(iso0PinBlock, 16).xor(new BigInteger(accountNumber, 16));

String pinBlock = clearPinBlock.toString(16);
log.info("pinBlock={}", pinBlock);

int pinLength = Integer.parseInt(pinBlock.substring(0, 1));
String clearPin = pinBlock.substring(1, 1 + pinLength);
```

**Sample input**

```java
String clearPinBlock = "04420db7fa9aadb6"
String cardNumber = "5290848056552490"
```

**Sample output**

```java
String clearPin = "4205"
```

**Option 2 - ISO format 1 PIN block** -- If the PIN block format is set as ISO-1 in the API configuration, decrypt the PIN block using the following steps:

```java
// Input IOS-1 formatted PinBlock 
// Output clearPin

int pinLength = Integer.parseInt(pinBlockIso1.substring(1, 2));
String clearPin = pinBlockIso1.substring(2, 2 + pinLength);
```

**Sample input**

```java
String clearPinBlock = "144205AAFFBBDBBD"
```

**Sample output**

```java
String clearPin = "4205"
```

