《GBT 15852.1-2008 信息技术 安全技术 消息鉴别码 第1部分:采用分组密码的机制》
《GBT 15852.1-2020 信息技术 安全技术 消息鉴别码 第1部分:采用分组密码的机制》
ISO 9797 Message Authentication Codes (MACs)
Part 1: Mechanisms using a block cipher
https://www.doc88.com/p-57347146035924.html
Part 2: Mechanisms using a dedicated hash-function
https://www.doc88.com/p-0159667006070.html
Part 3: Mechanisms using a universal hash-function
https://www.doc88.com/p-9734810928184.html
RFC4493: The AES-CMAC Algorithm
https://datatracker.ietf.org/doc/rfc4493
T. Iwata and K. Kurosawa. OMAC: One-Key CBC MAC
https://csrc.nist.gov/CSRC/media/Projects/Block-Cipher-Techniques/documents/BCM/proposed-modes/omac/omac-ad.pdf
NIST SP800-38A Recommendation for Block Cipher modes of Operation – Methods and Techniques
NIST SP800-38B Recommendation for Block Cipher Modes of Operation – the CMAC Mode for Authentication
https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38b.pdf
ANSI X3.92 Data Encryption Algorithm (DEA)
ANSI X3.106 Modems of DEA Operation
ANSI X4.16 American National Standard for financial services, financial transaction cards, magnetic stripe encoding
ANSI X9.8 Personal Identification Number (PIN) Management and Security
ANSI X9.9 Financial institution message authentication (wholesale)
ANSI X9.19 Financial Institution Retail Message Authentication (MAC)
ISO 7810 Identification cards – Physical characteristics
ISO 7811 Identification cards – Recording technique
ISO 7812 Identification cards – Identification of issuers
ISO 7813 Identification cards – Financial transaction cards
ISO 7816 Identification cards – Integrated circuit(s) cards with contacts
ISO 8583 Bank card originated messages – Interchange message specifications – Content for financial transactions
ISO 8731-1 Banking: Approved algorithms for message authentication
Part 1 – DEA
https://www.doc88.com/p-373487237594.html
Part 2 – Message Authentication algorithms
https://www.doc88.com/p-1426368720696.html
ISO 9807 Banking and releated financial service – Requirements for Message Authentication(Retail)
https://www.doc88.com/p-9992812642133.html
CMAC, Cipher-based MAC, 即基于AES对称加密算法的MAC,可参考ISO 9797-1
标准,其中详细定义了CMAC算法,其对应的国标为《GBT 15852.1》
。
最早的CMAC是基于DES的CBC模式构造的MAC,即CBC-MAC,可参考ANSI X9.9
、ANSI X9.19
。
然后 John Black 和 Phillip Rogaway 于2000年提出的避免CBC-MAC安全缺陷的XCBC模式(Extend Cipher Block Chaining Mode),作为CBC模式的扩展,用来构造MAC,即XCBC-MAC
。
再后来,Tetsu Iwata 和 Kaoru Kurosawa 基于XCBC-MAC提出了OMAC
,即One-Key CBC-MAC
,接着又精益求精第提出了OMAC1,前面的OMAC被重新命名为OMAC2,可参考其论文。
现在提到CMAC通常是指OMAC,而OMAC通常是指OMAC1,可参考NIST SP800-38B
标准,也可参考RFC4493
文档,其中的测试数据里使用了AES128/192/256算法和3DES算法。
基于《GBT 15852.1》
的算法1和填充1可以实现《GBT 36322》中的SDF_CalculateMAC
接口。
ISO 9797-1:1999
;2020版本对标ISO 9797-1:2011
。OMAC算法特点
100000000*
到完整分组,然后与key2相异或,然后再加密。实现
org.bouncycastle.crypto.macs.CMac
。对应到《GBT 15852.1-2020》的算法5。在使用时可直接通过java的 Mac
进行使用,算法名称是在相应的对称算法后面添加CMAC
。算法 | 密钥诱导 (可选;2种) | 消息填充 (4种) | 初始变换 (3种) | 最终迭代 (4种) | 输出变换 (3种) | 截断操作 (2种) |
---|---|---|---|---|---|---|
算法1: CBC-MAC | 1/2/3 | 1 | 1 | 1 | 1 | |
算法2: EMAC | 1(可能) | 1/2/3 | 1 | 1 | 2-key2 | 1 |
算法3: ANSI retail MAC | 1/2/3 | 1 | 1 | 3-key2 | 1 | |
算法4: MacDES | 1(可能) | 1/2/3 | 2-K1 | 1 | 2-key2 | 1 |
算法5: CMAC/OMAC1 | 2 | 4 | 1 | 3-(K1,K2) | 1 | 1 |
算法6: LMAC | 1(可能) | 1/2/3 | 1 | 2-K2 | 1 | 1 |
算法7: TrCBC | 4 | 1 | 1 | 1 | 2 | |
算法8: CBCR | 4 | 3 | 4 | 1 | 1 |
ParametersWithPadding类用来设置算法的各种参数。其中定义key1表示需要输入的原始密钥;key2表示在输出变换中使用的密钥,可能是在最开始设置的,也可能是通过密钥诱导生成的。
public class ParametersWithPadding implements CipherParameters {
int typeAlg; //算法类型:1~6
int typePad; //填充类型:1、2、3、4
int length; //仅 typePad=3 时有效,表示输入数据的总长度
int transformInit; //初始变换:1、2、3
int transformOut; //输出变换:1、2、3
int lastIteration; //最终迭代:1、2、3、4
int truncate; //截断操作:1、2
int keyInduce = 1; //密钥诱导方式:0、1;仅用于算法2中。
byte[] key1;
byte[] key2;
byte[] iv;
public ParametersWithPadding(
byte[] key1, byte[] key2,
int typeAlg, int typePad) {
this(key1, key2, typeAlg, typePad, 0);
}
public ParametersWithPadding(
byte[] key1, byte[] key2, byte[] iv,
int typeAlg, int typePad) {
this(key1, key2, iv, typeAlg, typePad, 0);
}
public ParametersWithPadding(
byte[] key1, byte[] key2,
int typeAlg, int typePad, int length) {
this(key1, key2, null, typeAlg, typePad, length);
}
public ParametersWithPadding(
byte[] key1, byte[] key2, byte[] iv,
int typeAlg, int typePad, int length) {
this.key1 = key1;
this.key2 = key2;
this.iv = iv;
this.typeAlg = typeAlg;
this.typePad = typePad;
this.length = length;
transformInit = 1;
transformOut = 1;
lastIteration = 1;
truncate = 1;
switch (typeAlg) {
case 2:
transformOut = 2;
break;
case 3:
transformOut = 3;
//算法3的两个密钥应独立选取;当key2为null时为避免错误,这里将其设置为key1,此时算法3和算法1一致
if (this.key2 == null)
this.key2 = key1;
break;
case 4:
transformInit = 2;
transformOut = 2;
break;
case 5:
lastIteration = 3;
break;
case 6:
lastIteration = 2;
break;
case 7:
truncate = 2;
break;
case 8:
transformInit = 3;
lastIteration = 4;
break;
}
}
/**
* 密钥诱导方式,仅用于算法2中。
*
* 默认为1。为了兼容2008标准或验证2008标准的附录测试,需要手动设置为0.
*
* @param keyInduce 0表示使用2008标准中的密钥诱导;在2008标准中定义,并在附录测试中用到;
* 1表示使用2020标准中的密钥诱导1。在2020标准中定义,但附录测试中提供了2个密钥,未用到。
*/
public void setKeyInduce(int keyInduce) {
this.keyInduce = keyInduce;
}
public CipherParameters getParameters() {
if (iv == null)
return new KeyParameter(key1);
else
return new ParametersWithIV(new KeyParameter(key1), iv);
}
在YCMac类中实现了标准中的8种算法。其中定义的K1,表示用在初始变换2或最终迭代3中的密钥;K2表示用在最终迭代2/3中的密钥。
2020标准中定义的MAC算法的8步操作在YCMac中的处理方法如下:
public class YCMac implements Mac {
private final byte[] mac; //mac值
private final int macSize; //所需的Mac大小
private final byte[] buf; //内部缓冲区
private int bufOff; //缓冲区当前数据长度
private byte[] K1; //用在初始变换2或最终迭代3中的密钥
private byte[] K2; //用在最终迭代2/3中的密钥
private final BlockCipher cipher; //底层对称算法对象
ParametersWithPadding parameters; //算法参数
public YCMac(BlockCipher cipher) {
this(cipher, cipher.getBlockSize() * 8);
}
public YCMac(BlockCipher cipher, int macSizeInBits) {
if ((macSizeInBits % 8) != 0) {
throw new IllegalArgumentException("MAC size must be multiple of 8");
}
if (macSizeInBits > (cipher.getBlockSize() * 8)) {
throw new IllegalArgumentException("MAC size must be less or equal to " + (cipher.getBlockSize() * 8));
}
this.cipher = new CBCBlockCipher(cipher);
this.macSize = macSizeInBits / 8;
mac = new byte[cipher.getBlockSize()];
buf = new byte[cipher.getBlockSize()];
bufOff = 0;
}
public String getAlgorithmName() {
return cipher.getAlgorithmName();
}
public void init(CipherParameters params) {
validate(params);
cipher.init(true, parameters.getParameters());
keyInduce(); //1.密钥诱导
reset();
//填充方式3:处理在开头添加的填充块
if (parameters.typePad == 3) {
byte[] temp = new byte[cipher.getBlockSize()];
byte[] bLen = Pack.intToBigEndian(parameters.length * 8);
System.arraycopy(bLen, 0, temp, temp.length - bLen.length, bLen.length);
update(temp, 0, temp.length);
}
}
void validate(CipherParameters params) {
if (params instanceof ParametersWithPadding)
parameters = (ParametersWithPadding) params;
else
throw new IllegalArgumentException("CMac mode only permits parameters type of ParametersWithPadding.");
}
public int getMacSize() {
return macSize;
}
public void update(byte in) {
if (bufOff == buf.length) {
cipher.processBlock(buf, 0, mac, 0);
bufOff = 0;
}
buf[bufOff++] = in;
}
public void update(byte[] in, int inOff, int len) {
if (len < 0) {
throw new IllegalArgumentException("Can't have a negative input length!");
}
int blockSize = cipher.getBlockSize();
int gapLen = blockSize - bufOff;
if (len > gapLen) {
System.arraycopy(in, inOff, buf, bufOff, gapLen);
initTransform(parameters.transformInit); //4.初始变换
bufOff = 0;
len -= gapLen;
inOff += gapLen;
//5.迭代应用分组密码
while (len > blockSize) {
cipher.processBlock(in, inOff, mac, 0);
len -= blockSize;
inOff += blockSize;
}
}
System.arraycopy(in, inOff, buf, bufOff, len);
bufOff += len;
}
public int doFinal(byte[] out, int outOff) {
int msgLen = bufOff;
paddingTransform(parameters.typePad); //2.消息填充。填充最后一个分组
lastIteration(parameters.lastIteration, msgLen); //6.最终迭代
outTransform(parameters.transformOut); //7.输出变换
truncate(parameters.truncate, out, outOff, msgLen); //8.截断操作
reset();
return macSize;
}
/**
* 密钥诱导。
*/
void keyInduce() {
KeyInduce keyInduce = new KeyInduce(cipher);
if (parameters.typeAlg == 2) {
//如果没有提供key2,则需要生成。
//为了兼容2008,通过keyInduce参数决定密钥生成方式。0表示使用2008标准中的密钥诱导;1表示使用2020标准中的密钥诱导1。
if (parameters.key2 == null) {
if(parameters.keyInduce==0)
parameters.key2 = keyInduce.induce0(parameters.key1);
else {
keyInduce.induce1(parameters.key1.length);
parameters.key1 = keyInduce.K1;
parameters.key2 = keyInduce.K2;
}
}
} else if (parameters.typeAlg == 4) {
//需要生成初始变换2的密钥K1:如果提供了key2,则用2008标准中的密钥诱导生成K1;
//如果没有提供key2,则需用密钥诱导1生成K1,以及要在输出变换2中使用的密钥key2
if (parameters.key2 != null)
K1 = keyInduce.induce0(parameters.key2);
else {
keyInduce.induce1(parameters.key1.length);
parameters.key2 = keyInduce.K1;
K1 = keyInduce.K2;
}
} else if (parameters.typeAlg == 5) {
//最终迭代3中的两个密钥用密钥诱导2生成;分别放在K1和K2中
keyInduce.induce2();
K1 = keyInduce.K1;
K2 = keyInduce.K2;
} else if (parameters.typeAlg == 6) {
//当只提供一个密钥时,需用密钥诱导1生成所需的两个密钥。用于最终迭代2的密钥放在K2中
if (parameters.key2 == null) {
keyInduce.induce1(parameters.key1.length);
parameters.key1 = keyInduce.K1;
K2 = keyInduce.K2; //用于最终迭代2,故不设置key2
cipher.init(true, parameters.getParameters()); //使用密钥诱导生成了key1,需要重新初始化cipher
} else
K2 = parameters.key2;
}
}
/**
* 填充。
*
* @param type 填充方式
*/
void paddingTransform(int type) {
int blockSize = cipher.getBlockSize();
if (type == 1 || type == 3) {
if (bufOff != blockSize)
new ZeroBytePadding().addPadding(buf, bufOff);
} else if (type == 2) {
if (bufOff == blockSize) {
cipher.processBlock(buf, 0, mac, 0);
bufOff = 0;
}
new ISO7816d4Padding().addPadding(buf, bufOff);
} else if (type == 4) {
if (bufOff != blockSize)
new ISO7816d4Padding().addPadding(buf, bufOff);
}
}
/**
* 初始变换。
*
* @param type 初始变换方式
*/
void initTransform(int type) {
cipher.processBlock(buf, 0, mac, 0);
if (type == 2) {
//初始变换2:使用子密钥加密后再使用原密钥进行后续加密
reset();
cipher.init(true, new KeyParameter(K1));
cipher.processBlock(mac, 0, mac, 0);
cipher.init(true, new ParametersWithIV(parameters.getParameters(), mac));
} else if (type == 3) {
cipher.reset();
byte[] zeroes = new byte[cipher.getBlockSize()];
cipher.processBlock(zeroes, 0, mac, 0);
xor(buf, mac);
cipher.reset();
cipher.processBlock(buf, 0, mac, 0);
}
}
/**
* 输出变换。
*
* @param type 输出变换方式
*/
void outTransform(int type) {
if (type == 2) {
//使用子密钥再加密一次
reset();
//这里需要使用全0的IV进行初始化,因为在算法4的“初始化变换”中使用了非0的IV,如果只是reset,则IV不是全0,会导致结果有误
cipher.init(true, new ParametersWithIV(new KeyParameter(parameters.key2), new byte[cipher.getBlockSize()]));
cipher.processBlock(mac, 0, mac, 0);
} else if (type == 3) {
//使用子密钥解密后再使用原密钥加密
cipher.init(false, new KeyParameter(parameters.key2));
cipher.processBlock(mac, 0, mac, 0);
reset();
cipher.init(true, parameters.getParameters());
cipher.processBlock(mac, 0, mac, 0);
}
}
/**
* 最终迭代。
*
* @param type 迭代类型
* @param msgLen 最后的消息长度
*/
void lastIteration(int type, int msgLen) {
if (type == 1)
cipher.processBlock(buf, 0, mac, 0);
if (type == 2) {
xor(mac, buf);
reset();
cipher.init(true, new ParametersWithIV(new KeyParameter(K2), new byte[cipher.getBlockSize()]));
cipher.processBlock(mac, 0, mac, 0);
} else if (type == 3) {
if (msgLen % cipher.getBlockSize() == 0)
xor(buf, K1);
else
xor(buf, K2);
cipher.processBlock(buf, 0, mac, 0);
} else if (type == 4) {
byte[] temp = new byte[cipher.getBlockSize()];
xor(mac, buf);
if (msgLen % cipher.getBlockSize() == 0)
shiftRight(mac, temp);
else
shiftLeft(mac, temp);
System.arraycopy(temp, 0, mac, 0, temp.length);
reset();
cipher.init(true, parameters.getParameters());
cipher.processBlock(mac, 0, mac, 0);
}
}
/**
* 截断操作。
*
* @param type 截断类型
* @param out 输出缓冲
* @param outOff 输出偏移
* @param msgLen 最后的消息长度
*/
void truncate(int type, byte[] out, int outOff, int msgLen) {
if (type == 2 && msgLen % cipher.getBlockSize() != 0)
System.arraycopy(mac, mac.length - macSize, out, outOff, macSize);
else
System.arraycopy(mac, 0, out, outOff, macSize);
}
public void reset() {
//clean the buffer.
Arrays.fill(buf, (byte) 0);
bufOff = 0;
//reset the underlying cipher.
cipher.reset();
}
static void xor(byte[] a, byte[] b) {
for (int i = 0; i < a.length; i++)
a[i] ^= b[i];
}
/**
* 循环左移。
*/
private static int shiftLeft(byte[] input, byte[] output) {
int i = input.length;
int bit = 0;
while (--i >= 0) {
int b = input[i] & 0xff;
output[i] = (byte) ((b << 1) | bit);
bit = (b >>> 7) & 1;
}
return bit;
}
/**
* 循环右移。
*/
private static int shiftRight(byte[] input, byte[] output) {
int i = 0;
int bit = 0;
while (i < input.length) {
int b = input[i] & 0xff;
output[i] = (byte) ((b >>> 1) | bit);
bit = (b << 7) & 0x80;
i++;
}
output[0] |= bit;
return bit;
}
/**
* 密钥诱导。
*/
static class KeyInduce {
final BlockCipher cipher;
byte[] K1;
byte[] K2;
KeyInduce(BlockCipher blockCipher) {
this.cipher = blockCipher;
}
/**
* 密钥诱导1.
*
* @param keyLength 分组密钥密钥长度k
*/
void induce1(int keyLength) {
int t = keyLength / cipher.getBlockSize();
K1 = genKey(0, t);
cipher.reset();
K2 = genKey(t, 2 * t);
}
/**
* 密钥诱导2.
*/
void induce2() {
byte[] zeroes = new byte[cipher.getBlockSize()];
byte[] L = new byte[zeroes.length];
cipher.processBlock(zeroes, 0, L, 0);
K1 = multx(L);
K2 = multx(K1);
}
/**
* 《GBT 15852.1-2008》密钥诱导。
*
* 用在算法2和算法4中。
*
* 过程:对K从第1个4比特组开始,每隔4比特交替取补和不变
*
* @param key 密钥
* @return 子密钥
*/
public byte[] induce0(byte[] key) {
byte[] result = key.clone();
for (int i = 0; i < result.length; i++)
result[i] = (byte) ((~result[i] & 0xF0) ^ (result[i] & 0x0F));
return result;
}
byte[] genKey(int start, int end) {
int blockSize = cipher.getBlockSize();
int len = end - start;
byte[] S = new byte[len * blockSize];
for (int i = start; i < end; i++) {
byte[] ct = new byte[blockSize];
byte[] temp = Pack.intToBigEndian(i + 1);
System.arraycopy(temp, 0, ct, ct.length - temp.length, temp.length);
cipher.processBlock(ct, 0, S, (i - start) * blockSize);
}
return Arrays.copyOfRange(S, (len - 1) * blockSize, S.length);
}
/**
* multx(CMac.doubleLu).
*
* @param in 比特串T
* @see CMac
*/
byte[] multx(byte[] in) {
byte[] ret = new byte[in.length];
int carry = shiftLeft(in, ret);
byte[] poly = lookupPoly(cipher.getBlockSize());
int mask = (-carry) & 0xff;
ret[in.length - 3] ^= poly[1] & mask;
ret[in.length - 2] ^= poly[2] & mask;
ret[in.length - 1] ^= poly[3] & mask;
return ret;
}
/**
* lookup.
*
* @see CMac
*/
static byte[] lookupPoly(int blockSizeLength) {
int xor = 0;
switch (blockSizeLength * 8) {
case 64:
xor = 0x1B;
break;
case 128:
xor = 0x87;
break;
}
return Pack.intToBigEndian(xor);
}
}
}
在2008标准中定义了一个密钥诱导,在2020标准中定义了另外两个不同的密钥诱导,即密钥诱导1和密钥诱导2。密钥诱导在YCMac中的keyInduce方法中按照不同的算法进行处理。
但对于算法2有点特殊,为了兼容2008标准及其附录测试,在ParametersWithPadding中定义了keyInduce参数表示密钥诱导方式(仅用于算法2中)。
算法1/3/7/8不需要密钥诱导,其他算法的密钥诱导参看下面的代码:
void keyInduce() {
KeyInduce keyInduce = new KeyInduce(cipher);
if (parameters.typeAlg == 2) {
//如果没有提供key2,则需要生成。
//为了兼容2008,通过keyInduce参数决定密钥生成方式。0表示使用2008标准中的密钥诱导;1表示使用2020标准中的密钥诱导1。
if (parameters.key2 == null) {
if(parameters.keyInduce==0)
parameters.key2 = keyInduce.induce0(parameters.key1);
else {
keyInduce.induce1(parameters.key1.length);
parameters.key1 = keyInduce.K1;
parameters.key2 = keyInduce.K2;
}
}
} else if (parameters.typeAlg == 4) {
//需要生成初始变换2的密钥K1:如果提供了key2,则用2008标准中的密钥诱导生成K1;
//如果没有提供key2,则需用密钥诱导1生成K1,以及要在输出变换2中使用的密钥key2
if (parameters.key2 != null)
K1 = keyInduce.induce0(parameters.key2);
else {
keyInduce.induce1(parameters.key1.length);
parameters.key2 = keyInduce.K1;
K1 = keyInduce.K2;
}
} else if (parameters.typeAlg == 5) {
//最终迭代3中的两个密钥用密钥诱导2生成;分别放在K1和K2中
keyInduce.induce2();
K1 = keyInduce.K1;
K2 = keyInduce.K2;
} else if (parameters.typeAlg == 6) {
//当只提供一个密钥时,需用密钥诱导1生成所需的两个密钥。用于最终迭代2的密钥放在K2中
if (parameters.key2 == null) {
keyInduce.induce1(parameters.key1.length);
parameters.key1 = keyInduce.K1;
K2 = keyInduce.K2; //用于最终迭代2,故不设置key2
cipher.init(true, parameters.getParameters()); //使用密钥诱导生成了key1,需要重新初始化cipher
} else
K2 = parameters.key2;
}
}
在YMacTest的 test_GBT15852_2020() 里对《GBT 15852.1-2020 信息技术 安全技术 消息鉴别码 第1部分:采用分组密码的机制》-附录B 进行了测试和验证。
CMAC测试: 《GBT 15852.1-2020 信息技术 安全技术 消息鉴别码 第1部分:采用分组密码的机制》-附录B
消息1:This is the test message for mac
Hex: 54686973206973207468652074657374206d65737361676520666f72206d6163
填充方式1:
D1 | 54 68 69 73 20 69 73 20 74 68 65 20 74 65 73 74
D2 | 20 6D 65 73 73 61 67 65 20 66 6F 72 20 6D 61 63
填充方式2:
D1 | 54 68 69 73 20 69 73 20 74 68 65 20 74 65 73 74
D2 | 20 6D 65 73 73 61 67 65 20 66 6F 72 20 6D 61 63
D3 | 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
填充方式3:
D1 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00
D2 | 54 68 69 73 20 69 73 20 74 68 65 20 74 65 73 74
D3 | 20 6D 65 73 73 61 67 65 20 66 6F 72 20 6D 61 63
填充方式4:
D1 | 54 68 69 73 20 69 73 20 74 68 65 20 74 65 73 74
D2 | 20 6D 65 73 73 61 67 65 20 66 6F 72 20 6D 61 63
消息2:This is the test message
hex: 54686973206973207468652074657374206d65737361676520
填充方式1:
D1 | 54 68 69 73 20 69 73 20 74 68 65 20 74 65 73 74
D2 | 20 6D 65 73 73 61 67 65 20 00 00 00 00 00 00 00
填充方式2:
D1 | 54 68 69 73 20 69 73 20 74 68 65 20 74 65 73 74
D2 | 20 6D 65 73 73 61 67 65 20 80 00 00 00 00 00 00
填充方式3:
D1 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C8
D2 | 54 68 69 73 20 69 73 20 74 68 65 20 74 65 73 74
D3 | 20 6D 65 73 73 61 67 65 20 00 00 00 00 00 00 00
填充方式4:
D1 | 54 68 69 73 20 69 73 20 74 68 65 20 74 65 73 74
D2 | 20 6D 65 73 73 61 67 65 20 80 00 00 00 00 00 00
mac算法测试:
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法1:
填充方法1:
MAC= 16 E0 29 04 EF B7 65 B7
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法1:
填充方法2:
MAC= 4B 65 53 AF 3C 4E 27 44
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法1:
填充方法3:
MAC= 71 AF 7E 45 53 40 4C BC
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法1:
填充方法1:
MAC= BA 89 E4 5F E8 AB F2 42
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法1:
填充方法2:
MAC= 42 1A D1 69 0A A1 52 E2
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法1:
填充方法3:
MAC= 6A 4A 86 F5 B5 E4 68 DA
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法2:
填充方法1:
MAC= 1E 9A 71 D3 BC 92 DF A7
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法2:
填充方法2:
MAC= E4 23 E3 55 99 AF D9 48
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法2:
填充方法3:
MAC= 40 03 BA 1B 6A DC 53 A8
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法2:
填充方法1:
MAC= 4E C3 C7 FA CF AA C6 07
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法2:
填充方法2:
MAC= F0 26 25 CE AD 00 8D 4E
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法2:
填充方法3:
MAC= FF D5 F1 F2 E5 ED A5 CB
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法3:
填充方法1:
MAC= 27 63 21 1B 2B CA F7 19
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法3:
填充方法2:
MAC= 51 E9 92 8C 22 38 33 0C
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法3:
填充方法3:
MAC= 7C D4 8C 42 42 E4 55 75
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法3:
填充方法1:
MAC= E3 2D 99 A6 89 C0 52 59
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法3:
填充方法2:
MAC= 19 72 47 22 9C E9 D7 B6
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法3:
填充方法3:
MAC= 3C 43 0F 1E A4 3B 54 0C
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法4:
填充方法1:
MAC= DD 10 52 A7 AF E8 99 9B
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法4:
填充方法2:
MAC= 7E 1A 9A 5E 0E F0 94 7F
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法4:
填充方法3:
MAC= 28 A7 0D 6B CC F7 44 22
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法4:
填充方法1:
MAC= AA 9D B3 D9 65 1F 86 2B
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法4:
填充方法2:
MAC= 94 94 76 D3 5F 17 26 1E
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法4:
填充方法3:
MAC= C9 D3 4E 16 C4 9A B6 43
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法5:
填充方法4:
MAC= 69 2C 43 71 00 F3 B5 EE
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法5:
填充方法4:
MAC= 47 38 A6 C7 60 B2 80 FC
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法6:
填充方法1:
MAC= B3 8A 96 19 5B AA 61 FC
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法6:
填充方法2:
MAC= A0 C4 65 EE 58 96 97 2F
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法6:
填充方法3:
MAC= 43 05 0D 51 C6 56 AE 60
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法6:
填充方法1:
MAC= 8C F6 E6 43 14 FE F4 17
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法6:
填充方法2:
MAC= 60 DD 95 5E D0 CA 3D 7A
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法6:
填充方法3:
MAC= 61 E0 00 49 E2 69 62 A3
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法7:
填充方法4:
MAC= 16 E0 29 04 EF B7 65 B7
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法7:
填充方法4:
MAC= 84 6F A2 A5 D8 34 45 A9
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520666f72206d6163
MAC算法8:
填充方法4:
MAC= E4 0E D7 9C 31 49 A1 C9
--------------------------------------------------
消息: 54686973206973207468652074657374206d65737361676520
MAC算法8:
填充方法4:
MAC= A9 9D 13 01 3E 89 2E E2
以下是2008标准的相关内容,现可忽略。
MAC算法(6种) | 数据填充(3种) | 初始变换(2种) | 输出变换(3种) | 输入密钥1 | 输入密钥2 | 生成诱导密钥 |
---|---|---|---|---|---|---|
算法1 | 任选 | 1 | 1 | √ | × | × |
算法2 | 任选 | 1 | 2 | √ | × | √ |
算法3 | 任选 | 1 | 3 | √ | √ | × |
算法4 | 任选 | 2 | 2 | √ | √ | √ |
算法5 | 任选 | √ | √ | |||
算法6 | 任选 | √ | √ | √ |
其中,算法5
是两个算法1
的MAC相异或;算法6
是两个算法4
的MAC相异或 。
算法1,填充1
的支持update
模式的CMAC方法。SDF_CalculateMAC
接口,支持IV的输入输出,支持多包处理,可以用此方法来实现。/**
* 《GBT 15852.1-2008 信息技术 安全技术 消息鉴别码 第1部分:采用分组密码的机制》算法1~4。
*
* @author YaoYuan
* @since 2022/11/2
*/
public class CMac14 implements Mac {
private byte[] mac; //mac值
private byte[] buf; //内部缓冲区
private int bufOff; //缓冲区当前数据长度
private BlockCipher cipher; //底层对称算法对象
private int macSize; //所需的Mac大小
private byte[] subKey; //子密钥
ParametersWithPadding parameters; //算法参数
private boolean needTransformInit = false; //是否需要初始变换
public CMac14(BlockCipher cipher) {
this(cipher, cipher.getBlockSize() * 8);
}
public CMac14(BlockCipher cipher, int macSizeInBits) {
if ((macSizeInBits % 8) != 0) {
throw new IllegalArgumentException("MAC size must be multiple of 8");
}
if (macSizeInBits > (cipher.getBlockSize() * 8)) {
throw new IllegalArgumentException("MAC size must be less or equal to "+ (cipher.getBlockSize() * 8));
}
this.cipher = new CBCBlockCipher(cipher);
this.macSize = macSizeInBits / 8;
mac = new byte[cipher.getBlockSize()];
buf = new byte[cipher.getBlockSize()];
bufOff = 0;
}
public String getAlgorithmName() {
return cipher.getAlgorithmName();
}
public void init(CipherParameters params) {
validate(params);
cipher.init(true, parameters.getParameters());
//设置子密钥
if (parameters.typeAlg == 2)
subKey = YCMac.genKey(parameters.key1);
else if (parameters.typeAlg == 3)
subKey = parameters.key2.clone();
else if (parameters.typeAlg == 4)
subKey = YCMac.genKey(parameters.key2);
//只有在初始变换2中才需要额外的初始变换操作
if (parameters.transformInit == 2)
needTransformInit = true;
reset();
//填充方式3:处理在开头添加的填充块
if (parameters.typePad == 3) {
byte[] temp = new byte[cipher.getBlockSize()];
byte[] bLen = Pack.intToBigEndian(parameters.length * 8);
System.arraycopy(bLen, 0, temp, temp.length - bLen.length, bLen.length);
update(temp, 0, temp.length);
}
}
void validate(CipherParameters params) {
if (params instanceof ParametersWithPadding)
parameters = (ParametersWithPadding) params;
else
throw new IllegalArgumentException("CMac mode only permits parameters type of ParametersWithPadding.");
}
public int getMacSize() {
return macSize;
}
public void update(byte in) {
if (bufOff == buf.length) {
cipher.processBlock(buf, 0, mac, 0);
bufOff = 0;
}
buf[bufOff++] = in;
}
public void update(byte[] in, int inOff, int len) {
if (len < 0) {
throw new IllegalArgumentException("Can't have a negative input length!");
}
int blockSize = cipher.getBlockSize();
int gapLen = blockSize - bufOff;
if (len > gapLen) {
System.arraycopy(in, inOff, buf, bufOff, gapLen);
cipher.processBlock(buf, 0, mac, 0);
//初始变换
if (needTransformInit) {
needTransformInit = false;
initTransform(parameters.transformInit);
//重设算法4的密钥
if (parameters.typeAlg == 4)
subKey = parameters.key2.clone();
}
bufOff = 0;
len -= gapLen;
inOff += gapLen;
while (len > blockSize) {
cipher.processBlock(in, inOff, mac, 0);
len -= blockSize;
inOff += blockSize;
}
}
System.arraycopy(in, inOff, buf, bufOff, len);
bufOff += len;
}
public int doFinal(byte[] out, int outOff) {
//最后一个分组的填充
paddingTransform(parameters.typePad);
cipher.processBlock(buf, 0, mac, 0);
//输出变换
outTransform(parameters.transformOut);
System.arraycopy(mac, 0, out, outOff, macSize);
reset();
return macSize;
}
/**
* 填充。
*
* @param type 填充方式
*/
void paddingTransform(int type) {
int blockSize = cipher.getBlockSize();
if (type == 1 || type == 3) {
if (bufOff != blockSize)
new ZeroBytePadding().addPadding(buf, bufOff);
} else {
if (bufOff == blockSize) {
cipher.processBlock(buf, 0, mac, 0);
bufOff = 0;
}
new ISO7816d4Padding().addPadding(buf, bufOff);
}
}
/**
* 初始变换。
*
* @param type 变换方式
*/
void initTransform(int type) {
//只有初始变换2才需要处理:使用子密钥加密后再使用原密钥进行后续加密
if (type == 2) {
reset();
cipher.init(true, new KeyParameter(subKey));
cipher.processBlock(mac, 0, mac, 0);
cipher.init(true, new ParametersWithIV(parameters.getParameters(), mac));
}
}
/**
* 输出变换。
*
* @param type 变换方式
*/
void outTransform(int type) {
if (type == 2) {
//使用子密钥再加密一次
reset();
//这里需要使用全0的IV进行初始化,因为在算法4的“初始化变换”中使用了非0的IV,如果只是reset,则IV不是全0,会导致结果有误
cipher.init(true, new ParametersWithIV(new KeyParameter(subKey), new byte[cipher.getBlockSize()]));
cipher.processBlock(mac, 0, mac, 0);
} else if (type == 3) {
//先解密再加密
cipher.init(false, new KeyParameter(subKey));
cipher.processBlock(mac, 0, mac, 0);
reset();
cipher.init(true, parameters.getParameters());
cipher.processBlock(mac, 0, mac, 0);
}
}
public void reset() {
//clean the buffer.
Arrays.fill(buf, (byte) 0);
bufOff = 0;
//reset the underlying cipher.
cipher.reset();
}
}
在例子中测试了:
IDEA中的测试输出:
CMAC测试: 《GBT 15852.1-2008 信息技术 安全技术 消息鉴别码 第1部分:采用分组密码的机制》-附录A
消息1:Now is the time for all
Hex: 4e6f77206973207468652074696d6520666f7220616c6c20
填充方式1:
D1 | 4E 6F 77 20 69 73 20 74
D2 | 68 65 20 74 69 6D 65 20
D3 | 66 6F 72 20 61 6C 6C 20
填充方式2:
D1 | 4E 6F 77 20 69 73 20 74
D2 | 68 65 20 74 69 6D 65 20
D3 | 66 6F 72 20 61 6C 6C 20
D4 | 80 00 00 00 00 00 00 00
填充方式3:
D1 | 00 00 00 00 00 00 00 C0
D2 | 4E 6F 77 20 69 73 20 74
D3 | 68 65 20 74 69 6D 65 20
D4 | 66 6F 72 20 61 6C 6C 20
消息2:Now is the time for it
hex: 4e6f77206973207468652074696d6520666f72206974
填充方式1:
D1 | 4E 6F 77 20 69 73 20 74
D2 | 68 65 20 74 69 6D 65 20
D3 | 66 6F 72 20 69 74 00 00
填充方式2:
D1 | 4E 6F 77 20 69 73 20 74
D2 | 68 65 20 74 69 6D 65 20
D3 | 66 6F 72 20 69 74 80 00
填充方式3:
D1 | 00 00 00 00 00 00 00 B0
D2 | 4E 6F 77 20 69 73 20 74
D3 | 68 65 20 74 69 6D 65 20
D4 | 66 6F 72 20 69 74 00 00
mac算法测试:
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法1:
填充方法1:
MAC= 70 A3 06 40
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法1:
填充方法2:
MAC= 10 E1 F0 F1
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法1:
填充方法3:
MAC= 2C 58 FB 8F
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法1:
填充方法1:
MAC= E4 5B 3A D2
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法1:
填充方法2:
MAC= A9 24 C7 21
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法1:
填充方法3:
MAC= B1 EC D6 FC
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法2:
填充方法1:
MAC= 10 F9 BC 67
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法2:
填充方法2:
MAC= BE 7C 2A B7
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法2:
填充方法3:
MAC= 8E FC 8B C7
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法2:
填充方法1:
MAC= 21 5E 9C E6
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法2:
填充方法2:
MAC= 17 36 AC 1A
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法2:
填充方法3:
MAC= 05 38 26 96
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法3:
填充方法1:
MAC= A1 C7 2E 74
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法3:
填充方法2:
MAC= E9 08 62 30
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法3:
填充方法3:
MAC= AB 05 94 63
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法3:
填充方法1:
MAC= 2E 2B 14 28
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法3:
填充方法2:
MAC= 5A 69 2C E6
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法3:
填充方法3:
MAC= C5 9F 7E ED
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法4:
填充方法1:
MAC= AD 35 02 B7
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法4:
填充方法2:
MAC= 61 C3 33 E3
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法4:
填充方法3:
MAC= 95 2A F8 38
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法4:
填充方法1:
MAC= 05 F1 08 4C
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法4:
填充方法2:
MAC= A1 BC 09 31
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法4:
填充方法3:
MAC= AF DE E0 F9
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法5:
填充方法1:
MAC= F4 E4 02 B6 B7 2C 13 17
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法5:
填充方法2:
MAC= 70 F0 5E C9 E4 F7 2F 99
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法5:
填充方法3:
MAC= D6 1F 51 F2 EA 2A 2D 63
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法5:
填充方法1:
MAC= 0F 24 BD A4 AC 22 0F 4F
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法5:
填充方法2:
MAC= E0 04 13 41 9A FC 16 0B
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法5:
填充方法3:
MAC= DD DF 5E D3 0F 18 EB FC
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法6:
填充方法1:
MAC= 57 7E F2 21 18 CE 5D BA
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法6:
填充方法2:
MAC= 60 74 60 B8 D8 C0 FD FA
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f7220616c6c20
MAC算法6:
填充方法3:
MAC= FD 3D BB 6E F1 65 07 54
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法6:
填充方法1:
MAC= 10 F7 47 D1 4F 72 C2 29
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法6:
填充方法2:
MAC= B2 9B 9A 76 DD 1C 39 12
--------------------------------------------------
消息: 4e6f77206973207468652074696d6520666f72206974
MAC算法6:
填充方法3:
MAC= F6 45 FB 7D 4D 4A 42 B4
com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 org.yy.mac.YMacTest,test_omac1
omac SP800-38B verify:
====================================================================
algorithm : AES128
key: 2b7e151628aed2a6abf7158809cf4f3c
msg:
mac: bb1d6929e95937287fa37d129b756746
====================================================================
algorithm : AES128
key: 2b7e151628aed2a6abf7158809cf4f3c
msg: 6bc1bee22e409f96e93d7e117393172a
mac: 070a16b46b4d4144f79bdd9dd04a287c
====================================================================
algorithm : AES128
key: 2b7e151628aed2a6abf7158809cf4f3c
msg: 6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411
mac: dfa66747de9ae63030ca32611497c827
====================================================================
algorithm : AES128
key: 2b7e151628aed2a6abf7158809cf4f3c
msg: 6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710
mac: 51f0bebf7e3b9d92fc49741779363cfe
====================================================================
algorithm : AES192
key: 8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b
msg:
mac: d17ddf46adaacde531cac483de7a9367
====================================================================
algorithm : AES192
key: 8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b
msg: 6bc1bee22e409f96e93d7e117393172a
mac: 9e99a7bf31e710900662f65e617c5184
====================================================================
algorithm : AES192
key: 8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b
msg: 6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411
mac: 8a1de5be2eb31aad089a82e6ee908b0e
====================================================================
algorithm : AES192
key: 8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b
msg: 6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710
mac: a1d5df0eed790f794d77589659f39a11
====================================================================
algorithm : AES256
key: 603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4
msg:
mac: 028962f61b7bf89efc6b551f4667d983
====================================================================
algorithm : AES256
key: 603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4
msg: 6bc1bee22e409f96e93d7e117393172a
mac: 28a7023f452e8f82bd4bf28d8c37c35c
====================================================================
algorithm : AES256
key: 603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4
msg: 6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411
mac: aaf3d8f1de5640c232f5b169b9c911e6
====================================================================
algorithm : AES256
key: 603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4
msg: 6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710
mac: e1992190549f6ed5696a2c056c315410
====================================================================
algorithm : DESede3
key: 8aa83bf8cbda10620bc1bf19fbb6cd58bc313d4a371ca8b5
msg:
mac: b7a688e122ffaf95
====================================================================
algorithm : DESede3
key: 8aa83bf8cbda10620bc1bf19fbb6cd58bc313d4a371ca8b5
msg: 6bc1bee22e409f96
mac: 8e8f293136283797
====================================================================
algorithm : DESede3
key: 8aa83bf8cbda10620bc1bf19fbb6cd58bc313d4a371ca8b5
msg: 6bc1bee22e409f96e93d7e117393172aae2d8a57
mac: 743ddbe0ce2dc2ed
====================================================================
algorithm : DESede3
key: 8aa83bf8cbda10620bc1bf19fbb6cd58bc313d4a371ca8b5
msg: 6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51
mac: 33e6b1092400eae5
====================================================================
algorithm : DESede
key: 4cf15134a2850dd58a3d10ba80570d38
msg:
mac: bd2ebf9a3ba00361
====================================================================
algorithm : DESede
key: 4cf15134a2850dd58a3d10ba80570d38
msg: 6bc1bee22e409f96
mac: 4ff2ab813c53ce83
====================================================================
algorithm : DESede
key: 4cf15134a2850dd58a3d10ba80570d38
msg: 6bc1bee22e409f96e93d7e117393172aae2d8a57
mac: 62dd1b471902bd4e
====================================================================
algorithm : DESede
key: 4cf15134a2850dd58a3d10ba80570d38
msg: 6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51
mac: 31b1e431dabc4eb8