Commit 7c20a5c5879 for nodejs
commit 7c20a5c587922297a5148a429fda7c0f19e12ed2
Author: Filip Skokan <panva.ip@gmail.com>
Date: Tue Sep 22 22:18:28 2026 +0200
crypto: check RSA JWK alg with SHA-3 hashes
Reject a supplied JWK alg when no matching identifier exists for the
requested RSA algorithm and hash.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/66237
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Aviv Keller <me@aviv.sh>
diff --git a/lib/internal/crypto/rsa.js b/lib/internal/crypto/rsa.js
index 27b23087d56..81ca6723429 100644
--- a/lib/internal/crypto/rsa.js
+++ b/lib/internal/crypto/rsa.js
@@ -202,7 +202,7 @@ function rsaImportKey(
algorithm.name === 'RSA-PSS' ? normalizeHashName.kContextJwkRsaPss :
normalizeHashName.kContextJwkRsaOaep);
- if (expected && keyData.alg !== expected)
+ if (keyData.alg !== expected)
throw lazyDOMException(
'JWK "alg" does not match the requested algorithm',
'DataError');
diff --git a/test/parallel/test-webcrypto-rsa-jwk-sha3-alg.js b/test/parallel/test-webcrypto-rsa-jwk-sha3-alg.js
new file mode 100644
index 00000000000..88bc64194e6
--- /dev/null
+++ b/test/parallel/test-webcrypto-rsa-jwk-sha3-alg.js
@@ -0,0 +1,42 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const { isBoringSSL } = require('../common/crypto');
+if (isBoringSSL)
+ common.skip('missing SHA-3');
+
+const assert = require('assert');
+const { createPrivateKey, createPublicKey } = require('crypto');
+const fixtures = require('../common/fixtures');
+const { subtle } = globalThis.crypto;
+
+(async () => {
+ const privateKey = createPrivateKey(fixtures.readKey('rsa_private_2048.pem'));
+ const privateJwk = privateKey.export({ format: 'jwk' });
+ const publicJwk = createPublicKey(privateKey).export({ format: 'jwk' });
+
+ for (const name of ['RSA-PSS', 'RSASSA-PKCS1-v1_5', 'RSA-OAEP']) {
+ for (const hash of ['SHA3-256', 'SHA3-384', 'SHA3-512']) {
+ for (const jwk of [publicJwk, privateJwk]) {
+ const usages = name === 'RSA-OAEP' ? [jwk.d ? 'decrypt' : 'encrypt'] :
+ [jwk.d ? 'sign' : 'verify'];
+ const algorithm = { name, hash };
+ // There is no JWK alg identifier for RSA with SHA-3. Omitting alg is
+ // valid, but an identifier for SHA-2 or an unknown identifier is not.
+ const key = await subtle.importKey('jwk', jwk, algorithm, true, usages);
+ const exported = await subtle.exportKey('jwk', key);
+ assert.strictEqual(Object.hasOwn(exported, 'alg'), false);
+ const imported = await subtle.importKey('jwk', exported, algorithm, true, usages);
+ assert.deepStrictEqual(imported.algorithm, key.algorithm);
+ assert.deepStrictEqual(await subtle.exportKey('jwk', imported), exported);
+ for (const alg of ['RS256', 'PS256', 'RSA-OAEP-256', 'unknown']) {
+ await assert.rejects(subtle.importKey(
+ 'jwk', { ...jwk, alg }, algorithm, true, usages), { name: 'DataError' });
+ }
+ }
+ }
+ }
+})().then(common.mustCall());