Rsa

ECDSA 密鑰和 RSA 密鑰可以互換嗎?

  • February 24, 2019

是否可以將 ES512 密鑰對用於 RS512 簽名?是否可以將 RS512 密鑰對用於 ES512 簽名?我還在NodeJS jwa 包中發布了一個問題。

我使用以下命令為 EC512 創建了一個密鑰對:

openssl ecparam -genkey -name secp521r1 -noout -out ecdsa-p521-private.pem
openssl ec -in ecdsa-p521-private.pem -pubout -out ecdsa-p521-public.pem

然後,我沒有使用 ECDSA,而是使用了 RSA:

jwa = require('jwa');
rsa = jwa('RS512');
signature = rsa.sign('hello', fs.readFileSync('ecdsa-p521-private.pem').toString());
result = rsa.verify('hello', signature, fs.readFileSync('ecdsa-p521-public.pem').toString());

結果是真的!

這是預期的行為嗎?是否可以將 ECDSA 密鑰用於 RSA,反之亦然?

謝謝

    • 編輯 - -

以下是可供選擇的可能算法列表(用於我的 json-web-token 實現),因此我們將使用通用術語:

  • HS256:使用 SHA-256 雜湊算法的 HMAC
  • HS384:使用 SHA-384 雜湊算法的 HMAC
  • HS512:使用 SHA-512 雜湊算法的 HMAC
  • RS256:使用 SHA-256 雜湊算法的 RSASSA
  • RS384:使用 SHA-384 雜湊算法的 RSASSA
  • RS512:使用 SHA-512 雜湊算法的 RSASSA
  • ES256:ECDSA 使用 P-256 曲線和 SHA-256 雜湊算法
  • ES384:ECDSA 使用 P-384 曲線和 SHA-384 雜湊算法
  • ES512:ECDSA 使用 P-521 曲線和 SHA-512 雜湊算法
    • 編輯 - -

按照 Squeamish Ossifrage 的使用 ed25519 的建議,這是我的 Node.JS 程式碼,用於簽名和驗證“非標準 ed25519 json-web-tokens”。

const jwt = require('jsonwebtoken');
const base64url = require('base64url');
const ed25519 = require('ed25519');

const ed25519Header1 = base64url.encode(Buffer.from('{"alg":"Ed25519","typ":"JWT"}'));
const ed25519Header2 = base64url.encode(Buffer.from('{"typ":"JWT","alg":"Ed25519"}'));
const noneHeader = base64url.encode(Buffer.from('{"alg":"none","typ":"JWT"}'));

function sign(obj, privateKey, options) {
 const noneToken = jwt.sign(obj, '', { ...options, algorithm: "none" });
 if (jwt.decode(noneToken, {complete: true}).header.typ !== 'JWT') {
   throw new Error("Invalid object to sign");
 }
 const [, payload] = noneToken.split('.');
 const signature = base64url.encode(ed25519.Sign(
   Buffer.from(`${ed25519Header1}.${payload}`),
   { privateKey } // passing an object makes sure we want to sign with private-key, not seed.
 ));
 return `${ed25519Header1}.${payload}.${signature}`;
}


function verify(token, publicKey, options) {
 const [tokenHeader, payload, signature, ...leftover] = token.split('.');
 if ((tokenHeader !== ed25519Header1) && (tokenHeader !== ed25519Header2)) {
   throw new Error("ed25519 jwt malformed - unexpected header");
 }
 const signatureBuffer = base64url.toBuffer(signature || '');
 if (base64url.encode(signatureBuffer) !== signature) {
   throw new Error("ed25519 jwt malformed - invalid signature format");
 }
 // signature is a string, therefore payload is a string (not undefined)
 if (!ed25519.Verify(
   Buffer.from(`${tokenHeader}.${payload}`),
   base64url.toBuffer(signature),
   publicKey
 )) {
   throw new Error("Invalid signature");
 }
 return jwt.verify([noneHeader, payload, '', ...leftover].join('.'), '', {
   ...options,
   algorithms: ['none']
 });
}

使用ed25519 包的功能可以生成一對publicKeyprivateKeyprivateKey應該是 64 字節長,publicKey應該是 32 字節長)。MakeKeyPair(...)

該程式碼允許享受 ed25519 簽名和jsonwebtoken 包的功能。

例如:

var token = sign({hello:"world"}, privateKey, {expiresIn:'1 minute'})
console.log(verify(t, publicKey)); // prints { hello: 'world', iat: 1526078738, exp: 1526078798 }
// After 1 minute:
verify(t, publicKey); // Throws "TokenExpiredError: jwt expired"

原始碼

function createKeySigner(bits) {
return function sign(thing, privateKey) {
   if (!bufferOrString(privateKey) && !(typeof privateKey === 'object'))
     throw typeError(MSG_INVALID_SIGNER_KEY);
   thing = normalizeInput(thing);
   // Even though we are specifying "RSA" here, this works with ECDSA
   // keys as well.
   var signer = crypto.createSign('RSA-SHA' + bits);
   var sig = (signer.update(thing), signer.sign(privateKey, 'base64'));
   return base64url.fromBase64(sig);
 }
}

具體見評論。這裡發生的情況是,即使您將“帶有 SHA-512 的 RSA”指定為簽名算法('RS512'參數,在該庫的奇怪術語中),程式碼完全知道私鑰是用於 ECDSA,而不是 RSA(查看私鑰文件,它以“BEGIN EC PRIVATE KEY”開頭),因此它“有用地”透明地使用 ECDSA 而不是 RSA。

這並不神秘,只是一些 Node.js 程式碼在做 Node.js 的事情(即做它可以做的而不是你要求的,並希望得到最好的——一種相當可疑的方法,用於與安全相關的所有事情,特別是密碼學)。

引用自:https://crypto.stackexchange.com/questions/59164