一般需要分为三个步骤:
算出L的值,然后算出K1,K2的值,可以对比AES在线加密工具作为对比
public static byte[] Aes_Cmac01(byte[] key, byte[] data) {
// 子密钥生成
// 步骤1,将具有密钥K的AES-128应用于全零输入块。
byte[] L = aesEncryptNoPadding(key, new byte[16], new byte[16]);
Log.i(TAG, "configUUIDValue L: " + ByteUtils.bytes2HexStr(L));
// 步骤2,通过以下操作得出K1:
//如果L的最高有效位等于0,则K1是L的左移1位。
byte[] FirstSubkey = Rol(L);
if ((L[0] & 0x80) == 0x80) {
// 否则,K1是const_Rb的异或和L左移1位。
FirstSubkey[15] ^= 0x87;
}
// Log.i(TAG, "configUUIDValue K1: "+ ByteUtils.bytes2HexStr(FirstSubkey));
// FirstSubkey = ByteUtils.hexStr2Bytes("AC362C7FCCE2BD996153C64B7D39A82A");
Log.i(TAG, "configUUIDValue K1: " + ByteUtils.bytes2HexStr(FirstSubkey));
// 步骤3,通过以下操作得出K2:
//如果K1的最高有效位等于0,则K2是K1左移1位
byte[] SecondSubkey = Rol(FirstSubkey);
if ((FirstSubkey[0] & 0x80) == 0x80) {
// 否则,K2是const_Rb的异或,且K1左移1位
SecondSubkey[15] ^= 0x87;
}
// Log.i(TAG, "configUUIDValue K2: "+ ByteUtils.bytes2HexStr(SecondSubkey));
// SecondSubkey = ByteUtils.hexStr2Bytes("586C58FF99C57B32C2A78C96FA7350D3");
Log.i(TAG, "configUUIDValue K2: " + ByteUtils.bytes2HexStr(SecondSubkey));
Log.i(TAG, "configUUIDValue data: " + ByteUtils.bytes2HexStr(data));
// MAC 计算
if (((data.length != 0) && (data.length % 16 == 0)) == true) {
//如果输入消息块的大小等于块大小(128位)
// 最后一个块在处理之前应与K1异或
for (int j = 0; j < FirstSubkey.length; j++) {
data[data.length - 16 + j] ^= FirstSubkey[j];
}
} else {
// 否则,最后一个块应填充10 ^ i
byte[] padding = new byte[16 - data.length % 16];
padding[0] = (byte) 0x80;
byte[] newData = new byte[data.length + padding.length];
System.arraycopy(data, 0, newData, 0, data.length);
System.arraycopy(padding, 0, newData, data.length, padding.length);
// data = data.Concat(padding.AsEnumerable()).ToArray();
// 并与K2进行异或运算
for (int j = 0; j < SecondSubkey.length; j++) {
newData[newData.length - 16 + j] ^= SecondSubkey[j];
}
data = newData;
}
Log.i(TAG, "configUUIDValue data1: " + ByteUtils.bytes2HexStr(data));
// 先前处理的结果将是最后一次加密的输入。
byte[] encResult = aesEncryptNoPadding(key, new byte[16], data);
// 先前处理的结果将是最后一次加密的输入。
byte[] HashValue = new byte[16];
System.arraycopy(encResult, encResult.length - HashValue.length, HashValue, 0, HashValue.length);
Log.i(TAG, "configUUIDValue data HashValue: " + ByteUtils.bytes2HexStr(HashValue));
return HashValue;
}
private static byte[] Rol(byte[] b) {
byte[] output = new byte[b.length];
byte overflow = 0;
for (int i = b.length - 1; i >= 0; i--) {
output[i] = (byte) (b[i] << 1);
output[i] |= overflow;
if ((b[i] & 0x80) > 0) {
overflow = 1;
} else {
overflow = 0;
}
}
return output;
}
public static byte[] aesEncryptNoPadding(byte[] keys, byte[] iv, byte[] data) {
try {
//1.根据字节数组生成AES密钥
SecretKey key = new SecretKeySpec(keys, "AES");
//2.根据指定算法AES自成密码器 "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
//3.CBC模式需要向量vi
IvParameterSpec ivps = new IvParameterSpec(iv);
//4.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.ENCRYPT_MODE, key, ivps);
//5.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
byte[] byte_encode = data;
//6.根据密码器的初始化方式--加密:将数据加密
byte[] byte_AES = cipher.doFinal(byte_encode);
Log.i(TAG, "aesEncryptNoPadding: keys:" + ByteUtils.bytes2HexStr(keys));
Log.i(TAG, "aesEncryptNoPadding: 加密前:"+ByteUtils.bytes2HexStr(data));
Log.i(TAG, "aesEncryptNoPadding: 加密后:"+ByteUtils.bytes2HexStr(byte_AES));
//7.返回
return byte_AES;
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
return null;
}
public static byte[] aesEncryptPadding(byte[] keys, byte[] iv, byte[] data) {
try {
//1.根据字节数组生成AES密钥
SecretKey key=new SecretKeySpec(keys, "AES");
//2.根据指定算法AES自成密码器 "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
//3.CBC模式需要向量vi
IvParameterSpec ivps = new IvParameterSpec(iv);
//4.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.ENCRYPT_MODE, key,ivps);
//5.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
byte [] byte_encode=data;
//6.根据密码器的初始化方式--加密:将数据加密
byte [] byte_AES=cipher.doFinal(byte_encode);
//7.返回
return byte_AES;
}catch (GeneralSecurityException e) {
e.printStackTrace();
}
return null;
}
public static byte[] aesDecryptPadding(byte[] keys, byte[] iv, byte[] data) {
try {
//1.根据字节数组生成AES密钥
SecretKey key=new SecretKeySpec(keys, "AES");
//2.根据指定算法AES自成密码器 "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
//3.CBC模式需要向量vi
IvParameterSpec ivps = new IvParameterSpec(iv);
//4.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.DECRYPT_MODE, key,ivps);
//5.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
byte [] byte_encode=data;
//6.根据密码器的初始化方式--加密:将数据加密
byte [] byte_AES=cipher.doFinal(byte_encode);
//7.返回
return byte_AES;
}catch (GeneralSecurityException e) {
e.printStackTrace();
}
return null;
}
public static byte[] aesDecryptNoPadding(byte[] keys, byte[] iv, byte[] data) {
try {
//1.根据字节数组生成AES密钥
SecretKey key=new SecretKeySpec(keys, "AES");
//2.根据指定算法AES自成密码器 "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
//3.CBC模式需要向量vi
IvParameterSpec ivps = new IvParameterSpec(iv);
//4.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.DECRYPT_MODE, key,ivps);
//5.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
byte [] byte_encode=data;
//6.根据密码器的初始化方式--加密:将数据加密
byte [] byte_AES=cipher.doFinal(byte_encode);
//7.返回
return byte_AES;
}catch (GeneralSecurityException e) {
e.printStackTrace();
}
return null;
}



