Rsa
ECDSA 密鑰和 RSA 密鑰可以互換嗎?
是否可以將 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 包的功能可以生成一對publicKey和privateKey(privateKey應該是 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 的事情(即做它可以做的而不是你要求的,並希望得到最好的——一種相當可疑的方法,用於與安全相關的所有事情,特別是密碼學)。