本文只實現SM2的加簽以及驗籤。
首先需要安裝包 Portable.BouncyCastle。然後建立類 EasyGmutil
Code
namespace ConsoleApp1 { public class EasyGmUtil { private static X9ECParameters x9ECParameters = GMNamedCurves.GetByName("sm2p256v1"); private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.Curve, x9ECParameters.G, x9ECParameters.N);
/** * * @param msg * @param userId * @param privateKey * @return r||s,直接拼接byte陣列的rs */ public static byte[] signSm3WithSm2(byte[] msg, byte[] userId, byte[] privateKeyBytes) { ECPrivateKeyParameters privateKeyParameters = getPrivatekeyFromD(new BigInteger(1, privateKeyBytes)); return rsAsn1ToPlainByteArray(signSm3WithSm2Asn1Rs(msg, userId, privateKeyParameters)); } /** * @param msg * @param userId * @param privateKey * @return rs in <b>asn1 format</b> */ public static byte[] signSm3WithSm2Asn1Rs(byte[] msg, byte[] userId, AsymmetricKeyParameter privateKey) { try { ISigner signer = SignerUtilities.InitSigner("SM3withSM2", true, privateKey, new SecureRandom()); signer.BlockUpdate(msg, 0, msg.Length); byte[] sig = signer.GenerateSignature(); return sig; } catch (Exception e) { return null; } } /** * * @param msg * @param userId * @param rs r||s,直接拼接byte陣列的rs * @param publicKey * @return */ public static bool verifySm3WithSm2(byte[] msg, byte[] userId, byte[] rs, byte[] publicKeyBytes) { if (rs == null || msg == null || userId == null) return false; if (rs.Length != RS_LEN * 2) return false; if (publicKeyBytes.Length != 64 && publicKeyBytes.Length != 65) throw new ArgumentException("err key length"); BigInteger x, y; if (publicKeyBytes.Length > 64) { x = fromUnsignedByteArray(publicKeyBytes, 1, 32); y = fromUnsignedByteArray(publicKeyBytes, 33, 32); } else { x = fromUnsignedByteArray(publicKeyBytes, 0, 32); y = fromUnsignedByteArray(publicKeyBytes, 32, 32); } ECPublicKeyParameters publicKey = getPublickeyFromXY(x, y); return verifySm3WithSm2Asn1Rs(msg, userId, rsPlainByteArrayToAsn1(rs), publicKey); } public static BigInteger fromUnsignedByteArray(byte[] var0, int var1, int var2) { byte[] var3 = var0; if (var1 != 0 || var2 != var0.Length) { var3 = new byte[var2]; Array.Copy(var0, var1, var3, 0, var2); } return new BigInteger(1, var3); } /** * * @param msg * @param userId * @param rs in <b>asn1 format</b> * @param publicKey * @return */ public static bool verifySm3WithSm2Asn1Rs(byte[] msg, byte[] userId, byte[] sign, AsymmetricKeyParameter publicKey) { try { ISigner signer = SignerUtilities.GetSigner("SM3withSM2"); signer.Init(false, publicKey); signer.BlockUpdate(msg, 0, msg.Length); return signer.VerifySignature(sign); } catch (Exception e) { return false; } } public static ECPrivateKeyParameters getPrivatekeyFromD(BigInteger d) { return new ECPrivateKeyParameters(d, ecDomainParameters); } public static ECPublicKeyParameters getPublickeyFromXY(BigInteger x, BigInteger y) { return new ECPublicKeyParameters(x9ECParameters.Curve.CreatePoint(x, y), ecDomainParameters); } private const int RS_LEN = 32; private static byte[] bigIntToFixexLengthBytes(BigInteger rOrS) { // for sm2p256v1, n is 00fffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123, // r and s are the result of mod n, so they should be less than n and have length<=32 byte[] rs = rOrS.ToByteArray(); if (rs.Length == RS_LEN) return rs; else if (rs.Length == RS_LEN + 1 && rs[0] == 0) return Arrays.CopyOfRange(rs, 1, RS_LEN + 1); else if (rs.Length < RS_LEN) { byte[] result = new byte[RS_LEN]; Arrays.Fill(result, (byte)0); Buffer.BlockCopy(rs, 0, result, RS_LEN - rs.Length, rs.Length); return result; } else { throw new ArgumentException("err rs: " + Hex.ToHexString(rs)); } } /** * BC的SM3withSM2簽名得到的結果的rs是asn1格式的,這個方法轉化成直接拼接r||s * @param rsDer rs in asn1 format * @return sign result in plain byte array */ private static byte[] rsAsn1ToPlainByteArray(byte[] rsDer) { Asn1Sequence seq = Asn1Sequence.GetInstance(rsDer); byte[] r = bigIntToFixexLengthBytes(DerInteger.GetInstance(seq[0]).Value); byte[] s = bigIntToFixexLengthBytes(DerInteger.GetInstance(seq[1]).Value); byte[] result = new byte[RS_LEN * 2]; Buffer.BlockCopy(r, 0, result, 0, r.Length); Buffer.BlockCopy(s, 0, result, RS_LEN, s.Length); return result; } /** * BC的SM3withSM2驗籤需要的rs是asn1格式的,這個方法將直接拼接r||s的位元組陣列轉化成asn1格式 * @param sign in plain byte array * @return rs result in asn1 format */ private static byte[] rsPlainByteArrayToAsn1(byte[] sign) { if (sign.Length != RS_LEN * 2) throw new ArgumentException("err rs. "); BigInteger r = new BigInteger(1, Arrays.CopyOfRange(sign, 0, RS_LEN)); BigInteger s = new BigInteger(1, Arrays.CopyOfRange(sign, RS_LEN, RS_LEN * 2)); Asn1EncodableVector v = new Asn1EncodableVector(); v.Add(new DerInteger(r)); v.Add(new DerInteger(s)); try { return new DerSequence(v).GetEncoded("DER"); } catch (IOException e) { //log.Error("RsPlainByteArrayToAsn1 error: " + e.Message, e); return null; } } }
}
此處為簽名及驗籤 Demo
Code
string PrivateKey = "AJciGDkPWcwX"; // 私鑰,用於加簽,此處是亂寫的 string PublicKey = "BFaPS+Hj3eXg4ENOXDjrpLhwNIY"; // 公鑰,與私鑰配對,此處是亂寫的
string userId = "";
string str = "TestContent";
//對字串簽名
byte[] signByte = EasyGmUtil.signSm3WithSm2(Encoding.UTF8.GetBytes(str),
Encoding.UTF8.GetBytes(userId), Org.BouncyCastle.Utilities.Encoders.Base64.Decode(PrivateKey));
string signStr = Org.BouncyCastle.Utilities.Encoders.Base64.ToBase64String(signByte);
//驗籤
bool flag = EasyGmUtil.verifySm3WithSm2(Encoding.UTF8.GetBytes(str), Encoding.UTF8.GetBytes(userId),
Org.BouncyCastle.Utilities.Encoders.Base64.Decode(signStr),
Org.BouncyCastle.Utilities.Encoders.Base64.Decode(PublicKey));