Node.js和WebCrypto之间的ECDSA签名似乎不兼容? [英] ECDSA signatures between Node.js and WebCrypto appear to be incompatible?

查看:1393
本文介绍了Node.js和WebCrypto之间的ECDSA签名似乎不兼容?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用以下示例在Node.js中进行签名+验证: https://github.com/nodejs/node-v0.x-archive/issues/6904 。验证在Node.js中成功,但在WebCrypto中失败。类似地,使用WebCrypto签名的消息无法在Node.js中验证。



这里是我用来验证从Node.js脚本生成的签名的代码,使用WebCrypto - a href =https://jsfiddle.net/aj49e8sj/ =nofollow> https://jsfiddle.net/aj49e8sj/ 。在Chrome 54.0.2840.27和Firefox 48.0.2中测试

  //从https://github.com/nodejs/node -v0.x-archive / issues / 6904 
var keys = {
priv:'----- BEGIN EC PRIVATE KEY ----- \\\
'+
'MHcCAQEEIF + jnWY1D5kbVYDNvxxo / Y + ku2uJPDwS0r / VuPZQrjjVoAoGCCqGSM49\\\
'+
'AwEHoUQDQgAEurOxfSxmqIRYzJVagdZfMMSjRNNhB8i3mXyIMq704m2m52FdfKZ2\\\
'+
'pQhByd5eyj3lgZ7m7jbchtdgyOF8Io /为1ng == \\\
'+
'----- END EC PRIVATE KEY ----- \\\
',
pub:'----- BEGIN PUBLIC KEY ----- \\\
'+
'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEurOxfSxmqIRYzJVagdZfMMSjRNNh\\\
'+
'B8i3mXyIMq704m2m52FdfKZ2pQhByd5eyj3lgZ7m7jbchtdgyOF8Io / 1ng == \\\
'+
'----- END PUBLIC KEY ----- \\\
'
};
var message =(new TextEncoder('UTF-8'))。encode('hello');

// Node.js脚本中使用的算法是ecdsa-with-SHA1,使用prime256v1生成的密钥
var algorithm = {
name:'ECDSA',
namedCurve:'P-256',
hash:{
name:'SHA-1'
}
};

//通过上述Node.js脚本获得的签名
var sig64 ='MEUCIQDkAtiomagyHFi7dNfxMrzx / U0Gk / ZhmwCqaL3TimvlswIgPgeDqgZNqfR5 / FZZASYsczUAhGSXjuycLhWnvk20qKc =';

//将base64字符串解码为ArrayBuffer
var b64Decode =(str)=> Uint8Array.from(atob(str),x => x.charCodeAt(0));

//从公钥获取base64字符串
const key64 = keys.pub.split('\\\
')
.filter(x => x.length> ; 0&&!x.startsWith('-----'))
.join('');

//转换为缓冲区
var sig = b64Decode(sig64);
var keySpki = b64Decode(key64);

//导入并验证
//想要验证结果:true'但会得到'false'
var importKey = crypto.subtle.importKey('spki',keySpki ,algorithm,true,['verify'])
.then(key => crypto.subtle.verify(algorithm,key,sig,message))
.then(result => console。 log('验证结果:'+结果));

相似的问题使用SHA-256代替SHA-1:使用Node.js / crypto生成ECDSA签名



我检查过的东西:




  • 我解码了Node.js键并验证它们具有与生成的键相同的OID通过WebCrypto。


  • 显示为SHA-1。



如何成功验证从Node.js收到的签名,反之亦然 - 验证Node.js中从WebCrypto生成的签名?



编辑:




  • WebCrypto签名(64字节):uTaUWTfF + AjN3aPj0b5Z2d1HybUEpV / PHV / P9RtfKaGXtcYnbgfO43IRg46rznG3 / WnWwJ2sV6mPOEnEPR0vWw ==

  • Node.js的签名(71字节):MEUCIQDkAtiomagyHFi7dNfxMrzx / U0Gk / ZhmwCqaL3TimvlswIgPgeDqgZNqfR5 / FZZASYsczUAhGSXjuycLhWnvk20qKc =



已验证的Node.js签名是DER编码的,而WebCrypto签名不是。



Windows格式是具有预设大小(由DS为DS和ECDSA的P确定(Windows不支持Char-2,但如果它确实可能为Char-2 ECDSA的M)。然后 r s 左侧填充 0



r = 0x305 和<$ c的法律示例太小$ c> s = 0x810522 with sizeof(Q)is 3 bytes:

  // r 
000305
// s
810522

对于OpenSSL格式,它根据DER的规则编码为SEQUENCE(INTEGER(r),INTEGER(s)),它看起来像

  // SEQUENCE 
30
//(有效负载长度)
0A
// INTEGER(r)
02
//
02
//注意省略前导0x00
0305
// INTEGER(s)
02
//(有效载荷长度)
04
//由于INTEGER是一个有符号类型,但是这表示一个正数,
// a 0x00必须插入以保持符号位清零。
00810522

或紧凑:




  • Windows: 000305810522

  • OpenSSL: 300A02020305020400810522



Windows格式始终是偶数,始终具有相同的长度。 OpenSSL格式通常大约6个字节,但在中间可以获得或丢失一个字节;



Base64解码您的 sig64 值表明它正在使用DER编码。使用WebCrypto生成几个签名;如果有任何不以 0x30 开头,那么你有IEEE / DER问题。


I'm using the following example for signing + verifying in Node.js: https://github.com/nodejs/node-v0.x-archive/issues/6904. The verification succeeds in Node.js but fails in WebCrypto. Similarly, a message signed using WebCrypto fails to verify in Node.js.

Here's the code I used to verify a signature produced from the Node.js script using WebCrypto - https://jsfiddle.net/aj49e8sj/. Tested in both Chrome 54.0.2840.27 and Firefox 48.0.2

// From https://github.com/nodejs/node-v0.x-archive/issues/6904
var keys = {
  priv: '-----BEGIN EC PRIVATE KEY-----\n' +
        'MHcCAQEEIF+jnWY1D5kbVYDNvxxo/Y+ku2uJPDwS0r/VuPZQrjjVoAoGCCqGSM49\n' +
        'AwEHoUQDQgAEurOxfSxmqIRYzJVagdZfMMSjRNNhB8i3mXyIMq704m2m52FdfKZ2\n' +
        'pQhByd5eyj3lgZ7m7jbchtdgyOF8Io/1ng==\n' +
        '-----END EC PRIVATE KEY-----\n',
  pub: '-----BEGIN PUBLIC KEY-----\n' +
       'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEurOxfSxmqIRYzJVagdZfMMSjRNNh\n' +
       'B8i3mXyIMq704m2m52FdfKZ2pQhByd5eyj3lgZ7m7jbchtdgyOF8Io/1ng==\n' +
       '-----END PUBLIC KEY-----\n'
};
var message = (new TextEncoder('UTF-8')).encode('hello');

// Algorithm used in Node.js script is ecdsa-with-SHA1, key generated with prime256v1
var algorithm = {
    name: 'ECDSA',
    namedCurve: 'P-256',
    hash: {
        name: 'SHA-1'
    }
};

// Signature from obtained via above Node.js script
var sig64 = 'MEUCIQDkAtiomagyHFi7dNfxMrzx/U0Gk/ZhmwCqaL3TimvlswIgPgeDqgZNqfR5/FZZASYsczUAhGSXjuycLhWnvk20qKc=';

// Decode base64 string into ArrayBuffer
var b64Decode = (str) => Uint8Array.from(atob(str), x => x.charCodeAt(0));

// Get base64 string from public key
const key64 = keys.pub.split('\n')
    .filter(x => x.length > 0 && !x.startsWith('-----'))
    .join('');

// Convert to buffers
var sig = b64Decode(sig64);
var keySpki = b64Decode(key64);

// Import and verify
// Want 'Verification result: true' but will get 'false'
var importKey = crypto.subtle.importKey('spki', keySpki, algorithm, true, ['verify'])
    .then(key => crypto.subtle.verify(algorithm, key, sig, message))
    .then(result => console.log('Verification result: ' + result));

Related question with a similar issue using SHA-256 instead of SHA-1: Generating ECDSA signature with Node.js/crypto

Things I've checked:

  • I decoded the Node.js keys and verified they have the same OID as keys generated via WebCrypto. This tells me I'm using the correct curves.
  • SHA-1 is explicitly identified as the hash to use in both locations.
  • ECDSA is explicitly identified in both Node.js and WebCrypto.

How can I successfully verify the signature received from Node.js and vice versa - verify a signature in Node.js produced from WebCrypto? Or are the implementations of the standard subtly different in such a way that makes them incompatible?

Edit:

  • WebCrypto signature (64 bytes): uTaUWTfF+AjN3aPj0b5Z2d1HybUEpV/phv/P9RtfKaGXtcYnbgfO43IRg46rznG3/WnWwJ2sV6mPOEnEPR0vWw==
  • Node.js signature (71 bytes): MEUCIQDkAtiomagyHFi7dNfxMrzx/U0Gk/ZhmwCqaL3TimvlswIgPgeDqgZNqfR5/FZZASYsczUAhGSXjuycLhWnvk20qKc=

Verified Node.js signature is DER encoded and WebCrypto signature is not.

解决方案

Having not used either of these libraries I can't say for certain, but one possibility is that they don't use the same encoding type for the signature. For DSA/ECDSA there are two main formats, IEEE P1363 (used by Windows) and DER (used by OpenSSL).

The "Windows" format is to have a preset size (determined by Q for DSA and P for ECDSA (Windows doesn't support Char-2, but if it did it'd probably be M for Char-2 ECDSA)). Then both r and s are left-padded with 0 until they meet that length.

In the too small to be legal example of r = 0x305 and s = 0x810522 with sizeof(Q) being 3 bytes:

// r
000305
// s
810522

For the "OpenSSL" format it is encoded under the rules of DER as SEQUENCE(INTEGER(r), INTEGER(s)), which looks like

// SEQUENCE
30
  // (length of payload)
  0A
  // INTEGER(r)
  02
    // (length of payload)
    02
    // note the leading 0x00 is omitted
    0305
  // INTEGER(s)
  02
    // (length of payload)
    04
    // Since INTEGER is a signed type, but this represented a positive number,
    // a 0x00 has to be inserted to keep the sign bit clear.
    00810522

or, compactly:

  • Windows: 000305810522
  • OpenSSL: 300A02020305020400810522

The "Windows" format is always even, always the same length. The "OpenSSL" format is usually about 6 bytes bigger, but can gain or lose a byte in the middle; so it's sometimes even, sometimes odd.

Base64-decoding your sig64 value shows that it is using the DER encoding. Generate a couple signatures with WebCrypto; if any don't start with 0x30 then you have the IEEE/DER problem.

这篇关于Node.js和WebCrypto之间的ECDSA签名似乎不兼容?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆