Medium severityGHSA Advisory· Published Apr 15, 2026· Updated Apr 17, 2026
CVE-2026-0636
CVE-2026-0636
Description
Improper neutralization of special elements used in an LDAP query ('LDAP injection') vulnerability in Legion of the Bouncy Castle Inc. BC-JAVA bcprov on all (prov modules). This vulnerability is associated with program files LDAPStoreHelper.
This issue affects BC-JAVA: from 1.74 before 1.84.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.bouncycastle:bcprov-jdk14Maven | >= 1.74, < 1.84 | 1.84 |
org.bouncycastle:bcprov-jdk15to18Maven | >= 1.74, < 1.84 | 1.84 |
org.bouncycastle:bcprov-jdk18onMaven | >= 1.74, < 1.84 | 1.84 |
Affected products
1Patches
1d20cdb8430e0refactored out common code from LDAP classes
3 files changed · +154 −158
prov/src/main/java/org/bouncycastle/jce/provider/X509LDAPCertStoreSpi.java+5 −96 modified@@ -34,6 +34,7 @@ import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.x509.CertificatePair; import org.bouncycastle.jce.X509LDAPCertStoreParameters; +import org.bouncycastle.ldap.LDAPUtils; import org.bouncycastle.util.Strings; /** @@ -50,26 +51,6 @@ public class X509LDAPCertStoreSpi extends CertStoreSpi { - private static String[] FILTER_ESCAPE_TABLE = new String['\\' + 1]; - - static - { - // Filter encoding table ------------------------------------- - - // fill with char itself - for (char c = 0; c < FILTER_ESCAPE_TABLE.length; c++) - { - FILTER_ESCAPE_TABLE[c] = String.valueOf(c); - } - - // escapes (RFC2254) - FILTER_ESCAPE_TABLE['*'] = "\\2a"; - FILTER_ESCAPE_TABLE['('] = "\\28"; - FILTER_ESCAPE_TABLE[')'] = "\\29"; - FILTER_ESCAPE_TABLE['\\'] = "\\5c"; - FILTER_ESCAPE_TABLE[0] = "\\00"; - } - /** * Initial Context Factory. */ @@ -124,42 +105,6 @@ private DirContext connectLDAP() return ctx; } - private String parseDN(String subject, String subjectAttributeName) - { - String temp = subject; - int begin = Strings.toLowerCase(temp).indexOf(Strings.toLowerCase(subjectAttributeName)); - temp = temp.substring(begin + subjectAttributeName.length()); - int end = temp.indexOf(','); - if (end == -1) - { - end = temp.length(); - } - while (temp.charAt(end - 1) == '\\') - { - end = temp.indexOf(',', end + 1); - if (end == -1) - { - end = temp.length(); - } - } - temp = temp.substring(0, end); - begin = temp.indexOf('='); - temp = temp.substring(begin + 1); - if (temp.charAt(0) == ' ') - { - temp = temp.substring(1); - } - if (temp.startsWith("\"")) - { - temp = temp.substring(1); - } - if (temp.endsWith("\"")) - { - temp = temp.substring(0, temp.length() - 1); - } - return filterEncode(temp); - } - public Collection engineGetCertificates(CertSelector selector) throws CertStoreException { @@ -277,7 +222,7 @@ private Set certSubjectSerialSearch(X509CertSelector xselector, subject = xselector.getSubjectAsString(); } } - String attrValue = parseDN(subject, subjectAttributeName); + String attrValue = LDAPUtils.parseDN(subject, subjectAttributeName); set.addAll(search(attrName, "*" + attrValue + "*", attrs)); if (serial != null && params.getSearchForSerialNumberIn() != null) @@ -374,13 +319,13 @@ public Collection engineGetCRLs(CRLSelector selector) { String issuerAttributeName = params .getCertificateRevocationListIssuerAttributeName(); - attrValue = parseDN((String)o, issuerAttributeName); + attrValue = LDAPUtils.parseDN((String)o, issuerAttributeName); } else { String issuerAttributeName = params .getCertificateRevocationListIssuerAttributeName(); - attrValue = parseDN(new X500Principal((byte[])o) + attrValue = LDAPUtils.parseDN(new X500Principal((byte[])o) .getName("RFC1779"), issuerAttributeName); } set.addAll(search(attrName, "*" + attrValue + "*", attrs)); @@ -415,43 +360,7 @@ public Collection engineGetCRLs(CRLSelector selector) return crlSet; } - - /** - * Escape a value for use in a filter. - * - * @param value the value to escape. - * @return a properly escaped representation of the supplied value. - */ - private String filterEncode(String value) - { - if (value == null) - { - return null; - } - - // make buffer roomy - StringBuilder encodedValue = new StringBuilder(value.length() * 2); - - int length = value.length(); - - for (int i = 0; i < length; i++) - { - char c = value.charAt(i); - - if (c < FILTER_ESCAPE_TABLE.length) - { - encodedValue.append(FILTER_ESCAPE_TABLE[c]); - } - else - { - // default: add the char - encodedValue.append(c); - } - } - - return encodedValue.toString(); - } - + /** * Returns a Set of byte arrays with the certificate or CRL encodings. *
prov/src/main/java/org/bouncycastle/ldap/LDAPUtils.java+112 −0 added@@ -0,0 +1,112 @@ +package org.bouncycastle.ldap; + +import org.bouncycastle.util.Strings; + +/** + * General utility methods for assisting with preparation of LDAP queries. + */ +public class LDAPUtils +{ + private static String[] FILTER_ESCAPE_TABLE = new String['\\' + 1]; + + static + { + // Filter encoding table ------------------------------------- + + // fill with char itself + for (char c = 0; c < FILTER_ESCAPE_TABLE.length; c++) + { + FILTER_ESCAPE_TABLE[c] = String.valueOf(c); + } + + // escapes (RFC2254) + FILTER_ESCAPE_TABLE['*'] = "\\2a"; + FILTER_ESCAPE_TABLE['('] = "\\28"; + FILTER_ESCAPE_TABLE[')'] = "\\29"; + FILTER_ESCAPE_TABLE['\\'] = "\\5c"; + FILTER_ESCAPE_TABLE[0] = "\\00"; + } + + /** + * Parse out the contents of a particular subject attribute name from the string form of an X.500 DN. + * + * @param subject string form of an X.500 DN. + * @param subjectAttributeName the RDN attribute name of interest. + * @return an escaped string suitable for use in an LDAP query. + */ + public static String parseDN(String subject, String subjectAttributeName) + { + String temp = subject; + int begin = Strings.toLowerCase(temp).indexOf(Strings.toLowerCase(subjectAttributeName)); + if (begin == -1) + { + return ""; + } + temp = temp.substring(begin + subjectAttributeName.length()); + int end = temp.indexOf(','); + if (end == -1) + { + end = temp.length(); + } + while (temp.charAt(end - 1) == '\\') + { + end = temp.indexOf(',', end + 1); + if (end == -1) + { + end = temp.length(); + } + } + temp = temp.substring(0, end); + begin = temp.indexOf('='); + temp = temp.substring(begin + 1); + if (temp.charAt(0) == ' ') + { + temp = temp.substring(1); + } + if (temp.startsWith("\"")) + { + temp = temp.substring(1); + } + if (temp.endsWith("\"")) + { + temp = temp.substring(0, temp.length() - 1); + } + return filterEncode(temp); + } + + /** + * Escape a value for use in a filter. + * + * @param value the value to escape. + * @return a properly escaped representation of the supplied value. + */ + private static String filterEncode(String value) + { + if (value == null) + { + return null; + } + + // make buffer roomy + StringBuilder encodedValue = new StringBuilder(value.length() * 2); + + int length = value.length(); + + for (int i = 0; i < length; i++) + { + char c = value.charAt(i); + + if (c < FILTER_ESCAPE_TABLE.length) + { + encodedValue.append(FILTER_ESCAPE_TABLE[c]); + } + else + { + // default: add the char + encodedValue.append(c); + } + } + + return encodedValue.toString(); + } +}
prov/src/main/java/org/bouncycastle/x509/util/LDAPStoreHelper.java+37 −62 modified@@ -35,6 +35,7 @@ import org.bouncycastle.jce.provider.X509CRLParser; import org.bouncycastle.jce.provider.X509CertPairParser; import org.bouncycastle.jce.provider.X509CertParser; +import org.bouncycastle.ldap.LDAPUtils; import org.bouncycastle.util.StoreException; import org.bouncycastle.util.Strings; import org.bouncycastle.x509.X509AttributeCertStoreSelector; @@ -65,7 +66,6 @@ */ public class LDAPStoreHelper { - // TODO: cache results private X509LDAPCertStoreParameters params; @@ -95,7 +95,8 @@ public LDAPStoreHelper(X509LDAPCertStoreParameters params) */ private static final String URL_CONTEXT_PREFIX = "com.sun.jndi.url"; - private DirContext connectLDAP() throws NamingException + private DirContext connectLDAP() + throws NamingException { Properties props = new Properties(); props.setProperty(Context.INITIAL_CONTEXT_FACTORY, LDAP_PROVIDER); @@ -111,47 +112,6 @@ private DirContext connectLDAP() throws NamingException return ctx; } - private String parseDN(String subject, String dNAttributeName) - { - String temp = subject; - int begin = Strings.toLowerCase(temp).indexOf( - Strings.toLowerCase(dNAttributeName) + "="); - if (begin == -1) - { - return ""; - } - temp = temp.substring(begin + dNAttributeName.length()); - int end = temp.indexOf(','); - if (end == -1) - { - end = temp.length(); - } - while (temp.charAt(end - 1) == '\\') - { - end = temp.indexOf(',', end + 1); - if (end == -1) - { - end = temp.length(); - } - } - temp = temp.substring(0, end); - begin = temp.indexOf('='); - temp = temp.substring(begin + 1); - if (temp.charAt(0) == ' ') - { - temp = temp.substring(1); - } - if (temp.startsWith("\"")) - { - temp = temp.substring(1); - } - if (temp.endsWith("\"")) - { - temp = temp.substring(0, temp.length() - 1); - } - return temp; - } - private Set createCerts(List list, X509CertStoreSelector xselector) throws StoreException { @@ -224,7 +184,7 @@ private List certSubjectSerialSearch(X509CertStoreSelector xselector, { for (int i = 0; i < subjectAttributeNames.length; i++) { - attrValue = parseDN(subject, subjectAttributeNames[i]); + attrValue = LDAPUtils.parseDN(subject, subjectAttributeNames[i]); list .addAll(search(attrNames, "*" + attrValue + "*", attrs)); @@ -235,7 +195,7 @@ private List certSubjectSerialSearch(X509CertStoreSelector xselector, attrValue = serial; list.addAll(search( splitString(params.getSearchForSerialNumberIn()), - attrValue, attrs)); + attrValue, attrs)); } if (serial == null && subject == null) { @@ -246,7 +206,6 @@ private List certSubjectSerialSearch(X509CertStoreSelector xselector, } - /** * Can use the subject of the forward certificate of the set certificate * pair or the subject of the forward @@ -290,7 +249,7 @@ private List crossCertificatePairSubjectSearch( { for (int i = 0; i < subjectAttributeNames.length; i++) { - attrValue = parseDN(subject, subjectAttributeNames[i]); + attrValue = LDAPUtils.parseDN(subject, subjectAttributeNames[i]); list .addAll(search(attrNames, "*" + attrValue + "*", attrs)); @@ -385,7 +344,7 @@ private List attrCertSubjectSerialSearch( { for (int i = 0; i < subjectAttributeNames.length; i++) { - attrValue = parseDN(subject, subjectAttributeNames[i]); + attrValue = LDAPUtils.parseDN(subject, subjectAttributeNames[i]); list .addAll(search(attrNames, "*" + attrValue + "*", attrs)); @@ -441,11 +400,11 @@ private List cRLIssuerSearch(X509CRLStoreSelector xselector, if (xselector.getAttrCertificateChecking() != null) { Principal principals[] = xselector.getAttrCertificateChecking().getIssuer().getPrincipals(); - for (int i=0; i<principals.length; i++) + for (int i = 0; i < principals.length; i++) { if (principals[i] instanceof X500Principal) { - issuers.add(principals[i]); + issuers.add(principals[i]); } } } @@ -457,7 +416,7 @@ private List cRLIssuerSearch(X509CRLStoreSelector xselector, for (int i = 0; i < issuerAttributeNames.length; i++) { - attrValue = parseDN(issuer, issuerAttributeNames[i]); + attrValue = LDAPUtils.parseDN(issuer, issuerAttributeNames[i]); list .addAll(search(attrNames, "*" + attrValue + "*", attrs)); @@ -485,7 +444,8 @@ private List cRLIssuerSearch(X509CRLStoreSelector xselector, * directory. */ private List search(String attributeNames[], String attributeValue, - String[] attrs) throws StoreException + String[] attrs) + throws StoreException { String filter = null; if (attributeNames == null) @@ -599,7 +559,8 @@ private Set createCRLs(List list, X509CRLStoreSelector xselector) } private Set createCrossCertificatePairs(List list, - X509CertPairStoreSelector xselector) throws StoreException + X509CertPairStoreSelector xselector) + throws StoreException { Set certPairSet = new HashSet(); @@ -626,7 +587,7 @@ private Set createCrossCertificatePairs(List list, pair = new X509CertificatePair(new CertificatePair( Certificate .getInstance(new ASN1InputStream( - forward).readObject()), + forward).readObject()), Certificate .getInstance(new ASN1InputStream( reverse).readObject()))); @@ -652,7 +613,8 @@ private Set createCrossCertificatePairs(List list, } private Set createAttributeCertificates(List list, - X509AttributeCertStoreSelector xselector) throws StoreException + X509AttributeCertStoreSelector xselector) + throws StoreException { Set certSet = new HashSet(); @@ -719,12 +681,14 @@ public Collection getAuthorityRevocationLists(X509CRLStoreSelector selector) * The attributeCertificateRevocationList holds a list of attribute * certificates that have been revoked. * </p> + * * @param selector The CRL selector to use to find the CRLs. * @return A possible empty collection with CRLs. * @throws StoreException */ public Collection getAttributeCertificateRevocationLists( - X509CRLStoreSelector selector) throws StoreException + X509CRLStoreSelector selector) + throws StoreException { String[] attrs = splitString(params .getAttributeCertificateRevocationListAttribute()); @@ -754,12 +718,14 @@ public Collection getAttributeCertificateRevocationLists( * The attributeAuthorityList holds a list of AA certificates that have been * revoked. * </p> + * * @param selector The CRL selector to use to find the CRLs. * @return A possible empty collection with CRLs * @throws StoreException */ public Collection getAttributeAuthorityRevocationLists( - X509CRLStoreSelector selector) throws StoreException + X509CRLStoreSelector selector) + throws StoreException { String[] attrs = splitString(params.getAttributeAuthorityRevocationListAttribute()); String attrNames[] = splitString(params @@ -789,7 +755,8 @@ public Collection getAttributeAuthorityRevocationLists( * @throws StoreException */ public Collection getCrossCertificatePairs( - X509CertPairStoreSelector selector) throws StoreException + X509CertPairStoreSelector selector) + throws StoreException { String[] attrs = splitString(params.getCrossCertificateAttribute()); String attrNames[] = splitString(params.getLdapCrossCertificateAttributeName()); @@ -850,6 +817,7 @@ public Collection getUserCertificates(X509CertStoreSelector selector) * <p> * The aAcertificate holds the privileges of an attribute authority. * </p> + * * @param selector The selector to find the attribute certificates. * @return A possible empty collection with attribute certificates. * @throws StoreException @@ -882,12 +850,14 @@ public Collection getAACertificates(X509AttributeCertStoreSelector selector) * authority and holds a description of the privilege and its delegation * rules. * </p> + * * @param selector The selector to find the attribute certificates. * @return A possible empty collection with attribute certificates. * @throws StoreException */ public Collection getAttributeDescriptorCertificates( - X509AttributeCertStoreSelector selector) throws StoreException + X509AttributeCertStoreSelector selector) + throws StoreException { String[] attrs = splitString(params.getAttributeDescriptorCertificateAttribute()); String attrNames[] = splitString(params @@ -916,6 +886,7 @@ public Collection getAttributeDescriptorCertificates( * store self-issued certificates (if any) and certificates issued to this * CA by CAs in the same realm as this CA. * </p> + * * @param selector The selector to find the certificates. * @return A possible empty collection with certificates. * @throws StoreException @@ -948,7 +919,8 @@ public Collection getCACertificates(X509CertStoreSelector selector) * @throws StoreException */ public Collection getDeltaCertificateRevocationLists( - X509CRLStoreSelector selector) throws StoreException + X509CRLStoreSelector selector) + throws StoreException { String[] attrs = splitString(params.getDeltaRevocationListAttribute()); String attrNames[] = splitString(params.getLdapDeltaRevocationListAttributeName()); @@ -973,12 +945,14 @@ public Collection getDeltaCertificateRevocationLists( * <p> * The attributeCertificateAttribute holds the privileges of a user * </p> + * * @param selector The selector to find the attribute certificates. * @return A possible empty collection with attribute certificates. * @throws StoreException */ public Collection getAttributeCertificateAttributes( - X509AttributeCertStoreSelector selector) throws StoreException + X509AttributeCertStoreSelector selector) + throws StoreException { String[] attrs = splitString(params.getAttributeCertificateAttributeAttribute()); String attrNames[] = splitString(params @@ -1007,7 +981,8 @@ public Collection getAttributeCertificateAttributes( * @throws StoreException */ public Collection getCertificateRevocationLists( - X509CRLStoreSelector selector) throws StoreException + X509CRLStoreSelector selector) + throws StoreException { String[] attrs = splitString(params.getCertificateRevocationListAttribute()); String attrNames[] = splitString(params
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
3News mentions
0No linked articles in our index yet.