Terminalpublic abstract class Terminal extends Object implements ConstantsThis class implements the common functionalities of a POS terminal and transit terminal. |
Fields Summary |
---|
protected static String | hostNameThe host where the cref or jcwde tool is running | protected static int | portThe port the cref or jcwde is listening on | protected static byte[] | staticKeyDataThe static DES key - shared b/w the on-card and off-card applications | private com.sun.javacard.apduio.CadT1Client | cadThe CAD client | private Cipher | cipherThe cipher used to encrypt - using the static DES key - the derivation
data to form the session key | private Mac | macThe MAC initialized with the DES key and used to verify incoming messages
and to sign outgoing messages | private SecretKey | sessionKeyDES session key, generated from the derivation data | private SecretKeyFactory | keyFactoryThe DES session key factory |
Constructors Summary |
---|
public Terminal(String hostName, int hostPort, byte[] staticKeyData)Creates a terminal.
Socket socket = new Socket(hostName, hostPort);
socket.setTcpNoDelay(true);
BufferedInputStream input = new BufferedInputStream(socket
.getInputStream());
BufferedOutputStream output = new BufferedOutputStream(socket
.getOutputStream());
cad = new CadT1Client(input, output);
KeySpec keySpec = new DESKeySpec(fixParity(staticKeyData));
keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey staticKey = keyFactory.generateSecret(keySpec);
cipher = Cipher.getInstance("DES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, staticKey, new IvParameterSpec(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0}));
|
Methods Summary |
---|
protected static void | commonUsage()Prints the common usage message.
System.out
.println("[-?] [-h <hostname>] [-p <port>] -k <8-bytes DES static key> <command list>");
| protected void | copyShort(short i, byte[] buffer, int offset)Copies a short integer into a byte array.
buffer[offset] = (byte) ((i >> 8) & 0x00ff);
buffer[offset + 1] = (byte) (i & 0x00ff);
| private byte[] | fixParity(byte[] keyData)
for (int i = 0; i < keyData.length; i++) {
short parity = 0;
keyData[i] &= 0xFE;
for (int j = 1; j < 8; j++) {
if ((keyData[i] & ((byte) (1 << j))) != 0) {
parity++;
}
}
if ((parity % 2) == 0) {
keyData[i] |= 1;
}
}
return keyData;
| protected byte[] | generateHostChallenge()Generates a host challenge.
byte[] hostChallenge = new byte[CHALLENGE_LENGTH];
SecureRandom random = new SecureRandom();
random.nextBytes(hostChallenge);
return hostChallenge;
| protected byte[] | generateKeyDerivationData(byte[] hostChallenge, byte[] cardChallenge)Generates the session key derivation data from the passed-in host and
card challenges.
// Derivation data: [[4-bytes host challenge], [4-bytes card challenge]]
byte[] keyDerivationData = new byte[CHALLENGE_LENGTH * 2];
// Append host challenge to derivation data
System.arraycopy(hostChallenge, 0, keyDerivationData, 0,
CHALLENGE_LENGTH);
// Append card challenge to derivation data
System.arraycopy(cardChallenge, (short) 0, keyDerivationData,
CHALLENGE_LENGTH, CHALLENGE_LENGTH);
return keyDerivationData;
| protected void | generateSessionKey(byte[] keyDerivationData)Generates a new DES session key from the derivation data.
byte[] paddedData = pad(keyDerivationData, 0, keyDerivationData.length, cipher.getBlockSize());
byte[] sessionKeyData = fixParity(cipher.doFinal(paddedData));
// Generate new session key from derivation data
KeySpec keySpec = new DESKeySpec(sessionKeyData);
sessionKey = keyFactory.generateSecret(keySpec);
| protected short | getShort(byte[] buffer, int offset)Gets a short integer from a byte array.
return (short) ((((short) buffer[offset]) << 8) | buffer[offset + 1]);
| void | initializeSession()Initializes a session with the on-card applet using a mutual
authentication scheme.
// C-APDU: [CLA, INS, P1, P2, LC, [4-bytes Host Challenge]]
Apdu apdu = new Apdu();
apdu.command[Apdu.CLA] = TRANSIT_CLA;
apdu.command[Apdu.INS] = INITIALIZE_SESSION;
apdu.command[Apdu.P1] = 0;
apdu.command[Apdu.P2] = 0;
// Generate card challenge
byte[] hostChallenge = generateHostChallenge();
byte[] data = new byte[hostChallenge.length];
System.arraycopy(hostChallenge, 0, data, 0, hostChallenge.length);
apdu.setDataIn(data);
System.err.println(apdu);
cad.exchangeApdu(apdu);
System.err.println(apdu);
if (apdu.getStatus() == SW_NO_ERROR) {
// R-APDU: [[4-bytes Card Challenge], [2-bytes Status Word],
// [8-bytes MAC]]
data = apdu.getDataOut();
// Check status word
byte[] cardChallenge = new byte[CHALLENGE_LENGTH];
System.arraycopy(data, 0, cardChallenge, 0, CHALLENGE_LENGTH);
// Generate key derivation data from host challenge and card
// challenge
byte[] keyDerivationData = generateKeyDerivationData(hostChallenge,
cardChallenge);
// Generate session key from derivation data
generateSessionKey(keyDerivationData);
// Initialize MAC with current session key for verification
mac = new Mac(sessionKey);
// Check response message MAC
if (mac.checkMAC(data, apdu.getLe() - MAC_LENGTH)) {
System.err.println("OK");
} else {
throw new Exception("InitializeSession: Wrong signature");
}
} else {
throw new Exception("InitializeSession: Error " + apdu.getStatus());
}
| private byte[] | pad(byte[] msg, int offset, int length, int blockLength)
// Add 1 to add 0x80 at the end.
int paddedLength = length + 1;
int numBlocks = (int) (paddedLength / blockLength);
int remBytes = paddedLength - (numBlocks * blockLength);
if (remBytes > 0) {
numBlocks++;
}
byte[] paddedMsg = new byte[numBlocks * blockLength];
System.arraycopy(msg, offset, paddedMsg, 0, length);
paddedMsg[length] = (byte) 0x80;
// Fill message with zeroes to fit blocks
for (int i = (length + 1); i < paddedMsg.length; i++) {
paddedMsg[i] = (byte) 0x00;
}
return paddedMsg;
| private static byte[] | parseByteArray(java.lang.String s)
byte[] array = new byte[s.length() / 2];
for (int i = 0; i < s.length(); i += 2) {
array[i / 2] = (byte) Integer.parseInt(s.substring(i, i + 2), 16);
}
return array;
| protected static int | parseCommonArgs(java.lang.String[] args)Parses the common CLI arguments.
int i = 0;
for (; i < args.length && args[i].startsWith("-"); i++) {
if (args[i].equals("-?")) {
commonUsage();
System.exit(0);
} else if (args[i].equals("-h")) {
if (++i < args.length) {
hostName = args[i];
} else {
commonUsage();
System.exit(2);
}
} else if (args[i].equals("-p")) {
if (++i < args.length) {
port = Integer.valueOf(args[i]).intValue();
} else {
commonUsage();
System.exit(2);
}
} else if (args[i].equals("-k")) {
if (++i < args.length) {
if ((args[i].length() / 2) < LENGTH_DES_BYTE) {
commonUsage();
System.exit(1);
}
staticKeyData = parseByteArray(args[i]);
} else {
commonUsage();
System.exit(2);
}
} else if (args[i].equals("--")) {
i++;
break;
} else {
commonUsage();
System.exit(2);
}
}
if (staticKeyData == null) {
return -1;
}
return i;
| void | powerDown()Powers down the CAD.
try {
cad.powerDown(true);
} catch (Exception e) {}
| void | powerUp()Powers up the CAD.
cad.powerUp();
| protected byte[] | processRequest(byte type, byte[] requestMessage)Processes a generic request: a transit terminal request or a POS terminal
request.
Apdu apdu = new Apdu();
apdu.command[Apdu.CLA] = TRANSIT_CLA;
apdu.command[Apdu.INS] = PROCESS_REQUEST;
apdu.command[Apdu.P1] = 0;
apdu.command[Apdu.P2] = 0;
byte[] data = new byte[(2 + requestMessage.length) + MAC_LENGTH];
data[0] = type; // TLV Tag
data[1] = (byte) requestMessage.length; // TLV Length
// TLV Value
System.arraycopy(requestMessage, 0, data, 2, requestMessage.length);
mac.generateMAC(data, 2 + requestMessage.length);
apdu.setDataIn(data);
System.err.println(apdu);
cad.exchangeApdu(apdu);
System.err.println(apdu);
if (apdu.getStatus() == SW_NO_ERROR) {
// R-APDU: [[Response Message], [2-bytes Status Word], [8-bytes
// MAC]]
byte[] responseMessage = apdu.getDataOut();
// Check response message MAC
if (mac.checkMAC(responseMessage, (apdu.getLe() - MAC_LENGTH))) {
// Check status word
data = new byte[apdu.getLe() - (2 + MAC_LENGTH)];
System.arraycopy(responseMessage, 0, data, 0, data.length);
return data;
}
}
return null;
| void | selectApplet()Selects the on-card transit applet.
// C-APDU: [CLA, INS, P1, P2, LC, [ AID_TRANSIT ]]
Apdu apdu = new Apdu();
apdu.command[Apdu.CLA] = CLA_ISO7816;
apdu.command[Apdu.INS] = INS_SELECT;
apdu.command[Apdu.P1] = 0x04;
apdu.command[Apdu.P2] = 0;
apdu.setDataIn(AID_TRANSIT);
System.out.println(apdu);
cad.exchangeApdu(apdu);
System.out.println(apdu);
if (apdu.getStatus() == SW_NO_ERROR) {
System.out.println("OK");
} else {
System.out.println("Error: " + apdu.getStatus());
}
| void | verifyPIN(byte[] pin)Verifies the user-provided PIN against the on-card PIN.
// C-APDU: [CLA, INS, P1, P2, LC, [ PIN ]]
Apdu apdu = new Apdu();
apdu.command[Apdu.CLA] = CLA_ISO7816;
apdu.command[Apdu.INS] = INS_VERIFY;
apdu.command[Apdu.P1] = 0;
apdu.command[Apdu.P2] = 0;
apdu.setDataIn(pin);
System.out.println(apdu);
cad.exchangeApdu(apdu);
System.out.println(apdu);
if (apdu.getStatus() == SW_NO_ERROR) {
System.out.println("OK");
} else {
System.out.println("Error: " + apdu.getStatus());
}
|
|