Skip to main content
3 of 3
edited tags
Der Kommissar
  • 20.1k
  • 4
  • 68
  • 158

Mifare AES-128 symmetric key diversification

I have implemented a symmetric (AES 128-bit) key diversification algorithm for Java following the NXP Notes. It works as expected but am not the best Java programmer around, I guess I have done quite a mess so I need help on how can improve the code and make it better?

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

/**
 *
 * @author Chris Skinner
 */
public class AES {

    static SecretKeySpec skeySpec = null;
    static public byte[] K = new byte[16]; // 128 bit key
    static public byte[] K1 = new byte[16]; // 128 bit sub key
    static public byte[] K2 = new byte[16]; // 128 bit sub key
    static final public byte[] Z16 = new byte[16]; // 128 bit zero
    static Cipher cipher = null;
    private static final IvParameterSpec ZERO_IV = new IvParameterSpec(new byte[16]);

    public static byte[] getDiversifiedKey() throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException // make K1 K2 from key
    {
        byte bRb = (byte) 0x87; // Rb for AES128
        // key must be 16 bytes

        //Step 1

        byte[] key = hexStringToByteArray("00112233445566778899AABBCCDDEEFF");
        try {
            skeySpec = new SecretKeySpec(key, "AES");
            if (cipher == null) {
                cipher = Cipher.getInstance("AES/ECB/NoPadding");
            }
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
            K1 = cipher.doFinal(Z16);
            //Step 2
            System.out.println("KO " + bytesToHex(K1));
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException ex) // nosuchalgorithm, invalidkey,nosuchpadding...
        {
            System.out.println("\n error 400 AES   " + ex.getMessage());
        }
        boolean highL = ((K1[0] & 0x80) != 0);
        K1 = shl(K1);
        if (highL) {
            K1[15] = (byte)(K1[15] ^ bRb);
        }
        highL = ((K1[0] & 0x80) != 0);
        K2 = shl(K1);
        if (highL) {
            K2[15] = (byte)(K2[15] ^ bRb);
        }
        //Step 3
        System.out.println("K1 " + bytesToHex(K1));


        //Step 4
        System.out.println("K2 " + bytesToHex(K2));


        byte[] div_constant = hexStringToByteArray("01");

        //Step 5
        byte[] uid = hexStringToByteArray("04782E21801D80");

        //Step 6
        byte[] aid = hexStringToByteArray("3042F5");

        //Step 7
        byte[] sysid = hexStringToByteArray("4E585020416275");

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        outputStream.write(div_constant);
        outputStream.write(uid);
        outputStream.write(aid);
        outputStream.write(sysid);

        //Step 8 & 9
        byte c[] = outputStream.toByteArray();


        //Step 10 & 11
        boolean padded = false;
        if (c.length < 32) {
            padded = true;
            byte[] eg = hexStringToByteArray("80");
            outputStream.write(eg);
            byte[] st = outputStream.toByteArray();

            byte[] end = new byte[32 - st.length];

            outputStream.write(end);

            c = outputStream.toByteArray();
            outputStream.close();

        }

        byte[] xor_component;
        if (padded) {
            xor_component = K2;
        } else {
            xor_component = K1;
        }

        int mz = c.length;
        int n = mz / 16;

        byte[] MLast = new byte[16];
        int lastn = (n - 1) * 16;
        int lastz = mz - lastn;
        System.arraycopy(c, lastn, MLast, 0, lastz);
        byte[] first = Arrays.copyOfRange(c, 0, 16);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        stream.write(first);
        stream.write(xor16(MLast, xor_component));
        byte[] xored_d = stream.toByteArray();

        //Step 12
        System.out.println(bytesToHex(xored_d));


        cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ZERO_IV);
        byte[] encrypted = cipher.doFinal(xored_d);
        byte[] dkey = Arrays.copyOfRange(encrypted, 0, 32);
        System.out.println(bytesToHex(dkey));
        System.out.println(bytesToHex(Arrays.copyOfRange(dkey, 16, 32)));
        return dkey;
    }
    final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();

    public static String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }


    static byte[] xor16(byte[] ba, byte[] bb) {
        byte[] bout = new byte[ba.length];
        for (short j = 0; j < ba.length; j++) {
            bout[j] = (byte)(ba[j] ^ bb[j]);
        }
        return bout;
    }

    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte)((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
        }
        return data;
    }

    static byte[] shl(byte[] bin) // << 16 byte array
    {
        byte[] bout = new byte[16];
        for (short j = 0; j < 15; j++) // java b[0] is the highorder
        {
            int sot = ((bin[j + 1] & 0x80) >> 7);
            int sef = (bin[j] << 1) | sot;
            bout[j] = (byte) sef;
        }
        bout[15] = (byte)(bin[15] << 1);
        return bout;
    }
}