CVE-2026-4600
Description
Versions of the package jsrsasign before 11.1.1 are vulnerable to Improper Verification of Cryptographic Signature via the DSA domain-parameter validation in KJUR.crypto.DSA.setPublic (and the related DSA/X509 verification flow in src/dsa-2.0.js). An attacker can forge DSA signatures or X.509 certificates that X509.verifySignature() accepts by supplying malicious domain parameters such as g=1, y=1, and a fixed r=1, which make the verification equation true for any hash.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
jsrsasignnpm | < 11.1.1 | 11.1.1 |
Affected products
1- cpe:2.3:a:jsrsasign_project:jsrsasign:*:*:*:*:*:node.js:*:*Range: <11.1.1
Patches
137b4c06b145cMerge pull request #646 from Kr0emer/fix/bug-002-dsa-domain-params-validation
3 files changed · +81 −2
src/dsa-2.0.js+20 −1 modified@@ -47,7 +47,24 @@ KJUR.crypto.DSA = function() { _getVbyList = _ASN1HEX.getVbyList, _getVbyListEx = _ASN1HEX.getVbyListEx, _isASN1HEX = _ASN1HEX.isASN1HEX, - _BigInteger = BigInteger; + _BigInteger = BigInteger, + _BI_ONE = BigInteger.ONE; + + var _validatePublicArgs = function(p, q, g, y) { + if (p == null || q == null || g == null || y == null) + throw new Error("invalid DSA public key"); + + // FIPS 186-4 4.7: domain parameters and public key shall be validated. + if (_BI_ONE.compareTo(q) >= 0 || q.compareTo(p) >= 0) + throw new Error("invalid DSA public key"); + if (_BI_ONE.compareTo(g) >= 0 || g.compareTo(p) >= 0) + throw new Error("invalid DSA public key"); + if (_BI_ONE.compareTo(y) >= 0 || y.compareTo(p) >= 0) + throw new Error("invalid DSA public key"); + if (g.modPow(q, p).compareTo(_BI_ONE) != 0) + throw new Error("invalid DSA public key"); + }; + this.p = null; this.q = null; this.g = null; @@ -120,6 +137,8 @@ KJUR.crypto.DSA = function() { * @since jsrsasign 7.0.0 dsa 2.0.0 */ this.setPublic = function(p, q, g, y) { + _validatePublicArgs(p, q, g, y); + this.isPublic = true; this.p = p; this.q = q;
test/qunit-do-dsa.html+11 −0 modified@@ -129,6 +129,17 @@ ok(dsa2.verifyWithMessageHash(sHashHex, hSigVal), ""); }); +test("setPublicHex rejects invalid domain parameter g=1", function() { + var dsa = new KJUR.crypto.DSA(); + var f = false; + try { + dsa.setPublicHex("17", "0b", "01", "01"); + } catch (ex) { + f = true; + } + ok(f, "invalid dsa public key rejected"); +}); + test("signWithMessageHash retries when s is zero", function() { var pSmall = new BigInteger("17", 16); var qSmall = new BigInteger("0b", 16);
test/qunit-do-x509.html+50 −1 modified@@ -611,6 +611,56 @@ equal(x.verifySignature(pubkey), false, "false"); }); +test("verifySignature DSA forged cert with g=1 shall be rejected", function() { +var DERSeq = function(arr) { return new KJUR.asn1.DERSequence({array: arr}); }; +var DERSet = function(arr) { return new KJUR.asn1.DERSet({array: arr}); }; +var DERInt = function(v) { + if (typeof v == "string") return new KJUR.asn1.DERInteger({hex: v}); + return new KJUR.asn1.DERInteger({"int": v}); +}; +var DEROid = function(o) { return new KJUR.asn1.DERObjectIdentifier({oid: o}); }; +var DERUtf8 = function(s) { return new KJUR.asn1.DERUTF8String({str: s}); }; +var DERBits = function(hex) { return new KJUR.asn1.DERBitString({hex: "00" + hex}); }; +var DERUtcT = function(s) { return new KJUR.asn1.DERUTCTime({str: s}); }; +var DERTag = function(t, o) { + return new KJUR.asn1.DERTaggedObject({tag: t, explicit: true, obj: o}); +}; +var rdnCN = function(cn) { return DERSeq([DERSet([DERSeq([DEROid("2.5.4.3"), DERUtf8(cn)])])]); }; + +var pHex = "17"; +var qHex = "0b"; +var gHex = "01"; +var yHex = "01"; + +var dsaParams = DERSeq([DERInt(pHex), DERInt(qHex), DERInt(gHex)]); +var spki = DERSeq([DERSeq([DEROid("1.2.840.10040.4.1"), dsaParams]), + DERBits(DERInt(yHex).getEncodedHex())]); + +var tbs = DERSeq([ + DERTag("a0", DERInt(2)), + DERInt(1), + DERSeq([DEROid("1.2.840.10040.4.3")]), + rdnCN("Malicious CA"), + DERSeq([DERUtcT("250101000000Z"), DERUtcT("351231235959Z")]), + rdnCN("Malicious CA"), + spki +]); +var forgedSigHex = DERSeq([DERInt(1), DERInt(7)]).getEncodedHex(); +var certHex = DERSeq([tbs, DERSeq([DEROid("1.2.840.10040.4.3")]), DERBits(forgedSigHex)]).getEncodedHex(); +var certPEM = KJUR.asn1.ASN1Util.getPEMStringFromHex(certHex, "CERTIFICATE"); + +var accepted = false; +try { + var x = new X509(); + x.readCertPEM(certPEM); + var pubkey = x.getPublicKey(); + accepted = x.verifySignature(pubkey); +} catch (ex) { + accepted = false; +} +equal(accepted, false, "forged certificate rejected"); +}); + test("verifySignature ECDSA k1.self.sha1.cer with k1.pub.p8 VALID", function() { var pubkey = KEYUTIL.getKey(K1PUBP8); var x = new X509(); @@ -691,4 +741,3 @@ </body> </html> -
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/kjur/jsrsasign/commit/37b4c06b145c7bfd6bc2a6df5d0a12c56b15ef60nvdPatchWEB
- gist.github.com/Kr0emer/bf15ddc097176e951659a24a8e9002a7nvdExploitMitigationThird Party AdvisoryWEB
- github.com/advisories/GHSA-wvqx-v3f6-w8rhghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-4600ghsaADVISORY
- security.snyk.io/vuln/SNYK-JS-JSRSASIGN-15370940nvdThird Party AdvisoryWEB
- github.com/kjur/jsrsasign/pull/646nvdIssue TrackingWEB
News mentions
0No linked articles in our index yet.