本文主要講解“國密加密算法”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; else return (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; else return (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 67 for (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 63 for (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,TT2 int 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) ABCDEFGH for (j = 0; j < 8; j++) { vv[j] ^= vv_[j]; } // Reset xOff = 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); }
/// <summary> /// 字符串轉16進制字節數組 /// </summary> /// <param name="data"></param> /// <returns></returns> public byte[] ToHexByte(string data) { byte[] msg1 = Encoding.Default.GetBytes(data); string hexString = BytesToHexString(msg1); byte[] returnBytes = new byte[hexString.Length / 2]; for (int i = 0; i < returnBytes.Length; i++) returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 10); return returnBytes; }
/// <summary> /// byte[]數組轉16進制字符串 /// </summary> /// <param name="input">byte[]數組</param> /// <returns>16進制字符串</returns> public string BytesToHexString(byte[] input) { StringBuilder hexString = new StringBuilder(64); for (int i = 0; i < input.Length; i++) { hexString.Append(String.Format("{0:X2}", input[i])); } return hexString.ToString(); } }