001/* 002 * jPOS Project [http://jpos.org] 003 * Copyright (C) 2000-2026 jPOS Software SRL 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jpos.security; 020 021import org.jpos.iso.ISOUtil; 022import org.jpos.util.Loggeable; 023import org.jpos.iso.ISOException; 024 025import java.io.PrintStream; 026import java.io.Serializable; 027import java.security.InvalidParameterException; 028 029 030/** 031 * <p> 032 * The PIN (Personal Identification Number), is used to authenticate card 033 * holders. A user enters his/her PIN on the pin-pad (also called pin entry device) 034 * of a terminal (whether ATM or POS). The terminal forms the PIN Block, which 035 * is a mix of the PIN and the account number.<br> 036 * In a typical environment, the PIN Block (not the PIN) gets encrypted and sent 037 * to the acquirer. This Encrypted PIN Block is the typical content of the 038 * PIN Data ISO Field (Field 52). 039 * This class represents an encrypted PIN, no matter by whom it is encrypted. 040 * Typically a PIN is encrypted under one of these three:<br> 041 * 1- Under a terminal PIN key (like TPK or DUKPT)<br> 042 * 2- Under an Interchange PIN key (like ZPK)<br> 043 * 3- Under the the security module itself (i.e. under LMK)<br> 044 * This class knows nothing about, who encrypted it. 045 * </p> 046 * <p> 047 * This class represents an encrypted PIN using:<br> 048 * 1- The PIN Block (encrypted)<br> 049 * 2- The account number (the 12 right-most digits of the account number excluding the check digit)<br> 050 * 3- The PIN Block Format<br> 051 * </p> 052 * <p> 053 * The PIN Block Format specifies how the clear pin (as entered by the card holder) 054 * and the account number get mixed to form the PIN Block.<br> 055 * </p> 056 * @author Hani Samuel Kirollos 057 * @version $Revision$ $Date$ 058 * @see SMAdapter 059 */ 060public class EncryptedPIN 061 implements Serializable, Loggeable { 062 063 private static final long serialVersionUID = -9117335317030664867L; 064 /** 065 * Account Number (the 12 right-most digits of the account number excluding the check digit) 066 */ 067 String accountNumber; 068 /** 069 * This is the ByteArray holding the PIN Block 070 * The PIN Block can be either clear or encrypted 071 * It is typically DES or 3DES encrypted, with length 8 bytes. 072 */ 073 byte[] pinBlock; 074 /** 075 * The PIN Block Format 076 * value -1 means no block format defined 077 */ 078 byte pinBlockFormat; 079 080 /** Default constructor. Creates an empty EncryptedPIN. */ 081 public EncryptedPIN () { 082 super(); 083 } 084 085 /** 086 * Constructs an EncryptedPIN from raw PIN block bytes. 087 * @param pinBlock the encrypted PIN block bytes 088 * @param pinBlockFormat the PIN block format identifier 089 * @param accountNumber account number, including BIN and the check digit 090 */ 091 public EncryptedPIN (byte[] pinBlock, byte pinBlockFormat, String accountNumber) { 092 setPINBlock(pinBlock); 093 setPINBlockFormat(pinBlockFormat); 094 setAccountNumber(extractAccountNumberPart(accountNumber)); 095 } 096 /** 097 * Constructs an EncryptedPIN from raw PIN block bytes, optionally extracting the account number part. 098 * @param pinBlock the encrypted PIN block bytes 099 * @param pinBlockFormat the PIN block format identifier 100 * @param accountNumber if {@code extract} is false then account number, including BIN and the check digit; 101 * if {@code extract} is true then 12 right-most digits of the account number, excluding the check digit 102 * @param extract true to extract 12 right-most digits off the account number 103 */ 104 public EncryptedPIN (byte[] pinBlock, byte pinBlockFormat, String accountNumber, boolean extract) { 105 setPINBlock(pinBlock); 106 setPINBlockFormat(pinBlockFormat); 107 setAccountNumber(extract ? extractAccountNumberPart(accountNumber) : accountNumber); 108 } 109 110 111 /** 112 * Constructs an EncryptedPIN from a hex-encoded PIN block string. 113 * @param pinBlockHexString the PIN block represented as a hex string instead of a byte[] 114 * @param pinBlockFormat the PIN block format identifier 115 * @param accountNumber account number, including BIN and the check digit 116 */ 117 public EncryptedPIN (String pinBlockHexString, byte pinBlockFormat, String accountNumber) { 118 this(ISOUtil.hex2byte(pinBlockHexString), pinBlockFormat, accountNumber); 119 } 120 /** 121 * Constructs an EncryptedPIN from a hex-encoded PIN block string, optionally extracting the account number part. 122 * @param pinBlockHexString the PIN block represented as a hex string instead of a byte[] 123 * @param pinBlockFormat the PIN block format identifier 124 * @param accountNumber if {@code extract} is false then account number, including BIN and the check digit; 125 * if {@code extract} is true then 12 right-most digits of the account number, excluding the check digit 126 * @param extract true to extract 12 right-most digits off the account number 127 */ 128 public EncryptedPIN (String pinBlockHexString, byte pinBlockFormat, String accountNumber, boolean extract) { 129 this(ISOUtil.hex2byte(pinBlockHexString), pinBlockFormat, accountNumber, extract); 130 } 131 132 /** 133 * dumps PIN basic information 134 * @param p a PrintStream usually supplied by Logger 135 * @param indent indention string, usually suppiled by Logger 136 * @see org.jpos.util.Loggeable 137 */ 138 public void dump (PrintStream p, String indent) { 139 String inner = indent + " "; 140 p.print(indent + "<encrypted-pin"); 141 p.print(" format=\"0" + getPINBlockFormat() + "\""); 142 p.println(">"); 143 p.println(inner + "<pin-block>" + ISOUtil.hexString(getPINBlock()) + "</pin-block>"); 144 p.println(inner + "<account-number>" + getAccountNumber() + "</account-number>"); 145 p.println(indent + "</encrypted-pin>"); 146 } 147 148 /** 149 * Sets the encrypted PIN block bytes. 150 * @param pinBlock the encrypted PIN block bytes 151 */ 152 public void setPINBlock (byte[] pinBlock) { 153 this.pinBlock = pinBlock; 154 } 155 156 /** 157 * Returns the encrypted PIN block bytes. 158 * @return the encrypted PIN block bytes 159 */ 160 public byte[] getPINBlock () { 161 return pinBlock; 162 } 163 164 /** 165 * Sets the PIN block format identifier. 166 * @param pinBlockFormat the PIN block format identifier 167 */ 168 public void setPINBlockFormat (byte pinBlockFormat) { 169 this.pinBlockFormat = pinBlockFormat; 170 } 171 172 /** 173 * Returns the PIN block format identifier. 174 * @return the PIN block format identifier 175 */ 176 public byte getPINBlockFormat () { 177 return this.pinBlockFormat; 178 } 179 180 /** 181 * Sets the 12 right-most digits of the account number excluding the check digit 182 * @param extractedAccountNumber 12 right-most digits of the account number, excluding the check digit. 183 */ 184 public void setAccountNumber (String extractedAccountNumber) { 185 if(extractedAccountNumber.length() != 12) 186 throw new InvalidParameterException( 187 "Extracted account number length should be 12, got '" 188 +ISOUtil.protect(extractedAccountNumber) + "'" 189 ); 190 this.accountNumber = extractedAccountNumber; 191 } 192 193 /** 194 * Returns the extracted account number part. 195 * @return the 12 right-most digits of the account number, excluding the check digit 196 */ 197 public String getAccountNumber () { 198 return accountNumber; 199 } 200 201 /** 202 * This method extracts the 12 right-most digits of the account number, 203 * execluding the check digit. 204 * @param accountNumber (PAN) consists of the BIN (Bank Identification Number), accountNumber 205 * and a check digit. 206 * @return the 12 right-most digits of the account number, execluding the check digit. 207 * In case if account number length is lower that 12 proper count of 0 digts is added 208 * on the left side for align to 12 209 */ 210 public static String extractAccountNumberPart (String accountNumber) { 211 String accountNumberPart = null; 212 try { 213 accountNumberPart = ISOUtil.takeLastN(accountNumber, 13); 214 accountNumberPart = ISOUtil.takeFirstN(accountNumberPart, 12); 215 } catch(ISOException ignored) { 216 // NOPMD return original accountNumber 217 } 218 return accountNumberPart; 219 } 220 221 222} 223 224 225