使用JS中的SJCL和Ruby中的OpenSSL进行椭圆曲线加密

时间:2021-06-08 18:25:06

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.

我正在开发一个Web应用程序,它必须能够在服务器端使用ECC加密数据并在浏览器中解密。我发现在JS中唯一可以使用的库是SJCL。但是,由于目前SJCL中的ECC支持似乎有点放弃,我使用了fork,它具有关键的序列化支持和演示,以便于理解。

First, I generate an ECC key pair in JS:

首先,我在JS中生成一个ECC密钥对:

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.

然后我尝试将此公钥转换为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.

但是,当与两个库生成共享密钥时,我发现SJCL生成了一个长度为96字节的“标记”,而OpenSSL则生成了48个字节。

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).

原来我的问题是SJCL不使用普通的ECDH。它基于快速谷歌搜索使用似乎是ECMQV的东西。因此,'标签'SJCL输出是曲线上的一个点(一个点的x和y坐标,2 * 48字节),而OpenSSL输出是一个共享秘密(一个点的x坐标,由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.

我的问题是我不知道OpenSSL中是否有对ECMQV的支持(如果我是正确的,有一些专利问题)。即使有,红宝石绑定似乎也不支持它。

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?
  • 如果是的话,有没有人知道我可以使用的任何其他ruby库而不是支持ECMQV的OpenSSL?

1 个解决方案

#1


2  

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.

看起来你在javascript代码中使用了ElGamal。我真的找不到ruby的任何实现,替代方案是使用Crypto ++或libgcrypt并编写一些粘合代码。

Ps: instead of that kstr = line, you can simply write kstr = ar.pack 'N*'

Ps:而不是kstr = line,你可以简单地写kstr = ar.pack'N *'

#1


2  

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.

看起来你在javascript代码中使用了ElGamal。我真的找不到ruby的任何实现,替代方案是使用Crypto ++或libgcrypt并编写一些粘合代码。

Ps: instead of that kstr = line, you can simply write kstr = ar.pack 'N*'

Ps:而不是kstr = line,你可以简单地写kstr = ar.pack'N *'