summaryrefslogtreecommitdiff
path: root/smccTest/src/main/java/at/gv/egiz/smcc/activation/SecureChannel.java
diff options
context:
space:
mode:
Diffstat (limited to 'smccTest/src/main/java/at/gv/egiz/smcc/activation/SecureChannel.java')
-rw-r--r--smccTest/src/main/java/at/gv/egiz/smcc/activation/SecureChannel.java501
1 files changed, 327 insertions, 174 deletions
diff --git a/smccTest/src/main/java/at/gv/egiz/smcc/activation/SecureChannel.java b/smccTest/src/main/java/at/gv/egiz/smcc/activation/SecureChannel.java
index 9bbd39fb..0590dac2 100644
--- a/smccTest/src/main/java/at/gv/egiz/smcc/activation/SecureChannel.java
+++ b/smccTest/src/main/java/at/gv/egiz/smcc/activation/SecureChannel.java
@@ -2,199 +2,352 @@
* 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.DNIeCryptoUtil;
+import at.gv.egiz.smcc.util.TLVSequence;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
+import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Arrays;
-import java.util.logging.Level;
-import java.util.logging.Logger;
import javax.crypto.spec.SecretKeySpec;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
*
+ *
+00002393 APDU: 00 A4 02 04 02 0E 01 00
+00015613 SW: 62 19 80 02 01 2C C5 02 00 AC 82 01 41 83 02 0E 01 88 00 8A 01 05 A1 03 8B 01 05 90 00
+ * 80: num data bytes: 01 2c
+ * c5: ?
+ * 82: fd=41 shareable, transparent structure
+ * 83: fid=0e 01
+ * 88: no short fid
+ * 8a: activated
+ * a1: security attribute template (proprietary format): 8b 01 05
+00001770 APDU: 00 22 81 A4 06 83 01 81 80 01 54
+00013461 SW: 90 00
+00000592 APDU: 00 84 00 00 08
+00024160 SW: 5B 3D B2 93 3F E8 CE 7C 90 00
+00002153 APDU: 00 82 00 00 68 B1 0C 9A F3 6D FA B0 70 D8 4D 6A 0B 3C E9 8E 5E 50 D0 2A 82 1E CD 70 77 6F CC D4 E8 4D 0E A3 E6 B8 87 2E 31 0F B1 0B 42 5F EB C6 36 B0 EC 18 86 94 0D 5B 67 6C 1A 96 8F C7 2B 8E 4B 85 2A 91 63 C9 E4 66 43 42 D9 55 FF 44 5C C9 DE A6 44 D3 46 37 DA 47 02 A3 63 BA E7 4D CB 52 64 5D F6 B4 94 6B 51 02 0A DD 8F 10 55 00
+00186875 SW: B2 72 5B 23 D2 F6 CB 09 9B 76 1D 62 2E 58 87 24 2A 51 8A 7B F3 79 D8 E9 A3 0B B9 B1 CB 72 77 DD B4 A1 C1 8E 22 5B 0F FA 28 F4 DC 00 B0 D0 96 8F 48 58 85 44 65 7F 11 46 A1 BB DA 17 F2 05 82 23 EC 5E B9 55 92 63 EF 41 A7 BE C8 7E C1 6F 83 82 7A A5 6E AD 94 15 35 37 43 A9 88 89 60 41 A8 87 76 41 EE DB 97 E3 70 CC 90 00
+00003139 APDU: 0C B0 00 00 0D 97 01 DF 8E 08 CE BD FA EC FA B2 C5 D7 00
+00041878 SW: 81 81 AC A8 82 00 A8 B6 16 83 14 80 04 00 00 00 23 00 79 05 03 D0 40 00 00 17 00 12 01 02 00 7F 49 82 00 44 86 40 D4 7C 12 55 E4 7B 0C 7D 4E BB 17 E4 83 E5 3D 56 DF 45 7E 99 CB CC 93 D2 C2 5E 4D 91 27 6E 8B E7 6D 23 53 F6 AB 2E A6 DD B7 1C 68 FB 59 CD D0 45 2B 10 0E 27 00 6E AA 1C 49 90 67 A9 9F 59 D1 97 C1 00 C0 01 80 9E 82 00 40 DE 22 37 4C 41 E0 F7 94 9A 5A E4 76 B8 9B 00 B8 23 7C E9 4A 92 FD B0 FB 25 4A A7 0E 4D 5F 6F 3D 3A 54 28 F8 90 A1 7D 60 28 F8 72 B7 0F 9F A6 A8 53 15 F2 9F 88 37 D4 6B 77 F7 69 C1 B9 E7 2A 43 99 02 62 82 8E 08 25 1C C9 6E 87 22 DD DB 62 82
+
* @author clemens
*/
public class SecureChannel extends CardChannel {
- CardChannel basicChannel;
-
- SecretKeySpec kenc;
- SecretKeySpec kmac;
- byte[] kencssc;
- byte[] kmacssc;
-
- public SecureChannel(CardChannel basicChannel,
- SecretKeySpec kenc, SecretKeySpec kmac,
- byte[] kencssc, byte[] kmacssc) {
- this.basicChannel = basicChannel;
- this.kenc = kenc;
- this.kmac = kmac;
- this.kencssc = kencssc;
- this.kmacssc = kmacssc;
- }
-
-
- @Override
- public Card getCard() {
- return basicChannel.getCard();
- }
-
- @Override
- public int getChannelNumber() {
- return basicChannel.getChannelNumber();
- }
-
- @Override
- public ResponseAPDU transmit(CommandAPDU capdu) throws CardException {
- try {
- kmacssc[7] += 1;
- System.out.println("incrementing kmac_ssc: " + toString(kmacssc));
- System.out.println("kmac: " + toString(kmac.getEncoded()));
- CommandAPDU capdu_ = new CommandAPDU(protectAPDU(capdu.getBytes()));
- System.out.println(" cmd apdu* " + toString(capdu_.getBytes()));
- return basicChannel.transmit(capdu_);
- } catch (Exception ex) {
- System.out.println("failed to transmit protected APDU: " + ex.getMessage());
- throw new CardException(ex);
- }
- }
-
- @Override
- public int transmit(ByteBuffer bb, ByteBuffer bb1) throws CardException {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
- @Override
- public void close() throws CardException {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
- /**
- *
- * @param apdu
- * @return
- * @throws IllegalArgumentException
- */
- protected byte[] protectAPDU(byte[] apdu) throws IllegalArgumentException, InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, GeneralSecurityException {
-
- byte[] apdu_ = null;
- byte[] mac_in = null;
- int i_mac = 0;
-
- if (apdu.length < 4) {
- throw new IllegalArgumentException("invalid Command APDU " + toString(apdu));
- } else if (apdu.length == 4) {
- // no command data, no response data, status not protected
- // CLA|INS|P1|P2 -> CLA'|INS|P1|P2|Lc'|TLmac
- apdu_ = new byte[15];
-
- // authenticate header: CLA* b3=1
- apdu_[0] = (byte) (apdu[0] | (byte) 0x0c); // CLA*: b8-6=000 b4-3=11
- apdu_[1] = apdu[1];
- apdu_[2] = apdu[2];
- apdu_[3] = apdu[3];
-
- // Lc': TLmac (blocksize=8)
- apdu_[4] = (byte) 0x0a;
- // cryptographic checksum
- apdu_[5] = (byte) 0x8e;
- apdu_[6] = (byte) 0x08;
- i_mac = 7;
-
- // 2 data objects: SSC, header
- mac_in = new byte[2*8];
-
- //TODO increment SSC
- System.arraycopy(kmacssc, 0, mac_in, 0, 8);
-
- // CLA** INS P1 P2 padding
- byte[] header_ = RetailCBCMac.pad(Arrays.copyOf(apdu_, 4), 8);
- System.arraycopy(header_, 0, mac_in, 8, 8);
-
- System.out.println("data covered by cryptographic checksum: " + toString(mac_in));
-
- } else if (apdu.length == 5) {
- // CLA|INS|P1|P2|Le -> CLA'|INS|P1|P2|Lc'|TLle|TLmac|00
- // no command data, response data
- apdu_ = new byte[19];
-
- // authenticate header: CLA* b3=1
- apdu_[0] = (byte) (apdu[0] | (byte) 0x0c); // CLA*: b8-6=000 b4-3=11
- apdu_[1] = apdu[1];
- apdu_[2] = apdu[2];
- apdu_[3] = apdu[3];
-
- // Lc': TLle TLmac
- apdu_[4] = (byte) 0x0d;
-
- // Ne
- apdu_[5] = (byte) 0x97;
- apdu_[6] = (byte) 0x01;
- apdu_[7] = apdu[4];
-
- // cryptographic checksum
- apdu_[8] = (byte) 0x8e;
- apdu_[9] = (byte) 0x08;
- i_mac = 10;
- apdu_[18] = 0x00;
-
- // 3 data objects: SSC, header, Le
- mac_in = new byte[3*8];
-
- //TODO increment SSC
- System.arraycopy(kmacssc, 0, mac_in, 0, 8);
-
- // CLA** INS P1 P2 padding
- byte[] header_ = RetailCBCMac.pad(Arrays.copyOf(apdu_, 4), 8);
- System.arraycopy(header_, 0, mac_in, 8, 8);
-
- System.arraycopy(RetailCBCMac.pad(Arrays.copyOfRange(apdu_, 5, 8), 8), 0, mac_in, 16, 8);
- System.out.println("data covered by cryptographic checksum: " + toString(mac_in));
-
-
- } else if (apdu.length == 5 + apdu[4]){
- // CLA|INS|P1|P2|Lc|Data -> CLA'|INS|P1|P2|Lc'|TLplain|TLmac
- // 81 (plain value) 8e (cryptographic checksum)
-
- } else {
- // CLA|INS|P1|P2|Lc|Data|Le -> CLA'|INS|P1|P2|Lc'|TLplain|TLle|TLmac|00
- // 81 (plain value) 97 (Ne) 8e (cryptographic checksum)
-
- // TODO extended APDUs
-
- }
-
-
- byte[] mac = RetailCBCMac.retailMac(mac_in, "DES", "DESede", kmac, 8, 8);
- System.out.println("mac: " + toString(mac));
- System.arraycopy(mac, 0, apdu_, i_mac, 8);
- return apdu_;
-
- }
-
- 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();
- }
+ protected static final Logger log = LoggerFactory.getLogger(SecureChannel.class);
+
+ //TODO access
+ CardChannel channel;
+ SecretKeySpec kenc;
+ SecretKeySpec kmac;
+ byte[] kencssc;
+ byte[] kmacssc;
+ int blocksize;
+
+ public SecureChannel(CardChannel basicChannel,
+ SecretKeySpec kenc, SecretKeySpec kmac, int blocksize,
+ byte[] kencssc, byte[] kmacssc) {
+ this.channel = basicChannel;
+ this.kenc = kenc;
+ this.kmac = kmac;
+
+ if (kencssc.length != kmacssc.length || kmacssc.length < blocksize) {
+ throw new IllegalArgumentException("invalid ssc or blocksize");
+ }
+ this.kencssc = kencssc;
+ this.kmacssc = kmacssc;
+ this.blocksize = blocksize;
+
+ }
+
+ @Override
+ public Card getCard() {
+ return channel.getCard();
+ }
+
+ @Override
+ public int getChannelNumber() {
+ return channel.getChannelNumber();
+ }
+
+ @Override
+ public ResponseAPDU transmit(CommandAPDU capdu) throws CardException {
+
+ byte[] apdu = capdu.getBytes();
+
+ try {
+ if (apdu.length < 4) {
+ throw new IllegalArgumentException("invalid Command APDU " + toString(apdu));
+ } else if (apdu.length == 4) {
+ CommandAPDU capdu_ = new CommandAPDU(protectCase1b(apdu));
+ log.info("cmd apdu*: {}", toString(capdu_.getBytes()));
+ ResponseAPDU resp_ = channel.transmit(capdu_);
+ log.info(" -> resp*: {}", toString(resp_.getBytes()));
+ return unProtectCase1b(resp_);
+ } else if (apdu.length == 5) {
+ CommandAPDU capdu_ = new CommandAPDU(protectCase2(apdu));
+ log.info("cmd apdu*: {}", toString(capdu_.getBytes()));
+ ResponseAPDU resp_ = channel.transmit(capdu_);
+ log.info(" -> resp*: {}", toString(resp_.getBytes()));
+ return unProtectCase2(resp_);
+
+ } else if (apdu.length == 5 + apdu[4]) {
+ // CLA|INS|P1|P2|Lc|Data -> CLA'|INS|P1|P2|Lc'|TLplain|TLmac
+ // 81 (plain value) 8e (cryptographic checksum)
+ } else {
+ // CLA|INS|P1|P2|Lc|Data|Le -> CLA'|INS|P1|P2|Lc'|TLplain|TLle|TLmac|00
+ // 81 (plain value) 97 (Ne) 8e (cryptographic checksum)
+ // TODO extended APDUs
+ }
+
+ throw new UnsupportedOperationException("not implemented");
+
+ } catch (Exception ex) {
+ System.out.println("failed to transmit protected APDU: " + ex.getMessage());
+ throw new CardException(ex);
+ }
+ }
+
+ @Override
+ public int transmit(ByteBuffer bb, ByteBuffer bb1) throws CardException {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void close() throws CardException {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ /**
+ * Case 1 of command-response pair defined in ISO 7816-3
+ * no command data, no response data, status not protected
+ *
+ * CLA|INS|P1|P2 -> CLA'|INS|P1|P2|Lc'|TLmac
+ * @param apdu
+ * @return
+ * @throws GeneralSecurityException mac-calculation error
+ */
+ byte[] protectCase1b(byte[] apdu) throws GeneralSecurityException {
+
+ byte[] apdu_ = new byte[16];
+
+ // authenticate header: CLA* b3=1
+ apdu_[0] = (byte) (apdu[0] | (byte) 0x0c); // CLA*: b8-6=000 b4-3=11
+ apdu_[1] = apdu[1];
+ apdu_[2] = apdu[2];
+ apdu_[3] = apdu[3];
+
+ // Lc': TLmac
+ apdu_[4] = (byte) (blocksize + 2); //0x0a;
+
+ // cryptographic checksum
+ apdu_[5] = (byte) 0x8e;
+ apdu_[6] = (byte) blocksize; //0x08;
+
+ // Le' (case 1b)
+ apdu_[7 + blocksize] = 0x00;
+
+ // 2 data objects: SSC, header
+ byte[] mac_in = new byte[2 * blocksize];
+
+ // SSC (rightmost blocksize bytes)
+ incrementSSC();
+ System.arraycopy(kmacssc, kmacssc.length-blocksize, mac_in, 0, blocksize);
+
+ // CLA** INS P1 P2 padding
+ byte[] paddedHeader = RetailCBCMac.pad(Arrays.copyOf(apdu_, 4), blocksize);
+ System.arraycopy(paddedHeader, 0, mac_in, blocksize, blocksize);
+
+ byte[] mac = RetailCBCMac.retailMac(mac_in, RetailCBCMac.PADDING.NoPadding,
+ "DES", "DESede", kmac, blocksize, blocksize);
+
+ log.debug("cryptographic checksum ({}): {}", toString(mac_in), toString(mac));
+
+ // insert mac in 8E object
+ System.arraycopy(mac, 0, apdu_, 7, blocksize);
+ return apdu_;
+ }
+
+ ResponseAPDU unProtectCase1b(ResponseAPDU resp_) throws GeneralSecurityException, CardException {
+
+ if (resp_.getSW() == 0x9000) {
+
+ byte[] respData = resp_.getData();
+
+ // T*Lsw1sw2 | TLcc (CC size == blocksize)
+
+ byte[] mac_in = new byte[blocksize + 4];
+
+ // SSC
+ incrementSSC();
+ System.arraycopy(kmacssc, kmacssc.length-blocksize, mac_in, 0, blocksize);
+
+ // data
+ System.arraycopy(respData, 0, resp_, blocksize, 4);
+
+ byte[] mac_ = RetailCBCMac.retailMac(mac_in, RetailCBCMac.PADDING.ISO9797_2,
+ "DES", "DESede", kmac, blocksize, blocksize);
+
+ log.debug("cryptographic checksum ({}): {}", toString(mac_in), toString(mac_));
+
+ TLVSequence respSeq = new TLVSequence(respData);
+
+ byte[] cc = respSeq.getValue(0x8e);
+ if (!Arrays.equals(mac_, cc)) {
+ throw new CardException("invalid cryptographic checksum " + toString(cc));
+ }
+
+ byte[] sw = respSeq.getValue(0x99);
+ if (sw[0] != (byte) 0x90 || sw[1] != (byte) 0x00) {
+ throw new CardException("invalid status-word object " + toString(sw));
+ }
+
+ return new ResponseAPDU(sw);
+ }
+
+ log.info("unexpected response " + toString(resp_.getBytes()));
+ return resp_;
+ }
+
+ /**
+ * Case 2 of command-response pair defined in ISO 7816-3
+ * no command data, response data
+ *
+ * CLA|INS|P1|P2|Le -> CLA'|INS|P1|P2|Lc'|TLle|TLmac|00
+ *
+ * @param apdu
+ * @return
+ * @throws GeneralSecurityException mac-calculation error
+ */
+ byte[] protectCase2(byte[] apdu) throws GeneralSecurityException {
+
+ byte[] apdu_ = new byte[19];
+ // authenticate header: CLA** b3=1
+ apdu_[0] = (byte) (apdu[0] | (byte) 0x0c); //CLA**: b8-6=000 b4-3=11
+ apdu_[1] = apdu[1];
+ apdu_[2] = apdu[2];
+ apdu_[3] = apdu[3];
+
+ // Lc': TLle TLmac
+ apdu_[4] = (byte) (blocksize + 2 + 3); //0x0d;
+
+ // Ne
+ apdu_[5] = (byte) 0x97;
+ apdu_[6] = (byte) 0x01;
+ apdu_[7] = apdu[4];
+
+ // cryptographic checksum
+ apdu_[8] = (byte) 0x8e;
+ apdu_[9] = (byte) blocksize; //0x08;
+
+ // Le'
+ apdu_[10 + blocksize] = 0x00;
+
+ // 3 data objects: SSC, header, Le
+ byte[] mac_in = new byte[3 * blocksize];
+
+ // SSC (rightmost blocksize bytes)
+ incrementSSC();
+ System.arraycopy(kmacssc, kmacssc.length-blocksize, mac_in, 0, blocksize);
+
+ // CLA** INS P1 P2 padding
+ byte[] paddedHeader = RetailCBCMac.pad(Arrays.copyOf(apdu_, 4), blocksize);
+ System.arraycopy(paddedHeader, 0, mac_in, blocksize, blocksize);
+
+ // TL Le
+ byte[] paddedTLLe = RetailCBCMac.pad(Arrays.copyOfRange(apdu_, 5, 8), blocksize);
+ System.arraycopy(paddedTLLe, 0, mac_in, 2*blocksize, blocksize);
+
+ byte[] mac = RetailCBCMac.retailMac(mac_in, RetailCBCMac.PADDING.NoPadding,
+ "DES", "DESede", kmac, blocksize, blocksize);
+
+ log.debug("cryptographic checksum ({}): {}", toString(mac_in), toString(mac));
+
+ // insert mac in 8E object
+ System.arraycopy(mac, 0, apdu_, 10, blocksize);
+ return apdu_;
+ }
+
+ ResponseAPDU unProtectCase2(ResponseAPDU resp_) throws GeneralSecurityException, CardException {
+
+ if (resp_.getSW() == 0x9000) {
+
+ byte[] respData = resp_.getData();
+
+ // T*Lplain | T*Lsw1sw2 | TLcc (CC size == blocksize)
+
+ int TLccInd = respData.length - 2 - blocksize;
+
+ byte[] mac_in = new byte[respData.length - 2]; //+blocksize-blocksize
+
+ // SSC (rightmost blocksize bytes)
+ incrementSSC();
+ System.arraycopy(kmacssc, kmacssc.length-blocksize, mac_in, 0, blocksize);
+
+ // data
+ System.arraycopy(respData, 0, mac_in, blocksize, TLccInd);
+
+ byte[] mac_ = RetailCBCMac.retailMac(mac_in, RetailCBCMac.PADDING.ISO9797_2,
+ "DES", "DESede", kmac, blocksize, blocksize);
+
+ log.debug("cryptographic checksum ({}): {}", toString(mac_in), toString(mac_));
+
+ TLVSequence respSeq = new TLVSequence(respData);
+
+ byte[] cc = respSeq.getValue(0x8e);
+ if (!Arrays.equals(mac_, cc)) {
+ throw new CardException("invalid cryptographic checksum " + toString(cc));
+ }
+
+ byte[] sw = respSeq.getValue(0x99);
+ if (sw[0] != (byte) 0x90 || sw[1] != (byte) 0x00) {
+ throw new CardException("invalid status-word object " + toString(sw));
+ }
+
+ byte[] plain = respSeq.getValue(0x81);
+ byte[] resp = new byte[plain.length + 2];
+ System.arraycopy(plain, 0, resp, 0, plain.length);
+ resp[resp.length - 2] = (byte) 0x90;
+ resp[resp.length - 1] = (byte) 0x00;
+ return new ResponseAPDU(resp);
+ }
+
+ log.info("unexpected response " + toString(resp_.getBytes()));
+ return resp_;
+ }
+
+ void incrementSSC() {
+ //TODO
+ kmacssc[7] += 1;
+ System.out.println(" [SC] incrementing kmac_ssc: " + toString(kmacssc));
+ }
+
+ 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();
+ }
}