赞
踩
公司有个lora项目,要用到Lora-ns,虽然有其他公司现成的解决方案,但是需要有我们自己的个性化逻辑,因此需要重新手写NS的相关代码,根据semtech公司制定的协议标准编写了C#版本的lora-ns。
其中里面有个AES128-CMAC在网上比较少,我也只在StackOverflow上找到了对应的算法逻辑,如下:
/// <summary> /// 获取cmac /// </summary> /// <param name="key"></param> /// <param name="data"></param> /// <returns></returns> public static byte[] Aes_Cmac(byte[] key, byte[] data) { // SubKey generation // step 1, AES-128 with key K is applied to an all-zero input block. byte[] L = AesEncrypt(key, new byte[16], new byte[16]); // step 2, K1 is derived through the following operation: byte[] FirstSubkey = Rol(L); //If the most significant bit of L is equal to 0, K1 is the left-shift of L by 1 bit. if ((L[0] & 0x80) == 0x80) FirstSubkey[15] ^= 0x87; // Otherwise, K1 is the exclusive-OR of const_Rb and the left-shift of L by 1 bit. // step 3, K2 is derived through the following operation: byte[] SecondSubkey = Rol(FirstSubkey); // If the most significant bit of K1 is equal to 0, K2 is the left-shift of K1 by 1 bit. if ((FirstSubkey[0] & 0x80) == 0x80) SecondSubkey[15] ^= 0x87; // Otherwise, K2 is the exclusive-OR of const_Rb and the left-shift of K1 by 1 bit. // MAC computing if (((data.Length != 0) && (data.Length % 16 == 0)) == true) { // If the size of the input message block is equal to a positive multiple of the block size (namely, 128 bits), // the last block shall be exclusive-OR'ed with K1 before processing for (int j = 0; j < FirstSubkey.Length; j++) data[data.Length - 16 + j] ^= FirstSubkey[j]; } else { // Otherwise, the last block shall be padded with 10^i byte[] padding = new byte[16 - data.Length % 16]; padding[0] = 0x80; data = data.Concat<byte>(padding.AsEnumerable()).ToArray(); // and exclusive-OR'ed with K2 for (int j = 0; j < SecondSubkey.Length; j++) data[data.Length - 16 + j] ^= SecondSubkey[j]; } // The result of the previous process will be the input of the last encryption. 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; } static 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; } /// <summary> /// AES加密 /// </summary> /// <param name="clearTxt"></param> /// <returns></returns> public static byte[] AesEncrypt(byte[] keys, byte[] iv, byte[] data) { using (RijndaelManaged cipher = new RijndaelManaged()) { cipher.Mode = CipherMode.CBC; cipher.Padding = PaddingMode.None; cipher.Key = keys; cipher.IV = iv; using (ICryptoTransform encryptor = cipher.CreateEncryptor()) { using (MemoryStream ms = new MemoryStream()) { using (CryptoStream writer = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) { writer.Write(data, 0, data.Length); writer.FlushFinalBlock(); return ms.ToArray(); } } } } }
本人对算法这块了解甚少,所以也不清楚代码内容的这些操作是什么具体含义,不过测试了数据该代码是可以通过,并且得出对应的MIC校验码。
同时,我也根据LoraWan的C源码找到了对应的camc代码,根据这个逻辑也写了个C#版本的cmac代码。如下:
#region 按照C-LoraWan协议编写C# cmac校验 public struct AesCmacCtx { public byte[] X; public byte[] M_last; public int M_n; } /// <summary> /// 按照C-LoraWan协议编写C# cmac校验 /// </summary> /// <param name="mixBxBuffer">数据载荷校验B0,否则为null</param> /// <param name="buffer">待校验内容</param> /// <param name="bufferSize">待校验内容长度</param> /// <param name="key">待加密的秘钥</param> public static byte[] ComputCmac(byte[] mixBxBuffer, byte[] buffer, int bufferSize, byte[] key) { AesCmacCtx ctx = new AesCmacCtx(); AES_CMAC_Init(ref ctx); //*-*micBxBuffer if (mixBxBuffer != null && mixBxBuffer.Length > 0) { AES_CMAC_Update(ref ctx, mixBxBuffer, key, 16); } AES_CMAC_Update(ref ctx, buffer, key, bufferSize); AES_CMAC_Final(ref ctx, key, out byte[] Cmac); return Cmac; //string cmac; //foreach (var item in Cmac) //{ // Console.WriteLine(item.ToString("X2")); //} //cmac = (Cmac[0] << 24 | Cmac[1] << 16 | Cmac[2] << 8 | Cmac[3]).ToString("X8"); } public static void AES_CMAC_Init(ref AesCmacCtx ctx) { ctx.X = new byte[16]; ctx.M_n = 0; ctx.M_last = new byte[16]; } public static void LSHIFT(byte[] v, ref byte[] r) { for (int i = 0; i < 15; i++) { r[i] = (byte)((v[i] << 1) | (v[i + 1] >> 7)); } r[15] = (byte)(v[15] << 1); } public static void XOR(byte[] v, ref byte[] r) { for (int i = 0; i < 16; i++) { r[i] = (byte)(r[i] ^ v[i]); } } public static void AES_CMAC_Update(ref AesCmacCtx ctx, byte[] data, byte[] appKey, int len) { int mlen = 0; byte[] tempIn = new byte[16]; byte[] tempData; if (ctx.M_n > 0) { mlen = Math.Min(16 - ctx.M_n, len); for (int i = 0; i < mlen; i++) { ctx.M_last[ctx.M_n + i] = data[i]; } ctx.M_n += mlen; if (ctx.M_n < 16 || len == mlen) return; XOR(ctx.M_last, ref ctx.X); Array.Copy(ctx.X, tempIn, 16); tempIn = AesEncrypt(appKey, new byte[16], tempIn); Array.Copy(tempIn, ctx.X, 16); tempData = new byte[data.Length - mlen]; Array.Copy(data, mlen, tempData, 0, data.Length - mlen); data = new byte[data.Length - mlen]; Array.Copy(tempData, data, tempData.Length); len -= mlen; } while (len > 16) { XOR(data, ref ctx.X); Array.Copy(ctx.X, tempIn, 16); tempIn = AesEncrypt(appKey, new byte[16], tempIn); Array.Copy(tempIn, ctx.X, 16); tempData = new byte[data.Length - 16]; Array.Copy(data, 16, tempData, 0, data.Length - 16); data = new byte[data.Length - 16]; Array.Copy(tempData, data, tempData.Length); len -= 16; } Array.Copy(data, ctx.M_last, len); ctx.M_n = len; } public static void AES_CMAC_Final(ref AesCmacCtx ctx, byte[] appKey, out byte[] Cmac) { Cmac = new byte[16]; byte[] tempK = new byte[16]; byte[] tempIn = new byte[16]; tempK = AesEncrypt(appKey, new byte[16], tempK); if ((int)(tempK[0] & 0x80) > 0) { LSHIFT(tempK, ref tempK); tempK[15] ^= 0x87; } else LSHIFT(tempK, ref tempK); if (ctx.M_n == 16) { XOR(tempK, ref ctx.M_last); } else { if ((int)(tempK[0] & 0x80) > 0) { LSHIFT(tempK, ref tempK); tempK[15] ^= 0x87; } else LSHIFT(tempK, ref tempK); ctx.M_last[ctx.M_n] = 0x80; while (++ctx.M_n < 16) { ctx.M_last[ctx.M_n] = 0x00; } XOR(tempK, ref ctx.M_last); } XOR(ctx.M_last, ref ctx.X); Array.Copy(ctx.X, tempIn, 16); tempIn = AesEncrypt(appKey, new byte[16], tempIn); tempK = new byte[16]; Array.Copy(tempIn, Cmac, 16); } #endregion
该代码块也是亲测可用,算法逻辑都是和LoraWan的C源码一模一样
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。