Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

C# Elliptical Curve Cryptography with Bouncy Castle Curve P-128

0.00/5 (No votes)
28 Oct 2016 1  
Tweaking of the Bouncy Castle library to make it work with SecP128r1 curve1251

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)
{
    //using ECDSA algorithm for the key generation
    var gen = new CEBA.Org.BouncyCastle.Crypto.Generators.ECKeyPairGenerator("ECDSA");

    //Creating Random
    var secureRandom = new SecureRandom();

    //Parameters creation using the random and keysize
    var keyGenParam = new KeyGenerationParameters(secureRandom, keySize);

    //Initializing generation algorithm with the Parameters--This method Init i modified
    gen.Init(keyGenParam);

    //Generation of Key Pair
    return gen.GenerateKeyPair();
}  

/// <summary>
/// This method creates 256 bit keys and creates the 
/// public/private key pair (if they are not yet created only)
/// </summary>
private void GeneratePKeys(int intSize)
{
    //Generating p-128 keys 128 specifies strength
    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;

//This is tweaked code used same name namespace 
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;
        }

        /// <summary>
        /// Init method of the class 
        /// CEBA.Org.BouncyCastle.Crypto.Generators.ECKeyPairGenerator updated as below
        /// </summary>
        /// <param name="parameters"></param>
        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;
                    //This case 128 won't be available in the Bouncy castle source, i added this case
                    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();
            }
        }

        /**
         * Given the domain parameters this routine generates an EC key
         * pair in accordance with X9.62 section 5.2.1 pages 26, 27.
         */
        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)
        {
            // TODO ECGost3410NamedCurves support (returns ECDomainParameters though)

            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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here