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;
}
}