本文主要講解“國密加密算法”SM系列之SM2的C#實現方法,加密規則請詳閱國密局發布的文檔。
首先需第三方Nuget包:Portable.BouncyCastle (源碼來自http://www.bouncycastle.org/csharp/)
SM2的加密需使用到SM3加密處理
1.1 SM2密碼計算
/// <summary> /// 密碼計算 /// </summary> public class Cipher { private int ct = 1; /// <summary> /// 橢圓曲線E上點P2 /// </summary> private ECPoint p2; private SM3Digest sm3keybase; private SM3Digest sm3c3; private readonly byte[] key = new byte[32]; private byte keyOff = 0; public Cipher() { } private void Reset() { sm3keybase = new SM3Digest(); sm3c3 = new SM3Digest(); byte[] p; p = p2.Normalize().XCoord.ToBigInteger().ToByteArray(); sm3keybase.BlockUpdate(p, 0, p.Length); sm3c3.BlockUpdate(p, 0, p.Length); p = p2.Normalize().YCoord.ToBigInteger().ToByteArray(); sm3keybase.BlockUpdate(p, 0, p.Length); ct = 1; NextKey(); } private void NextKey() { SM3Digest sm3keycur = new SM3Digest(sm3keybase); sm3keycur.Update((byte)(ct >> 24 & 0x00ff)); sm3keycur.Update((byte)(ct >> 16 & 0x00ff)); sm3keycur.Update((byte)(ct >> 8 & 0x00ff)); sm3keycur.Update((byte)(ct & 0x00ff)); sm3keycur.DoFinal(key, 0); keyOff = 0; ct++; } public virtual ECPoint InitEnc(SM2 sm2, ECPoint userKey) { AsymmetricCipherKeyPair key = sm2.EccKeyPairGenerator.GenerateKeyPair(); ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)key.Private; ECPublicKeyParameters ecpub = (ECPublicKeyParameters)key.Public; BigInteger k = ecpriv.D; ECPoint c1 = ecpub.Q; p2 = userKey.Multiply(k); Reset(); return c1; } public virtual void Encrypt(byte[] data) { //p2.Normalize(); sm3c3.BlockUpdate(data, 0, data.Length); for (int i = 0; i < data.Length; i++) { if (keyOff == key.Length) NextKey(); data[i] ^= key[keyOff++]; } } public virtual void InitDec(BigInteger userD, ECPoint c1) { p2 = c1.Multiply(userD); Reset(); } public virtual void Decrypt(byte[] data) { for (int i = 0; i < data.Length; i++) { if (keyOff == key.Length) NextKey(); data[i] ^= key[keyOff++]; } sm3c3.BlockUpdate(data, 0, data.Length); } public virtual void Dofinal(byte[] c3) { byte[] p = p2.Normalize().YCoord.ToBigInteger().ToByteArray(); sm3c3.BlockUpdate(p, 0, p.Length); sm3c3.DoFinal(c3, 0); Reset(); } }
1.2 加密處理中心
/// <summary> /// 加密處理中心 /// </summary> public class SM2 { public static SM2 Instance { get { return new SM2(); } } public static SM2 InstanceTest { get { return new SM2(); } } #region 曲線參數 /// <summary> /// 曲線參數 /// </summary> public static readonly string[] CurveParameter = { "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",// p,0 "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",// a,1 "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",// b,2 "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",// n,3 "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",// gx,4 "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0" // gy,5 }; /// <summary> /// 橢圓曲線參數 /// </summary> public string[] EccParam = CurveParameter; /// <summary> /// 橢圓曲線參數P /// </summary> public readonly BigInteger EccP; /// <summary> /// 橢圓曲線參數A /// </summary> public readonly BigInteger EccA; /// <summary> /// 橢圓曲線參數B /// </summary> public readonly BigInteger EccB; /// <summary> /// 橢圓曲線參數N /// </summary> public readonly BigInteger EccN; /// <summary> /// 橢圓曲線參數Gx /// </summary> public readonly BigInteger EccGx; /// <summary> /// 橢圓曲線參數Gy /// </summary> public readonly BigInteger EccGy; #endregion /// <summary> /// 橢圓曲線 /// </summary> public readonly ECCurve EccCurve; /// <summary> /// 橢圓曲線的點G /// </summary> public readonly ECPoint EccPointG; /// <summary> /// 橢圓曲線 bc規范 /// </summary> public readonly ECDomainParameters EccBcSpec; /// <summary> /// 橢圓曲線密鑰對生成器 /// </summary> public readonly ECKeyPairGenerator EccKeyPairGenerator; private SM2() { EccParam = CurveParameter; EccP = new BigInteger(EccParam[0], 16); EccA = new BigInteger(EccParam[1], 16); EccB = new BigInteger(EccParam[2], 16); EccN = new BigInteger(EccParam[3], 16); EccGx = new BigInteger(EccParam[4], 16); EccGy = new BigInteger(EccParam[5], 16); ECFieldElement ecc_gx_fieldelement = new FpFieldElement(EccP, EccGx); ECFieldElement ecc_gy_fieldelement = new FpFieldElement(EccP, EccGy); EccCurve = new FpCurve(EccP, EccA, EccB); EccPointG = new FpPoint(EccCurve, ecc_gx_fieldelement, ecc_gy_fieldelement); EccBcSpec = new ECDomainParameters(EccCurve, EccPointG, EccN); ECKeyGenerationParameters ecc_ecgenparam; ecc_ecgenparam = new ECKeyGenerationParameters(EccBcSpec, new SecureRandom()); EccKeyPairGenerator = new ECKeyPairGenerator(); EccKeyPairGenerator.Init(ecc_ecgenparam); } /// <summary> /// 獲取雜湊值H /// </summary> /// <param name="z">Z值</param> /// <param name="data">待簽名消息</param> /// <returns></returns> public virtual byte[] Sm2GetH(byte[] z, byte[] data) { SM3Digest sm3 = new SM3Digest(); //Z sm3.BlockUpdate(z, 0, z.Length); //待簽名消息 sm3.BlockUpdate(data, 0, data.Length); // H byte[] md = new byte[sm3.GetDigestSize()]; sm3.DoFinal(md, 0); return md; } /// <summary> /// 獲取Z值 /// Z=SM3(ENTL∣∣userId∣∣a∣∣b∣∣gx∣∣gy ∣∣x∣∣y) /// </summary> /// <param name="userId">簽名方的用戶身份標識</param> /// <param name="userKey">簽名方公鑰</param> /// <returns></returns> public virtual byte[] Sm2GetZ(byte[] userId, ECPoint userKey) { SM3Digest sm3 = new SM3Digest(); byte[] p; // ENTL由2個字節標識的ID的比特長度 int len = userId.Length * 8; sm3.Update((byte)(len >> 8 & 0x00ff)); sm3.Update((byte)(len & 0x00ff)); // userId用戶身份標識ID sm3.BlockUpdate(userId, 0, userId.Length); // a,b為系統曲線參數; p = EccA.ToByteArray(); sm3.BlockUpdate(p, 0, p.Length); p = EccB.ToByteArray(); sm3.BlockUpdate(p, 0, p.Length); // gx、gy為基點 p = EccGx.ToByteArray(); sm3.BlockUpdate(p, 0, p.Length); p = EccGy.ToByteArray(); sm3.BlockUpdate(p, 0, p.Length); // x,y用戶的公鑰的X和Y p = userKey.Normalize().XCoord.ToBigInteger().ToByteArray(); sm3.BlockUpdate(p, 0, p.Length); p = userKey.Normalize().YCoord.ToBigInteger().ToByteArray(); sm3.BlockUpdate(p, 0, p.Length); // Z byte[] md = new byte[sm3.GetDigestSize()]; sm3.DoFinal(md, 0); return md; } }
1.3 加密調用
/// <summary> /// Sm2算法 /// 對標國際RSA算法 /// </summary> public class Sm2Crypto { /// <summary> /// 數據 /// </summary> public string Str { get; set; } /// <summary> /// 數據 /// </summary> public byte[] Data { get; set; } /// <summary> /// 公鑰 /// </summary> public string PublicKey { get; set; } /// <summary> /// 私鑰 /// </summary> public string PrivateKey { get; set; } /// <summary> /// 獲取密鑰 /// </summary> /// <param name="privateKey">私鑰</param> /// <param name="publicKey">公鑰</param> public static void GetKey(out string privateKey, out string publicKey) { SM2 sm2 = SM2.Instance; AsymmetricCipherKeyPair key = sm2.EccKeyPairGenerator.GenerateKeyPair(); ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)key.Private; ECPublicKeyParameters ecpub = (ECPublicKeyParameters)key.Public; publicKey = Encoding.Default.GetString(Hex.Encode(ecpub.Q.GetEncoded())).ToUpper(); privateKey = Encoding.Default.GetString(Hex.Encode(ecpriv.D.ToByteArray())).ToUpper(); } #region 解密 public object Decrypt(Sm2Crypto entity) { var data = !string.IsNullOrEmpty(entity.Str) ? Hex.Decode(entity.Str) : entity.Data; return Encoding.Default.GetString(Decrypt(Hex.Decode(entity.PrivateKey), data)); } /// <summary> /// 解密 /// </summary> /// <param name="privateKey"></param> /// <param name="encryptedData"></param> /// <returns></returns> private static byte[] Decrypt(byte[] privateKey, byte[] encryptedData) { if (null == privateKey || privateKey.Length == 0) { return null; } if (encryptedData == null || encryptedData.Length == 0) { return null; } String data = Encoding.Default.GetString(Hex.Encode(encryptedData)); byte[] c1Bytes = Hex.Decode(Encoding.Default.GetBytes(data.Substring(0, 130))); int c2Len = encryptedData.Length - 97; byte[] c2 = Hex.Decode(Encoding.Default.GetBytes(data.Substring(130, 2 * c2Len))); byte[] c3 = Hex.Decode(Encoding.Default.GetBytes(data.Substring(130 + 2 * c2Len, 64))); SM2 sm2 = SM2.Instance; BigInteger userD = new BigInteger(1, privateKey); ECPoint c1 = sm2.EccCurve.DecodePoint(c1Bytes); //c1.Normalize(); Cipher cipher = new Cipher(); cipher.InitDec(userD, c1); cipher.Decrypt(c2); cipher.Dofinal(c3); return c2; } #endregion #region 加密 public string Encrypt(Sm2Crypto entity) { var data = !string.IsNullOrEmpty(entity.Str) ? Encoding.Default.GetBytes(entity.Str) : entity.Data; return Encrypt(Hex.Decode(entity.PublicKey), data); } /// <summary> /// 加密 /// </summary> /// <param name="publicKey"></param> /// <param name="data"></param> /// <returns></returns> private static string Encrypt(byte[] publicKey, byte[] data) { if (null == publicKey || publicKey.Length == 0) { return null; } if (data == null || data.Length == 0) { return null; } byte[] source = new byte[data.Length]; Array.Copy(data, 0, source, 0, data.Length); Cipher cipher = new Cipher(); SM2 sm2 = SM2.Instance; ECPoint userKey = sm2.EccCurve.DecodePoint(publicKey); //userKey.Normalize(); ECPoint c1 = cipher.InitEnc(sm2, userKey); cipher.Encrypt(source); byte[] c3 = new byte[32]; cipher.Dofinal(c3); String sc1 = Encoding.Default.GetString(Hex.Encode(c1.GetEncoded())); String sc2 = Encoding.Default.GetString(Hex.Encode(source)); String sc3 = Encoding.Default.GetString(Hex.Encode(c3)); return (sc1 + sc2 + sc3).ToUpper(); } #endregion }