Now that 3DES is phasing out, a new signature is need to replace the veteran X9.19. So here comes the brand new (from 2006!) AES CMAC signature. It uses AES instead of 3DES, but externally it uses the same 128bit long keys. I've programmed, with help from StackOverflow, a version in C#, and another version using the BouncyCastle library. Both ways return the same result, and I don't know which one performs better. The program is easy to understand, the only difficult step lays in the subkey generation. I've cut&pasted the RFC part that explains it to be sure I've done the correct thing.
byte[] AESEncrypt(byte[] key, byte[] iv, byte[] data)
{
using (MemoryStream ms = new MemoryStream())
{
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
cs.Write(data, 0, data.Length);
cs.FlushFinalBlock();
return ms.ToArray();
}
}
}
byte[] Rol(byte[] b)
{
byte[] r = new byte[b.Length];
byte carry = 0;
for (int i = b.Length - 1; i >= 0; i--)
{
ushort u = (ushort)(b[i] << 1);
r[i] = (byte)((u & 0xff) + carry);
carry = (byte)((u & 0xff00) >> 8);
}
return r;
}
byte[] AESCMAC(byte[] key, byte[] data)
{
byte[] L = AESEncrypt(key, new byte[16], new byte[16]);
byte[] FirstSubkey = Rol(L);
if ((L[0] & 0x80) == 0x80)
FirstSubkey[15] ^= 0x87;
byte[] SecondSubkey = Rol(FirstSubkey);
if ((FirstSubkey[0] & 0x80) == 0x80)
SecondSubkey[15] ^= 0x87;
if (((data.Length != 0) && (data.Length % 16 == 0)) == true)
{
for (int j = 0; j < FirstSubkey.Length; j++)
data[data.Length - 16 + j] ^= FirstSubkey[j];
}
else
{
data = AddPaddingAES(data);
for (int j = 0; j < 16; j++) data[data.Length - 16 + j] ^= SecondSubkey[j];
}
byte[] encResult = AESEncrypt(key, new byte[16], data);
byte[] HashValue = new byte[16];
Array.Copy(encResult, encResult.Length - HashValue.Length, HashValue, 0, HashValue.Length);
return HashValue;
}
All that code from above, that seems very complex, can be reduced to a few lines using BouncyCastle. But you know, in the code lays the truth, and with the library you don't see it...
Org.BouncyCastle.Crypto.Engines.AesEngine aes = new Org.BouncyCastle.Crypto.Engines.AesEngine();
Org.BouncyCastle.Crypto.Macs.CMac cMac = new Org.BouncyCastle.Crypto.Macs.CMac(aes, 64);
Org.BouncyCastle.Crypto.Parameters.KeyParameter kp = new Org.BouncyCastle.Crypto.Parameters.KeyParameter(key);
cMac.Init(kp);
int MacLenght = cMac.GetMacSize();
byte[] output = new byte[MacLenght];
cMac.BlockUpdate(data, 0, data.Length);
cMac.DoFinal(output, 0);
txtBase64.Text = ConvertByteArrayToHexString(output);
There are some examples in a RFC.
And that's all, another signature done! |