Picking Apart ECIES (Elliptic Curve Integrated Encryption Scheme)

Now, elliptic curves do not allow you to perform public-key encryption directly, so we must use a Hybrid Encryption method. With this…

Picking Apart ECIES (Elliptic Curve Integrated Encryption Scheme)

Now, elliptic curves do not allow you to perform public-key encryption directly, so we must use a Hybrid Encryption method. With this, Alice creates a key pair of a and Q_A, where Q_A is her public key. She then passes Q_A to Bob.

Alice now produces S=r.Q_A, which is another point on the curve. The value of S is then used to create a symmetric key (S_k) — normally by just taking the x-co-ordinate point of the point S. Alice then encrypts a message with S_k and then passes her public key (Q_A) and the ciphertext (C) to Bob. From his private key (b) and Q_A, Bob is then able to regenerate the value of S and the same symmetric key (S_k). He will then be able to decrypt the message:

So, now let’s implement this, and where the r value is the private key of Bob, and R is his public key. First we need to generate a key pair for Bob and Alice. In this case, we will use the secp256k1 curve (as used in Bitcoin and Ethereum) [here]:

var curvename="secp256k1";

ECKeyPairGenerator gen = new ECKeyPairGenerator();
SecureRandom secureRandom = new SecureRandom();
X9ECParameters ecps = CustomNamedCurves.GetByName(curvename);
ECDomainParameters ecDomainParameters = new ECDomainParameters(ecps.Curve, ecps.G, ecps.N, ecps.H, ecps.GetSeed());
ECKeyGenerationParameters ecKeyGenerationParameters = new ECKeyGenerationParameters(ecDomainParameters, secureRandom);
gen.Init(ecKeyGenerationParameters);
var Bob= gen.GenerateKeyPair();
var Alice= gen.GenerateKeyPair();

And example of the key created is [here]:

  == Private key ===
== D ===3841231771602710188410453485275935201897382741865567142206078928610978212482
== Public key ===
== Q_x ===7fa36e791fb894f57f8c63b66b3f9a7ca02889d112e8991ac719849bfb746e34
== Q_t ===e0da1bc673fa6fed4bb322e1f233cd63b30cd9028e192ac5daff07c9215780a2

And where we have 256-bits for the private key (D), and 256 bits each of the x and y co-ordinates of the public key (Q). Next Alice will create an encryption engine [here]:

   var d = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
var e = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };
var p = new IesWithCipherParameters(d, e, 64, 128);

// Encrypt

var cryptEng = new IesEngine(new ECDHBasicAgreement(),
new Kdf2BytesGenerator(new Sha256Digest()),
new HMac(new Sha256Digest()));

In this case, we have a 64 bit HMAC to authenticate the message, and a 128 bit encryption key (generated with PBKDF2) that will be generated. Overall, we are just using the standard ECDH mechanism for key exchange, and use the SHA256 hashing method. Once created we can now use Bob’s public key (Q_B) and Alice’s private key (b) to encrypt the data (str) [here]:


cryptEng.Init(true, Alice.Private, Bob.Public, p);
byte[] data = Encoding.ASCII.GetBytes(str);
byte[] enc = cryptEng.ProcessBlock(data,0, data.Length);

Notice that “true” is set of the engine — and which enables encryption. If it was “false” we would decrypt. Alice then passed the ciphertext bytes (enc) to Bob, and where Bob then takes Alice’s public key (Q_a) and his private key (b) to reproduce the secret key (S_k) [here]:

var decryptEng = new IesEngine(new ECDHBasicAgreement(),
new Kdf2BytesGenerator(new Sha256Digest()),
new HMac(new Sha256Digest()));

decryptEng.Init(true, Bob.Private, Alice.Public, p);
byte[] plain = decryptEng.ProcessBlock(enc,0,data.Length);

We should now be able to reproduce the message. The fully code is [here]:

namespace ECIES
{
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Generators;
using System.Text;

using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto.EC;
using Org.BouncyCastle.Crypto.Agreement;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Macs;

class Program
{
static void Main(string[] args)
{

string str="Hello";

var curvename="secp256k1";


if (args.Length >0) str=args[0];
if (args.Length >1) curvename=args[1];



try {


ECKeyPairGenerator gen = new ECKeyPairGenerator();
SecureRandom secureRandom = new SecureRandom();
X9ECParameters ecps = CustomNamedCurves.GetByName(curvename);
ECDomainParameters ecDomainParameters = new ECDomainParameters(ecps.Curve, ecps.G, ecps.N, ecps.H, ecps.GetSeed());
ECKeyGenerationParameters ecKeyGenerationParameters = new ECKeyGenerationParameters(ecDomainParameters, secureRandom);
gen.Init(ecKeyGenerationParameters);
var Bob= gen.GenerateKeyPair();
var Alice= gen.GenerateKeyPair();

var d = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
var e = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };
var p = new IesWithCipherParameters(d, e, 64, 128);

// Encrypt

var cryptEng = new IesEngine(new ECDHBasicAgreement(),new Kdf2BytesGenerator(new Sha256Digest()), new HMac(new Sha256Digest()));

cryptEng.Init(true, Alice.Private, Bob.Public, p);

byte[] data = Encoding.ASCII.GetBytes(str);

byte[] enc = cryptEng.ProcessBlock(data,0, data.Length);

// Decrypt
var decryptEng = new IesEngine(new ECDHBasicAgreement(),new Kdf2BytesGenerator(new Sha256Digest()), new HMac(new Sha256Digest()));


decryptEng.Init(true, Bob.Private, Alice.Public, p);
byte[] plain = decryptEng.ProcessBlock(enc,0,data.Length);

var p1=new byte[data.Length];
Buffer.BlockCopy(plain, 0, p1, 0, data.Length);

Console.WriteLine("Plain: {0} ",str);
Console.WriteLine("Cipher: {0} [{1}]",Convert.ToHexString(enc),Convert.ToBase64String(enc));
Console.WriteLine("Decrypted: {0}" ,System.Text.Encoding.UTF8.GetString(p1));

Console.WriteLine("\n== Curve: {0} ",curvename);
Console.WriteLine("\n==Alice keys == ");

var privateKey = (ECPrivateKeyParameters) Alice.Private;
var publicKey = (ECPublicKeyParameters) Alice.Public;



Console.WriteLine("\n == Private key === ");
Console.WriteLine(" == D ==={0} ",privateKey.D.ToString());
Console.WriteLine(" == Public key === ");
Console.WriteLine(" == Q_x ==={0} ",publicKey.Q.XCoord);
Console.WriteLine(" == Q_t ==={0} ",publicKey.Q.YCoord);

Console.WriteLine("\n==Bob keys == ");

privateKey = (ECPrivateKeyParameters) Bob.Private;
publicKey = (ECPublicKeyParameters) Bob.Public;

Console.WriteLine("\n == Private key === ");
Console.WriteLine(" == D ==={0} ",privateKey.D.ToString());
Console.WriteLine(" == 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}", ecps.G, ecps.N, ecps.H);

Console.WriteLine("A={0}\nB={1}\nField size={2}",ecps.Curve.A,ecps.Curve.B,ecps.Curve.FieldSize);


} catch (Exception e) {
Console.WriteLine("Error: {0}",e.Message);
}

}
}
}

A sample run for the secp256k1 curve is [here]:

Plain: Hello 
Cipher: 31038D7ADD91B90C6E71E50D32AEC1559FE96DB503AF43AA629617A43982E9D0C3C05A30FF [MQONet2RuQxuceUNMq7BVZ/pbbUDr0OqYpYXpDmC6dDDwFow/w==]
Decrypted: Hello
== Curve: secp256k1

==Alice keys ==

== Private key ===
== D ===39855571539735288245174841749593796378311316776578132394591841364246914713315
== Public key ===
== Q_x ===61320495a5a0efbe6b1860bc1d696bd6ef8af297dd962275131d888d7afcb8f5
== Q_t ===84741948d3df4b708fa8c22f1c12bf6fd806319cdf79cf835fdfc74a4625b7ed

==Bob keys ==

== Private key ===
== D ===62019888968221020889047373090215882270275387610524242198420808128215464174367
== Public key ===
== Q_x ===6d10d8b4315de87ab2ccd82185615895f5c037d190f6c393a920ebaa396af65f
== Q_t ===6f458a7a92fd00719fc9abea7db2917aba91d9f10883ac584b1de80833cce185


Curve details: G=(79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8,1), N=115792089237316195423570985008687907852837564279074904382605163141518161494337, H=1
A=0
B=7
Field size=256

and for NIST P-256 [here]:

Plain: Hello 
Cipher: 91434A7306EBC47FF9CD8B51252BDA8BDD01EFAC654AC2E676FD3E2B6225593FC4B1D969F1 [kUNKcwbrxH/5zYtRJSvai90B76xlSsLmdv0+K2IlWT/Esdlp8Q==]
Decrypted: Hello

== Curve: P-256

==Alice keys ==

== Private key ===
== D ===72742223512423626839604052339160414626364888362784611776688095462938781924195
== Public key ===
== Q_x ===c24f13068058f844d2043981f294d0e24ab36e57d468d2677fae6fbceb425f52
== Q_t ===94d859745dd1c0b93a1324a5db6195b69cad5d21f24aec32a93acb1028a6045f

==Bob keys ==

== Private key ===
== D ===64433841991888082987226572118487149060591503000145367802550524952878206734630
== Public key ===
== Q_x ===1c436a0e6b283b5b0c6c9094b6fdacedfcf98c243a1c541e1cb7c334496f6f78
== Q_t ===90c90c299d332d1bbcd729e4acb588ecda8b5126c042b13422b18ac1585903b


Curve details: G=(6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296,4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5,1), N=115792089210356248762697446949407573529996955224135760342422259061068512044369, H=1
A=ffffffff00000001000000000000000000000000fffffffffffffffffffffffc
B=5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b
Field size=256

The great advantage of using Alice’s public key in the generation of S_k is that it is an equivalent to a digital signature. If Alice does not have a key pair, she can generate a random scalar value (r) and produce R=rG. She can then use S= r.Q_b to derive the shared secret key. The value of R will be passed along with the ciphertext (C):

Conclusions

Isn’t that a beautiful method? At it’s core is the ECDH approach that we use for virtually every Web page connection that we make, but it is done in an off-line approach. And, so we have the power of public key encryption matched with the speed of symmetric key encryption.