2016-09-18 7 views
3

Я использую следующий пример для подписания + проверки в Node.js: https://github.com/nodejs/node-v0.x-archive/issues/6904. Проверка завершается успешно в Node.js, но не работает в WebCrypto. Аналогично, сообщение, подписанное с использованием WebCrypto, не проверяется в Node.js.Подписи ECDSA между Node.js и WebCrypto кажутся несовместимыми?

Вот код, который я использовал для проверки подписи, созданной с помощью сценария Node.js, с использованием WebCrypto - 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 и подтвердил, что они имеют тот же OID, что и ключи, сгенерированные через WebCrypto. Это говорит мне, что я использую правильные кривые.
  • SHA-1 явно обозначается как хеш для использования в обоих местоположениях.
  • ECDSA явно идентифицирован как в Node.js, так и в WebCrypto.

Как я могу успешно проверить подпись, полученную от Node.js, и наоборот - проверить подпись в Node.js, созданной с помощью WebCrypto? Или реализация стандартно тонко отличается таким образом, что делает их несовместимыми?

Редактировать:

  • 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» должен иметь предустановленный размер (определяется Q для DSA и P для ECDSA (Windows не поддерживает Char-2, но если это так, вероятно, будет M для Char-2 ECDSA)). Затем и r, и s оставляются с 0 до тех пор, пока они не будут соответствовать этой длине.

В слишком малы, чтобы быть законным примером r = 0x305 и s = 0x810522 с SizeOf (Q) составляют 3 байт:

// r 
000305 
// s 
810522 

Для формата «OpenSSL» оно кодируется согласно правилам DER в виде последовательности (Integer (г), Integer (ы)), который выглядит как

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

или компактно:

  • Wi 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

 Смежные вопросы

  • Нет связанных вопросов^_^