Elliptic curve cryptography with SJCL in JS and Op

2019-02-09 17:28发布

问题:

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:

  • are my findings documented above correct?
  • if yes, does anyone know any other ruby library which I could use instead of OpenSSL, that supports ECMQV?

回答1:

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*'