Криптография с эллиптической кривой с помощью SJCL в JS и OpenSSL в Ruby

Я работаю над веб-приложением, которое должно иметь возможность шифровать данные с помощью ECC на стороне сервера и расшифровывать их в браузере. Единственная найденная мной библиотека в JS - это SJCL. Однако, поскольку поддержка ECC в SJCL в настоящий момент кажется немного заброшенной, я использовал форк, который имеет поддержку сериализации ключей и демонстрацию для более легкого понимания.

Сначала я генерирую пару ключей ECC в JS:

keypair = sjcl.ecc.elGamal.generateKeys(384, 10);
document.writeln(JSON.stringify(keypair.pub.serialize()));

Это выводит что-то вроде:

{"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}

Затем я попытался преобразовать этот открытый ключ в формат, понятный 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)

До этого момента мой код работает нормально. То есть он не производит никаких исключений.

Однако при создании общего секрета с обеими библиотеками я обнаружил, что SJCL генерирует "тег" длиной 96 байтов, тогда как OpenSSL выдает 48 байтов.

Оказывается, моя проблема в том, что SJCL не использует простой ECDH. Он использует что-то вроде ECMQV на основе быстрого поиска в Google. Таким образом, вывод SJCL 'tag' представлял собой точку на кривой (координаты x и y точки, 2*48 байт), в то время как вывод OpenSSL был общим секретом (координата x точки, как предписано ECDH).

Моя проблема в том, что я не знаю, есть ли какая-либо поддержка ECMQV в OpenSSL (есть некоторые проблемы с патентами, если я прав). Даже если бы и было, привязка ruby, похоже, не поддерживает его.

Итак, мои актуальные вопросы:

  • мои выводы, документированные выше, верны?
  • если да, кто-нибудь знает какую-либо другую библиотеку ruby, которую я мог бы использовать вместо OpenSSL, которая поддерживает ECMQV?

1 ответ

Решение

Похоже, вы используете ElGamal в своем коде JavaScript. Я действительно не смог найти никакой реализации для ruby, альтернативы используют Crypto++ или libgcrypt и пишут некоторый клейкий код.

Ps: вместо этого kstr = линия, вы можете просто написать kstr = ar.pack 'N*'

Другие вопросы по тегам