Using Elliptic-Curves Diffie-Hellman, I want to connect SLCL - JS (documentation) on client and OpenSSL - Ruby (documentation) on server.
I found a similar question here but it wasn't really answered properly and it was also not what I am really looking for since it uses sjcl.ecc.elGamal.generateKeys(384, 10)
whereas I am hoping of using sjcl.ecc.curves['c384']
<- NIST
Nevertheless, I still used and modified his code to test because I had problems using sjcl.ecc.curves['c384']
producing a single public point-key, and this is what I came up with.
//Javascript
keypair = sjcl.ecc.elGamal.generateKeys(384, 10);
console.log(keypair.pub._point.toBits()); //Changed from his serialize()
This outputs to
[-1992414123, 638637875, 1917312913, 73389700, -425224557, 743777818, 970253455, 723842951, -1751664279, 982132367, -1949786746, 1067402923, -869929568, 157928816, 1651634060, 1968161300, -216192372, -1858642177, -1345910998, -2128793177, -1325754797, 143080818, 1868787479, -484135391]
Using the output to ruby:
#Ruby
pointArr = [-1992414123, 638637875, 1917312913, 73389700, -425224557, 743777818, 970253455, 723842951, -1751664279, 982132367, -1949786746, 1067402923, -869929568, 157928816, 1651634060, 1968161300, -216192372, -1858642177, -1345910998, -2128793177, -1325754797, 143080818, 1868787479, -484135391]
# ugly bit magic to somehow convert the above array into a proper byte array (in form of a string)
pointStr = [(pointArr.map { |i| (i>=0)?('0'*(8-i.to_s(16).length)+i.to_s(16)):("%08X" % (2**32-1+i+1)) }*'').upcase].pack("H*")
#My modified code
pointInt = pointStr.unpack('B*').first.to_i(2) #Convert BitStr to integer
pointBN = OpenSSL::BN.new(pointInt.to_s, 10) #Int to BigNumber (to be used as param below)
group = OpenSSL::PKey::EC::Group.new('secp384r1') #EC Group to be used
client_pub_point = OpenSSL::PKey::EC::Point.new(group, pointBN)
# ^
# ^ ABOVE'S MY PROBLEM -> OpenSSL::PKey::EC::Point::Error: invalid encoding
# ^
#Server EC: code taken and modified from https://www.ruby-forum.com/topic/3966195
ec = OpenSSL::PKey::EC.new(group)
ec.generate_key
pub = OpenSSL::PKey::EC.new(group)
pub.public_key = client_pub_point
#Compute Shared Key
shared_key = ec.dh_compute_key(pub.public_key)
puts shared_key.unpack('I>*')
This 'puts' something like below when original code from [(link)] (https://www.ruby-forum.com/topic/3966195) above is used
3747233514
2683763564
475565567
1087119841
857380668
2490387914
3548975947
2348082236
2093543365
1477205987
4289120093
3330807042
That should be it, but just in case here's my test
irb(main):113:0> ec = OpenSSL::PKey::EC.new(group)
=> #<OpenSSL::PKey::EC:0x37f4250>
irb(main):114:0> ec.generate_key
=> #<OpenSSL::PKey::EC:0x37f4250>
irb(main):115:0> pub = OpenSSL::PKey::EC.new(group)
=> #<OpenSSL::PKey::EC:0x374f070>
irb(main):116:0> pub.public_key = ec.public_key
=> #<OpenSSL::PKey::EC::Point:0x37f8090>
irb(main):117:0> pub.public_key.to_bn
=> 7699789176960498967958014210931326569901199635665512831714857096185925821659134057981449113945854620725216613989823482205311316333140754760317456176281271361802541262755346331375041208726203461213190230560617504850860621520632944763
irb(main):119:0> OpenSSL::PKey::EC::Point.new(group, pub.public_key.to_bn)
=> #<OpenSSL::PKey::EC:0x4029f48>
#The ABOVE FORMAT works, unlike the error I got like the following
irb(main):122:0> pointBN
=> 832312614609895991150696681555479456971598284480953722479085426901428295415600048953528780331647571635767075686130334170313461289491500162782258792834115040597490936949579748064005380309022482780162833924377801386781542770068991521
irb(main):123:0> OpenSSL::PKey::EC::Point.new(group, pointBN)
OpenSSL::PKey::EC::Point::Error: invalid encoding
But comparing the working and the not-working just above, it seems that total number of decimal digits are the same, so I think I'm somewhat on the right track, but I just couldn't really work it out.
For those who might run into this kind of problem, these are my reference codes (1) (2) (3) (4) (5)
I'm stuck on this for two days now and there seems to be not much written about this on the net, and I couldn't find any other JS library that supports elliptic curve. Any help would be much appreciated.
#Server EC: code taken and modified from https://www.ruby-forum.com/topic/3966195
ec = OpenSSL::PKey::EC.new(group)
ec.generate_key
...
Usually, we just call EC_KEY_new_by_curve_name
with the NID of a well known curve. The NIDs would include:
NID_secp160k1
NID_secp192k1
NID_secp224k1
NID_secp256k1
NID_secp384r1
NID_secp521r1
- other primary field curves
- other binary field curves
I think the complete list of them is available in the OpenSSL sources, obj_mac.h
:
#define SN_secp112r1 "secp112r1"
#define NID_secp112r1 704
#define OBJ_secp112r1 OBJ_secg_ellipticCurve,6L
#define SN_secp112r2 "secp112r2"
#define NID_secp112r2 705
#define OBJ_secp112r2 OBJ_secg_ellipticCurve,7L
#define SN_secp128r1 "secp128r1"
#define NID_secp128r1 706
#define OBJ_secp128r1 OBJ_secg_ellipticCurve,28L
#define SN_secp128r2 "secp128r2"
#define NID_secp128r2 707
#define OBJ_secp128r2 OBJ_secg_ellipticCurve,29L
#define SN_secp160k1 "secp160k1"
#define NID_secp160k1 708
#define OBJ_secp160k1 OBJ_secg_ellipticCurve,9L
#define SN_secp160r1 "secp160r1"
#define NID_secp160r1 709
#define OBJ_secp160r1 OBJ_secg_ellipticCurve,8L
#define SN_secp160r2 "secp160r2"
#define NID_secp160r2 710
#define OBJ_secp160r2 OBJ_secg_ellipticCurve,30L
#define SN_secp192k1 "secp192k1"
#define NID_secp192k1 711
#define OBJ_secp192k1 OBJ_secg_ellipticCurve,31L
#define SN_secp224k1 "secp224k1"
#define NID_secp224k1 712
#define OBJ_secp224k1 OBJ_secg_ellipticCurve,32L
#define SN_secp224r1 "secp224r1"
#define NID_secp224r1 713
#define OBJ_secp224r1 OBJ_secg_ellipticCurve,33L
#define SN_secp256k1 "secp256k1"
#define NID_secp256k1 714
#define OBJ_secp256k1 OBJ_secg_ellipticCurve,10L
#define SN_secp384r1 "secp384r1"
#define NID_secp384r1 715
#define OBJ_secp384r1 OBJ_secg_ellipticCurve,34L
#define SN_secp521r1 "secp521r1"
#define NID_secp521r1 716
#define OBJ_secp521r1 OBJ_secg_ellipticCurve,35L
#define SN_sect113r1 "sect113r1"
#define NID_sect113r1 717
#define OBJ_sect113r1 OBJ_secg_ellipticCurve,4L
#define SN_sect113r2 "sect113r2"
#define NID_sect113r2 718
#define OBJ_sect113r2 OBJ_secg_ellipticCurve,5L
#define SN_sect131r1 "sect131r1"
#define NID_sect131r1 719
#define OBJ_sect131r1 OBJ_secg_ellipticCurve,22L
#define SN_sect131r2 "sect131r2"
#define NID_sect131r2 720
#define OBJ_sect131r2 OBJ_secg_ellipticCurve,23L
#define SN_sect163k1 "sect163k1"
#define NID_sect163k1 721
#define OBJ_sect163k1 OBJ_secg_ellipticCurve,1L
#define SN_sect163r1 "sect163r1"
#define NID_sect163r1 722
#define OBJ_sect163r1 OBJ_secg_ellipticCurve,2L
#define SN_sect163r2 "sect163r2"
#define NID_sect163r2 723
#define OBJ_sect163r2 OBJ_secg_ellipticCurve,15L
#define SN_sect193r1 "sect193r1"
#define NID_sect193r1 724
#define OBJ_sect193r1 OBJ_secg_ellipticCurve,24L
#define SN_sect193r2 "sect193r2"
#define NID_sect193r2 725
#define OBJ_sect193r2 OBJ_secg_ellipticCurve,25L
#define SN_sect233k1 "sect233k1"
#define NID_sect233k1 726
#define OBJ_sect233k1 OBJ_secg_ellipticCurve,26L
#define SN_sect233r1 "sect233r1"
#define NID_sect233r1 727
#define OBJ_sect233r1 OBJ_secg_ellipticCurve,27L
#define SN_sect239k1 "sect239k1"
#define NID_sect239k1 728
#define OBJ_sect239k1 OBJ_secg_ellipticCurve,3L
#define SN_sect283k1 "sect283k1"
#define NID_sect283k1 729
#define OBJ_sect283k1 OBJ_secg_ellipticCurve,16L
#define SN_sect283r1 "sect283r1"
#define NID_sect283r1 730
#define OBJ_sect283r1 OBJ_secg_ellipticCurve,17L
#define SN_sect409k1 "sect409k1"
#define NID_sect409k1 731
#define OBJ_sect409k1 OBJ_secg_ellipticCurve,36L
#define SN_sect409r1 "sect409r1"
#define NID_sect409r1 732
#define OBJ_sect409r1 OBJ_secg_ellipticCurve,37L
#define SN_sect571k1 "sect571k1"
#define NID_sect571k1 733
#define OBJ_sect571k1 OBJ_secg_ellipticCurve,38L
#define SN_sect571r1 "sect571r1"
#define NID_sect571r1 734
#define OBJ_sect571r1 OBJ_secg_ellipticCurve,39L
...
#Compute Shared Key
shared_key = ec.dh_compute_key(pub.public_key)
There's an example of ECDH on the OpenSSL wiki. I believe Matt Caswell provided it. You can find it at Elliptic Curve Diffie Hellman.
I think the OpenSSL wiki example was also copy/pasted once here: ECDH secrets generated by BouncyCastle Java API and by OpenSSL are different.
I don't know SJCL or Ruby, so I can't help you.
sjcl.ecc.curves['c384'] <- NIST
I believe you would want NID_secp384r1
for this, but I don't know Ruby so take it at face value. For a list of named curves to NIDs, see OpenSSL: openssl/ CHANGES openssl/apps/ ecparam.c openssl/crypto....
If you are going to save the key to disk and possibly use it later in a certificate (for example, ECDSA signing), then you will want to set the OPENSSL_EC_NAMED_CURVE
flag. The flag simply saves the group parameters by name (for example, ASN1 OID: prime256v1
) rather than listing all the domain parameters in ASN1 objects (field type, primary or binary field, base point, etc).
You do that with:
EC_KEY_set_asn1_flag(key, OPENSSL_EC_NAMED_CURVE);
If you don't set the flag, then you will encounter "no shared ciphers" error when negotiating suites. Here are the symptoms on the client and server:
Client (s_client):
139925962778272:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure:s3_pkt.c:1256:SSL alert number 40
139925962778272:error:1409E0E5:SSL routines:SSL3_WRITE_BYTES:ssl handshake failure:s3_pkt.c:596:
Server (s_server):
140339533272744:error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher:s3_srvr.c:1353:
I've finally solved this problem! :)
I used a different Javascript library. If you're looking on solving similar problem like this, please refer to the other question of mine (link)