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 public EncryptedPIN () { 081 super(); 082 } 083 084 /** 085 * 086 * @param pinBlock 087 * @param pinBlockFormat 088 * @param accountNumber account number, including BIN and the check digit 089 */ 090 public EncryptedPIN (byte[] pinBlock, byte pinBlockFormat, String accountNumber) { 091 setPINBlock(pinBlock); 092 setPINBlockFormat(pinBlockFormat); 093 setAccountNumber(extractAccountNumberPart(accountNumber)); 094 } 095 /** 096 * @param pinBlock 097 * @param pinBlockFormat 098 * @param accountNumber if <code>extract</code> is false then account number, including BIN and the check digit 099 * or if parameter <code>extract</code> is true then 12 right-most digits of the account number, excluding the check digit 100 * @param extract true to extract 12 right-most digits off the account number 101 */ 102 public EncryptedPIN (byte[] pinBlock, byte pinBlockFormat, String accountNumber, boolean extract) { 103 setPINBlock(pinBlock); 104 setPINBlockFormat(pinBlockFormat); 105 setAccountNumber(extract ? extractAccountNumberPart(accountNumber) : accountNumber); 106 } 107 108 109 /** 110 * @param pinBlockHexString the PIN Block represented as a HexString instead of a byte[] 111 * @param pinBlockFormat 112 * @param accountNumber account number, including BIN and the check digit 113 */ 114 public EncryptedPIN (String pinBlockHexString, byte pinBlockFormat, String accountNumber) { 115 this(ISOUtil.hex2byte(pinBlockHexString), pinBlockFormat, accountNumber); 116 } 117 /** 118 * @param pinBlockHexString the PIN Block represented as a HexString instead of a byte[] 119 * @param pinBlockFormat 120 * @param accountNumber if <code>extract</code> is false then account number, including BIN and the check digit 121 * or if parameter <code>extract</code> is true then 12 right-most digits of the account number, excluding the check digit 122 * @param extract true to extract 12 right-most digits off the account number 123 */ 124 public EncryptedPIN (String pinBlockHexString, byte pinBlockFormat, String accountNumber, boolean extract) { 125 this(ISOUtil.hex2byte(pinBlockHexString), pinBlockFormat, accountNumber, extract); 126 } 127 128 /** 129 * dumps PIN basic information 130 * @param p a PrintStream usually supplied by Logger 131 * @param indent indention string, usually suppiled by Logger 132 * @see org.jpos.util.Loggeable 133 */ 134 public void dump (PrintStream p, String indent) { 135 String inner = indent + " "; 136 p.print(indent + "<encrypted-pin"); 137 p.print(" format=\"0" + getPINBlockFormat() + "\""); 138 p.println(">"); 139 p.println(inner + "<pin-block>" + ISOUtil.hexString(getPINBlock()) + "</pin-block>"); 140 p.println(inner + "<account-number>" + getAccountNumber() + "</account-number>"); 141 p.println(indent + "</encrypted-pin>"); 142 } 143 144 /** 145 * 146 * @param pinBlock 147 */ 148 public void setPINBlock (byte[] pinBlock) { 149 this.pinBlock = pinBlock; 150 } 151 152 /** 153 * 154 * @return pinBlock 155 */ 156 public byte[] getPINBlock () { 157 return pinBlock; 158 } 159 160 /** 161 * 162 * @param pinBlockFormat 163 */ 164 public void setPINBlockFormat (byte pinBlockFormat) { 165 this.pinBlockFormat = pinBlockFormat; 166 } 167 168 /** 169 * 170 * @return PIN Block Format 171 */ 172 public byte getPINBlockFormat () { 173 return this.pinBlockFormat; 174 } 175 176 /** 177 * Sets the 12 right-most digits of the account number excluding the check digit 178 * @param extractedAccountNumber 12 right-most digits of the account number, excluding the check digit. 179 */ 180 public void setAccountNumber (String extractedAccountNumber) { 181 if(extractedAccountNumber.length() != 12) 182 throw new InvalidParameterException( 183 "Extracted account number length should be 12, got '" 184 +ISOUtil.protect(extractedAccountNumber) + "'" 185 ); 186 this.accountNumber = extractedAccountNumber; 187 } 188 189 /** 190 * @return accountNumber (the 12 right-most digits of the account number excluding the check digit) 191 */ 192 public String getAccountNumber () { 193 return accountNumber; 194 } 195 196 /** 197 * This method extracts the 12 right-most digits of the account number, 198 * execluding the check digit. 199 * @param accountNumber (PAN) consists of the BIN (Bank Identification Number), accountNumber 200 * and a check digit. 201 * @return the 12 right-most digits of the account number, execluding the check digit. 202 * In case if account number length is lower that 12 proper count of 0 digts is added 203 * on the left side for align to 12 204 */ 205 public static String extractAccountNumberPart (String accountNumber) { 206 String accountNumberPart = null; 207 try { 208 accountNumberPart = ISOUtil.takeLastN(accountNumber, 13); 209 accountNumberPart = ISOUtil.takeFirstN(accountNumberPart, 12); 210 } catch(ISOException ignored) { 211 // NOPMD return original accountNumber 212 } 213 return accountNumberPart; 214 } 215 216 217} 218 219 220