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());