Функция Crypto.sign() для подписи сообщения с заданным закрытым ключом

Мне нужно подписать сообщение с помощью функции crypto.sign() в NodeJS, чтобы получить действительный JWT. У меня есть закрытый ключ (база 64), например:

      Dm2xriMD6riJagld4WCA6zWqtuWh40UzT/ZKO0pZgtHATOt0pGw90jG8BQHCE3EOjiCkFR2/gaW6JWi+3nZp8A==

И я попытался получить подпись:

      const getJWT = () => {
  const privateKey =
    "Dm2xriMD6riJagld4WCA6zWqtuWh40UzT/ZKO0pZgtHATOt0pGw90jG8BQHCE3EOjiCkFR2/gaW6JWi+3nZp8A==";
  const payload = {
    iss: "test",
    aud: "test.com",
    iat: 1650101178,
    exp: 1650101278,
    sub: "12345678-1234-1234-1234-123456789123"
  };
  const token = encode(payload, privateKey);
  return token
};

const encode = (payload, key) => {
  const header = {
    typ: "JWT",
    alg: "EdDSA"
  };
  const headerBase64URL = base64url(JSON.stringify(header));
  const payloadBase64URL = base64url(JSON.stringify(payload));
  const msg = Buffer.from(`${headerBase64URL}.${payloadBase64URL}`);
  const keyDecoded = Buffer.from(key, "base64");
  const signature = crypto.sign("Ed25519", msg, keyDecoded); //Here is the problem
  const signatureBase64url = base64url(Buffer.from(signature));
  return `${msg}.${signatureBase64url}`;
};

Я получил эту ошибку:

      internal/crypto/sig.js:142
  return _signOneShot(keyData, keyFormat, keyType, keyPassphrase, data,
         ^

Error: error:0909006C:PEM routines:get_name:no start line

  library: 'PEM routines',
  function: 'get_name',
  reason: 'no start line',
  code: 'ERR_OSSL_PEM_NO_START_LINE'

Как я могу адаптировать свой закрытый ключ к допустимому формату?

1 ответ

The crypto.sign()для Ed25519 требуется закрытый ключ в формате PKCS#8. Ваш ключ представляет собой необработанный ключ, состоящий из конкатенации необработанного закрытого 32-байтового ключа и необработанного общедоступного 32-байтового ключа в кодировке base64. Ключ PKCS#8 в кодировке DER можно получить и импортировать следующим образом:

  • Base64 декодирует ваш ключ. Используйте первые 32 байта вашего необработанного 64-байтового ключа (т.е. необработанный закрытый ключ).
  • Соедините следующий префикс для закрытого ключа Ed25519 (шестнадцатеричный): 302e020100300506032b657004220420
  • Импортируйте этот ключ PKCS#8 в кодировке DER.

Соответственно, ключевой импорт в getJWT()необходимо изменить следующим образом:

      const privateKey = toPkcs8der('Dm2xriMD6riJagld4WCA6zWqtuWh40UzT/ZKO0pZgtHATOt0pGw90jG8BQHCE3EOjiCkFR2/gaW6JWi+3nZp8A==');

с

      const toPkcs8der = (rawB64) => {
    var rawPrivate = Buffer.from(rawB64, 'base64').subarray(0, 32);
    var prefixPrivateEd25519 = Buffer.from('302e020100300506032b657004220420','hex');
    var der = Buffer.concat([prefixPrivateEd25519, rawPrivate]);
    return crypto.createPrivateKey({key: der, format: "der", type: "pkcs8"})
}

Кроме того, в encode()функция:

  • Удалить строку const keyDecoded = Buffer.from(key, "base64")

  • Создайте подпись с помощью

            const signature = crypto.sign(null, msg, key)
    

    Обратите внимание, что для Ed25519 nullдолжен быть передан как первый параметр в sign()вызов. Алгоритм исходит из ключа.

С этими изменениями код NodeJS возвращает следующий JWT:

      eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSJ9.eyJpc3MiOiJ0ZXN0IiwiYXVkIjoidGVzdC5jb20iLCJpYXQiOjE2NTAxMDExNzgsImV4cCI6MTY1MDEwMTI3OCwic3ViIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MTIzIn0.f7WG_02UKljrMeVVOTNNBAGxtLXJUT_8QAnujNhomV18Pn5cU-0lHRgVlmRttOlqI7Iol_fHut3C4AOXxDGnAQ

который соответствует ожидаемому JWT.

Другие вопросы по тегам