CVE-2025-8885
Description
Allocation of Resources Without Limits or Throttling vulnerability in Legion of the Bouncy Castle Inc. BC Java bcprov on All (API modules), Legion of the Bouncy Castle Inc. BC-FJA bc-fips on All allows Excessive Allocation. This vulnerability is associated with program files https://github.com/bcgit/bc-java/blob/main/core/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdenti... https://github.com/bcgit/bc-java/blob/main/core/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.Java .
This issue affects BC Java: from 1.0 through 1.77; BC-FJA: from 1.0.0 through 1.0.2.5, from 2.0.0 through 2.0.1.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.bouncycastle:bcprov-jdk14Maven | >= 1.0, < 1.78 | 1.78 |
org.bouncycastle:bcprov-jdk15to18Maven | >= 1.0, < 1.78 | 1.78 |
org.bouncycastle:bcprov-jdk18onMaven | >= 1.0, < 1.78 | 1.78 |
org.bouncycastle:bctls-jdk14Maven | >= 1.0, < 1.78 | 1.78 |
org.bouncycastle:bctls-jdk15to18Maven | >= 1.0, < 1.78 | 1.78 |
org.bouncycastle:bctls-jdk18onMaven | >= 1.0, < 1.78 | 1.78 |
org.bouncycastle:bc-fipsMaven | >= 1.0.0, < 1.0.2.6 | 1.0.2.6 |
org.bouncycastle:bc-fipsMaven | >= 2.0.0, < 2.0.1 | 2.0.1 |
Affected products
1Patches
13790993df5d2ASN.1: Limit OID contents to 4096 bytes
3 files changed · +142 −73
core/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java+6 −0 modified@@ -546,15 +546,21 @@ static ASN1Primitive createPrimitiveDERObject( case OBJECT_DESCRIPTOR: return ASN1ObjectDescriptor.createPrimitive(defIn.toByteArray()); case OBJECT_IDENTIFIER: + { + ASN1ObjectIdentifier.checkContentsLength(defIn.getRemaining()); // TODO Ideally only clone if we used a buffer return ASN1ObjectIdentifier.createPrimitive(getBuffer(defIn, tmpBuffers), true); + } case OCTET_STRING: return ASN1OctetString.createPrimitive(defIn.toByteArray()); case PRINTABLE_STRING: return ASN1PrintableString.createPrimitive(defIn.toByteArray()); case RELATIVE_OID: + { + ASN1RelativeOID.checkContentsLength(defIn.getRemaining()); // TODO Ideally only clone if we used a buffer return ASN1RelativeOID.createPrimitive(getBuffer(defIn, tmpBuffers), true); + } case T61_STRING: return ASN1T61String.createPrimitive(defIn.toByteArray()); case UNIVERSAL_STRING:
core/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java+71 −39 modified@@ -22,6 +22,17 @@ ASN1Primitive fromImplicitPrimitive(DEROctetString octetString) } }; + /** + * Implementation limit on the length of the contents octets for an Object Identifier. + * <p/> + * We adopt the same value used by OpenJDK. In theory there is no limit on the length of the contents, or + * the number of subidentifiers, or the length of individual subidentifiers. In practice, supporting + * arbitrary lengths can lead to issues, e.g. denial-of-service attacks when attempting to convert a + * parsed value to its (decimal) string form. + */ + private static final int MAX_CONTENTS_LENGTH = 4096; + private static final int MAX_IDENTIFIER_LENGTH = MAX_CONTENTS_LENGTH * 4 + 1; + public static ASN1ObjectIdentifier fromContents(byte[] contents) { if (contents == null) @@ -103,12 +114,16 @@ public static ASN1ObjectIdentifier tryFromID(String identifier) { throw new NullPointerException("'identifier' cannot be null"); } - if (!isValidIdentifier(identifier)) + if (identifier.length() <= MAX_IDENTIFIER_LENGTH && isValidIdentifier(identifier)) { - return null; + byte[] contents = parseIdentifier(identifier); + if (contents.length <= MAX_CONTENTS_LENGTH) + { + return new ASN1ObjectIdentifier(contents, identifier); + } } - return new ASN1ObjectIdentifier(parseIdentifier(identifier), identifier); + return null; } private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7F; @@ -126,28 +141,13 @@ public static ASN1ObjectIdentifier tryFromID(String identifier) */ public ASN1ObjectIdentifier(String identifier) { - if (identifier == null) - { - throw new NullPointerException("'identifier' cannot be null"); - } - if (!isValidIdentifier(identifier)) - { - throw new IllegalArgumentException("string " + identifier + " not an OID"); - } + checkIdentifier(identifier); - this.contents = parseIdentifier(identifier); - this.identifier = identifier; - } + byte[] contents = parseIdentifier(identifier); + checkContentsLength(contents.length); - private ASN1ObjectIdentifier(byte[] contents, boolean clone) - { - if (!ASN1RelativeOID.isValidContents(contents)) - { - throw new IllegalArgumentException("invalid OID contents"); - } - - this.contents = clone ? Arrays.clone(contents) : contents; - this.identifier = null; + this.contents = contents; + this.identifier = identifier; } private ASN1ObjectIdentifier(byte[] contents, String identifier) @@ -164,12 +164,12 @@ private ASN1ObjectIdentifier(byte[] contents, String identifier) */ public ASN1ObjectIdentifier branch(String branchID) { - if (!ASN1RelativeOID.isValidIdentifier(branchID, 0)) - { - throw new IllegalArgumentException("string " + branchID + " not a valid OID branch"); - } + ASN1RelativeOID.checkIdentifier(branchID); - byte[] contents = Arrays.concatenate(this.contents, ASN1RelativeOID.parseIdentifier(branchID)); + byte[] branchContents = ASN1RelativeOID.parseIdentifier(branchID); + checkContentsLength(this.contents.length + branchContents.length); + + byte[] contents = Arrays.concatenate(this.contents, branchContents); String identifier = getId() + "." + branchID; return new ASN1ObjectIdentifier(contents, identifier); @@ -246,6 +246,49 @@ public String toString() return getId(); } + static void checkContentsLength(int contentsLength) + { + if (contentsLength > MAX_CONTENTS_LENGTH) + { + throw new IllegalArgumentException("exceeded OID contents length limit"); + } + } + + static void checkIdentifier(String identifier) + { + if (identifier == null) + { + throw new NullPointerException("'identifier' cannot be null"); + } + if (identifier.length() > MAX_IDENTIFIER_LENGTH) + { + throw new IllegalArgumentException("exceeded OID contents length limit"); + } + if (!isValidIdentifier(identifier)) + { + throw new IllegalArgumentException("string " + identifier + " not a valid OID"); + } + } + + static ASN1ObjectIdentifier createPrimitive(byte[] contents, boolean clone) + { + checkContentsLength(contents.length); + + final OidHandle hdl = new OidHandle(contents); + ASN1ObjectIdentifier oid = pool.get(hdl); + if (oid != null) + { + return oid; + } + + if (!ASN1RelativeOID.isValidContents(contents)) + { + throw new IllegalArgumentException("invalid OID contents"); + } + + return new ASN1ObjectIdentifier(clone ? Arrays.clone(contents) : contents, null); + } + private static boolean isValidIdentifier(String identifier) { if (identifier.length() < 3 || identifier.charAt(1) != '.') @@ -447,15 +490,4 @@ public boolean equals(Object o) return false; } } - - static ASN1ObjectIdentifier createPrimitive(byte[] contents, boolean clone) - { - final OidHandle hdl = new OidHandle(contents); - ASN1ObjectIdentifier oid = pool.get(hdl); - if (oid != null) - { - return oid; - } - return new ASN1ObjectIdentifier(contents, clone); - } }
core/src/main/java/org/bouncycastle/asn1/ASN1RelativeOID.java+65 −34 modified@@ -19,6 +19,17 @@ ASN1Primitive fromImplicitPrimitive(DEROctetString octetString) } }; + /** + * Implementation limit on the length of the contents octets for a Relative OID. + * <p/> + * We adopt the same value used by OpenJDK for Object Identifier. In theory there is no limit on the + * length of the contents, or the number of subidentifiers, or the length of individual subidentifiers. In + * practice, supporting arbitrary lengths can lead to issues, e.g. denial-of-service attacks when + * attempting to convert a parsed value to its (decimal) string form. + */ + private static final int MAX_CONTENTS_LENGTH = 4096; + private static final int MAX_IDENTIFIER_LENGTH = MAX_CONTENTS_LENGTH * 4 - 1; + public static ASN1RelativeOID fromContents(byte[] contents) { if (contents == null) @@ -70,12 +81,16 @@ public static ASN1RelativeOID tryFromID(String identifier) { throw new NullPointerException("'identifier' cannot be null"); } - if (!isValidIdentifier(identifier, 0)) + if (identifier.length() <= MAX_IDENTIFIER_LENGTH && isValidIdentifier(identifier, 0)) { - return null; + byte[] contents = parseIdentifier(identifier); + if (contents.length <= MAX_CONTENTS_LENGTH) + { + return new ASN1RelativeOID(contents, identifier); + } } - return new ASN1RelativeOID(parseIdentifier(identifier), identifier); + return null; } private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7F; @@ -88,37 +103,13 @@ public static ASN1RelativeOID tryFromID(String identifier) public ASN1RelativeOID(String identifier) { - if (identifier == null) - { - throw new NullPointerException("'identifier' cannot be null"); - } - if (!isValidIdentifier(identifier, 0)) - { - throw new IllegalArgumentException("string " + identifier + " not a relative OID"); - } - - this.contents = parseIdentifier(identifier); - this.identifier = identifier; - } - - private ASN1RelativeOID(ASN1RelativeOID oid, String branchID) - { - if (!isValidIdentifier(branchID, 0)) - { - throw new IllegalArgumentException("string " + branchID + " not a valid relative OID branch"); - } + checkIdentifier(identifier); - this.contents = Arrays.concatenate(oid.contents, parseIdentifier(branchID)); - this.identifier = oid.getId() + "." + branchID; - } + byte[] contents = parseIdentifier(identifier); + checkContentsLength(contents.length); - private ASN1RelativeOID(byte[] contents, boolean clone) - { - if (!isValidContents(contents)) - throw new IllegalArgumentException("invalid relative OID contents"); - - this.contents = clone ? Arrays.clone(contents) : contents; - this.identifier = null; + this.contents = contents; + this.identifier = identifier; } private ASN1RelativeOID(byte[] contents, String identifier) @@ -129,7 +120,15 @@ private ASN1RelativeOID(byte[] contents, String identifier) public ASN1RelativeOID branch(String branchID) { - return new ASN1RelativeOID(this, branchID); + checkIdentifier(branchID); + + byte[] branchContents = parseIdentifier(branchID); + checkContentsLength(this.contents.length + branchContents.length); + + byte[] contents = Arrays.concatenate(this.contents, branchContents); + String identifier = getId() + "." + branchID; + + return new ASN1RelativeOID(contents, identifier); } public synchronized String getId() @@ -183,15 +182,47 @@ boolean encodeConstructed() return false; } + static void checkContentsLength(int contentsLength) + { + if (contentsLength > MAX_CONTENTS_LENGTH) + { + throw new IllegalArgumentException("exceeded relative OID contents length limit"); + } + } + + static void checkIdentifier(String identifier) + { + if (identifier == null) + { + throw new NullPointerException("'identifier' cannot be null"); + } + if (identifier.length() > MAX_IDENTIFIER_LENGTH) + { + throw new IllegalArgumentException("exceeded relative OID contents length limit"); + } + if (!isValidIdentifier(identifier, 0)) + { + throw new IllegalArgumentException("string " + identifier + " not a valid relative OID"); + } + } + static ASN1RelativeOID createPrimitive(byte[] contents, boolean clone) { + checkContentsLength(contents.length); + final ASN1ObjectIdentifier.OidHandle hdl = new ASN1ObjectIdentifier.OidHandle(contents); ASN1RelativeOID oid = pool.get(hdl); if (oid != null) { return oid; } - return new ASN1RelativeOID(contents, clone); + + if (!isValidContents(contents)) + { + throw new IllegalArgumentException("invalid relative OID contents"); + } + + return new ASN1RelativeOID(clone ? Arrays.clone(contents) : contents, null); } static boolean isValidContents(byte[] contents)
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
4News mentions
0No linked articles in our index yet.