APDU – Response APDU and Command APDU Java Example


/*
* Copyright (c) 2009 Sun Microsystems, Inc.
* All rights reserved.
* Use is subject to license terms.
*/
package apdutool2;

import java.util.Hashtable;

/**
* This class represent a pair of C-APDU and R-APDU.
*
* It is used internally by several components of the Java Card development kit.
*/
public class Apdu {

/**
* Size of the command array
*/
public static final int COMMAND_ARRAY_SIZE = 6;

/**
* The offset in the command array to the ISO 7816-3 CLA octet.
*/
public static final int CLA = 0;

/**
* The offset in the command array to the ISO 7816-3 INS octet.
*/
public static final int INS = 1;

/**
* The offset in the command array to the ISO 7816-3 P1 octet.
*/
public static final int P1 = 2;

/**
* The offset in the command array to the ISO 7816-3 P2 octet.
*/
public static final int P2 = 3;

/**
* The offset in the command array to the ISO 7816-3 P3 octet.
*/
public static final int P3 = 4;

/**
* The value which indicates this Apdu is an ISO 7816-3 Case 1 apdu
*/
public static final int CASE_1 = 1;

/**
* The value which indicates this Apdu is an ISO 7816-3 Case 2 Short apdu
*/
public static final int CASE_2S = 2;

/**
* The value which indicates this Apdu is an ISO 7816-3 Case 3 Short apdu
*/
public static final int CASE_3S = 3;

/**
* The value which indicates this Apdu is an ISO 7816-3 Case 4 Short apdu
*/
public static final int CASE_4S = 4;

/**
* The value which indicates this Apdu is an ISO 7816-3 Case 2 Extended apdu
*/
public static final int CASE_2E = 5;

/**
* The value which indicates this Apdu is an ISO 7816-3 Case 3 Extended apdu
*/
public static final int CASE_3E = 6;

/**
* The value which indicates this Apdu is an ISO 7816-3 Case 4 Extended apdu
*/
public static final int CASE_4E = 7;

/**
* Mask to extract channel information out of the CLA byte.
*/
public static final int LOGICAL_CHN_MASK = 0x03;

/**
* Mask to extract APDU type information: either ISO ecoding or not.
*/
public static final int APDU_TYPE_MASK = 0xF0;

static final int CMD_HDR_BYTES = 4;

/**
* Internal representation of the C-APDU header
*/
public byte[] command;
/**
* Value of Lc
*/
public int Lc;
/**
* Data part of the C-APDU
*/
public byte[] dataIn;
/**
* Value of Le
*/
public int Le;
/**
* Data part of the R-APDU
*/
public byte[] dataOut;
/**
* Status as byte array
*/
public byte[] sw1sw2;
/**
* Extended APDU flag
*/
public boolean isExtended;

/**
* Descriptive Output Flag
*
*/
public boolean formatOP;

public static final Hashtable STATUS_CODES = new Hashtable(){
{
put((short)0x9000,"No Error");
put((short)0x6100,"Response Bytes Remaining");
put((short)0x6700,"Wrong length");
put((short)0x6982,"Security Condition is not Satisfied");
put((short)0x6983,"Invalid File");
put((short)0x6984,"Invalid Data");
put((short)0x6985,"Conditions are not Satisfied");
put((short)0x6986,"Command is not Allowed");
put((short)0x6999,"Applet Selection Failed");
put((short)0x6A80,"Wrong Data");
put((short)0x6A81,"Function is not Supported");
put((short)0x6A82,"File not Found");
put((short)0x6A83,"Record not Found");
put((short)0x6A86,"Incorrect Parameters (P1,P2)");
put((short)0x6B00,"Incorrect Parameters (P1,P2)");
put((short)0x6C00,"Correct Expected Length (Le)");
put((short)0x6D00,"INS value is not Supported");
put((short)0x6E00,"CLA value is not Supported");
put((short)0x6F00,"No Precise Diagnosis");
put((short)0x6A84,"Not enough Memory Space in the File");
put((short)0x6881,"Card does not Support the operation on the specified Logical Channel");
put((short)0x6882,"Card does not support Secure Messaging");
put((short)0x6200,"Warning, Card State is Unchanged");
put((short)0x6883,"Last Command in chain is Expected");
put((short)0x6884,"Command Chaining is not Supported");

}
};

/**
*
* @param b
*/
public Apdu(byte[] b) {
this();
command[Apdu.CLA] = b[0];
command[Apdu.INS] = b[1];
command[Apdu.P1] = b[2];
command[Apdu.P2] = b[3];

byte[] di = new byte[b.length - 5];
System.arraycopy(b, 5, di, 0, di.length);
setDataIn(di);
setLe(0x7F);
}

/**
* Creates a new instance of Apdu.
*/
public Apdu() {
command = new byte[Apdu.COMMAND_ARRAY_SIZE];
Lc = 0;
dataIn = null;
Le = 0;
dataOut = null;
sw1sw2 = new byte[2];
}

/**
* Get internal representation of the header of the C-APDU The header is
* just CLA, INS, P1, P2
*
* @return The header of the C-APDU
*/
public byte[] getCommand() {
byte[] commandBytes = new byte[4];
System.arraycopy(command, 0, commandBytes, 0, 4);
return commandBytes;
}

/**
* Get the data part of the C-APDU
*
* @return The data
*/
public byte[] getDataIn() {
return dataIn;
}

/**
* Set the data of the C-APDU
*
* @param dataIn
* The data
*/
public void setDataIn(byte[] dataIn) {
this.dataIn = dataIn;
if (dataIn == null) {
this.Lc = 0;
} else {
if (dataIn.length > 0x7FFF) {
throw new IllegalArgumentException();
}
this.Lc = dataIn.length;
if (dataIn.length > 255) {
isExtended = true;
}
}
}

/**
* Set the value of Lc
*
* @param Lc
* value of the Lc
*/
public void setLc(int Lc) {
if ((Lc < 0) || (Lc > 0x7FFF)) {
throw new IllegalArgumentException();
}
this.Lc = Lc;
this.dataIn = new byte[Lc];
}

/**
* Set the data of the C-APDU
*
* @param dataIn
* The data
* @param length
* the data length
*/
public void setDataIn(byte[] dataIn, int length) {
if ((length < 0) || (length > 0x7FFF)) {
throw new IllegalArgumentException();
}
this.dataIn = dataIn;
this.Lc = length;
}

/**
* Get Lc
*
* @return The Lc
*/
public int getLc() {
return Lc;
}

/**
* Set the data part of the R-APDU
*
* @param dataOut
* The data
*/
public void setDataOut(byte[] dataOut) {
this.dataOut = dataOut;
if (dataOut == null) {
this.Le = 0;
} else {
if (dataOut.length > 0x7FFF) {
throw new IllegalArgumentException();
}
this.Le = dataOut.length;
}
}

/**
* Set Le
*
* @param Le
* value of Le
*/
public void setLe(int Le) {
if ((Le < 0) || (Le > 0x7FFF)) {
throw new IllegalArgumentException();
}
this.Le = Le;
this.dataOut = new byte[Le];
}

// This method sets the output data of the Apdu object to the
// array specified, modifying the LE to the amout passed as parameter.
/**
* Set the data part of the R-APDU
*
* @param dataOut
* The data
* @param length
* The data length
*/
public void setDataOut(byte[] dataOut, int length) {
if ((length < 0) || (length > 0x7FFF)) {
throw new IllegalArgumentException();
}
this.dataOut = dataOut;
if (dataOut == null && length == 0) {
this.Le = 0;
} else if (dataOut == null && length > 0) {
throw new IllegalArgumentException();
} else if (dataOut.length < length) { throw new IllegalArgumentException(); } this.Le = length; } /** * Get Le * * @return The Le */ public int getLe() { return Le; } /** * Get the data part of the R-APDU * * @return The data part of the R-APDU */ public byte[] getDataOut() { return dataOut; } /** * Get status bytes * * @return The status bytes */ public byte[] getSw1Sw2() { return sw1sw2; } /** * Get status (SW bytes) as integer * * @return The status */ public int getStatus() { return (((sw1sw2[0] & 0x00FF) << 8) | (sw1sw2[1] & 0x00FF)) & 0xffff; } /** * Return the ISO 7816-3 Case of this Apdu * * @return An int value indicating the ISO case. */ public int getCase() { if (Lc == 0 && Le == 0) { return Apdu.CASE_1; } if (Lc == 0 && Le >= 0 && Le <= 256) { return Apdu.CASE_2S; } else if (Lc == 0 && Le > 256) {
return Apdu.CASE_2E;
} else if (Lc > 0 && Lc <= 255 && Le == 0) { return Apdu.CASE_3S; } else if (Lc >= 256 && Le == 0) {
return Apdu.CASE_3E;
} else if (Lc > 0 && Lc <= 255 && Le > 0 && Le < 256) { return Apdu.CASE_4S; } else { return Apdu.CASE_4E; } } // This private method implements ISO 7816-4 logic to determine // the logical channel to whom this APDU command is intended to. /** * Determines the channel number * * @return The channel number */ public byte getChannelInfo() { return command[Apdu.CLA]; } public byte makeISOInterIndustryCLA(byte CLAByte){ boolean extChannel = (CLAByte & 0x40) != 0; if(extChannel) { return (byte) ((CLAByte & 0x0F) | 0x40); } return (byte) (CLAByte & 0x03); } /** * returns the command APDU as a byte array. * * @return The command APDU */ public byte[] getCommandApduBytes() { // based on the case returns the command apdu bytes byte[] apduBytes; switch (getCase()) { case CASE_1: apduBytes = new byte[Apdu.CMD_HDR_BYTES]; System.arraycopy(command, 0, apduBytes, 0, Apdu.CMD_HDR_BYTES); break; case CASE_2S: apduBytes = new byte[Apdu.CMD_HDR_BYTES + 1]; // 1 for one byte le System.arraycopy(command, 0, apduBytes, 0, Apdu.CMD_HDR_BYTES); apduBytes[4] = (byte) Le; // fifth byte is Le break; case CASE_2E: // 3 extra bytes for extended length Le apduBytes = new byte[Apdu.CMD_HDR_BYTES + 3]; System.arraycopy(command, 0, apduBytes, 0, Apdu.CMD_HDR_BYTES); // last 3 bytes contain the Le apduBytes[Apdu.CMD_HDR_BYTES] = (byte) 0; apduBytes[Apdu.CMD_HDR_BYTES + 1] = (byte) ((Le >> 8) & 0x00FF);
apduBytes[Apdu.CMD_HDR_BYTES + 2] = (byte) (Le & 0x00FF);
break;
case CASE_3S:
// 1 byte for lc + lc bytes
apduBytes = new byte[Apdu.CMD_HDR_BYTES + 1 + Lc];
System.arraycopy(command, 0, apduBytes, 0, Apdu.CMD_HDR_BYTES);
apduBytes[Apdu.CMD_HDR_BYTES] = (byte) Lc;
System.arraycopy(dataIn, 0, apduBytes, Apdu.CMD_HDR_BYTES + 1, Lc);
break;
case CASE_3E:
// 3 bytes for lc + lc bytes
apduBytes = new byte[Apdu.CMD_HDR_BYTES + 3 + Lc];
System.arraycopy(command, 0, apduBytes, 0, Apdu.CMD_HDR_BYTES);
// copy extended length Lc
apduBytes[Apdu.CMD_HDR_BYTES] = (byte) 0;
apduBytes[Apdu.CMD_HDR_BYTES + 1] = (byte) ((Lc >> 8) & 0x00FF);
apduBytes[Apdu.CMD_HDR_BYTES + 2] = (byte) (Lc & 0x00FF);
// copy the dataIn
System.arraycopy(dataIn, 0, apduBytes, Apdu.CMD_HDR_BYTES + 3, Lc);
break;
case CASE_4S:
// 1 byte for Lc, Lc bytes and then 1 byte for le
apduBytes = new byte[Apdu.CMD_HDR_BYTES + 1 + Lc + 1];
System.arraycopy(command, 0, apduBytes, 0, Apdu.CMD_HDR_BYTES);
// copy the Lc
apduBytes[Apdu.CMD_HDR_BYTES] = (byte) Lc;
// copy the data
System.arraycopy(dataIn, 0, apduBytes, Apdu.CMD_HDR_BYTES + 1, Lc);
// copy the Le
apduBytes[Apdu.CMD_HDR_BYTES + 1 + Lc] = (byte) Le;
break;
default: // case 4E
// 3 byte for Lc, Lc bytes and then 2 byte for le
// 1 byte for Lc, Lc bytes and then 1 byte for le
apduBytes = new byte[Apdu.CMD_HDR_BYTES + 3 + Lc + 2];
System.arraycopy(command, 0, apduBytes, 0, Apdu.CMD_HDR_BYTES);
// copy extended length Lc
apduBytes[Apdu.CMD_HDR_BYTES] = (byte) 0;
apduBytes[Apdu.CMD_HDR_BYTES + 1] = (byte) ((Lc >> 8) & 0x00FF);
apduBytes[Apdu.CMD_HDR_BYTES + 2] = (byte) (Lc & 0x00FF);
// copy the dataIn
System.arraycopy(dataIn, 0, apduBytes, Apdu.CMD_HDR_BYTES + 3, Lc);
// copy the Le
apduBytes[Apdu.CMD_HDR_BYTES + 3 + Lc] = (byte) ((Le >> 8) & 0x00FF);
apduBytes[Apdu.CMD_HDR_BYTES + 4 + Lc] = (byte) (Le & 0x00FF);
}

return apduBytes;
}

/**
* returns the response APDU as a byte array.
*
* @return The response APDU
*/
public byte[] getResponseApduBytes() {
byte[] newApdu;
if (isExtended) {
newApdu = new byte[Apdu.CMD_HDR_BYTES + 3 + Lc + 2 + Le + 2];
} else {
newApdu = new byte[Apdu.CMD_HDR_BYTES + 1 + Lc + 1 + Le + 2];
}
// copy the command bytes
System.arraycopy(command, 0, newApdu, 0, Apdu.CMD_HDR_BYTES);
// copy Lc
if (isExtended) {
newApdu[Apdu.CMD_HDR_BYTES] = (byte) 00;
newApdu[Apdu.CMD_HDR_BYTES + 1] = (byte) ((Lc >> 8) & 0x00FF);
newApdu[Apdu.CMD_HDR_BYTES + 2] = (byte) (Lc & 0x00FF);
} else {
newApdu[Apdu.CMD_HDR_BYTES] = (byte) Lc;
}
// copy data In
if (isExtended) {
if (Lc != 0) {
System.arraycopy(dataIn, 0, newApdu, (Apdu.CMD_HDR_BYTES + 3), Lc);
}
} else {
if (Lc != 0) {
System.arraycopy(dataIn, 0, newApdu, (Apdu.CMD_HDR_BYTES + 1), Lc);
}
}
// copy Le, which is now the actual length
if (isExtended) {
newApdu[Apdu.CMD_HDR_BYTES + 3 + Lc] = (byte) ((Le >> 8) & 0x00FF);
newApdu[Apdu.CMD_HDR_BYTES + 3 + Lc + 1] = (byte) (Le & 0x00FF);
} else {
newApdu[Apdu.CMD_HDR_BYTES + 1 + Lc] = (byte) Le;
}

// copy the data bytes, if any

if (isExtended) {
if (dataOut.length != 0) {
System.arraycopy(dataOut, 0, newApdu, (Apdu.CMD_HDR_BYTES + 3 + Lc + 2), Le);
}
} else {
if (dataOut.length != 0) {
System.arraycopy(dataOut, 0, newApdu, (Apdu.CMD_HDR_BYTES + 1 + Lc + 1), Le);
}
}
// copy the 2 SW bytes
System.arraycopy(sw1sw2, 0, newApdu, (newApdu.length - 2), 2);
return newApdu;
}

/**
* Generate a string representation of this Apdu in the ApduTool output
* format.
*
* @return String representation of this Apdu
*/
@Override
public String toString() {

StringBuffer sb = new StringBuffer();

sb.append("CLA: " + Apdu.byteHexString(command[Apdu.CLA]) + ", ");
sb.append("INS: " + Apdu.byteHexString(command[Apdu.INS]) + ", ");
sb.append("P1: " + Apdu.byteHexString(command[Apdu.P1]) + ", ");
sb.append("P2: " + Apdu.byteHexString(command[Apdu.P2]) + ", ");
if (isExtended) {
sb.append("Lc: " + Apdu.byteHexString((byte) 0) + ", ");
sb.append(Apdu.byteHexString((byte) (Lc >> 8)) + ", ");
sb.append(Apdu.byteHexString((byte) (Lc & 0x00FF)));
} else {
sb.append("Lc: " + Apdu.byteHexString(Lc));
}
for (int i = 0; i < Lc; i++) { sb.append(", " + Apdu.byteHexString(dataIn[i])); } if (formatOP){ sb.append("\n"); short status = (short)((short)sw1sw2[0] & 0xff) ; status = (short)((short)(status << 8) | (short)((short)sw1sw2[1] & 0x00ff)); String statusStr = (String)STATUS_CODES.get(status); sb.append("Status : "+statusStr); sb.append(" (SW1: " + Apdu.byteHexString(sw1sw2[0])); sb.append(" SW2: " + Apdu.byteHexString(sw1sw2[1])+ ")"); if (Le > 0 && dataOut != null) {
sb.append(" ,Data: ");
for (int i = 0; i < Le; i++) { if (i >= dataOut.length) {
break;
}
sb.append(Apdu.byteHexString(dataOut[i])+ " ");
}
}

} else {
if (isExtended) {
sb.append("Le: " + Apdu.byteHexString((byte) 0) + ", ");
sb.append(Apdu.byteHexString((byte) (Le >> 8)) + ", ");
sb.append(Apdu.byteHexString((byte) (Le & 0x00FF)));
} else {
sb.append(", Le: " + Apdu.byteHexString(Le));
}
if (dataOut != null) {
for (int i = 0; i < Le; i++) { if (i >= dataOut.length) {
break;
}
sb.append(", " + Apdu.byteHexString(dataOut[i]));
}
}
sb.append(", SW1: " + Apdu.byteHexString(sw1sw2[0]));
sb.append(", SW2: " + Apdu.byteHexString(sw1sw2[1]));
}

return sb.toString();
}

private static String byteHexString(int data) {
StringBuffer sb = new StringBuffer();
if ((data & 0xff) < 0x10) { sb.append("0"); } sb.append(Integer.toHexString(data & 0xff)); return sb.toString(); } }

Leave a Reply

Your email address will not be published. Required fields are marked *