diff options
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(); + } } |