public bool VerifySignature(byte[] message, byte[] signature, byte[] pubkey) { if (pubkey.Length == 33 && (pubkey[0] == 0x02 || pubkey[0] == 0x03)) { try { pubkey = ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1).EncodePoint(false).Skip(1).ToArray(); } catch { return false; } } else if (pubkey.Length == 65 && pubkey[0] == 0x04) { pubkey = pubkey.Skip(1).ToArray(); } else if (pubkey.Length != 64) { throw new ArgumentException(); } BigInteger x = new BigInteger(1, pubkey.Take(32).ToArray()); BigInteger y = new BigInteger(1, pubkey.Skip(32).ToArray()); X9ECParameters ecParams = NistNamedCurves.GetByName("P-256"); ECDomainParameters domainParameters = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H, ecParams.GetSeed()); var G = ecParams.G; Org.BouncyCastle.Math.EC.ECCurve curve = ecParams.Curve; Org.BouncyCastle.Math.EC.ECPoint q = curve.CreatePoint(x, y); ECPublicKeyParameters pubkeyParam = new ECPublicKeyParameters(q, domainParameters); var verifier = SignerUtilities.GetSigner("SHA-256withECDSA"); verifier.Init(false, pubkeyParam); verifier.BlockUpdate(message, 0, message.Length); // expected format is SEQUENCE {INTEGER r, INTEGER s} var derSignature = new DerSequence( // first 32 bytes is "r" number new DerInteger(new BigInteger(1, signature.Take(32).ToArray())), // last 32 bytes is "s" number new DerInteger(new BigInteger(1, signature.Skip(32).ToArray()))) .GetDerEncoded(); ///old verify method /// /* const int ECDSA_PUBLIC_P256_MAGIC = 0x31534345; pubkey = BitConverter.GetBytes(ECDSA_PUBLIC_P256_MAGIC).Concat(BitConverter.GetBytes(32)).Concat(pubkey).ToArray(); using (CngKey key = CngKey.Import(pubkey, CngKeyBlobFormat.EccPublicBlob)) using (ECDsaCng ecdsa = new ECDsaCng(key)) { var result = ecdsa.VerifyData(message, signature, HashAlgorithmName.SHA256); } */ /////////////////// return verifier.VerifySignature(derSignature); }
/// <summary> /// Hashes a seed t into a point T on the curve. Returns null if t is unsuitable. /// </summary> /// <param name="curve">The elliptic curve in Weierstrass form</param> /// <param name="t">The seed</param> /// <returns>A random point T uniquely determined by seed t, otherwise null</returns> public static ECPoint?HashToWeierstrassCurve(ECCurve curve, byte[] t) { ECFieldElement x, ax, x3, y, y2; BigInteger P = curve.Field.Characteristic; SHA256? sha256 = SHA256.Create(); BigInteger hash = new BigInteger(sha256.ComputeHash(t)); // Check that the hash is within valid range if (hash.CompareTo(BigInteger.One) < 0 || hash.CompareTo(P) >= 0) { return(null); } // A valid point (x,y) must satisfy: y^2 = x^3 + Ax + B mod P // Convert hash from BigInt to FieldElement x modulo P x = curve.FromBigInteger(hash); // x ax = x.Multiply(curve.A); // Ax x3 = x.Square().Multiply(x); // x^3 = x^2 * x y2 = x3.Add(ax).Add(curve.B); // y^2 = x^3 + Ax + B y = y2.Sqrt(); // y = sqrt(x^3 + Ax + B) // y == null if square root mod P does not exist if (y == null) { return(null); } ECPoint T = curve.CreatePoint(x.ToBigInteger(), y.ToBigInteger()); // Use the built-in point validator, which also checks for membership // in weak subgroups if (!T.IsValid()) { return(null); } return(T); }