【IC卡 国密SM4算法 密钥分散,加解密,MAC计算】


在这里记录一下工作中调查国密算法SM4过程中掌握的心得体会。

密钥分散

对基于 SM4 的分散算法的描述。
密钥分散算法简称 Diversify,是指将一个双长度的密钥 MK,对分散数据进行处理,推导出双长度的密钥 DK。
将分散数据的 8 个字节,作为输入数据的左半部分:
将分散数据的 8 个字节求反,作为输入数据的右半部分;
用 MK 对输入数据进行 SM4 运算。

        /// <summary>
        /// 基于SM4的分散算法。
        /// 将分散数据的 8 个字节,作为输入数据的左半部分;将分散数据的 8 个字节求反,作为输入数据的右半部分; 用CKKey 对输入数据进行 SM4 运算。
        /// </summary>
        /// <param name="SourceData"></param>
        /// <param name="srcIndex"></param>
        /// <param name="CKKey"></param>
        /// <param name="subKey"></param>
        public static void GetSubKey_SM4(byte[] SourceData, int srcIndex, byte[] CKKey, ref byte[] subKey)
        {
            int num;
            byte[] pvInOutputBlock = new byte[0x10];
            for (num = 0; num < 8; num++)
            {
                pvInOutputBlock[num] = SourceData[num + srcIndex];
            }

            for (num = 0; num < 8; num++)
            {
                pvInOutputBlock[8 + num] = (byte)~SourceData[num + srcIndex];
            }

            CombinedSM4Operation(ref pvInOutputBlock, null, CKKey, DES.ZERO_CBC_IV | DES.TRIPLE_DES_ENCRYPTION_EDE2, 1);
            Array.Copy(pvInOutputBlock, 0, subKey, 0, 16);
        }

加解密

SM4属于对称算法,加密和解密密钥相同。
SM4算法:
SM4算法是指使用长度为16字节的密钥K将16字节明文数据块加密成密文数据块,如下所
示:
Y = SM4 (K) [X]
解密的方式如下:
X = SM4-1 (K)[X]
计算过程:
数据的加密解密计算采用ECB 作为块操作模式按照如下步骤对数据进行加密:
第一步: 用Ld(1 字节)表示明文数据的长度,在明文数据前加上Ld 产生新的数据块。
第二步:将该数据块分成以分组长度 128 位为单位的数据块,表示为块 1、块 2、…、块 n。
第三步:如果最后(或唯一) 的数据块的长度是分组长度,转到第四步:如果不足分组长度,则在其后加入16进制数’80’,如果达到分组长度,则转到第四步:否则在其后加入16进制数’00’直到长度达到分组长度。
第四步:使用加密密钥对每一个数据块进行加密。
第五步: 计算结束后,所有加密后的数据块依照原顺序连接在一起。

按照如下步骤对数据进行解密:
第一步: 将该数据块分成以分组长度128位为单位的数据块,,表示为块1、块2、…、块n。
第二步: 使用解密密钥对每个数据块进行解密。
第三步: 计算结束后,所有解密后的数据块依照原顺序连接在一起。
第四步: 第一个字节为 Ld,从第二字节起,取前 Ld 字节数据作为明文输出。

使用了BouncyCastle.Crypto开源库,NuGet安装即可。
代码中的一些变量自己定义。

        /// <summary>
        /// SM4加解密算法
        /// 数据的加密解密计算采用ECB作为块操作模式。
        /// </summary>
        /// <param name="pvInOutputBlock">数据块</param>
        /// <param name="pvCbcBlock">保留</param>
        /// <param name="pbKeyString">密钥</param>
        /// <param name="eSm4Mode">加解密模式</param>
        /// <param name="cnt">轮数</param>
        public static void CombinedSM4Operation(ref byte[] pvInOutputBlock, byte[] pvCbcBlock, byte[] pbKeyString, uint eSm4Mode, uint cnt)
        {
            // 补0到最小位数
            string data = CommonFunc.Instance.ConvertByteToString(pvInOutputBlock);
            if ((data.Length % 0x20) != 0)
            {
                if ((data.Length % 0x20) != 0)
                {
                    data = data.PadRight(0x20 * ((data.Length / 0x20) + (((data.Length % 0x20) == 0) ? 0 : 1)), '0');
                }
            }
            pvInOutputBlock = CommonFunc.Instance.ConvertStringToByte(data);

            bool forEncryption = (eSm4Mode == (DES.ZERO_CBC_IV | DES.TRIPLE_DES_ENCRYPTION_EDE2)); // true 表示加密, false表示解密

            SM4Engine sm4 = new SM4Engine(); // 创建 SM4 算法实例
            
            sm4.Init(forEncryption, new KeyParameter(pbKeyString)); // 设置加密模式和密钥 默认ECB模式

            byte[] encryptedData = new byte[pvInOutputBlock.Length]; // 存储加密后的数据

            if (cnt == 0)
            {
                cnt = 1;
            }
            // 按照 ECB 算法使用加密密钥对每一个数据块进行加密
            for (int i = 0; i < cnt; i ++)
            {
                if (pvInOutputBlock.Length < (i + 1) * sm4.GetBlockSize())
                { break; }
                sm4.ProcessBlock(pvInOutputBlock, i* sm4.GetBlockSize(), encryptedData, i* sm4.GetBlockSize());
            }

            Array.Copy(encryptedData, pvInOutputBlock, encryptedData.Length);
        }

MAC计算

报文认证码的计算采用分组长度为 128 位分组计算,采用 CBC 分组操作模式按照如下步骤计算报文认证码 MAC:
第一步: 终端 GET CHALLENGE 命令从 IC 获得一个 4/8/16 字节随机数,后补 12/8/0字节‘00’作为初始数据。
第二步:将所有输入数据按指定顺序连接成一个数据块。
第三步:将该数据块分成以分组长度 128 位为单位的数据块,表示为块 1、块 2、···、块n。
第四步:在最后的数据块后加入 16 进制数’80’,如果此时达到分组长度,则转到第五否则在其后加入 16 进制数‘00’直到长度达到分组长度。
第五步:按下图所述的算法对这些数据块使用指定密钥进行加密来产生 MAC。
第六步: 返回MAC计算数据,具体怎么取还要看需求。一般取计算结果前4字节长度的MAC。

这是第五步中的算法代码

        /// <summary>
        ///  SM4 MAC计算方法
        /// </summary>
        /// <param name="pvSubKeys">密钥</param>
        /// <param name="pvCbcBlock">IV</param>
        /// <param name="pbInputData">数据块</param>
        /// <param name="eNBytes">保留</param>
        /// <param name="pbMac32Out">返回值mac</param>
        public static void CalcPBOCMac_SM4(byte[] pvSubKeys, byte[] pvCbcBlock, byte[] pbInputData, uint eNBytes, ref byte[] pbMac32Out)
        {
            // 补0到最小位数
            string data = CommonFunc.Instance.ConvertByteToString(pvCbcBlock);
            if ((data.Length % 0x20) != 0)
            {
                if ((data.Length % 0x20) != 0)
                {
                    data = data.PadRight(0x20 * ((data.Length / 0x20) + (((data.Length % 0x20) == 0) ? 0 : 1)), '0');
                }
            }

            pvCbcBlock = CommonFunc.Instance.ConvertStringToByte(data);

            byte[] szTempArray = new byte[16];
            byte[] szBlockArray = new byte[16];
            Array.Clear(szBlockArray, 0, szBlockArray.Length);
            Array.Clear(szTempArray, 0, szTempArray.Length);
            if (pvCbcBlock == null)
            {
                pvCbcBlock = szBlockArray;
            }
            uint num = eNBytes / 16u;
            if (num * 16 < eNBytes)
            {
                num++;
                pbInputData[eNBytes] = 128;
            }

            // 创建SM4引擎
            SM4Engine sm4Engine = new SM4Engine();

            // 设置为加密模式,并设置密钥
            sm4Engine.Init(true, new KeyParameter(pvSubKeys));
  
            byte[] encryptedData = new byte[16]; // 存储加密后的数据
            for (uint num2 = 0u; num2 < num; num2++)
            {
                DataXOR16(ref szTempArray, pbInputData, (int)(num2 * 16), pvCbcBlock, 0);
                sm4Engine.ProcessBlock(szTempArray, 0, encryptedData, 0);
                Array.Copy(encryptedData, pvCbcBlock, 16);
            }

            Array.Copy(encryptedData, pbMac32Out, 4);
        }
		// 异或方法 
        private static void DataXOR16(ref byte[] xoData, byte[] des, int desIndex, byte[] source, int sourceIndex)
        {
            for (byte i = 0; i < 16; i = (byte)(i + 1))
            {
                xoData[i] = (byte)(des[i + desIndex] ^ source[i + sourceIndex]);
            }
        }

实例测试

TODO 之后更新

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
国密算法SM4是一种对称加密算法,可以用于对数据进行加密和解密。CBC(Cipher Block Chaining)模式是SM4算法的一种工作模式,它将明文分组与前一个密文分组进行异或运算后再进行加密。下面是SM4 CBC模式的加解密过程: 加密过程: 1. 初始化一个随机的IV(Initialization Vector)作为首个密文分组。 2. 将明文按照分组长度进行划分,每个分组的大小与SM4算法的分组长度相同(通常为128位)。 3. 对每个分组进行如下操作: a. 将当前明文分组与上一个密文分组进行异或运算。 b. 使用SM4算法对异或结果进行加密,得到当前密文分组。 4. 将所有密文分组连接起来,得到最终的密文。 解密过程: 1. 初始化一个随机的IV作为首个密文分组。 2. 将密文按照分组长度进行划分,每个分组的大小与SM4算法的分组长度相同(通常为128位)。 3. 对每个分组进行如下操作: a. 使用SM4算法对当前密文分组进行解密。 b. 将解密结果与上一个密文分组进行异或运算,得到当前明文分组。 4. 将所有明文分组连接起来,得到最终的明文。 需要注意的是,在CBC模式中,每个分组的加密都依赖于前一个分组的密文,因此在加密和解密过程中需要保持IV的一致性。同时,在使用CBC模式时,需要对明文进行填充(padding)以适应分组长度。常用的填充方式包括PKCS7和ZeroPadding。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值