用C#实现AES-128 CMAC算法

公司有个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源码一模一样

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值