C#实现SM3国密加密
本文主要讲解“国密加密算法”SM系列之SM3的C#实现方法,加密规则请详阅国密局发布的文档。
首先需第三方Nuget包:Portable.BouncyCastle (源码来自http://www.bouncycastle.org/csharp/)
1.1常规处理
/// <summary>/// General/// </summary>public abstract class GeneralDigest : IDigest{/// <summary>/// 内部缓冲区的大小/// </summary>private const int ByteLength = 64;/// <summary>/// 消息摘要/// </summary>private readonly byte[] XBuf;/// <summary>/// 待更新的消息摘要的索引/// </summary>private int XBufOff;/// <summary>/// 待更新的消息摘要的大小/// </summary>private long ByteCount;/// <summary>/// 构造函数/// </summary>internal GeneralDigest(){XBuf = new byte[4];}/// <summary>/// 复制构造函数/// </summary>/// <param name="t"></param>internal GeneralDigest(GeneralDigest t){XBuf = new byte[t.XBuf.Length];Array.Copy(t.XBuf, 0, XBuf, 0, t.XBuf.Length);XBufOff = t.XBufOff;ByteCount = t.ByteCount;}/// <summary>/// 用一个字节更新消息摘要。/// </summary>/// <param name="input"></param>public void Update(byte input){XBuf[XBufOff++] = input;if (XBufOff == XBuf.Length){ProcessWord(XBuf, 0);XBufOff = 0;}ByteCount++;}/// <summary>/// 用字节块更新消息摘要/// </summary>/// <param name="input"></param>/// <param name="inOff"></param>/// <param name="length"></param>public void BlockUpdate(byte[] input, int inOff, int length){//更新当前消息摘要while ((XBufOff != 0) && (length > 0)){Update(input[inOff]);inOff++;length--;}//处理完整的消息摘要while (length > XBuf.Length){ProcessWord(input, inOff);inOff += XBuf.Length;length -= XBuf.Length;ByteCount += XBuf.Length;}//填充剩余的消息摘要while (length > 0){Update(input[inOff]);inOff++;length--;}}/// <summary>/// 产生最终的摘要值/// </summary>public void Finish(){long bitLength = (ByteCount << 3);//添加字节Update(unchecked((byte)128));while (XBufOff != 0) Update(unchecked((byte)0));ProcessLength(bitLength);ProcessBlock();}/// <summary>/// 重启/// </summary>public virtual void Reset(){ByteCount = 0;XBufOff = 0;Array.Clear(XBuf, 0, XBuf.Length);}/// <summary>/// 摘要应用其压缩功能的内部缓冲区的大小/// </summary>/// <returns></returns>public int GetByteLength(){return ByteLength;}/// <summary>/// 处理消息摘要/// ABCDEFGH 串联/// </summary>/// <param name="input"></param>/// <param name="inOff"></param>internal abstract void ProcessWord(byte[] input, int inOff);internal abstract void ProcessLength(long bitLength);/// <summary>/// 迭代压缩/// </summary>internal abstract void ProcessBlock();/// <summary>/// 算法名称/// </summary>public abstract string AlgorithmName { get; }/// <summary>/// 消息摘要生成的摘要的大小/// </summary>/// <returns></returns>public abstract int GetDigestSize();/// <summary>/// 关闭摘要,产生最终的摘要值。doFinal调用使摘要复位。/// </summary>/// <param name="output"></param>/// <param name="outOff"></param>/// <returns></returns>public abstract int DoFinal(byte[] output, int outOff);}
1.2 执行无符号按位右移
/// <summary>/// 使用指定的数字执行无符号按位右移/// </summary>public class SupportClass{/// <summary>/// 使用指定的数字执行无符号按位右移/// </summary>/// <param name="number">要操作的编号</param>/// <param name="bits">要移位的比特数</param>/// <returns>移位操作产生的数字</returns>public static int URShift(int number, int bits){if (number >= 0)return number >> bits;elsereturn (number >> bits) + (2 << ~bits);}/// <summary>/// 使用指定的数字执行无符号按位右移/// </summary>/// <param name="number">要操作的编号</param>/// <param name="bits">要移位的比特数</param>/// <returns>移位操作产生的数字</returns>public static int URShift(int number, long bits){return URShift(number, (int)bits);}/// <summary>/// 使用指定的数字执行无符号按位右移/// </summary>/// <param name="number">要操作的编号</param>/// <param name="bits">要移位的比特数</param>/// <returns>移位操作产生的数字</returns>public static long URShift(long number, int bits){if (number >= 0)return number >> bits;elsereturn (number >> bits) + (2L << ~bits);}/// <summary>/// 使用指定的数字执行无符号按位右移/// </summary>/// <param name="number">要操作的编号</param>/// <param name="bits">要移位的比特数</param>/// <returns>移位操作产生的数字</returns>public static long URShift(long number, long bits){return URShift(number, (int)bits);}}
1.3 SM3处理
/// <summary>/// /// ⊕ 等价于 ^/// ^ 等价于 &/// v 等价于 |/// </summary>public class SM3Digest : GeneralDigest{public override string AlgorithmName{get{return "SM3";}}/// <summary>/// 消息摘要生成的摘要的大小/// </summary>/// <returns></returns>public override int GetDigestSize(){return DigestLength;}/// <summary>/// SM3算法产生的哈希值大小/// </summary>private const int DigestLength = 32;/// <summary>/// 初始值IV/// </summary>private static readonly int[] IV = new int[] {0x7380166f, 0x4914b2b9, 0x172442d7,unchecked((int)0xda8a0600), unchecked((int)0xa96f30bc), 0x163138aa,unchecked((int)0xe38dee4d), unchecked((int)0xb0fb0e4e)};/// <summary>/// 备份的字寄存器/// </summary>private readonly int[] v = new int[8];/// <summary>/// 使用中的字寄存器/// </summary>private readonly int[] v_ = new int[8];private static readonly int[] X0 = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };private readonly int[] X = new int[68];private int xOff;/// <summary>/// 0到15的Tj常量/// </summary>private readonly int TOne = 0x79cc4519;/// <summary>/// 16到63的Tj常量/// </summary>private readonly int TSecond = 0x7a879d8a;public SM3Digest(){Reset();}/// <summary>/// 复制构造函数/// </summary>/// <param name="t"></param>public SM3Digest(SM3Digest t) : base(t){Array.Copy(t.X, 0, X, 0, t.X.Length);xOff = t.xOff;Array.Copy(t.v, 0, v, 0, t.v.Length);}/// <summary>/// 将复制的对象状态还原到该对象。/// 此方法的实现应尝试避免或最小化内存分配以执行重置。/// </summary>public override void Reset(){base.Reset();Array.Copy(IV, 0, v, 0, IV.Length);xOff = 0;Array.Copy(X0, 0, X, 0, X0.Length);}internal override void ProcessBlock(){int j;int[] ww = X;//64位比特串int[] ww_ = new int[64];#region 块消息扩展//消息扩展16 TO 67for (j = 16; j < 68; j++){ww[j] = P1(ww[j - 16] ^ ww[j - 9] ^ (Rotate(ww[j - 3], 15))) ^ (Rotate(ww[j - 13], 7)) ^ ww[j - 6];}//消息扩展0 TO 63for (j = 0; j < 64; j++){ww_[j] = ww[j] ^ ww[j + 4];}#endregion#region 压缩函数int[] vv = v;int[] vv_ = v_;//A,B,C,D,E,F,G,H为字寄存器Array.Copy(vv, 0, vv_, 0, IV.Length);//中间变量SS1,SS2,TT1,TT2int SS1, SS2, TT1, TT2;int aaa;//将消息分组B(i)划分为16个字for (j = 0; j < 16; j++){aaa = Rotate(vv_[0], 12);SS1 = aaa + vv_[4] + Rotate(TOne, j);SS1 = Rotate(SS1, 7);SS2 = SS1 ^ aaa;TT1 = FFOne(vv_[0], vv_[1], vv_[2]) + vv_[3] + SS2 + ww_[j];TT2 = GGOne(vv_[4], vv_[5], vv_[6]) + vv_[7] + SS1 + ww[j];#region 更新各个寄存器vv_[3] = vv_[2];vv_[2] = Rotate(vv_[1], 9);vv_[1] = vv_[0];vv_[0] = TT1;vv_[7] = vv_[6];vv_[6] = Rotate(vv_[5], 19);vv_[5] = vv_[4];vv_[4] = P0(TT2);#endregion}for (j = 16; j < 64; j++){aaa = Rotate(vv_[0], 12);SS1 = aaa + vv_[4] + Rotate(TSecond, j);SS1 = Rotate(SS1, 7);SS2 = SS1 ^ aaa;TT1 = FFSecond(vv_[0], vv_[1], vv_[2]) + vv_[3] + SS2 + ww_[j];TT2 = GGSecond(vv_[4], vv_[5], vv_[6]) + vv_[7] + SS1 + ww[j];#region 更新各个寄存器vv_[3] = vv_[2];vv_[2] = Rotate(vv_[1], 9);vv_[1] = vv_[0];vv_[0] = TT1;vv_[7] = vv_[6];vv_[6] = Rotate(vv_[5], 19);vv_[5] = vv_[4];vv_[4] = P0(TT2);#endregion}#endregion//256比特的杂凑值y =vv_(j+1) ABCDEFGHfor (j = 0; j < 8; j++){vv[j] ^= vv_[j];}// ResetxOff = 0;Array.Copy(X0, 0, X, 0, X0.Length);}internal override void ProcessWord(byte[] in_Renamed, int inOff){int n = in_Renamed[inOff] << 24;n |= (in_Renamed[++inOff] & 0xff) << 16;n |= (in_Renamed[++inOff] & 0xff) << 8;n |= (in_Renamed[++inOff] & 0xff);X[xOff] = n;if (++xOff == 16){ProcessBlock();}}internal override void ProcessLength(long bitLength){if (xOff > 14){ProcessBlock();}X[14] = (int)(SupportClass.URShift(bitLength, 32));X[15] = (int)(bitLength & unchecked((int)0xffffffff));}/// <summary>/// 写入到大端/// </summary>/// <param name="n"></param>/// <param name="bs"></param>/// <param name="off"></param>public static void IntToBigEndian(int n, byte[] bs, int off){bs[off] = (byte)(SupportClass.URShift(n, 24));bs[++off] = (byte)(SupportClass.URShift(n, 16));bs[++off] = (byte)(SupportClass.URShift(n, 8));bs[++off] = (byte)(n);}/// <summary>/// 关闭摘要,产生最终的摘要值。doFinal调用使摘要复位。/// </summary>/// <param name="out_Renamed"></param>/// <param name="outOff"></param>/// <returns></returns>public override int DoFinal(byte[] out_Renamed, int outOff){Finish();for (int i = 0; i < 8; i++){IntToBigEndian(v[i], out_Renamed, outOff + i * 4);}Reset();return DigestLength;}/// <summary>/// x循环左移n比特运算/// </summary>/// <param name="x"></param>/// <param name="n"></param>/// <returns></returns>private static int Rotate(int x, int n){return (x << n) | (SupportClass.URShift(x, (32 - n)));}#region 置换函数/// <summary>/// 置换函数P0/// </summary>/// <param name="x"></param>/// <returns></returns>private static int P0(int x){return (x) ^ Rotate(x, 9) ^ Rotate(x, 17);}/// <summary>/// 置换函数P1/// </summary>/// <param name="x"></param>/// <returns></returns>private static int P1(int x){return (x) ^ Rotate(x, 15) ^ Rotate(x, 23);}#endregion#region 布尔函数/// <summary>/// 0到15的布尔函数FF (X⊕^Y⊕Z)/// </summary>/// <param name="X"></param>/// <param name="Y"></param>/// <param name="Z"></param>/// <returns></returns>private static int FFOne(int X, int Y, int Z){return (X ^ Y ^ Z);}/// <summary>/// 16到63的布尔函数FF/// </summary>/// <param name="X"></param>/// <param name="Y"></param>/// <param name="Z"></param>/// <returns></returns>private static int FFSecond(int X, int Y, int Z){return ((X & Y) | (X & Z) | (Y & Z));}/// <summary>/// 0到15的布尔函数GG/// </summary>/// <param name="X"></param>/// <param name="Y"></param>/// <param name="Z"></param>/// <returns></returns>private static int GGOne(int X, int Y, int Z){return (X ^ Y ^ Z);}/// <summary>/// 16到63的布尔函数GG/// </summary>/// <param name="X"></param>/// <param name="Y"></param>/// <param name="Z"></param>/// <returns></returns>private static int GGSecond(int X, int Y, int Z){return ((X & Y) | (~X & Z));}#endregion}
1.4调用
/// <summary>/// Sm3算法(10进制的ASCII) /// 在SHA-256基础上改进实现的一种算法 /// 对标国际MD5算法和SHA算法/// </summary>public static class Sm3Crypto{/// <summary>/// sm3加密(使用自定义密钥)/// </summary>/// <param name="data"></param>/// <returns></returns>public static byte[] ToSM3byte(string data, string key){byte[] msg1 = Encoding.Default.GetBytes(data);byte[] key1 = Encoding.Default.GetBytes(key);KeyParameter keyParameter = new KeyParameter(key1); SM3Digest sm3 = new SM3Digest();HMac mac = new HMac(sm3);//带密钥的杂凑算法mac.Init(keyParameter);mac.BlockUpdate(msg1, 0, msg1.Length);byte[] result = new byte[mac.GetMacSize()];mac.DoFinal(result, 0);return Hex.Encode(result);}/// <summary>/// sm3加密/// </summary>/// <param name="data"></param>/// <returns></returns>public static byte[] ToSM3byte(this string data){ var msg = data.ToHexByte();//把字符串转成16进制的ASCII码 SM3Digest sm3 = new SM3Digest();sm3.BlockUpdate(msg, 0, msg.Length);byte[] md = new byte[sm3.GetDigestSize()];//SM3算法产生的哈希值大小sm3.DoFinal(md, 0);return Hex.Encode(md);}}