一尘不染

Node.js和WebCrypto之间的ECDSA签名似乎不兼容?

node.js

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

这是我用来验证使用WebCrypto- https:
//jsfiddle.net/aj49e8sj/从Node.js脚本生成的签名的代码。已在Chrome 54.0.2840.27和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));

使用SHA-256而不是SHA-1的类似问题的相关问题:使用Node.js /
crypto生成ECDSA签名

我检查过的事情:

  • 我解码了Node.js密钥,并验证了它们与通过WebCrypto生成的密钥具有相同的OID。这告诉我我使用了正确的曲线。
  • SHA-1被明确标识为要在两个位置使用的哈希。
  • 在Node.js和WebCrypto中都明确标识了ECDSA。

我如何才能成功验证从Node.js收到的签名,反之亦然-验证从WebCrypto生成的Node.js中的签名?还是以使它们不兼容的方式使标准的实现细微不同?

编辑:

  • WebCrypto签名(64字节):uTaUWTfF + AjN3aPj0b5Z2d1HybUEpV / phv / P9RtfKaGXtcYnbgfO43IRg46rznG3 / WnWwJ2sV6mPOEnEPR0vWw ==
  • Node.js签名(71字节):MEUCIQDkAtiomagyHFi7dNfxMrzx / U0Gk / ZhmwCqaL3TimvlswIgPgeDqgZNqfR5 / FZZASYsczUAhGSXjuycLhWnvk20qKc =

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


阅读 300

收藏
2020-07-07

共1个答案

一尘不染

我不能肯定地说没有使用这两个库中的任何一个,但是一种可能性是它们没有为签名使用相同的编码类型。对于DSA / ECDSA,有两种主要格式,IEEEP1363(Windows使用)和DER(OpenSSL使用)。

“ Windows”格式应具有预设的大小(由DS的Q值和ECDSA的P值决定(Windows不支持Char-2,但如果是的话,Char-2ECDSA则可能为M))。然后将rs都用左填充,0直到它们达到该长度。

在太小是合法例如r = 0x305s = 0x810522用的sizeof(Q)是3个字节:

// r
000305
// s
810522

对于“ OpenSSL”格式,它按照DER规则编码为SEQUENCE(INTEGER(r),INTEGER(s)),看起来像

// 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

或者,紧凑地:

  • Windows: 000305810522
  • OpenSSL: 300A02020305020400810522

“ Windows”格式始终为偶数,且长度始终相同。“ OpenSSL”格式通常大约大6个字节,但是中间可能会增加或丢失一个字节;所以有时候甚至是奇怪

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

2020-07-07