Like it or not; we live in a digital world that is dominated by US companies and standards. For many, that is a good thing, as researchers and industry experts have generally worked together to produce standards that others can comply with. In cybersecurity, this is often driven by RFCs and NIST. But what if you don't trust NIST? And so, while many comply with the NIST-defined FIPS 186 standard for digital signatures, there is a Russian Federal standard for digital signatures: GOST R 34.10–2012. This is defined in RFC 7901 [here], and often just known as R3410 signing. We will use the Elliptic Curve signature method of EC-Gost R3410.
EC GOST R3410 Digital Signature using Bouncy Castle and C# (R,S) |
Method
An outline of ECDSA is:
Creating key pair
With our curve, we have a generator point of \(G\) and an order \(n\). We start by generating a private key (\(d\)) and then generate the public key of:
\(Q=d.G\)
The public key is a point on the curve, and where it is derived from adding the point \(G\), \(d\) times.
Signing the message
With a message (\(m\)), we aim to apply the private key, and then create a signature (\(r,s\)). First we create a random nonce value (\(k\)) and then determine the point:
\(P=k.G\)
Next we compute the x-axis point of this point:
\(r=P_x \pmod n \)
This gives us the \(r\) value of the signature. Next, we take the hash value of the message:
\(e=H(m)\)
And then compute the \(s\) value as:
\(s=k^{-1}.(e + d.r) \pmod n\)
The signature is (\(r,s\)).
Verifying the signature
We can verify by taking the message (\(m\), the signature (\(r,s\)) and the public key (\(Q\)):
\(e=H(m)\)
Next we compute:
\(w =s^{-1} \pmod n\)
\(u_1 = e.w\)
\(u_2 = r.w\)
We then compute the point:
\(X = u_1.G + u_2.Q \)
And then take the x-co-ordinate of \(X\):
\(x = X_x \pmod n\)
If \(x\) is equal to \(r\), the signature has been verified. This can be implemented as:
var signer2 = SignerUtilities.GetSigner(signtype); signer2.Init(false, publicKey); signer2.BlockUpdate(System.Text.Encoding.ASCII.GetBytes(msg), 0, msg.Length); var rtn=signer2.VerifySignature(signer.GenerateSignature());
Code
First we create a folder named "bc_ecdsa", 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 DSA { using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using Org.BouncyCastle.Crypto.Generators; using System.Text; using Org.BouncyCastle.Crypto.Signers; using Microsoft.VisualBasic; using Org.BouncyCastle.Asn1.CryptoPro; class Program { static void Main(string[] args) { string str="Hello"; string curvename="GostR3410-2001-CryptoPro-A"; if (args.Length >0) str=args[0]; if (args.Length >1) curvename=args[1]; byte[] data = Encoding.ASCII.GetBytes(str); var ecParams = ECGost3410NamedCurves.GetByName(curvename); /* GostR3410-2001-CryptoPro-A GostR3410-2001-CryptoPro-B GostR3410-2001-CryptoPro-C GostR3410-2001-CryptoPro-XchA GostR3410-2001-CryptoPro-XchB Tc26-Gost-3410-12-256-paramSetA Tc26-Gost-3410-12-512-paramSetA Tc26-Gost-3410-12-512-paramSetB Tc26-Gost-3410-12-512-paramSetC */ 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("ECDSA"); generator.Init(keygenParams); var keyPair = generator.GenerateKeyPair(); AsymmetricCipherKeyPair pair = generator.GenerateKeyPair(); var signEng = new ECGost3410Signer (); signEng.Init(true, pair.Private); var sig = signEng.GenerateSignature(data); signEng.Init(false, pair.Public); var rtn = signEng.VerifySignature(data,sig[0],sig[1]); var priv =(ECPrivateKeyParameters)pair.Private; var pub =(ECPublicKeyParameters)pair.Public; Console.WriteLine ("Message: {0}",str); Console.WriteLine ("Curve: {0}",curvename); Console.WriteLine ("=== ECGost Parameters =="); Console.WriteLine ("G: {0}",keygenParams.DomainParameters.G); Console.WriteLine ("N: {0}",keygenParams.DomainParameters.N); Console.WriteLine ("A: {0}",keygenParams.DomainParameters.Curve.A); Console.WriteLine ("B: {0}",keygenParams.DomainParameters.Curve.B); Console.WriteLine ("Order: {0}",keygenParams.DomainParameters.Curve.Order); Console.WriteLine ("\n=== ECGost Key =="); Console.WriteLine ("Name: {0}",priv.AlgorithmName); Console.WriteLine ("Private key: {0}",priv.D); Console.WriteLine ("Public key (x): {0}",pub.Q.AffineXCoord); Console.WriteLine ("Public key (y): {0}",pub.Q.AffineYCoord); Console.WriteLine ("\n=== ECGost Signature =="); Console.WriteLine ("r={0}, s={1}",sig[0],sig[1]); Console.WriteLine ("Verified: {0}",rtn); } } }
A sample run for GostR3410-2001-CryptoPro-XchB:
Message: Here Curve: GostR3410-2001-CryptoPro-XchB === ECGost Parameters == G: (0,41ece55743711a8c3cbf3783cd08c0ee4d4dc440d4641a8f366e550dfdb3bb67,1,9b9f605f5a858107ab1ec85e6b41c8aacf846e86789051d37998f7b9022d7598) N: 70390085352083305199547718019018437840920882647164081035322601458352298396601 A: 9b9f605f5a858107ab1ec85e6b41c8aacf846e86789051d37998f7b9022d7598 B: 805a Order: 70390085352083305199547718019018437840920882647164081035322601458352298396601 === ECGost Key == Name: ECDSA Private key: 60130586628493190906716883575939686463970488589514160685152828142123859179512 Public key (x): 7e1ab0b2e14f33a085375bc3a410b4fa297206a92b03431d6035f8e0556d9837 Public key (y): 25470b1ad5fefbdc6e2da02f27080e9ff6fe04b0a1a8f77b7d190d4c10d48b45 === ECGost Signature == r=36495145932997169416732262641889901086775198964051583633440224195517532819450, s=49900996739525414903572958043948968822553525839634585689241538183938592364568 Verified: True
And for Tc26-Gost-3410-12-512-paramSetA:
Message: Here Curve: Tc26-Gost-3410-12-512-paramSetA === ECGost Parameters == G: (3,7503cfe87a836ae3a61b8816e25450e6ce5e1c93acf1abc1778064fdcbefa921df1626be4fd036e93d75e6a50e3a41e98028fe5fc235f5b889a589cb5215f2a4,1,fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc4) N: 13407807929942597099574024998205846127479365820592393377723561443721764030073449232318290585817636498049628612556596899500625279906416653993875474742293109 A: fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc4 B: e8c2505dedfc86ddc1bd0b2b6667f1da34b82574761cb0e879bd081cfd0b6265ee3cb090f30d27614cb4574010da90dd862ef9d4ebee4761503190785a71c760 Order: 13407807929942597099574024998205846127479365820592393377723561443721764030073449232318290585817636498049628612556596899500625279906416653993875474742293109 === ECGost Key == Name: ECDSA Private key: 220499975294663534036787413162883270214945752506759991022584170931866114573234043008556396443002441048736285232223566170239972341526726271615676581770877 Public key (x): d5229aacde2f62ef18c5e1149c5ad8081e069168e733dbb0f68d132b8fa331c10b91271a602b363886c6c659aeeb87ba877aa6f158d37006f65f140f95a34aa8 Public key (y): c85b48e05308f845df3980d4afc5093fcacad2ccc5b4f750a11b1e2f5da33ea0f2585953ea08c53ed73e4b07c3c2497c3dc761b1f3bbbd246f5b5c07bc604289 === ECGost Signature == r=9234737613717496262387748957585563890639009700099378318399100694529819087163690384597213723286235657324749858133352963945859828989105163376439450965351622, s=11192946521425791998841254502233194053860875474852170424819125715926742056500277575230101877021920111977061482019555117613920847228368708523786779604244855 Verified: True