summaryrefslogtreecommitdiff
path: root/smccTest/src/main/java/at/gv/egiz/smcc/activation/Activation.java
diff options
context:
space:
mode:
Diffstat (limited to 'smccTest/src/main/java/at/gv/egiz/smcc/activation/Activation.java')
-rw-r--r--smccTest/src/main/java/at/gv/egiz/smcc/activation/Activation.java492
1 files changed, 492 insertions, 0 deletions
diff --git a/smccTest/src/main/java/at/gv/egiz/smcc/activation/Activation.java b/smccTest/src/main/java/at/gv/egiz/smcc/activation/Activation.java
new file mode 100644
index 00000000..1f201d0d
--- /dev/null
+++ b/smccTest/src/main/java/at/gv/egiz/smcc/activation/Activation.java
@@ -0,0 +1,492 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package at.gv.egiz.smcc.activation;
+
+import at.gv.egiz.smcc.SignatureCardException;
+import at.gv.egiz.smcc.util.TLVSequence;
+import iaik.security.provider.IAIK;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.smartcardio.Card;
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CardTerminal;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+import javax.smartcardio.TerminalFactory;
+
+/**
+ *
+ * @author clemens
+ */
+public class Activation {
+
+ /**
+ * cf. kp_mk_ss_test.xml
+ */
+ static SecretKeySpec MK_QSig = new SecretKeySpec(new byte[]{
+ (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05,
+ (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0A,
+ (byte) 0x0B, (byte) 0x0C, (byte) 0x0D, (byte) 0x0E, (byte) 0x0F,
+ (byte) 0x10, (byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14,
+ (byte) 0x15, (byte) 0x16, (byte) 0x17, (byte) 0x18}, "3DES");
+
+ /**
+ * DES_CBC[MK_Appl](SHA-256(CIN|KeyNum|KeyVer))
+ *
+ * @param masterKey
+ * @param cin
+ * @param keyNum
+ * @param keyVer
+ * @return
+ */
+ static SecretKeySpec deriveApplicationKey(SecretKeySpec masterKey, byte[] cin, byte keyNum, byte keyVer) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
+
+ System.out.println("derive application key for \n CIN "
+ + toString(cin)
+ + "\n key number 0x0"
+ + Byte.toString(keyNum)
+ + "\n key version 0x0"
+ + Byte.toString(keyVer));
+
+ MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
+
+ sha256.update(cin);
+ sha256.update(keyNum);
+ sha256.update(keyVer);
+ byte[] derivationParam = sha256.digest();
+
+ return deriveKey(masterKey, Arrays.copyOf(derivationParam, 24));
+ }
+
+ static SecretKeySpec deriveKENC(SecretKeySpec k_appl) throws NoSuchAlgorithmException, UnsupportedEncodingException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
+
+ System.out.println("derive k_enc");
+
+ MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
+ byte[] derivationParam = sha256.digest("K_ENC".getBytes("ASCII"));
+
+ return deriveKey(k_appl, Arrays.copyOf(derivationParam, 24));
+ }
+
+ static SecretKeySpec deriveKMAC(SecretKeySpec k_appl) throws NoSuchAlgorithmException, UnsupportedEncodingException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
+ System.out.println("derive k_mac");
+
+ MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
+ byte[] derivationParam = sha256.digest("K_MAC".getBytes("ASCII"));
+
+ return deriveKey(k_appl, Arrays.copyOf(derivationParam, 24));
+ }
+
+ /**
+ * DES_CBC[MK](derivationParam)
+ * 3DES/CBC/NoPadding
+ *
+ * @param masterKey
+ * @param derivationParam
+ * @return
+ */
+ static SecretKeySpec deriveKey(SecretKeySpec masterKey, byte[] derivationParam) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
+
+ if (derivationParam.length != 24) {
+ throw new RuntimeException("invalid 3TDES derivation parameter: " + toString(derivationParam));
+ }
+
+ Cipher tdes = Cipher.getInstance("3DES/CBC/NoPadding");
+ tdes.init(Cipher.ENCRYPT_MODE, masterKey, new IvParameterSpec(ZEROS));
+
+ System.out.println(tdes.getAlgorithm());
+ System.out.println("master key : " + toString(masterKey.getEncoded()));
+
+ System.out.println("derivation parameter ("
+ + derivationParam.length * 8 + "bit): "
+ + toString(derivationParam));
+ System.out.println("derivation key ("
+ + masterKey.getAlgorithm() + ") :"
+ + toString(masterKey.getEncoded()));
+
+ byte[] x = tdes.doFinal(derivationParam);
+
+ System.out.println("x (" + x.length * 8 + "bit): " + toString(x));
+
+ if (x.length != 24) {
+ throw new RuntimeException("invalid derived key: " + toString(x));
+ }
+
+ for (int offset = 0; offset < x.length; offset += 8) {
+ adjustParityBit(x, offset);
+ }
+
+ SecretKeySpec derivedKey = new SecretKeySpec(x, masterKey.getAlgorithm());
+
+ System.out.println("derived key ("
+ + derivedKey.getAlgorithm() + ") :"
+ + toString(derivedKey.getEncoded()));
+
+ return derivedKey;
+
+ }
+
+ public final static byte[] AID_QSig = new byte[] {
+ (byte) 0xd0, (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0x17,
+ (byte) 0x00, (byte) 0x12, (byte) 0x01};
+
+ private final static byte[] ZEROS = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ /** Bit mask for counting the ones. */
+ private final static byte[] BIT_MASK = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, (byte) 0x80
+ };
+
+ private static void adjustParityBit(byte[] x, int offset) {
+
+ for (int i = 0; i < 8; i++) {
+ int ones = 0;
+ for (int j = 1; j < BIT_MASK.length; j++) {
+ if ((x[i + offset] & BIT_MASK[j]) == BIT_MASK[j]) {
+ ones++;
+ }
+ }
+
+ if ((ones & 0x1) > 0) {
+ x[i + offset] &= (byte) 0xfe; // odd
+ } else {
+ x[i + offset] |= 0x1; // even
+ }
+ }
+ }
+
+
+ public static String toString(byte[] b) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('[');
+ if (b != null && b.length > 0) {
+ sb.append(Integer.toHexString((b[0] & 240) >> 4));
+ sb.append(Integer.toHexString(b[0] & 15));
+ for (int i = 1; i < b.length; i++) {
+ sb.append((i % 32 == 0) ? '\n' : ':');
+ sb.append(Integer.toHexString((b[i] & 240) >> 4));
+ sb.append(Integer.toHexString(b[i] & 15));
+ }
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+
+
+ CardTerminal ct;
+ Card icc;
+ CardChannel channel;
+
+ public void setUp() throws NoSuchAlgorithmException, CardException {
+
+ IAIK.addAsJDK14Provider();
+
+ System.out.println("create terminalFactory...\n");
+ TerminalFactory terminalFactory = TerminalFactory.getInstance("PC/SC", null);
+
+ System.out.println("get supported terminals...\n");
+ List<CardTerminal> terminals = terminalFactory.terminals().list();
+
+ if (terminals.size() < 1) {
+ throw new CardException("no terminals");
+ }
+
+ ct = terminals.get(0);
+ System.out.println("found " + terminals.size() + " terminals, using " + ct.getName() + "\n");
+
+ System.out.println("connecting " + ct.getName() + "\n");
+ icc = ct.connect("*");
+ byte[] atr = icc.getATR().getBytes();
+ byte[] historicalBytes = icc.getATR().getHistoricalBytes();
+ System.out.println("found card " + toString(atr) + " " + new String(historicalBytes, Charset.forName("ASCII")) + "\n\n");
+
+ channel = icc.getBasicChannel();
+ }
+
+ public void activate() throws CardException, SignatureCardException {
+
+ System.out.println("SELECT MF");
+ CommandAPDU cmdAPDU = new CommandAPDU(0x00, 0xA4, 0x00, 0x0c, new byte[]{(byte) 0x3F, (byte) 0x00});
+ System.out.println(" cmd apdu " + toString(cmdAPDU.getBytes()));
+ ResponseAPDU resp = channel.transmit(cmdAPDU);
+ System.out.println(" -> " + toString(resp.getBytes()) + "\n");
+
+ byte[] cin = getCIN();
+
+ byte[] k_qsigNumVer = getKApplNumberVersion(AID_QSig);
+
+ SecretKeySpec k_qsig;
+ try {
+ k_qsig = deriveApplicationKey(MK_QSig, cin, k_qsigNumVer[0], k_qsigNumVer[1]);
+ System.out.println("K_QS (" + k_qsig.getAlgorithm() + ")"
+ + toString(k_qsig.getEncoded()));
+ } catch (Exception ex) {
+ throw new SignatureCardException("failed to derive k_qs", ex);
+ }
+
+ System.out.println("SELECT EF.PuK_QS");
+ // P1=0x00 -> 67:00 (wrong length)
+ cmdAPDU = new CommandAPDU(0x00, 0xa4, 0x02, 0x04, new byte[]{(byte) 0x0e, (byte) 0x01}, 256);
+ System.out.println(" cmd apdu " + toString(cmdAPDU.getBytes()));
+ resp = channel.transmit(cmdAPDU);
+ System.out.println(" -> " + toString(resp.getBytes()) + "\n");
+
+ openSecureChannel(k_qsig, cin);
+ }
+
+
+ /**
+ * @precondition MF
+ *
+ * @return
+ * @throws CardException
+ * @throws SignatureCardException
+ */
+ byte[] getCIN() throws CardException, SignatureCardException {
+ CommandAPDU cmdAPDU;
+ ResponseAPDU resp;
+
+ System.out.println("READ EF.GDO (SFI=02)");
+ cmdAPDU = new CommandAPDU(0x00, 0xb0, 0x82, 0x00, 256);
+ System.out.println(" cmd apdu " + toString(cmdAPDU.getBytes()));
+ resp = channel.transmit(cmdAPDU);
+ System.out.println(" -> " + toString(resp.getBytes()) + "\n");
+
+ if (resp.getSW() == 0x9000) {
+ byte[] cin = new TLVSequence(resp.getData()).getValue(0x5a);
+ System.out.println("CIN: " + toString(cin));
+ return cin;
+ } else {
+ throw new SignatureCardException("Failed to read EF.GDO: 0x" + Integer.toHexString(resp.getSW()));
+ }
+ }
+
+ /**
+ * @precondition MF
+ * @postcondition AID
+ *
+ * @param aid
+ * @return
+ * @throws CardException
+ * @throws SignatureCardException
+ */
+ byte[] getKApplNumberVersion(byte[] aid) throws CardException, SignatureCardException {
+
+ System.out.println("SELECT AID " + toString(aid));
+ // P1=0x00 -> 67:00 (wrong length)
+ CommandAPDU cmdAPDU = new CommandAPDU(0x00, 0xa4, 0x04, 0x00, aid, 256);
+ System.out.println(" cmd apdu " + toString(cmdAPDU.getBytes()));
+ ResponseAPDU resp = channel.transmit(cmdAPDU);
+ System.out.println(" -> " + toString(resp.getBytes()) + "\n");
+
+ byte[] keyNumVer = null;
+
+ if (resp.getSW() == 0x9000) {
+ byte[] fciBytes = new TLVSequence(resp.getData()).getValue(0x6f);
+
+ TLVSequence fci = new TLVSequence(fciBytes);
+ System.out.println("FCI AID " + toString(aid));
+ System.out.println(fci);
+
+ TLVSequence proprietary = new TLVSequence(fci.getValue(0xa5));
+ System.out.println("proprietary information");
+ System.out.println(proprietary);
+
+ keyNumVer = proprietary.getValue(0x54);
+ if (keyNumVer == null || keyNumVer.length != 2) {
+ throw new SignatureCardException("invalid key number/version: "
+ + toString(keyNumVer));
+ }
+ return keyNumVer;
+
+ } else {
+ throw new SignatureCardException("Failed to read AID: 0x"
+ + Integer.toHexString(resp.getSW()));
+ }
+ }
+
+ void openSecureChannel(SecretKeySpec k_appl, byte[] cin) throws NoSuchAlgorithmException, UnsupportedEncodingException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, CardException {
+// function openSecureChannelG3(card, crypto, iccsn, kappl, kid, algo) {
+
+// if (typeof(kid) == "undefined")
+// kid = 0x81;
+//
+// if (typeof(algo) == "undefined")
+// algo = 0x54;
+//
+// // Perform mutual authentication procedure
+// GPSystem.trace("Performing mutual authentication");
+//
+// var kenc = deriveKENC(crypto, kappl);
+// var kmac = deriveKMAC(crypto, kappl);
+
+ SecretKeySpec k_enc = deriveKENC(k_appl);
+ SecretKeySpec k_mac = deriveKMAC(k_appl);
+
+//
+// // Manage SE: Set K_Appl for Mutual Authentication with Session Key establishment
+// var bb = new ByteBuffer("8301", HEX);
+// bb.append(kid);
+// bb.append(0x80);
+// bb.append(0x01);
+// bb.append(algo);
+//
+// card.sendApdu(0x00, 0x22, 0x81, 0xA4, bb.toByteString(), [0x9000])
+//
+// var rndicc = card.sendApdu(0x00, 0x84, 0x00, 0x00, 0x08, [0x9000]);
+
+ int dfSpecificKeyRef = 1;
+ byte[] crt_at = new byte[]{
+ (byte) 0x83, (byte) 0x01, (byte) (0x80 | (0x7f & dfSpecificKeyRef)),
+ // algorithm id 0x54???
+ (byte) 0x80, (byte) 0x01, (byte) 0x54
+ };
+
+ System.out.println("MSE SET AT for key agreement");
+ CommandAPDU cmdAPDU = new CommandAPDU(0x00, 0x22, 0x81, 0xa4, crt_at);
+ System.out.println(" cmd apdu " + toString(cmdAPDU.getBytes()));
+ ResponseAPDU resp = channel.transmit(cmdAPDU);
+ System.out.println(" -> " + toString(resp.getBytes()) + "\n");
+
+ System.out.println("GET CHALLENGE");
+ cmdAPDU = new CommandAPDU(0x00, 0x84, 0x00, 0x00, 0x08);
+ System.out.println(" cmd apdu " + toString(cmdAPDU.getBytes()));
+ resp = channel.transmit(cmdAPDU);
+ System.out.println(" -> " + toString(resp.getBytes()) + "\n");
+
+ byte[] rnd_icc = resp.getData();
+
+ if (rnd_icc.length != 8) {
+ throw new RuntimeException("invalid RND.ICC: " + toString(rnd_icc));
+ }
+
+ if (cin.length != 10) {
+ throw new RuntimeException("invalid CIN: " + toString(cin));
+ }
+
+// var rndifd = crypto.generateRandom(8);
+// var snifd = crypto.generateRandom(8);
+// var kifd = crypto.generateRandom(64); // 32 -> 64
+// var snicc = iccsn.bytes(2, 8);
+//
+
+ Random rand = new Random(System.currentTimeMillis());
+
+ byte[] rnd_ifd = new byte[8];
+ rand.nextBytes(rnd_ifd);
+ byte[] icc_id = Arrays.copyOfRange(cin, 2, 8);
+ byte[] ifd_id = new byte[8];
+ rand.nextBytes(ifd_id);
+ byte[] kd_ifd = new byte[64];
+ rand.nextBytes(kd_ifd);
+
+// var plain = rndifd.concat(snifd).concat(rndicc).concat(snicc).concat(kifd);
+// GPSystem.trace("Plain Block : " + plain);
+// print("Plain Block : " + plain);
+//
+// print("K_enc : " + kenc.getComponent(Key.DES));
+//
+// var cryptogram = crypto.encrypt(kenc, Crypto.DES_CBC, plain, new ByteString("0000000000000000", HEX));
+// GPSystem.trace("Cryptogram : " + cryptogram);
+// print("Cryptogram : " + cryptogram);
+//
+// print("K_mac : " + kmac.getComponent(Key.DES));
+//
+// var mac = crypto.sign(kmac, Crypto.DES_MAC_EMV, cryptogram.pad(Crypto.ISO9797_METHOD_2));
+// GPSystem.trace("MAC : " + mac);
+// print("MAC : " + mac);
+//
+//
+// var autresp = card.sendApdu(0x00, 0x82, 0x00, 0x00, cryptogram.concat(mac), 0); // 81 -> 00
+//
+// if (card.SW != 0x9000) {
+// GPSystem.trace("Mutual authenticate failed with " + card.SW.toString(16) + " \"" + card.SWMSG + "\"");
+// throw new GPError("MutualAuthentication", GPError.CRYPTO_FAILED, 0, "Card did not accept MAC");
+// }
+//
+// cryptogram = autresp.bytes(0, 96); // 64 -> 96
+// mac = autresp.bytes(96, 8); // 64 -> 96
+//
+// if (!crypto.verify(kmac, Crypto.DES_MAC_EMV, cryptogram.pad(Crypto.ISO9797_METHOD_2), mac)) {
+// throw new GPError("MutualAuthentication", GPError.CRYPTO_FAILED, 0, "Card MAC did not verify correctly");
+// }
+//
+// plain = crypto.decrypt(kenc, Crypto.DES_CBC, cryptogram, new ByteString("0000000000000000", HEX));
+// GPSystem.trace("Plain Block : " + plain);
+//
+// if (!plain.bytes(0, 8).equals(rndicc)) {
+// throw new GPError("MutualAuthentication", GPError.CRYPTO_FAILED, 0, "Card response does not contain matching RND.ICC");
+// }
+//
+// if (!plain.bytes(16, 8).equals(rndifd)) {
+// throw new GPError("MutualAuthentication", GPError.CRYPTO_FAILED, 0, "Card response does not contain matching RND.IFD");
+// }
+//
+// var kicc = plain.bytes(32, 64); // 32 -> 64
+// keyinp = kicc.xor(kifd);
+//
+// var hashin = keyinp.concat(new ByteString("00000001", HEX));
+// var hashres = crypto.digest(Crypto.SHA_256, hashin);
+// var kencval = hashres.bytes(0, 24);
+// var kencssc = hashres.bytes(24, 8);
+//
+// GPSystem.trace("Kenc : " + kencval);
+// GPSystem.trace("Kenc SSC : " + kencssc);
+// var kenc = new Key();
+// kenc.setComponent(Key.DES, kencval);
+//
+// var hashin = keyinp.concat(new ByteString("00000002", HEX));
+// var hashres = crypto.digest(Crypto.SHA_256, hashin);
+// var kmacval = hashres.bytes(0, 24);
+// var kmacssc = hashres.bytes(24, 8);
+//
+// GPSystem.trace("Kmac : " + kmacval);
+// GPSystem.trace("Kmac SSC : " + kmacssc);
+// var kmac = new Key();
+// kmac.setComponent(Key.DES, kmacval);
+//
+// var sc = new IsoSecureChannel(crypto);
+// sc.setEncKey(kenc);
+// sc.setMacKey(kmac);
+//
+// sc.setMACSendSequenceCounter(kmacssc);
+// sc.setEncryptionSendSequenceCounter(kencssc);
+// return sc;
+//}
+
+
+ }
+
+ public static void main(String[] args) {
+
+ try {
+ Activation test = new Activation();
+ test.setUp();
+ test.activate();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}