Estoy trabajando en una aplicación web que debe poder cifrar datos con ECC en el servidor y descifrarlos en el navegador. La única biblioteca que he encontrado que es capaz de esto en JS es SJCL. Sin embargo, dado que el soporte de ECC en SJCL parece un poco abandonado en este momento, he usado un fork, que tiene soporte de serialización de claves y un demo para una mejor comprensión.Criptografía de curva elíptica con SJCL en JS y OpenSSL en Ruby
En primer lugar, generar un par de claves ECC en JS:
keypair = sjcl.ecc.elGamal.generateKeys(384, 10);
document.writeln(JSON.stringify(keypair.pub.serialize()));
Esto da salida a algo como:
{"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}
Luego he tratado de convertir esta clave pública a un formato comprensible por 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)
Hasta este punto, mi código funciona bien. Es decir, no produce ninguna excepción.
Sin embargo, al generar un secreto compartido con ambas bibliotecas, encontré que SJCL generaba una 'etiqueta' de 96 bytes de longitud, mientras que OpenSSL emitía 48 bytes.
Resulta que mi problema es que SJCL no utiliza ECDH simple. Utiliza algo que parece ser ECMQV basado en una búsqueda rápida en Google. Por lo tanto, la salida 'tag' SJCL era un punto en la curva (coordenadas xey de un punto, 2 * 48 bytes), mientras que la salida OpenSSL era un secreto compartido (coordenada x de un punto, según lo dictado por ECDH).
Mi problema es que no sé si hay soporte para ECMQV en OpenSSL (hay algunos problemas de patentes, si estoy en lo cierto). Incluso si lo hubiera, el enlace de rubí no parece apoyarlo.
Así que mi pregunta real:
- están mis resultados documentados anteriormente correcta?
- En caso afirmativo, ¿alguien sabe alguna otra biblioteca de ruby que pueda utilizar en lugar de OpenSSL, que admita ECMQV?
Por qué no preguntar a los desarrolladores de SJCL? ¿Sabe que la mayoría de los esquemas que usan cifrado en el navegador (sin aplicar SSL/TLS) son inseguros? –
Bueno, parece que preguntarle a los desarrolladores no tiene sentido ahora. :) Y sobre ser inseguro: soy consciente de ello, no estoy tratando de implementar el SSL del pobre. Se trata de proteger los datos del usuario de los intrusos que comprometen el servidor. Por supuesto, un intruso podría modificar el código JS en el servidor para enviarle la contraseña secreta a través de AJAX o algo así, pero esa es una historia diferente. – gsx1022
No sé si es una historia diferente, es un escenario de ataque en su modelo de seguridad. Pero si mantiene los datos de usuario locales en el navegador, de hecho puede agregar seguridad en caso, por ejemplo, el contenedor de datos que contiene los datos de los usuarios es robado. –