diff options
| author | clemenso <clemenso@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2011-01-07 09:47:39 +0000 | 
|---|---|---|
| committer | clemenso <clemenso@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2011-01-07 09:47:39 +0000 | 
| commit | 3aae8daf74fdc71abc4352000efb5f6c6a96246a (patch) | |
| tree | d9c92da34c8a5c669ee56edc511e5fd5363455bd /smccTest/src/main/java/at | |
| parent | 097733261f61b27b5eaa4aed3fe023c5b3ac5f11 (diff) | |
| download | mocca-3aae8daf74fdc71abc4352000efb5f6c6a96246a.tar.gz mocca-3aae8daf74fdc71abc4352000efb5f6c6a96246a.tar.bz2 mocca-3aae8daf74fdc71abc4352000efb5f6c6a96246a.zip | |
Retail CBC MAC
git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@875 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4
Diffstat (limited to 'smccTest/src/main/java/at')
3 files changed, 969 insertions, 52 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(); +        } +    } +} diff --git a/smccTest/src/main/java/at/gv/egiz/smcc/activation/ActivationTest.java b/smccTest/src/main/java/at/gv/egiz/smcc/activation/ActivationTest.java index d032d45e..a9d147db 100644 --- a/smccTest/src/main/java/at/gv/egiz/smcc/activation/ActivationTest.java +++ b/smccTest/src/main/java/at/gv/egiz/smcc/activation/ActivationTest.java @@ -12,12 +12,24 @@ import iaik.asn1.ASN1Object;  import iaik.asn1.CodingException;  import iaik.asn1.DerCoder;  import iaik.security.provider.IAIK; +import java.io.UnsupportedEncodingException;  import java.math.BigInteger;  import java.nio.charset.Charset; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.MessageDigest;  import java.security.NoSuchAlgorithmException; -import java.security.spec.RSAPublicKeySpec; +import java.security.NoSuchProviderException;  import java.util.Arrays;  import java.util.List; +import java.util.Random; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +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; @@ -120,18 +132,24 @@ public class ActivationTest {          resp = channel.transmit(cmdAPDU);          System.out.println(" -> " + toString(resp.getBytes()) + "\n"); -        System.out.println("SELECT EF.GDO"); -        // alternative: read with SFI=02: 00 b0 82 00 fa -        // P1=0x00 -> 6a:80 (incorrect cmd data) -        // no Le -> 67:00 (wrong length) -        cmdAPDU = new CommandAPDU(0x00, 0xa4, 0x02, 0x00, new byte[] {(byte) 0x2f, (byte) 0x02}, 256); -        System.out.println(" cmd apdu " + toString(cmdAPDU.getBytes())); -        resp = channel.transmit(cmdAPDU); -        System.out.println(" -> " + toString(resp.getBytes()) + "\n"); - -        System.out.println("READ EF.GDO"); -        // 7.2.2 (case2), offset=0 -        cmdAPDU = new CommandAPDU(0x00, 0xb0, 0x00, 0x00, 256); +//        System.out.println("SELECT EF.GDO"); +//        // alternative: read with SFI=02: 00 b0 82 00 fa +//        // P1=0x00 -> 6a:80 (incorrect cmd data) +//        // no Le -> 67:00 (wrong length) +//        cmdAPDU = new CommandAPDU(0x00, 0xa4, 0x02, 0x00, new byte[] {(byte) 0x2f, (byte) 0x02}, 256); +//        System.out.println(" cmd apdu " + toString(cmdAPDU.getBytes())); +//        resp = channel.transmit(cmdAPDU); +//        System.out.println(" -> " + toString(resp.getBytes()) + "\n"); +// +//        System.out.println("READ EF.GDO"); +//        // 7.2.2 (case2), offset=0 +//        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"); + +        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"); @@ -145,23 +163,7 @@ public class ActivationTest {          }      } -    public static String toString(byte[] b) { -        StringBuffer sb = new StringBuffer(); -        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(); -    } - -    public void getPuK_GewSig() throws CardException, SignatureCardException, CodingException { +    public ASN1Object getCIO_PrK_SS() throws CardException, CodingException {          CommandAPDU cmdAPDU;          ResponseAPDU resp; @@ -173,14 +175,14 @@ public class ActivationTest {          System.out.println("SELECT DF.QualifizierteSignatur");          // P1=0x00 -> 67:00 (wrong length) -        cmdAPDU = new CommandAPDU(0x00, 0xa4, 0x04, 0x0c, new byte[] {(byte) 0xd0, (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, (byte) 0x12, (byte) 0x01}); +        cmdAPDU = new CommandAPDU(0x00, 0xa4, 0x04, 0x00, new byte[]{(byte) 0xd0, (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, (byte) 0x12, (byte) 0x01}, 256);          System.out.println(" cmd apdu " + toString(cmdAPDU.getBytes()));          resp = channel.transmit(cmdAPDU);          System.out.println(" -> " + toString(resp.getBytes()) + "\n");          System.out.println("SELECT EF.PrKD");          // P1=0x00 -> 67:00 (wrong length) -        cmdAPDU = new CommandAPDU(0x00, 0xa4, 0x02, 0x04, new byte[] {(byte) 0x50, (byte) 0x35}, 256); +        cmdAPDU = new CommandAPDU(0x00, 0xa4, 0x02, 0x04, new byte[]{(byte) 0x50, (byte) 0x35}, 256);          System.out.println(" cmd apdu " + toString(cmdAPDU.getBytes()));          resp = channel.transmit(cmdAPDU);          System.out.println(" -> " + toString(resp.getBytes()) + "\n"); @@ -203,8 +205,56 @@ public class ActivationTest {          BigInteger keyRef = (BigInteger) efPrK_QS.getComponentAt(1).getComponentAt(4).getValue();          System.out.println("PrK_QS keyRef: 0x" + Integer.toHexString(keyRef.intValue()) + "\n"); +        return efPrK_QS; +    } + +    public void getPuK_SicSig(byte[] cin) throws CardException, SignatureCardException, CodingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { +        CommandAPDU cmdAPDU; +        ResponseAPDU resp; + +        System.out.println("SELECT MF"); +        cmdAPDU = new CommandAPDU(0x00, 0xA4, 0x00, 0x0c, new byte[]{(byte) 0x3f, (byte) 0x00}); +        System.out.println(" cmd apdu " + toString(cmdAPDU.getBytes())); +        resp = channel.transmit(cmdAPDU); +        System.out.println(" -> " + toString(resp.getBytes()) + "\n"); + +        System.out.println("SELECT DF.QualifizierteSignatur"); +        // P1=0x00 -> 67:00 (wrong length) +        cmdAPDU = new CommandAPDU(0x00, 0xa4, 0x04, 0x00, new byte[]{(byte) 0xd0, (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, (byte) 0x12, (byte) 0x01}, 256); +        System.out.println(" cmd apdu " + toString(cmdAPDU.getBytes())); +        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(ISO7816Utils.TAG_FCI); + +            TLVSequence fci = new TLVSequence(fciBytes); +            System.out.println("FCI DF.QualifizierteSignatur"); +            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)); +            } + +            System.out.println("key number: 0x" + Byte.toString(keyNumVer[0])); +            System.out.println("key version: 0x" + Byte.toString(keyNumVer[1])); +        } else { +            throw new SignatureCardException("Failed to read DF.QualifizierteSignatur: 0x" + Integer.toHexString(resp.getSW())); +        } + +        SecretKeySpec kp_mk_ss = getKP_MK_SS(); + +        SecretKeySpec kp_ss = deriveApplicationKey(kp_mk_ss, cin, keyNumVer); +          int dfSpecificKeyRef = 1; -        byte[] crt_at = new byte[] { +        byte[] crt_at = new byte[]{              (byte) 0x83, (byte) 0x01, (byte) (0x80 | (0x7f & dfSpecificKeyRef)),              // algorithm id 0x54???              (byte) 0x80, (byte) 0x01, (byte) 0x54 @@ -217,44 +267,233 @@ public class ActivationTest {          System.out.println(" -> " + toString(resp.getBytes()) + "\n");          System.out.println("GET CHALLENGE"); -        // eg. RNDICC = [ed:9b:a3:78:83:2f:d3:6c:90:00]          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(); +         +        SecretKeySpec k_enc = deriveKey(kp_ss, "3DES/CBC/NoPadding", iv, K_ENC); +        SecretKeySpec k_mac = deriveKey(kp_ss, "3DES/CBC/NoPadding", iv, K_MAC); +        mutualAuth(cin, rnd_icc, k_enc, k_mac); -        // ICCSN + RNDICC => Kryptogramm_CRS (STARCOS31, p357 authentication according to e-SignK) -        // MUTUALAUTH -> Kryptogramm_Karte -> prüfung -> session key -        System.out.println("MUTUAL AUTHENTICATE TODO..."); -         +    } -        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"); +    private SecretKeySpec getKP_MK_SS() { +        byte[] kp_mk_ss_Bytes = 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}; +        SecretKeySpec kp_mk_ss = new SecretKeySpec(kp_mk_ss_Bytes, "3DES"); +        return kp_mk_ss; +    } -        System.out.println("READ EF.PuK_QS"); -        // 7.2.2 (case2), offset=0 -        cmdAPDU = new CommandAPDU(0x00, 0xb0, 0x00, 0x00, 256); +    protected SecretKeySpec deriveApplicationKey(SecretKeySpec keySpec, byte[] cin, byte[] keyNumVer) throws NoSuchProviderException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException { + +        MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); + +        sha256.update(cin); +        sha256.update(keyNumVer); +        byte[] derivationParam = sha256.digest(); +        System.out.println("derivationParam = SHA-256(CIN|kNum|kVer) (" + derivationParam.length * 8 + "bit) = " + toString(derivationParam)); +        derivationParam = Arrays.copyOf(derivationParam, 24); +        System.out.println("derivationParam (" + derivationParam.length * 8 + "bit) = " + toString(derivationParam)); + +        //        DESedeKeySpec kp_mk_ssSpec = new DESedeKeySpec(kp_mk_ss); +        System.out.println("Application Master Key KP_MK_SS (DES-EDE): " + toString(keySpec.getEncoded())); +        System.out.println("Derive application key KP_SS"); +        Cipher tripleDES = Cipher.getInstance("3DES/CBC/NoPadding", "IAIK"); +        tripleDES.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv)); +        System.out.println("derivationParam (" + derivationParam.length * 8 + "bit) = " + toString(derivationParam)); +        byte[] x = tripleDES.doFinal(derivationParam); +        System.out.println("kp_ss (" + x.length * 8 + "bit): " + toString(x)); +        for (int key_i = 0; key_i < x.length; key_i += 8) { +            for (int i = 0; i < 8; i++) { +                int ones = 0; +                for (int j = 1; j < BIT_MASK.length; j++) { +                    if ((x[i + key_i] & BIT_MASK[j]) == BIT_MASK[j]) { +                        ones++; +                    } +                } +                if ((ones & 0x1) > 0) { +                    x[i + key_i] &= (byte) 0xfe; // odd +                } else { +                    x[i + key_i] |= 0x1; // even +                } +            } +        } +        System.out.println("kp_ss (parity adjusted): " + toString(x)); +        SecretKeySpec kp_ss = new SecretKeySpec(x, "3DES"); +        return kp_ss; +    } + +    private void mutualAuth(byte[] cin, byte[] rnd_icc, SecretKeySpec kenc, SecretKeySpec kmac) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, CardException { +        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)); +        } + +        Random rand = new Random(System.currentTimeMillis()); + +        byte[] rnd_ifd = new byte[8]; +        rand.nextBytes(rnd_ifd); +        byte[] icc_id = Arrays.copyOfRange(cin, cin.length - 8, cin.length); +        byte[] ifd_id = new byte[8]; +        rand.nextBytes(ifd_id); +        byte[] kd_ifd = new byte[64]; +        rand.nextBytes(kd_ifd); + +        Cipher tDES = Cipher.getInstance("3DES/CBC/NoPadding"); +        tDES.init(Cipher.ENCRYPT_MODE, kenc, new IvParameterSpec(iv)); + +        byte[] sendData = new byte[4*8+64]; +        System.arraycopy(rnd_ifd, 0, sendData, 0, 8); +        System.arraycopy(ifd_id, 0, sendData, 8, 8); +        System.arraycopy(rnd_icc, 0, sendData, 16, 8); +        System.arraycopy(icc_id, 0, sendData, 24, 8); +        System.arraycopy(kd_ifd, 0, sendData, 32, 64); + +        System.out.println("cryptogram input (" + sendData.length + "byte): " +                + toString(sendData)); +         +//        tDES.update(rnd_ifd); // 8 byte +//        tDES.update(ifd_id);  // 8 byte +//        tDES.update(rnd_icc); // 8 byte +//        tDES.update(icc_id);  // 8 byte +//        tDES.update(kd_ifd);  // 64 byte + +        byte[] cryptogram = tDES.doFinal(sendData); +        System.out.println("cryptogram (" + cryptogram.length + "byte): " +                + toString(cryptogram)); + +        Mac retailMac = Mac.getInstance("CMacDESede"); +        retailMac.init(kmac); +        byte[] mac = retailMac.doFinal(cryptogram); +        System.out.println("MAC: " + toString(mac)); + +        byte[] c = new byte[cryptogram.length + mac.length]; +        System.arraycopy(cryptogram, 0, c, 0, cryptogram.length); +        System.arraycopy(mac, 0, c, cryptogram.length, mac.length); +        System.out.println(c.length + "bytes :" + toString(c)); +        CommandAPDU cmdAPDU = new CommandAPDU(0x00, 0x82, 0x00, 0x81, c, 256); // 81->00          System.out.println(" cmd apdu " + toString(cmdAPDU.getBytes())); -        resp = channel.transmit(cmdAPDU); +        ResponseAPDU resp = channel.transmit(cmdAPDU);          System.out.println(" -> " + toString(resp.getBytes()) + "\n"); +    } + + +    private SecretKeySpec deriveKey(SecretKeySpec masterkey, String cipherAlias, byte[] iv, byte[] derivationParam) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + +        if (derivationParam.length != 24) { +            throw new RuntimeException("invalid 3TDES derivation parameter: " + toString(derivationParam)); +        } + +        //3DES/CBC/NoPadding +        Cipher cipher = Cipher.getInstance(cipherAlias); +        cipher.init(Cipher.ENCRYPT_MODE, masterkey, new IvParameterSpec(iv)); -        System.out.println("PuK_QS:\n" + toString(resp.getData())); +        System.out.println("derivation parameter (" +                + derivationParam.length * 8 + "bit): " +                + toString(derivationParam)); +        System.out.println("derivation key (" +                + masterkey.getAlgorithm() + ") :" +                + toString(masterkey.getEncoded())); +        byte[] x = cipher.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;      } + +    private final static byte[] K_ENC; +    private final static byte[] K_MAC; + +    static { +        byte[] encBytes; +        byte[] macBytes; +        try { +            MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); +            encBytes = sha256.digest("K_ENC".getBytes("ASCII")); +            macBytes = sha256.digest("K_MAC".getBytes("ASCII")); +        } catch (NoSuchAlgorithmException ex) { +            encBytes = new byte[] {(byte)0xe0}; +            macBytes = new byte[] {(byte)0xe0}; +        } catch (UnsupportedEncodingException ex) { +            encBytes = new byte[] {(byte)0xe1}; +            macBytes = new byte[] {(byte)0xe1}; +        } +        K_ENC = Arrays.copyOf(encBytes, 24); +        K_MAC = Arrays.copyOf(macBytes, 24); +    } +     +    private final static byte[] iv = { +        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 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++; +                } +            } -    public static void main(String[] args) throws NoSuchAlgorithmException, CardException, SignatureCardException, CodingException { +            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(); +    } + +    public static void main(String[] args) throws NoSuchAlgorithmException, CardException, SignatureCardException, CodingException, InvalidKeyException, NoSuchProviderException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {          ActivationTest test = new ActivationTest();          test.setUp(); -//        test.getCIN(); +        byte[] cin = test.getCIN(); + +        test.getCIO_PrK_SS(); +          //        test.getSVNr(); -        test.getPuK_GewSig(); +        test.getPuK_SicSig(cin);      }  } 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 new file mode 100644 index 00000000..79f7be62 --- /dev/null +++ b/smccTest/src/main/java/at/gv/egiz/smcc/activation/RetailCBCMac.java @@ -0,0 +1,186 @@ +package at.gv.egiz.smcc.activation; + +import iaik.utils.CryptoUtils; + +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class RetailCBCMac { +   +  /** +   * Calculates a Retail CBC Mac from the given message. +   * <p> +   * The retail CBC Mac is calculated according to the following  +   * algorithm: +   * <ul> +   *    <li> +   *       Pad the message to a multiple n of the CBC cipher block size with +   *       a leading one bit followed by as many zero bits as necessary. +   *    </li> +   *    <li> +   *       Create a CBC cipher key from the first <code>cbcCipherKeyLen</code> +   *       bytes of the <code>csk</code> key and use it to calculate a  +   *       CBC Mac value from the first n-1 blocks of the padded message. +   *       For CBC Mac calculation initialize the CBC Cipher with an +   *       IV of all zero bytes. +   *    </li> +   *    <li> +   *       XOR the last block of the padded message with the CBC mac value +   *       and calculate the final retail MAC by encrypting the XOR result +   *       with the given Cipher algorithm in ECB mode using no padding. +   *    </li> +   * </ul> +   *  +   * @param msg the message +   * @param cbcCipherAlg the name of the CBC Cipher algorithm to be used +   * @param cipherAlg the name of the final Cipher algorithm to be used +   * @param csk the secret key to be used +   * @param cbcCipherKeyLen the length of the CBC cipher key to be used +   * @param blockSize the block size of the CBC Cipher +   *  +   * @return the retail CBC Mac value +   *  +   * @throws NoSuchAlgorithmException if any of the requested Cipher algorithms +   *                                  is not available  +   * @throws NoSuchProviderException if the IAIK provider is not installed +   * @throws InvalidKeyException if the key cannot be used with the Ciphers  +   * @throws GeneralSecurityException if the Cipher operation(s) fails +   */ +  static byte[] retailMac(byte[] msg,  +                          String cbcCipherAlg, +                          String cipherAlg, +                          SecretKey csk, +                          int cbcCipherKeyLen, +                          int blockSize)  +    throws NoSuchAlgorithmException,  +         NoSuchProviderException,  +         InvalidKeyException,  +         GeneralSecurityException { +     +    if (msg == null) { +      throw new NullPointerException("Message m must not be null!"); +    } +    if (csk == null) { +      throw new NullPointerException("Key csk must not be null!"); +    } +     +    // calculate key for CBC cipher +    byte[] rawCsk = csk.getEncoded(); +    int cskLen = rawCsk.length; +    SecretKey cbcCipherKey; +    if (cskLen == cbcCipherKeyLen) { +      cbcCipherKey = csk; +    } else if (cskLen < cbcCipherKeyLen) { +      throw new InvalidKeyException("Key too short!"); +    } else { +      byte[] rawCbcCipherKey = new byte[blockSize]; +      System.arraycopy(rawCsk, 0, rawCbcCipherKey, 0, blockSize); +      cbcCipherKey = new SecretKeySpec(rawCbcCipherKey, cbcCipherAlg); +    } +    // if necessary pad message with zeros +    byte[] paddedMsg = pad(msg, blockSize); +     +    // calculate CBC Mac for the first n-1 blocks +    int n = paddedMsg.length; +    int n_1 = n - blockSize; +    byte[] cbcMac = cbcMac(paddedMsg, 0, n_1, cbcCipherKey, cbcCipherAlg, blockSize); + +    // calculate retail mac +    byte[] xor = new byte[blockSize]; +    CryptoUtils.xorBlock(paddedMsg, n_1, cbcMac, 0, xor, 0, blockSize); +    Cipher cipher = Cipher.getInstance(cipherAlg+"/ECB/NoPadding", "IAIK"); +    cipher.init(Cipher.ENCRYPT_MODE, csk); +    byte[] retailMac = cipher.doFinal(xor); +    return retailMac; +  } +   +  /** +   * Calculates a simple CBC Mac from the given (already) padded message. +   *  +   * @param paddedMsg the (zero) padded message +   * @param off the start offset in the paddedMsg array +   * @param len the number of bytes to be processed, starting at <code>off</code> +   * @param key the Cipher key +   * @param cipherAlg the name of the CBC Cipher algorithm to be used +   * @param blockSize the block size of the CBC Cipher +   *  +   * @return the CBC Mac value +   *  +   * @throws NoSuchAlgorithmException if the requested Cipher algorithm +   *                                  is not available  +   * @throws NoSuchProviderException if the IAIK provider is not installed +   * @throws InvalidKeyException if the key cannot be used with the Ciphers  +   * @throws GeneralSecurityException if the Cipher operation fails +   */ +  static byte[] cbcMac(byte[] paddedMsg, +                       int off, +                       int len, +                       SecretKey key,  +                       String cipherAlg,  +                       int blockSize)  +    throws NoSuchAlgorithmException,  +           NoSuchProviderException,  +           InvalidKeyException,  +           GeneralSecurityException {   +            +    if (paddedMsg == null) { +      throw new NullPointerException("Message must not be null!"); +    } +    if (key == null) { +      throw new NullPointerException("Key csk must not be null!"); +    } +     +    +    Cipher cbcCipher = Cipher.getInstance(cipherAlg+"/CBC/NoPadding", "IAIK"); +    byte[] iv = new byte[blockSize]; +    cbcCipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); +    int finOff = off; +    if (len > blockSize) { +      finOff = len - blockSize; +      cbcCipher.update(paddedMsg, 0, finOff); +    } +    byte[] mac = cbcCipher.doFinal(paddedMsg, finOff, blockSize); +    return mac; +  } +   +  /** +   * Pads the given message to a multiple of the given blocksize with +   * a leading one bit followed by as many zero bits as necessary +   *  +   * @param msg the message to be padded +   * @param blockSize the block size +   *  +   * @return the padded message +   */ +  static byte[] pad(byte[] msg, int blockSize) { +    int paddingLen; +    byte[] paddedMsg; + +    int msgLen = msg.length; +    if (msgLen == 0) { +      paddingLen = blockSize; +    } else { +      paddingLen = blockSize - msgLen % blockSize; +    }   +    if (paddingLen > 0) { +      paddedMsg = new byte[msgLen + paddingLen]; +      System.arraycopy(msg, 0, paddedMsg, 0, msgLen); +      paddedMsg[msgLen] = (byte)0x80; +    } else { +      paddedMsg = msg; +    } +    return paddedMsg; +  } +   + +   +   + +} | 
