summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--smccTest/src/main/java/at/gv/egiz/smcc/activation/Activation.java201
-rw-r--r--smccTest/src/main/java/at/gv/egiz/smcc/activation/RetailCBCMac.java2
-rw-r--r--smccTest/src/main/java/at/gv/egiz/smcc/activation/SecureChannel.java137
3 files changed, 287 insertions, 53 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
index 8d102c80..516e9487 100644
--- a/smccTest/src/main/java/at/gv/egiz/smcc/activation/Activation.java
+++ b/smccTest/src/main/java/at/gv/egiz/smcc/activation/Activation.java
@@ -7,8 +7,25 @@ package at.gv.egiz.smcc.activation;
import at.gv.egiz.smcc.SignatureCardException;
import at.gv.egiz.smcc.util.TLVSequence;
+import iaik.asn1.ASN1;
+import iaik.asn1.ASN1Object;
+import iaik.asn1.CodingException;
+import iaik.asn1.DerCoder;
+import iaik.security.ecc.ECCException;
+import iaik.security.ecc.ecdsa.ECDSAParameter;
+import iaik.security.ecc.ecdsa.ECPublicKey;
+import iaik.security.ecc.math.ecgroup.AffineCoordinate;
+import iaik.security.ecc.math.ecgroup.CoordinateTypes;
+import iaik.security.ecc.math.ecgroup.ECGroupFactory;
+import iaik.security.ecc.math.ecgroup.ECPoint;
+import iaik.security.ecc.math.ecgroup.EllipticCurve;
+import iaik.security.ecc.math.field.FieldElement;
+import iaik.security.ecc.parameter.ECCParameterFactory;
+import iaik.security.ecc.spec.ECCParameterSpec;
+import iaik.security.ecc.util.PointFormatter;
import iaik.security.provider.IAIK;
import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
@@ -16,7 +33,7 @@ import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
-import java.security.SignatureException;
+import java.security.PublicKey;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
@@ -253,7 +270,52 @@ public class Activation {
resp = channel.transmit(cmdAPDU);
System.out.println(" -> " + toString(resp.getBytes()) + "\n");
- openSecureChannel(k_qsig, cin);
+// openSecureChannel(k_qsig, cin);
+
+ System.out.println("READ BINARY");
+ cmdAPDU = new CommandAPDU(0x00, 0xb0, 0x00, 0x00, 256);
+ System.out.println(" cmd apdu " + toString(cmdAPDU.getBytes()));
+ resp = channel.transmit(cmdAPDU);
+ System.out.println(" -> " + toString(resp.getBytes()) + "\n");
+
+ try{
+ ASN1Object puk = DerCoder.decode(resp.getData());
+ System.out.println("EF.PuK:\n" + ASN1.print(puk));
+
+ byte[] oid = (byte[]) puk.getComponentAt(1).getComponentAt(1).getValue();
+ if (oid == null || oid.length == 0) {
+ System.out.println("assume P-256");
+ oid = "1.2.840.10045.3.1.7".getBytes("ASCII");
+ }
+
+ System.out.println("OID: " + new String(oid));
+ byte[] Q = (byte[]) puk.getComponentAt(1).getComponentAt(0).getValue();
+// byte[] Qx = Arrays.copyOfRange(Q, 0, Q.length/2);
+// byte[] Qy = Arrays.copyOfRange(Q, Q.length/2, Q.length);
+//
+// System.out.println("Qx: " + toString(Qx));
+// System.out.println("Qy: " + toString(Qy));
+
+// ECPublicKey ecPuK = decodeECPublicKey(Q, new String(oid));
+// System.out.println("PuK: " + ecPuK);
+
+ } catch (CodingException ex) {
+ throw new SignatureCardException("failed to read EF.PuK", ex);
+ }
+ /*
+ [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: public exponent?
+ 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:90:00]
+
+ <ResponseAPDU SW="9000" rc="0" sequence="7">A88200A8B616831480040000002300789901D04000001700120102007F49820044864084BA7BF0AF355A67E0C9064EE53A63859903C775199221494A430FFAE20F3F2DC283FEF3C8EEF21FBF75448DC7DB9649BAC504DE0C6416C91D62882438128CDFC100C001809E82004087506037D74C9DCE7454A2F561A19FF24ED03D097A0CD8D45F3CB2DCF51684195632F39D72381F64BA2DCB65524C54E94265CB9E5F43EBCC02D23C1D9A02D26E</ResponseAPDU>
+ */
}
@@ -514,79 +576,116 @@ public class Activation {
}
System.out.println("successfully verified RND.ICC/IFD");
-
-//
-// var kicc = plain.bytes(32, 64); // 32 -> 64
-// keyinp = kicc.xor(kifd);
-//
+ //
+ // var kicc = plain.bytes(32, 64); // 32 -> 64
+ byte[] kd_icc = Arrays.copyOfRange(plain, 32, 96);
- // TDES session key negotiation according to E-Sign K [STARCOS,6.11]?
+ channel = secureChannel(kd_icc, kd_ifd);
- byte[] kd_icc = Arrays.copyOfRange(plain, 32, 96);
+ }
+
+ SecureChannel secureChannel(byte[] kd_icc, byte[] kd_ifd) throws NoSuchAlgorithmException {
+
+ // keyinp = kicc.xor(kifd);
+ //
+ // TDES session key negotiation according to E-Sign K [STARCOS,6.11]?
System.out.println("derive key input...");
byte[] kinp = new byte[kd_ifd.length];
for (int i = 0; i < kd_ifd.length; i++) {
- kinp[i] = (byte) (kd_icc[i] ^ kd_ifd[i]);
+ kinp[i] = (byte) (kd_icc[i] ^ kd_ifd[i]);
}
-
System.out.println("session key negotiation key (key seed): " + toString(kinp));
-
-// 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("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);
+ //
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
sha256.update(kinp);
- sha256.update(new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01});
+ sha256.update(new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01});
byte[] enc_ = sha256.digest();
-
SecretKeySpec kenc = new SecretKeySpec(Arrays.copyOfRange(enc_, 0, 24), "3DES");
byte[] kencssc = Arrays.copyOfRange(enc_, 24, 32);
-
System.out.println("session key kenc: " + toString(kenc.getEncoded()));
System.out.println("send sequence counter SSC_enc: " + toString(kencssc));
-
-// 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 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);
sha256.update(kinp);
- sha256.update(new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x02});
+ sha256.update(new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02});
enc_ = sha256.digest();
-
SecretKeySpec kmac = new SecretKeySpec(Arrays.copyOfRange(enc_, 0, 24), "3DES");
byte[] kmacssc = Arrays.copyOfRange(enc_, 24, 32);
-
System.out.println("session key kmac: " + toString(kmac.getEncoded()));
System.out.println("send sequence counter SSC_mac: " + toString(kmacssc));
+ //
+ // var sc = new IsoSecureChannel(crypto);
+ // sc.setEncKey(kenc);
+ // sc.setMacKey(kmac);
+ //
+ // sc.setMACSendSequenceCounter(kmacssc);
+ // sc.setEncryptionSendSequenceCounter(kencssc);
+ // return sc;
+ //}
+
+ return new SecureChannel(channel, kenc, kmac, kencssc, kmacssc);
+ }
-//
-// var sc = new IsoSecureChannel(crypto);
-// sc.setEncKey(kenc);
-// sc.setMacKey(kmac);
-//
-// sc.setMACSendSequenceCounter(kmacssc);
-// sc.setEncryptionSendSequenceCounter(kencssc);
-// return sc;
-//}
+ /**
+ * Decodes the given encoded EC PublicKey according to the Octet-String-to-Point conversion
+ * of ANSI X9.62 (1998), section 4.3.7.
+ * <p>
+ * This method is called on the client side to decode the public server key
+ * contained in an ECDH ServerKeyExchange message received from the server.
+ *
+ * @param ecPoint the (client) public key ECPoint, encoded according to
+ * ANSI X9.62 (1998), section 4.3.6
+ * @param oid the oid of the curve
+ *
+ * @return the decoded public EC key
+ *
+ * @exception Exception if an error occurs when decoding the key
+ */
+ public static ECPublicKey decodeECPublicKey(byte[] ecPoint, String oid) throws ECCException {
- channel = new SecureChannel(channel, kenc, kmac, kencssc, kmacssc);
+ ECCParameterFactory pFac = ECCParameterFactory.getInstance();
+ ECCParameterSpec spec = pFac.getParameterByOID(oid);
- }
+ // Now, we need an instance of elliptic curve factory
+ ECGroupFactory gfac = ECGroupFactory.getInstance();
+
+ BigInteger r = spec.getR();
+
+ FieldElement aElement = spec.getA();
+ FieldElement bElement = spec.getB();
+
+ // Now we get a curve of the form y^2 = x^3 + ax + b having order r from the
+ // ECGroupFactory. We assume that the curve is non-singular.
+ EllipticCurve ec = gfac.getCurve(aElement, bElement, r, CoordinateTypes.AFFINE_COORDINATES);
+
+
+ AffineCoordinate coord = PointFormatter.getInstance().getPointCodec().decodePoint(ecPoint, ec);
+
+ // With these coordinates, we can construct a point on the curve.
+ ECPoint point = ec.newPoint(coord);
+
+ ECDSAParameter params = new ECDSAParameter(spec);
+
+ return new ECPublicKey(params, point);
+
+ }
public static void main(String[] args) {
diff --git a/smccTest/src/main/java/at/gv/egiz/smcc/activation/RetailCBCMac.java b/smccTest/src/main/java/at/gv/egiz/smcc/activation/RetailCBCMac.java
index 79f7be62..f8cedc0e 100644
--- a/smccTest/src/main/java/at/gv/egiz/smcc/activation/RetailCBCMac.java
+++ b/smccTest/src/main/java/at/gv/egiz/smcc/activation/RetailCBCMac.java
@@ -123,7 +123,7 @@ public class RetailCBCMac {
int off,
int len,
SecretKey key,
- String cipherAlg,
+ String cipherAlg,
int blockSize)
throws NoSuchAlgorithmException,
NoSuchProviderException,
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 303b1b68..9bbd39fb 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
@@ -6,6 +6,13 @@
package at.gv.egiz.smcc.activation;
import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+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;
@@ -49,7 +56,17 @@ public class SecureChannel extends CardChannel {
@Override
public ResponseAPDU transmit(CommandAPDU capdu) throws CardException {
- return basicChannel.transmit(capdu);
+ 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
@@ -62,4 +79,122 @@ public class SecureChannel extends CardChannel {
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();
+ }
}