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