Introduction
Bouncy castle is the most popular among very few Elliptical Curve Cryptography open source libraries available out there for C#, but there are some limitations, it doesn't support the generation of the p-128 curve keys. This article helps in tweaking the Bouncy Castle to support P-128 curve.
Background
Elliptic curve cryptography (ECC) is an approach to public key cryptography based on the algebraic structure of elliptical over infinite fields. It represents a different way to do public-key cryptography, an alternative to the older RSA system and also offers certain advantages. It is popular in news as known to be used by FBI.
Very few open source libraries available for the ECC in C#, Bouncy Castle is the most widely used one, but as of now, it is not supporting P-128 curve.
Using the Code
Let's get started, generally generation of Bouncy castle key pair is as below GenerateKeys
method.
public AsymmetricCipherKeyPair GenerateKeys(int keySize)
{
var gen = new CEBA.Org.BouncyCastle.Crypto.Generators.ECKeyPairGenerator("ECDSA");
var secureRandom = new SecureRandom();
var keyGenParam = new KeyGenerationParameters(secureRandom, keySize);
gen.Init(keyGenParam);
return gen.GenerateKeyPair();
}
private void GeneratePKeys(int intSize)
{
var keyPair = GenerateKeys(intSize);
TextWriter textWriter = new StringWriter();
Org.BouncyCastle.OpenSsl.PemWriter pemWriter =
new Org.BouncyCastle.OpenSsl.PemWriter(textWriter);
pemWriter.WriteObject(keyPair.Private);
pemWriter.Writer.Flush();
string privateKey = textWriter.ToString();
txtPrivateKey.Text = privateKey;
ECPrivateKeyParameters privateKeyParam = (ECPrivateKeyParameters)keyPair.Private;
txtD.Text = privateKeyParam.D.ToString();
textWriter = new StringWriter();
pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(textWriter);
pemWriter.WriteObject(keyPair.Public);
pemWriter.Writer.Flush();
ECPublicKeyParameters publicKeyParam = (ECPublicKeyParameters)keyPair.Public;
string publickey = textWriter.ToString();
txtPublicKey.Text = publickey;
txtX.Text = publicKeyParam.Q.X.ToBigInteger().ToString();
txtY.Text = publicKeyParam.Q.Y.ToBigInteger().ToString();
}
The Bouncy castle class libraries consists of absract
class SecObjectIdentifiers
which has the supported curve types.
In the below sample, I tweaked class CEBA.Org.BouncyCastle.Crypto.Generators.ECKeyPairGenerator
for the SecP128r1 curve.
using System;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Asn1.TeleTrust;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.EC;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Math.EC.Multiplier;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
namespace CEBA.Org.BouncyCastle.Crypto.Generators
{
public class ECKeyPairGenerator: IAsymmetricCipherKeyPairGenerator
{
private readonly string algorithm;
private ECDomainParameters parameters;
private DerObjectIdentifier publicKeyParamSet;
private SecureRandom random;
public ECKeyPairGenerator()
: this("EC")
{
}
public ECKeyPairGenerator(
string algorithm)
{
if (algorithm == null)
throw new ArgumentNullException("algorithm");
this.algorithm = algorithm;
}
public void Init(KeyGenerationParameters parameters)
{
if (parameters is ECKeyGenerationParameters)
{
ECKeyGenerationParameters ecP = (ECKeyGenerationParameters)parameters;
this.publicKeyParamSet = ecP.PublicKeyParamSet;
this.parameters = ecP.DomainParameters;
}
else
{
DerObjectIdentifier oid;
switch (parameters.Strength)
{
case 192:
oid = X9ObjectIdentifiers.Prime192v1;
break;
case 224:
oid = SecObjectIdentifiers.SecP224r1;
break;
case 128:
oid = SecObjectIdentifiers.SecP128r1;
break;
case 239:
oid = X9ObjectIdentifiers.Prime239v1;
break;
case 256:
oid = X9ObjectIdentifiers.Prime256v1;
break;
case 384:
oid = SecObjectIdentifiers.SecP384r1;
break;
case 521:
oid = SecObjectIdentifiers.SecP521r1;
break;
default:
throw new InvalidParameterException("unknown key size.");
}
X9ECParameters ecps = FindECCurveByOid(oid);
this.publicKeyParamSet = oid;
this.parameters = new ECDomainParameters(
ecps.Curve, ecps.G, ecps.N, ecps.H, ecps.GetSeed());
}
this.random = parameters.Random;
if (this.random == null)
{
this.random = new SecureRandom();
}
}
public AsymmetricCipherKeyPair GenerateKeyPair()
{
BigInteger n = parameters.N;
BigInteger d;
int minWeight = n.BitLength >> 2;
for (; ; )
{
d = new BigInteger(n.BitLength, random);
if (d.CompareTo(BigInteger.Two) < 0 || d.CompareTo(n) >= 0)
continue;
if (WNafUtilities.GetNafWeight(d) < minWeight)
continue;
break;
}
ECPoint q = CreateBasePointMultiplier().Multiply(parameters.G, d);
if (publicKeyParamSet != null)
{
return new AsymmetricCipherKeyPair(
new ECPublicKeyParameters(algorithm, q, publicKeyParamSet),
new ECPrivateKeyParameters(algorithm, d, publicKeyParamSet));
}
return new AsymmetricCipherKeyPair(
new ECPublicKeyParameters(algorithm, q, parameters),
new ECPrivateKeyParameters(algorithm, d, parameters));
}
protected virtual ECMultiplier CreateBasePointMultiplier()
{
return new FixedPointCombMultiplier();
}
internal static X9ECParameters FindECCurveByOid(DerObjectIdentifier oid)
{
X9ECParameters ecP = CustomNamedCurves.GetByOid(oid);
if (ecP == null)
{
ecP = ECNamedCurveTable.GetByOid(oid);
}
return ecP;
}
internal static ECPublicKeyParameters GetCorrespondingPublicKey(
ECPrivateKeyParameters privKey)
{
ECDomainParameters ec = privKey.Parameters;
ECPoint q = new FixedPointCombMultiplier().Multiply(ec.G, privKey.D);
if (privKey.PublicKeyParamSet != null)
{
return new ECPublicKeyParameters(privKey.AlgorithmName, q, privKey.PublicKeyParamSet);
}
return new ECPublicKeyParameters(privKey.AlgorithmName, q, ec);
}
}
}
The sample Windows Form application generates the keys for the selected curve and displays them in the form as below. It displays:
Points of Interest
While learning about how to implement the ECC in C#, I found there are very limited sources available. So I tried this method to make Bouncy castle work with P-128 curve.
History
- 28-October-2016: First draft