2016-09-18 68 views
3

我正在使用以下示例在Node.js中進行簽名+驗證:https://github.com/nodejs/node-v0.x-archive/issues/6904。驗證在Node.js中成功,但在WebCrypto中失敗。同樣,使用WebCrypto簽名的消息無法在Node.js中進行驗證。Node.js和WebCrypto之間的ECDSA簽名似乎不兼容?

下面是我用來驗證使用WebCrypto從Node.js腳本產生的簽名的代碼 - https://jsfiddle.net/aj49e8sj/。經測試在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類似的問題相關的問題都:Generating ECDSA signature with Node.js/crypto

事情我已經檢查:

  • 我對Node.js鍵進行了解碼,並驗證它們與通過WebCrypto生成的鍵具有相同的OID。這告訴我我正在使用正確的曲線。
  • SHA-1被顯式識別爲在兩個位置使用的散列。
  • ECDSA在Node.js和WebCrypto中均已明確標識。

如何成功驗證從Node.js收到的簽名,反之亦然 - 驗證從WebCrypto生成的Node.js中的簽名?或者標準的實現在這種方式中有微妙的差異,使得它們不兼容?

編輯:

  • WebCrypto簽名(64個字節):uTaUWTfF + AjN3aPj0b5Z2d1HybUEpV/PHV/P9RtfKaGXtcYnbgfO43IRg46rznG3/WnWwJ2sV6mPOEnEPR0vWw ==
  • Node.js的簽名(71個字節):MEUCIQDkAtiomagyHFi7dNfxMrzx/U0Gk/ZhmwCqaL3TimvlswIgPgeDqgZNqfR5/FZZASYsczUAhGSXjuycLhWnvk20qKc =

已驗證Node.js簽名是DER編碼,而WebCrypto簽名不是。

回答

1

未使用這些庫中的任何一個,我不能肯定地說,但有一種可能性是它們不使用相同的編碼類型進行簽名。對於DSA/ECDSA,有兩種主要格式,IEEE P1363(由Windows使用)和DER(由OpenSSL使用)。 「Windows」格式應具有預設大小(由DSA和Q確定的ECDSA(Windows不支持Char-2,但如果確實如此,則可能爲M-Char-2 ECDSA ))。然後rs0左填充,直到它們符合該長度。

在太小是r = 0x305s = 0x810522用的sizeof(Q)是3個字節法律例如:

// r 
000305 
// s 
810522 

對於它被下DER的規則序列編碼的「OpenSSL的」格式(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 

或,緊湊地:

  • 無線ndows:000305810522
  • 的OpenSSL:300A02020305020400810522

的 「Windows」 格式總是偶數,總是相同的長度。 「OpenSSL」格式通常大約6個字節,但可以在中間增加或減少一個字節;所以有時甚至有時候是奇怪的。

Base64解碼您的sig64值表明它正在使用DER編碼。用WebCrypto生成一對簽名;如果有任何不以0x30開頭,那麼你有IEEE/DER問題。

+0

正確的錢! Node.js在底層使用了OpenSSL,所以在這方面的DER編碼是有道理的。 WebCrypto規範說結果是r和s連接:https://www.w3.org/TR/WebCryptoAPI/#ecdsa-operations – SiNiquity