We have a number of possible types of elliptic curve types. These have a field (the prime number used), the order (the number of elliptic curve points), an \(a\) value, a \(b\) value, and a generator point (\(G\)). For a Weierstrass curve the standard form is \(y^2=x^3+ax+b\). In this case, we will generate a random private key (\(D\)) and then generate the public key: \(Q=D.G\).
EC Curve Keys using Bouncy Castle and C# |
Method
With elliptic curve cryptography, we start with a definition for the curve, such as:
\(y^2 = x^3 + ax + b \pmod p\)
This defines values for \(a\), \(b\) and \(p\). Next we select a base point on the curve (\(G\), and generate a random scalar value (\(D\)). This is the private key, and where the public key is generated from a point multiplcation of:
\(Q=D.G\)
This results in an \((x,y)\) point on the curve.
Code
First we create a folder named "bc_ec02", and then go into that folder.We can create a Dotnet console project for .NET 8.0 with:
dotnet new console --framework net8.0
Next we can install the Bouncy Castle library with:
dotnet add package BouncyCastle.Crypto.dll --version 1.8.1
Next some code:
namespace ECCurves { using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Security; using Org.BouncyCastle.Crypto.Parameters; class Program { static void Main(string[] args) { var curvename="secp256k1"; if (args.Length >0) curvename=args[0]; try { X9ECParameters ecParams = ECNamedCurveTable.GetByName(curvename); var curveparam = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H, ecParams.GetSeed()); Org.BouncyCastle.Crypto.Parameters.ECKeyGenerationParameters keygenParams = new Org.BouncyCastle.Crypto.Parameters.ECKeyGenerationParameters (curveparam, new SecureRandom()); Org.BouncyCastle.Crypto.Generators.ECKeyPairGenerator generator = new Org.BouncyCastle.Crypto.Generators.ECKeyPairGenerator(); generator.Init(keygenParams); var keyPair = generator.GenerateKeyPair(); var privateKey = (ECPrivateKeyParameters) keyPair.Private; var publicKey = (ECPublicKeyParameters) keyPair.Public; Console.WriteLine("== Curve: {0} ",curvename); Console.WriteLine("\n== Private key === "); Console.WriteLine("== D ==={0} ",privateKey.D.ToString()); Console.WriteLine("\n== Public key === "); Console.WriteLine("== Q_x ==={0} ",publicKey.Q.XCoord); Console.WriteLine("== Q_t ==={0} ",publicKey.Q.YCoord); Console.WriteLine("\n\nCurve details: G={0}, N={1}, H={2}", ecParams.G, ecParams.N, ecParams.H); Console.WriteLine("A={0}\nB={1}\nField size={2}",ecParams.Curve.A,ecParams.Curve.B,ecParams.Curve.FieldSize); } catch (Exception e) { Console.WriteLine("Error: {0}",e.Message); } } } }
A sample run for secp2561 gives:
== Curve: secp256k1 == Private key === == D ===31064867539724903891960137032820676127521229242105362809544430719991795260503 == Public key === == Q_x ===8c65b935e0fd6e174c7b180f5d1db7cda52bf8222d11f7abc924067c40e11de8 == Q_t ===6e4602ae3c8f42683f4d7269c4abf55a28bcd69ff84060118f327d25c1074a1e Curve details: G=(79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8,1,0), N=115792089237316195423570985008687907852837564279074904382605163141518161494337, H=1 A =0 B=7 Field size=256
And for NIST (FIPS) P-256:
== Curve: P-256 == Private key === == D ===36520681454922814890347386938615718499728808447933454325466265828246263851321 == Public key === == Q_x ===d52722849984ff0a89d50de91877f66377ba4065c5802adc169c235ad37d1eea == Q_t ===7fc6d99095fe7712f5ec1f5b5f9d5425cf7ae9de4f2d77d4022f67d6f8aa3f75 Curve details: G=(6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296,4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5,1,ffffffff00000001000000000000000000000000fffffffffffffffffffffffc), N=115792089210356248762697446949407573529996955224135760342422259061068512044369, H=1 A =ffffffff00000001000000000000000000000000fffffffffffffffffffffffc B=5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b Field size=256