-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Error in 1.8.5.2 when upgrading from 1.8.5 #22
Comments
The error does not happen in the earlier release as it was not validating private key values. It was wrong in the earlier release, it's just explicitly wrong now. The reason for this is that the effective key is new BigInteger(privateKeyBytes).mod(curve.N) which in the example case is the same as BigInteger.ValueOf(-1).mod(curve.N). I won't ask why you are using a negative number to try and generate a private value. I don't think the tutorial author's intention was to show people how to generate random keys but to calculate a public key from a given private value. If it was their intention to show people how to generate random keys they are totally incorrect about how to do it safely. Following this approach you could easily generate a private value that was only a few bits in length thinking you'd generated a much bigger one. Please use the ECKeyPairGenerator to generate private keys. It will do it correctly. |
Good to know, I am no expert in this field. Is there documentation with a C# example that covers the proper way to generate a public and private key, sign a message with the private key and finally validate the signature using only the public key? |
Leaving my notes for posterity, there is not a whole lot to find through Internet searches. How to generate public and private keys in C#, sign messages with the private key and verify the message with only the public key. How to generate public and private key pair:int strength = 256; // can be 384 or 521 as well
ECKeyPairGenerator keyGenerator = new ECKeyPairGenerator("ECDSA");
keyGenerator.Init(new KeyGenerationParameters(new SecureRandom(), strength));
AsymmetricCipherKeyPair pair = keyGenerator.GenerateKeyPair();
ECPrivateKeyParameters privateKey = pair.Private as ECPrivateKeyParameters;
ECPublicKeyParameters publicKey = pair.Public as ECPublicKeyParameters;
byte[] privateKeyBytes = privateKey.D.ToByteArray();
byte[] publicKeyBytes = publicKey.Q.GetEncoded(); How to get public key from private key:X9ECParameters curve = SecNamedCurves.GetByName("secp256r1");
ECDomainParameters domain = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);
BigInteger d = new BigInteger(privateKeyBytes);
Org.BouncyCastle.Math.EC.ECPoint q = domain.G.Multiply(d);
ECPublicKeyParameters publicKey = new ECPublicKeyParameters("ECDSA", q, domain);
byte[] publicKeyBytes = publicKey.Q.GetEncoded(); How to sign a message using a private key:string signingAlgorithm = "SHA-512withECDSA";
X9ECParameters curve = SecNamedCurves.GetByName("secp256r1");
ECDomainParameters domain = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);
ECPrivateKeyParameters keyParameters = new ECPrivateKeyParameters("ECDSA", new BigInteger(privateKeyBytes), domain);
ISigner signer = SignerUtilities.GetSigner(signingAlgorithm);
signer.Init(true, keyParameters);
signer.BlockUpdate(messageBytes, 0, messageBytes.Length);
byte[] signature = signer.GenerateSignature(); How to validate a signed message using just the public key:string signingAlgorithm = "SHA-512withECDSA";
X9ECParameters curve = SecNamedCurves.GetByName("secp256r1");
ECDomainParameters domain = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);
Org.BouncyCastle.Math.EC.ECPoint q = curve.Curve.DecodePoint(publicKeyBytes);
ECPublicKeyParameters keyParameters = new ECPublicKeyParameters("ECDSA", q, domain);
ISigner signer = SignerUtilities.GetSigner(signingAlgorithm);
signer.Init(false, keyParameters);
signer.BlockUpdate(messageBytes, 0, messageBytes.Length);
byte[] signatureBytes = Convert.FromBase64String(signature);
return signer.VerifySignature(signatureBytes); |
Yes, ECDSA uses a random value in signature calculations. There is a specific deterministic version as well, but that is not the one being used above. Just a precaution, use BigInteger d = new BigInteger(1, privateKeyBytes) rather than BigInteger d = new BigInteger(privateKeyBytes). The 2 arg constructor with the 1 will treat privateKeyBytes as an encoding of an unsigned positive number. |
Ok good. If there is documentation you want me to add this to, I'd be happy to help do that. Out of curiosity, how do I create a deterministic signer? |
It appears that something in version 1.8.5.2 broke ECPrivateKeyParameters. Here is working code from version 1.8.5:
When called in version 1.8.5.2, I get an error on the last line of code that parameter d is out of bounds from 0 to n - 1. This error does not happen in version 1.8.5.
Following this tutorial: https://blog.todotnet.com/2018/02/public-private-keys-and-signing/
The text was updated successfully, but these errors were encountered: