Skip to content

Commit ef49b59

Browse files
Aleksei Voitylovgnu-andrew
authored andcommitted
8381796: Enhance Certificate parsing
Reviewed-by: evergizova, andrew Backport-of: 7af4aa0630d17fca1d689fcd0fe5ee18ad6c3bb9
1 parent 7c3c1a7 commit ef49b59

5 files changed

Lines changed: 246 additions & 20 deletions

File tree

src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2006, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
2525

2626
package sun.security.provider.certpath;
2727

28+
import java.io.FilterInputStream;
2829
import java.io.InputStream;
2930
import java.io.IOException;
3031
import java.net.HttpURLConnection;
@@ -153,6 +154,16 @@ private static int initializeTimeout() {
153154
return tmp * 1000;
154155
}
155156

157+
/**
158+
* Maximum size for a CRL downloaded through a URICertStore
159+
* in bytes. This can be controlled by the com.sun.security.crl.maxSize
160+
* Security or System property. The System property, if set, overrides
161+
* the Security property. The default size is 20MiB.
162+
*/
163+
private static final long MAX_CRL_DOWNLOAD_SIZE =
164+
SecurityProperties.getOverridableLongProp(
165+
"com.sun.security.crl.maxSize", 20971520, debug);
166+
156167
/**
157168
* Enumeration for the allowed schemes we support when following a
158169
* URI from an authorityInfoAccess extension on a certificate.
@@ -193,6 +204,13 @@ static AllowedScheme nameOf(String name) {
193204
private static final boolean CA_ISS_ALLOW_ANY;
194205

195206
static {
207+
// Add a debug message for the configured CRL download limit
208+
if (debug != null) {
209+
debug.println("Maximum downloadable CRL size: " +
210+
MAX_CRL_DOWNLOAD_SIZE +
211+
((MAX_CRL_DOWNLOAD_SIZE < 0) ? " (DISABLED)" : ""));
212+
}
213+
196214
boolean allowAny = false;
197215
try {
198216
if (Builder.USE_AIA) {
@@ -586,7 +604,19 @@ public synchronized Collection<X509CRL> engineGetCRLs(CRLSelector selector)
586604
if (debug != null) {
587605
debug.println("Downloading new CRL...");
588606
}
589-
crl = (X509CRL) factory.generateCRL(in);
607+
InputStream crlIn = (MAX_CRL_DOWNLOAD_SIZE > -1) ?
608+
new SizeLimitedInputStream(in, MAX_CRL_DOWNLOAD_SIZE) :
609+
in;
610+
try {
611+
crl = (X509CRL) factory.generateCRL(crlIn);
612+
} catch (IllegalArgumentException iae) {
613+
// IAE should only be thrown when the CRL exceeds a
614+
// configured maximum length.
615+
if (debug != null) {
616+
debug.println("Discarding CRL: " + iae.getMessage());
617+
crl = null;
618+
}
619+
}
590620
}
591621
return getMatchingCRLs(crl, selector);
592622
} catch (IOException | CRLException e) {
@@ -779,4 +809,59 @@ boolean matchRule(URI filterRule, URI caIssuer) {
779809
return true;
780810
}
781811
}
812+
813+
/**
814+
* Stream wrapper used when an InputStream passed into a CertificateFactory
815+
* needs to be size limited. It will throw IllegalArgumentException when
816+
* the downloaded resource via the underlying stream exceeds the maximum
817+
* limit.
818+
*/
819+
private static class SizeLimitedInputStream extends FilterInputStream {
820+
821+
private final long maxBytes;
822+
private long bytesRead = 0;
823+
824+
private SizeLimitedInputStream(InputStream in, long maxBytes) {
825+
super(in);
826+
this.maxBytes = maxBytes;
827+
}
828+
829+
@Override
830+
public int read() throws IOException {
831+
if (bytesRead >= maxBytes) {
832+
// We will use IAE here to differentiate this special case
833+
// from other IOEs that the underlying input stream might
834+
// legitimately throw.
835+
throw new IllegalArgumentException("InputStream exceeded max " +
836+
"size of " + maxBytes);
837+
}
838+
839+
int b = super.read();
840+
if (b != -1) {
841+
bytesRead++;
842+
}
843+
return b;
844+
}
845+
846+
@Override
847+
public int read(byte[] b, int off, int len) throws IOException {
848+
849+
if (bytesRead >= maxBytes) {
850+
// We will use IAE here to differentiate this special case
851+
// from other IOEs that the underlying input stream might
852+
// legitimately throw.
853+
throw new IllegalArgumentException("InputStream exceeded max " +
854+
"size of " + maxBytes);
855+
}
856+
857+
long remaining = maxBytes - bytesRead;
858+
int toRead = (int) Math.min(len, remaining);
859+
860+
int n = super.read(b, off, toRead);
861+
if (n != -1) {
862+
bytesRead += n;
863+
}
864+
return n;
865+
}
866+
}
782867
}

src/java.base/share/classes/sun/security/util/SecurityProperties.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
33
* Copyright (c) 2018 SAP SE. All rights reserved.
44
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
55
*
@@ -79,4 +79,34 @@ public static boolean includedInExceptions(String refName) {
7979
}
8080
return false;
8181
}
82+
83+
/**
84+
* A convenience routine for fetching a numeric value from a Security
85+
* or System property and returning it as a long. The value from the
86+
* property is obtained according to the logic in
87+
* {@link SecurityProperties#privilegedGetOverridable(String)}
88+
*
89+
* @param prop the property to query
90+
* @param defaultValue the default value
91+
* @param dbg a Debug object, if null no debug messages will be sent
92+
* @return the value of the property as a {@code long}. If a non-numeric
93+
* value is supplied, the default value will be returned.
94+
*/
95+
public static long getOverridableLongProp(String prop, long defaultValue,
96+
Debug dbg) {
97+
long longVal = defaultValue;
98+
try {
99+
String propVal = SecurityProperties.privilegedGetOverridable(prop);
100+
if (propVal != null) {
101+
longVal = Long.parseLong(propVal);
102+
}
103+
} catch (NumberFormatException nfe) {
104+
// We will use the default, but add a warning debug message
105+
if (dbg != null) {
106+
dbg.println("Warning: Non-numeric value found in property " +
107+
prop + ", using default value of " + defaultValue);
108+
}
109+
}
110+
return longVal;
111+
}
82112
}

src/java.base/share/conf/security/java.security

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,3 +1488,19 @@ jdk.tls.alpnCharset=ISO_8859_1
14881488
# com.sun.security.allowedAIALocations=http://some.company.com/cacert \
14891489
# ldap://ldap.company.com/dc=company,dc=com?caCertificate;binary
14901490
com.sun.security.allowedAIALocations=
1491+
1492+
#
1493+
# Certificate Revocation List (CRL) Download Size Limitation
1494+
#
1495+
# This property sets a size limit for CRLs downloaded via URIs provided
1496+
# in the CRL Distribution Points certificate extension. This property
1497+
# must be a numeric value that is the size in bytes of the DER-encoded CRL.
1498+
# For protocols that can return multi-value responses, such as LDAP, the
1499+
# size threshold is the sum of all CRLs downloaded from a single search
1500+
# query. CRLs that exceed this length will not be processed during certificate
1501+
# path validation. This size limit does not apply to CRLs that are imported
1502+
# through non-network-based means. A negative value will disable this size
1503+
# limitation. A non-numeric value will be ignored and the default size will
1504+
# be used instead. The default size limit is 20MiB.
1505+
# This property may be overridden by a System property of the same name.
1506+
com.sun.security.crl.maxSize = 20971520

src/java.naming/share/classes/sun/security/provider/certpath/ldap/LDAPCertStoreImpl.java

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -52,6 +52,7 @@
5252
import sun.security.provider.certpath.X509CertificatePair;
5353
import sun.security.util.Cache;
5454
import sun.security.util.Debug;
55+
import sun.security.util.SecurityProperties;
5556

5657
/**
5758
* Core implementation of a LDAP Cert Store.
@@ -96,6 +97,16 @@ final class LDAPCertStoreImpl {
9697
private final static String PROP_DISABLE_APP_RESOURCE_FILES =
9798
"sun.security.certpath.ldap.disable.app.resource.files";
9899

100+
/**
101+
* Maximum size for a CRL downloaded through an LDAPCertStoreImpl
102+
* in bytes. This can be controlled by the com.sun.security.crl.maxSize
103+
* Security or System property. The System property, if set, overrides
104+
* the Security property. The default size is 20MiB.
105+
*/
106+
private static final long MAX_CRL_DOWNLOAD_SIZE =
107+
SecurityProperties.getOverridableLongProp(
108+
"com.sun.security.crl.maxSize", 20971520, debug);
109+
99110
static {
100111
String s = AccessController.doPrivileged(
101112
(PrivilegedAction<String>) () -> System.getProperty(PROP_LIFETIME));
@@ -104,6 +115,13 @@ final class LDAPCertStoreImpl {
104115
} else {
105116
LIFETIME = DEFAULT_CACHE_LIFETIME;
106117
}
118+
119+
// Add a debug message for the configured CRL download limit
120+
if (debug != null) {
121+
debug.println("Maximum downloadable CRL size: " +
122+
MAX_CRL_DOWNLOAD_SIZE +
123+
((MAX_CRL_DOWNLOAD_SIZE < 0) ? " (DISABLED)" : ""));
124+
}
107125
}
108126

109127
/**
@@ -671,12 +689,12 @@ private Collection<X509Certificate> getMatchingCrossCerts(
671689
return certs;
672690
}
673691

674-
/*
692+
/**
675693
* Gets CRLs from an attribute id and location in the LDAP directory.
676694
* Returns a Collection containing only the CRLs that match the
677695
* specified X509CRLSelector.
678696
*
679-
* @param name the location holding the attribute
697+
* @param request the LDAP request used for this CRL fetch operation
680698
* @param id the attribute identifier
681699
* @param sel a X509CRLSelector that the CRLs must match
682700
* @return a Collection of CRLs found
@@ -688,7 +706,26 @@ private Collection<X509CRL> getCRLs(LDAPRequest request, String id,
688706
/* fetch the encoded crls from storage */
689707
byte[][] encodedCRL;
690708
try {
691-
encodedCRL = request.getValues(id);
709+
byte[][] tmpCrls = request.getValues(id);
710+
if (MAX_CRL_DOWNLOAD_SIZE > -1) {
711+
int totalSize = 0;
712+
for (byte[] tCrl : tmpCrls) {
713+
totalSize += tCrl.length;
714+
}
715+
if (totalSize <= MAX_CRL_DOWNLOAD_SIZE) {
716+
encodedCRL = tmpCrls;
717+
} else {
718+
if (debug != null) {
719+
debug.println("Received " + tmpCrls.length +
720+
" CRL(s). Combined length of " + totalSize +
721+
" exceeds configured maximum. Discarding.");
722+
}
723+
encodedCRL = new byte[0][];
724+
}
725+
} else {
726+
// Download limits disabled
727+
encodedCRL = tmpCrls;
728+
}
692729
} catch (NamingException namingEx) {
693730
throw new CertStoreException(namingEx);
694731
}

0 commit comments

Comments
 (0)