почему криптографическая функция nodejs принимает только формат pem privateKey? - PullRequest
0 голосов
/ 12 октября 2019

сначала мы используем эллиптический криптографический пакет. функция знака выглядит следующим образом:

signByPriv = function (privKeyData, text) {
    let msgHash = getmsgHash(text, "SHA-384");
    let key = ec.keyFromPrivate(Buffer.from(privKeyData,'base64').toString('hex'), 'hex')
    let signature = key.sign(msgHash);
    return signature
}

тогда мы хотим изменить ее на версию nodejs, потому что nodejs использует openssl под капотом - так что это будет быстрее

сначала моя функция знака какниже:

signByPriv = function (privKeyData, text) {
    const sign1 = crypto.createSign('SHA384');  //hash do inside
    sign1.write(text);
    sign1.end();
    const signature = sign1.sign(privKeyData, 'hex');
    return signature;
}

будет выдано сообщение об ошибке:

internal / crypto / sig.js: 86 const ret = this [kHandle] .sign (данные, формат,type, passphrase, rsaPadding,

Ошибка: ошибка: 0909006C: процедуры PEM: get_name: нет начальной строки

, поэтому я проверил документы nodejs и обнаружил, что нужно передать privKeyс форматом pem 101

signByPriv = function (privKeyData, text) {
    let key = turnBase64PrivToPemKey(privKeyData) //base64 => pem
    const sign1 = crypto.createSign('SHA384');  //hash do inside
    sign1.write(text);
    sign1.end();
    const signature = sign1.sign(privKeyData, 'hex');
    return signature;
}
turnBase64PrivToPemKey = function (base64Priv) {
    var key_hex = Buffer.from(base64Priv, 'base64').toString('hex');
    ecdh.setPrivateKey(key_hex, 'hex')
    var pubKey_hex = ecdh.getPublicKey().toString('hex');
    //pem格式私钥文件是由固定字符加上私钥和公钥拼接而成==同一条曲线,固定字符相同
    var mykey = '308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b0201010420' + key_hex + 'a144034200' + pubKey_hex;
    privKey = '-----BEGIN PRIVATE KEY-----\n' + Buffer.from(mykey, 'hex').toString('base64') + '\n-----END PRIVATE KEY-----';
    pubKey = crypto.createPublicKey(privKey); //也可恢复出公钥
    let Key = {
        privKey,
        pubKey
    }
    return Key;
}

и отлично, все функции подписи и проверки работают отлично。

, но бэкэнд может делать то же самое глупо ...

Кривая, которую мы выбрали, проста256v1

const ecdh = crypto.createECDH ('prime256v1')

, поэтому мне интересно, почему знак nodejs func не может принимать только привилегии base64?

потому что формат pem состоит только из закрытого ключа, открытого ключа и другой фиксированной строки.

Ответы [ 2 ]

2 голосов
/ 13 октября 2019

Sign и Verify не только поддерживают PEM-формат, но также и DER-формат (оба описаны в ответ от Маартена Бодьюса). Кроме того, и Pkcs8- ( RFC 5208 и здесь ) и ключи Sec1-EC ( SECG SEC1, раздел C.4 и здесь ) может быть использован. Однако raw EC-ключи напрямую не поддерживаются. Поэтому, если ключи доступны только как необработанные ключи, преобразование всегда необходимо. Но это преобразование гораздо проще реализовать, чем в опубликованном коде, так что (на мой взгляд) не требуется значительных дополнительных усилий, например, для подписи:

var buf1 = Buffer.from('308141020100301306072a8648ce3d020106082a8648ce3d030107042730250201010420', 'hex'); // specific byte-sequence for curve prime256v1
var buf2 = Buffer.from('<Raw private key as hex string>', 'hex'); // raw private key (32 bytes)
var privateKeyPkcs8Der = Buffer.concat([buf1, buf2], buf1.length + buf2.length);
var sign = crypto.createSign('SHA384');
sign.write(<data to sign>);
sign.end();
var signature = sign.sign({ key: privateKeyPkcs8Der, format: 'der', type: 'pkcs8' }); // specify format and type

Используемый ключдля подписи privateKeyPkcs8Der - это ключ Pkcs8 в формате DER ( без необработанного открытого ключа).

В отличие от опубликованного кода:

  • вместо PEM-формата используется DER-формат.
  • только необработанный закрытый ключ встраивается в контейнер Pkcs8, а не необработанный открытый ключ.
  • buf1 содержит последовательность байтов, принадлежащую простому256v1 (secp256r1) и Pkcs8-контейнер без необработанного открытого ключа. Примечание : Последовательность байтов немного отличается от последовательности байтов, используемой в размещенном коде. Это связано с тем, что последовательность байтов также содержит информацию о длине, и это зависит от того, встроен ли необработанный открытый ключ или нет.

То же самое относится к проверке:

var buf1 = Buffer.from('3059301306072a8648ce3d020106082a8648ce3d030107034200', 'hex'); // specific byte-sequence for curve prime256v1
var buf2 = Buffer.from('<Raw public key as hex string>', 'hex'); // raw public key (uncompressed, 65 bytes, startting with 04)
var publicKeyX509Der = Buffer.concat([buf1, buf2], buf1.length + buf2.length);
var verify = crypto.createVerify('SHA384');    
verify.write(<data to sign>);
verify.end();
var verified = verify.verify({ key: publicKeyX509Der, format: 'der', type: 'spki' }, signature); // specify format and type

Ключ, используемый для проверки, publicKeyX509Der, является ключом X.509-SubjectPublicKeyInfo-key (SECG SEC1, раздел C.3 ) в формате DER.

Как и в случае подписи:

  • вместо формата PEM используется формат DER.
  • buf1 содержит последовательность байтов, принадлежащую prime256v1.

В опубликованном коде методы класса ECDH используются для получения необработанного открытогоключ от необработанного закрытого ключа. Вместо этого метод createPublicKey - и export можно использовать для получения ключа X.509-SubjectPublicKeyInfo из ключа Pkcs8:

var publicKey = crypto.createPublicKey({ key: privKeyPkcs8DER, type: 'pkcs8', format: 'der' });
var publicKeyX509Der = publicKey.export({type: 'spki', format: 'der'})

Здесь privateKeyPkcs8Der - это ключ Pkcs8 ( с или без необработанного открытого ключа), а publicKeyX509Der - это ключ X.509-SubjectPublicKeyInfo, оба в DER-формат.


Примечание : Вместо Pkcs8-контейнера можно также использовать Sec1-контейнер. Однако тогда структура ключа и последовательности байтов должны быть соответствующим образом адаптированы. Использование Sec1-контейнера описано здесь , но для другой кривой (secp256 k 1), так что последовательности байтов не могут быть просто скопированы.

2 голосов
/ 12 октября 2019

PEM - это стандарт, который обрабатывается OpenSSL. Он определяет строки заголовка, нижний колонтитул и конкретный тип базы 64 и способы обработки данных. Строка заголовка и нижний колонтитул также четко определяют тип данных, закодированных в базе 64. Первоначально они в основном использовались для почты;PEM означает почту с улучшенной конфиденциальностью.

Так что это преимущества перед базой 64. Вы можете четко определить начало и конец PEM, тип объекта, содержащегося в PEM, и т. Д. Обратите внимание, что PEMНасколько мне известно, предшествует любой официальный стандарт для базы 64. Он, конечно, существовал до этого, но каждый стандарт, который требовал кодирования / декодирования, просто переопределял его внутренне.

Так что да, вот как OpenSSL начал использовать PEM, а NodeJS, как вы упомянули, использует это.


Обратите внимание, что многие библиотеки также содержат декодер base 64. В базе 64 обычно существует двоичный формат, называемый DER, содержащий данные, описываемые языком описания данных ASN.1. Если ваша библиотека может обрабатывать DER, то вы можете просто самостоятельно декодировать base 64 и использовать функциональность ASN.1 в своей библиотеке криптографии. Командная строка OpenSSL может сделать это с помощью команды base64 и переключателя -inform DER.

...