I am working on a web application which must be able to encrypt data with ECC on the server side and decrypt it in the browser. The only library I have found that is capable of this in JS is SJCL. However, since ECC support in SJCL seems a bit abandoned at the moment, I have used a fork, which has key serialization support and a demo for easier understanding.
First, I generate an ECC key pair in JS:
keypair = sjcl.ecc.elGamal.generateKeys(384, 10);
document.writeln(JSON.stringify(keypair.pub.serialize()));
This outputs something like:
{"point":[1110230655,241884220,775655552,-849225963,-883815628,-1984298210,-736346431,1387519594,-1810604283,-1235638489,1333314084,-1219216530,614640565,-1148742381,1038670260,1013716131,758346573,1162278003,1232401864,-1948620456,533899535,-1478577959,1853846180,-1553049184],"curve":384}
Then I have tried to convert this public key to a format understandable by OpenSSL.
ar = [1110230655,241884220,775655552,-849225963,-883815628,-1984298210,-736346431,1387519594,-1810604283,-1235638489,1333314084,-1219216530,614640565,-1148742381,1038670260,1013716131,758346573,1162278003,1232401864,-1948620456,533899535,-1478577959,1853846180,-1553049184]
# ugly bit magic to somehow convert the above array into a proper byte array (in form of a string)
kstr = [(ar.map { |i| (i>=0)?('0'*(8-i.to_s(16).length)+i.to_s(16)):("%08X" % (2**32-1+i+1)) }*'').upcase].pack("H*")
# opening a public key generated with the openssl cli tool showed a structure like this:
algokey = OpenSSL::ASN1::ObjectId 'id-ecPublicKey'
algovalue = OpenSSL::ASN1::ObjectId 'secp384r1'
algo = OpenSSL::ASN1::Sequence.new [algokey,algovalue]
# for some reason OpenSSL seems to prepend 0x04 to all public keys
key = OpenSSL::ASN1::BitString.new "\x04#{kstr}"
root = OpenSSL::ASN1::Sequence.new [algo,key]
pub = OpenSSL::PKey.read(root.to_der)
Until this point, my code works fine. That is, it does not produce any exceptions.
However, when generating a shared secret with both libraries, I found that SJCL generated a 'tag' that was 96 bytes long, while OpenSSL emitted 48 bytes.
Turns out my problem is that SJCL does not use plain ECDH. It uses something that seems to be ECMQV based on a quick google search. Therefore, the 'tag' SJCL output was a point on the curve (x and y coordinates of a point, 2*48 bytes), while what OpenSSL output was a shared secret (x coordinate of a point, as dictated by ECDH).
My problem is that I don't know if there is any support for ECMQV in OpenSSL (there are some patent problems, if I'm correct). Even if there was, the ruby binding does not seem to support it.
So my actual questions:
It looks like you're using ElGamal in your javascript code. I couldn't really find any implementation for ruby, alternatives are using Crypto++ or libgcrypt and writing some glue code.
Ps: instead of that kstr =
line, you can simply write kstr = ar.pack 'N*'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With