8368032: Enhance Certificate Checking

Reviewed-by: ahgross, coffeys, rhalade, mullan, abarashev
This commit is contained in:
Jamil Nimeh 2025-11-03 14:53:21 +00:00 committed by bchristi
parent 82e5771b0b
commit 07f981f6b0
4 changed files with 452 additions and 47 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -29,6 +29,7 @@ import java.io.InputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLConnection;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
@ -48,8 +49,11 @@ import java.security.cert.X509CRL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import sun.security.x509.AccessDescription;
import sun.security.x509.GeneralNameInterface;
@ -58,6 +62,8 @@ import sun.security.util.Cache;
import sun.security.util.Debug;
import sun.security.util.SecurityProperties;
import javax.security.auth.x500.X500Principal;
/**
* A <code>CertStore</code> that retrieves <code>Certificates</code> or
* <code>CRL</code>s from a URI, for example, as specified in an X.509
@ -182,6 +188,166 @@ class URICertStore extends CertStoreSpi {
return timeoutVal;
}
/**
* Enumeration for the allowed schemes we support when following a
* URI from an authorityInfoAccess extension on a certificate.
*/
private enum AllowedScheme {
HTTP(HttpFtpRuleMatcher.HTTP),
HTTPS(HttpFtpRuleMatcher.HTTPS),
LDAP(LdapRuleMatcher.LDAP),
LDAPS(LdapRuleMatcher.LDAPS),
FTP(HttpFtpRuleMatcher.FTP);
final URIRuleMatcher ruleMatcher;
AllowedScheme(URIRuleMatcher matcher) {
ruleMatcher = matcher;
}
/**
* Return an {@code AllowedScheme} based on a case-insensitive match
* @param name the scheme name to be matched
* @return the {@code AllowedScheme} that corresponds to the
* {@code name} provided, or null if there is no match.
*/
static AllowedScheme nameOf(String name) {
if (name == null) {
return null;
}
try {
return AllowedScheme.valueOf(name.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException _) {
return null;
}
}
}
private static Set<URI> CA_ISS_URI_FILTERS = null;
private static final boolean CA_ISS_ALLOW_ANY;
static {
boolean allowAny = false;
try {
if (Builder.USE_AIA) {
CA_ISS_URI_FILTERS = new LinkedHashSet<>();
String aiaPropVal = Optional.ofNullable(
SecurityProperties.getOverridableProperty(
"com.sun.security.allowedAIALocations")).
map(String::trim).orElse("");
if (aiaPropVal.equalsIgnoreCase("any")) {
allowAny = true;
if (debug != null) {
debug.println("allowedAIALocations: Warning: " +
"Allow-All URI filtering enabled!");
}
} else {
// Load all the valid rules from the Security property
if (!aiaPropVal.isEmpty()) {
String[] aiaUriStrs = aiaPropVal.trim().split("\\s+");
addCaIssUriFilters(aiaUriStrs);
}
if (CA_ISS_URI_FILTERS.isEmpty()) {
if (debug != null) {
debug.println("allowedAIALocations: Warning: " +
"No valid filters found. Deny-all URI " +
"filtering is active.");
}
}
}
}
} finally {
CA_ISS_ALLOW_ANY = allowAny;
}
}
/**
* Populate the filter collection from the list of AIA CA issuer URIs
* found in the {@code com.sun.security.allowedAIALocations} security
* or system property.
*
* @param aiaUriStrs array containing String URI filters
*/
private static void addCaIssUriFilters(String[] aiaUriStrs) {
for (String aiaStr : aiaUriStrs) {
if (aiaStr != null && !aiaStr.isEmpty()) {
try {
AllowedScheme scheme;
URI aiaUri = new URI(aiaStr).normalize();
// It must be absolute and non-opaque
if (!aiaUri.isAbsolute() || aiaUri.isOpaque()) {
if (debug != null) {
debug.println("allowedAIALocations: Skipping " +
"non-absolute or opaque URI " + aiaUri);
}
} else if (aiaUri.getHost() == null) {
// We do not allow rules with URIs that omit a hostname
// or address.
if (debug != null) {
debug.println("allowedAIALocations: Skipping " +
"URI rule with no hostname or address: " +
aiaUri);
}
} else if ((scheme = AllowedScheme.nameOf(
aiaUri.getScheme())) != null) {
// When it is an LDAP type, we can check the path
// portion (the DN) for proper structure and reject
// the rule early if it isn't correct.
if (scheme == AllowedScheme.LDAP ||
scheme == AllowedScheme.LDAPS) {
try {
new X500Principal(aiaUri.getPath().
replaceFirst("^/+", ""));
} catch (IllegalArgumentException iae) {
if (debug != null) {
debug.println("allowedAIALocations: " +
"Skipping LDAP rule: " + iae);
}
continue;
}
}
// When a URI has a non-null query or fragment
// warn the user upon adding the rule that those
// components will be ignored
if (aiaUri.getQuery() != null) {
if (debug != null) {
debug.println("allowedAIALocations: " +
"Rule will ignore non-null query");
}
}
if (aiaUri.getFragment() != null) {
if (debug != null) {
debug.println("allowedAIALocations: " +
"Rule will ignore non-null fragment");
}
}
CA_ISS_URI_FILTERS.add(aiaUri);
if (debug != null) {
debug.println("allowedAIALocations: Added " +
aiaUri + " to URI filters");
}
} else {
if (debug != null) {
debug.println("allowedAIALocations: Disallowed " +
"filter URI scheme: " +
aiaUri.getScheme());
}
}
} catch (URISyntaxException urise) {
if (debug != null) {
debug.println("allowedAIALocations: Skipping " +
"filter URI entry " + aiaStr +
": parse failure at index " + urise.getIndex());
}
}
}
}
}
/**
* Creates a URICertStore.
*
@ -244,6 +410,39 @@ class URICertStore extends CertStoreSpi {
return null;
}
URI uri = ((URIName) gn).getURI();
// Before performing any instantiation make sure that
// the URI passes any filtering rules. This processing should
// only occur if the com.sun.security.enableAIAcaIssuers is true
// and the "any" rule has not been specified.
if (Builder.USE_AIA && !CA_ISS_ALLOW_ANY) {
URI normAIAUri = uri.normalize();
AllowedScheme scheme = AllowedScheme.nameOf(normAIAUri.getScheme());
if (scheme == null) {
if (debug != null) {
debug.println("allowedAIALocations: No matching ruleset " +
"for scheme " + normAIAUri.getScheme());
}
return null;
}
// Go through each of the filter rules and see if any will
// make a positive match against the caIssuer URI. If nothing
// matches then we won't instantiate a URICertStore.
if (CA_ISS_URI_FILTERS.stream().noneMatch(rule ->
scheme.ruleMatcher.matchRule(rule, normAIAUri))) {
if (debug != null) {
debug.println("allowedAIALocations: Warning - " +
"The caIssuer URI " + normAIAUri +
" in the AuthorityInfoAccess extension is denied " +
"access. Use the com.sun.security.allowedAIALocations" +
" security/system property to allow access.");
}
return null;
}
}
try {
return URICertStore.getInstance(new URICertStoreParameters(uri));
} catch (Exception ex) {
@ -270,7 +469,7 @@ class URICertStore extends CertStoreSpi {
@Override
@SuppressWarnings("unchecked")
public synchronized Collection<X509Certificate> engineGetCertificates
(CertSelector selector) throws CertStoreException {
(CertSelector selector) throws CertStoreException {
if (ldap) {
// caching mechanism, see the class description for more info.
@ -462,4 +661,159 @@ class URICertStore extends CertStoreSpi {
super(spi, p, type, params);
}
}
/**
* URIRuleMatcher - abstract base class for the rule sets used for
* various URI schemes.
*/
static abstract class URIRuleMatcher {
protected final int wellKnownPort;
protected URIRuleMatcher(int port) {
wellKnownPort = port;
}
/**
* Attempt to match the scheme, host and port between a filter
* rule URI and a URI coming from an AIA extension.
*
* @param filterRule the filter rule to match against
* @param caIssuer the AIA URI being compared
* @return true if the scheme, host and port numbers match, false if
* any of the components do not match. If a port number is omitted in
* either the filter rule or AIA URI, the well-known port for that
* scheme is used in the comparison.
*/
boolean schemeHostPortCheck(URI filterRule, URI caIssuer) {
if (!filterRule.getScheme().equalsIgnoreCase(
caIssuer.getScheme())) {
return false;
} else if (!filterRule.getHost().equalsIgnoreCase(
caIssuer.getHost())) {
return false;
} else {
try {
// Check for port matching, taking into consideration
// default ports
int fPort = (filterRule.getPort() == -1) ? wellKnownPort :
filterRule.getPort();
int caiPort = (caIssuer.getPort() == -1) ? wellKnownPort :
caIssuer.getPort();
if (fPort != caiPort) {
return false;
}
} catch (IllegalArgumentException iae) {
return false;
}
}
return true;
}
/**
* Attempt to match an AIA URI against a specific filter rule. The
* specific rules to apply are implementation dependent.
*
* @param filterRule the filter rule to match against
* @param caIssuer the AIA URI being compared
* @return true if all matching rules pass, false if any fail.
*/
abstract boolean matchRule(URI filterRule, URI caIssuer);
}
static class HttpFtpRuleMatcher extends URIRuleMatcher {
static final HttpFtpRuleMatcher HTTP = new HttpFtpRuleMatcher(80);
static final HttpFtpRuleMatcher HTTPS = new HttpFtpRuleMatcher(443);
static final HttpFtpRuleMatcher FTP = new HttpFtpRuleMatcher(21);
private HttpFtpRuleMatcher(int port) {
super(port);
}
@Override
boolean matchRule(URI filterRule, URI caIssuer) {
// Check for scheme/host/port matching
if (!schemeHostPortCheck(filterRule, caIssuer)) {
return false;
}
// Check the path component to make sure the filter is at
// least a root of the AIA caIssuer URI's path. It must be
// a case-sensitive match for all platforms.
if (!isRootOf(filterRule, caIssuer)) {
if (debug != null) {
debug.println("allowedAIALocations: Match failed: " +
"AIA URI is not within the rule's path hierarchy.");
}
return false;
}
return true;
}
/**
* Performs a hierarchical containment check, ensuring that the
* base URI's path is a root component of the candidate path. The
* path comparison is case-sensitive. If the base path ends in a
* slash (/) then all candidate paths that begin with the base
* path are allowed. If it does not end in a slash, then it is
* assumed that the leaf node in the base path is a file component
* and both paths must match exactly.
*
* @param base the URI that contains the root path
* @param candidate the URI that contains the path being evaluated
* @return true if {@code candidate} is a child path of {@code base},
* false otherwise.
*/
private static boolean isRootOf(URI base, URI candidate) {
// Note: The URIs have already been normalized at this point and
// HTTP URIs cannot have null paths. If it's an empty path
// then consider the path to be "/".
String basePath = Optional.of(base.getPath()).
filter(p -> !p.isEmpty()).orElse("/");
String candPath = Optional.of(candidate.getPath()).
filter(p -> !p.isEmpty()).orElse("/");
return (basePath.endsWith("/")) ? candPath.startsWith(basePath) :
candPath.equals(basePath);
}
}
static class LdapRuleMatcher extends URIRuleMatcher {
static final LdapRuleMatcher LDAP = new LdapRuleMatcher(389);
static final LdapRuleMatcher LDAPS = new LdapRuleMatcher(636);
private LdapRuleMatcher(int port) {
super(port);
}
@Override
boolean matchRule(URI filterRule, URI caIssuer) {
// Check for scheme/host/port matching
if (!schemeHostPortCheck(filterRule, caIssuer)) {
return false;
}
// Obtain the base DN component and compare
try {
X500Principal filterBaseDn = new X500Principal(
filterRule.getPath().replaceFirst("^/+", ""));
X500Principal caIssBaseDn = new X500Principal(
caIssuer.getPath().replaceFirst("^/+", ""));
if (!filterBaseDn.equals(caIssBaseDn)) {
if (debug != null) {
debug.println("allowedAIALocations: Match failed: " +
"Base DN mismatch (" + filterBaseDn + " vs " +
caIssBaseDn + ")");
}
return false;
}
} catch (IllegalArgumentException iae) {
if (debug != null) {
debug.println("allowedAIALocations: Match failed on DN: " +
iae);
}
return false;
}
return true;
}
}
}

View File

@ -1655,3 +1655,48 @@ jdk.tls.alpnCharset=ISO_8859_1
# withEncryption method.
#
jdk.epkcs8.defaultAlgorithm=PBEWithHmacSHA256AndAES_128
#
# X.509 AuthorityInfoAccess caIssuer URI Filtering
#
# This property defines a whitespace-separated list of filters that
# are applied to URIs found in the authorityInfoAccess extension in
# X.509 certificates. Any caIssuers URIs in X.509 certificates are only
# followed when the com.sun.security.enableAIAcaIssuers System property is
# enabled and the filter allows the URI. By default this property imposes a
# deny-all ruleset. This property may be overridden by a System property
# of the same name.
#
# The filters must take the form of absolute, hierarchical URIs as defined by
# the java.net.URI class. Additionally, only the following protocols are
# allowed as filters: http, https, ldap and ftp.
# See RFC 5280, section 4.2.2.1 for details about the types of URIs allowed for
# the extension and their specific requirements.
# The filter matching rules are applied to each CA issuer URI as follows:
# 1. The scheme must match (case-insensitive).
# 2. A hostname or address must be specified in the filter URI. It must match
# the host or address in the caIssuers URI (case-insensitive). No name
# resolution is performed on hostnames to match IP addresses.
# 3. The port number must match. For filter and caIssuer URIs, when a port
# number is omitted, the well-known port for that scheme will be used in the
# comparison.
# 4. For hierarchical filesystem schemes (e.g. http[s], ftp):
# a. The normalized path portion of the filter URI is matched in a
# case-sensitive manner. If the final component of the path does not end
# in a slash (/), it is considered to be a file path component and must
# be an exact match of the caIssuer's URI file path component. If the
# final filter component ends in a slash, then it must either match or be
# a prefix of the caIssuer's URI path component (e.g. a filter path of
# /ab/cd/ will match a caIssuer path of /ab/cd/, /ab/cd/ef and
# /ab/cd/ef/ghi).
# b. Query strings will be ignored in filter rules and caIssuer URIs.
# c. Fragments will be ignored in filter rules and caIssuer URIs.
# 5. For ldap URIs:
# a. The base DN must be an exact match (case-insensitive).
# b. Any query string in the rule, if specified, is ignored.
# 6. A single value "any" (case-insensitive) will create an allow-all rule.
#
# As an example, here is a valid filter policy consisting of two rules:
# com.sun.security.allowedAIALocations=http://some.company.com/cacert \
# ldap://ldap.company.com/dc=company,dc=com?caCertificate;binary
com.sun.security.allowedAIALocations=

View File

@ -47,6 +47,7 @@ import com.sun.net.httpserver.*;
import java.io.*;
import java.math.BigInteger;
import java.net.InetSocketAddress;
import java.security.Security;
import java.security.cert.*;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
@ -69,6 +70,7 @@ public class AIACertTimeout {
private static X509Certificate eeCert;
public static void main(String[] args) throws Exception {
Security.setProperty("com.sun.security.allowedAIALocations", "any");
int servTimeoutMsec = (args != null && args.length >= 1) ?
Integer.parseInt(args[0]) : -1;
boolean expectedPass = args != null && args.length >= 2 &&

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -38,6 +38,7 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.security.Security;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
@ -47,7 +48,6 @@ import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -67,25 +67,27 @@ public class ExtensionsWithLDAP {
* Not After : Jan 17 18:03:59 2043 GMT
* Subject: CN=Root
*/
private static final String CA_CERT = ""
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIC8TCCAdmgAwIBAgIJAJsSNtj5wdqqMA0GCSqGSIb3DQEBDQUAMA8xDTALBgNV\n"
+ "BAMMBFJvb3QwHhcNMTUwOTAxMTgwMzU5WhcNNDMwMTE3MTgwMzU5WjAPMQ0wCwYD\n"
+ "VQQDDARSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvj892vPm\n"
+ "bB++x9QqqyBveP+ZqQ2B1stV7vh5JmDnOTevkZUOcemp3SXu/esNLSbpL+fARYXH\n"
+ "V5ubnrfip6RbvcxPfVIIDJrRTLIIsU6W7M6/LJLbLkEVGy4ZV4IHkOw9W2O92rcv\n"
+ "BkoqhzZnOTGR6uT3rRcKx4RevEKBKhZO+OPPf//lnckOybmYL7t7yQrajzHro76b\n"
+ "QTXYjAUq/DKhglXfC7vF/JzlAvG2IunGmIfjGcnuDo/9X3Bxef/q5TxCS35fvb7t\n"
+ "svC+g2QhTcBkQh4uNW2jSjlTIVp1uErCfP5aCjLaez5mqmb1hxPIlcvsNR23HwU6\n"
+ "bQO7z7NBo9Do6QIDAQABo1AwTjAdBgNVHQ4EFgQUmLZNOBBkqdYoElyxklPYHmAb\n"
+ "QXIwHwYDVR0jBBgwFoAUmLZNOBBkqdYoElyxklPYHmAbQXIwDAYDVR0TBAUwAwEB\n"
+ "/zANBgkqhkiG9w0BAQ0FAAOCAQEAYV4fOhDi5q7+XNXCxO8Eil2frR9jqdP4LaQp\n"
+ "3L0evW0gvPX68s2WmkPWzIu4TJcpdGFQqxyQFSXuKBXjthyiln77QItGTHWeafES\n"
+ "q5ESrKdSaJZq1bTIrrReCIP74f+fY/F4Tnb3dCqzaljXfzpdbeRsIW6gF71xcOUQ\n"
+ "nnPEjGVPLUegN+Wn/jQpeLxxIB7FmNXncdRUfMfZ43xVSKuMCy1UUYqJqTa/pXZj\n"
+ "jCMeRPThRjRqHlJ69jStfWUQATbLyj9KN09rUaJxzmUSt61UqJi7sjcGySaCjAJc\n"
+ "IcCdVmX/DmRLsdv8W36O3MgrvpT1zR3kaAlv2d8HppnBqcL3xg==\n"
+ "-----END CERTIFICATE-----";
private static final String CA_CERT =
"""
-----BEGIN CERTIFICATE-----
MIIC8TCCAdmgAwIBAgIJAJsSNtj5wdqqMA0GCSqGSIb3DQEBDQUAMA8xDTALBgNV
BAMMBFJvb3QwHhcNMTUwOTAxMTgwMzU5WhcNNDMwMTE3MTgwMzU5WjAPMQ0wCwYD
VQQDDARSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvj892vPm
bB++x9QqqyBveP+ZqQ2B1stV7vh5JmDnOTevkZUOcemp3SXu/esNLSbpL+fARYXH
V5ubnrfip6RbvcxPfVIIDJrRTLIIsU6W7M6/LJLbLkEVGy4ZV4IHkOw9W2O92rcv
BkoqhzZnOTGR6uT3rRcKx4RevEKBKhZO+OPPf//lnckOybmYL7t7yQrajzHro76b
QTXYjAUq/DKhglXfC7vF/JzlAvG2IunGmIfjGcnuDo/9X3Bxef/q5TxCS35fvb7t
svC+g2QhTcBkQh4uNW2jSjlTIVp1uErCfP5aCjLaez5mqmb1hxPIlcvsNR23HwU6
bQO7z7NBo9Do6QIDAQABo1AwTjAdBgNVHQ4EFgQUmLZNOBBkqdYoElyxklPYHmAb
QXIwHwYDVR0jBBgwFoAUmLZNOBBkqdYoElyxklPYHmAbQXIwDAYDVR0TBAUwAwEB
/zANBgkqhkiG9w0BAQ0FAAOCAQEAYV4fOhDi5q7+XNXCxO8Eil2frR9jqdP4LaQp
3L0evW0gvPX68s2WmkPWzIu4TJcpdGFQqxyQFSXuKBXjthyiln77QItGTHWeafES
q5ESrKdSaJZq1bTIrrReCIP74f+fY/F4Tnb3dCqzaljXfzpdbeRsIW6gF71xcOUQ
nnPEjGVPLUegN+Wn/jQpeLxxIB7FmNXncdRUfMfZ43xVSKuMCy1UUYqJqTa/pXZj
jCMeRPThRjRqHlJ69jStfWUQATbLyj9KN09rUaJxzmUSt61UqJi7sjcGySaCjAJc
IcCdVmX/DmRLsdv8W36O3MgrvpT1zR3kaAlv2d8HppnBqcL3xg==
-----END CERTIFICATE-----
""";
/*
* Certificate:
@ -106,39 +108,41 @@ public class ExtensionsWithLDAP {
* Authority Information Access:
* CA Issuers - URI:ldap://ldap.host.for.aia/dc=Root?cACertificate
*/
private static final String EE_CERT = ""
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIDHTCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQ0FADAPMQ0wCwYDVQQDDARSb290\n"
+ "MB4XDTE1MDkwMTE4MDM1OVoXDTQzMDExNzE4MDM1OVowDTELMAkGA1UEAwwCRUUw\n"
+ "ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpyz97liuWPDYcLH9TX8Bi\n"
+ "T78olCmAfmevvch6ncXUVuCzbdaKuKXwn4EVbDszsVJLoK5zdtP+X3iDhutj+IgK\n"
+ "mLhuczF3M9VIcWr+JJUyTH4+3h/RT8cjCDZOmk9iXkb5ifruVsLqzb9g+Vp140Oz\n"
+ "7leikne7KmclHvTfvFd0WDI7Gb9vo4f5rT717BXJ/n+M6pNk8DLpLiEu6eziYvXR\n"
+ "v5x+t5Go3x0eCXdaxEQUf2j876Wfr2qHRJK7lDfFe1DDsMg/KpKGiILYZ+g2qtVM\n"
+ "ZSxtp5BZEtfB5qV/IE5kWO+mCIAGpXSZIdbERR6pZUq8GLEe1T9e+sO6H24w2F19\n"
+ "AgMBAAGjgYUwgYIwNAYDVR0fBC0wKzApoCegJYYjbGRhcDovL2xkYXAuaG9zdC5m\n"
+ "b3IuY3JsZHAvbWFpbi5jcmwwSgYIKwYBBQUHAQEEPjA8MDoGCCsGAQUFBzAChi5s\n"
+ "ZGFwOi8vbGRhcC5ob3N0LmZvci5haWEvZGM9Um9vdD9jQUNlcnRpZmljYXRlMA0G\n"
+ "CSqGSIb3DQEBDQUAA4IBAQBWDfZHpuUx0yn5d3+BuztFqoks1MkGdk+USlH0TB1/\n"
+ "gWWBd+4S4PCKlpSur0gj2rMW4fP5HQfNlHci8JV8/bG4KuKRAXW56dg1818Hl3pc\n"
+ "iIrUSRn8uUjH3p9qb+Rb/u3mmVQRyJjN2t/zceNsO8/+Dd808OB9aEwGs8lMT0nn\n"
+ "ZYaaAqYz1GIY/Ecyx1vfEZEQ1ljo6i/r70C3igbypBUShxSiGsleiVTLOGNA+MN1\n"
+ "/a/Qh0bkaQyTGqK3bwvzzMeQVqWu2EWTBD/PmND5ExkpRICdv8LBVXfLnpoBr4lL\n"
+ "hnxn9+e0Ah+t8dS5EKfn44w5bI5PCu2bqxs6RCTxNjcY\n"
+ "-----END CERTIFICATE-----";
private static final String EE_CERT =
"""
-----BEGIN CERTIFICATE-----
MIIDHTCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQ0FADAPMQ0wCwYDVQQDDARSb290
MB4XDTE1MDkwMTE4MDM1OVoXDTQzMDExNzE4MDM1OVowDTELMAkGA1UEAwwCRUUw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpyz97liuWPDYcLH9TX8Bi
T78olCmAfmevvch6ncXUVuCzbdaKuKXwn4EVbDszsVJLoK5zdtP+X3iDhutj+IgK
mLhuczF3M9VIcWr+JJUyTH4+3h/RT8cjCDZOmk9iXkb5ifruVsLqzb9g+Vp140Oz
7leikne7KmclHvTfvFd0WDI7Gb9vo4f5rT717BXJ/n+M6pNk8DLpLiEu6eziYvXR
v5x+t5Go3x0eCXdaxEQUf2j876Wfr2qHRJK7lDfFe1DDsMg/KpKGiILYZ+g2qtVM
ZSxtp5BZEtfB5qV/IE5kWO+mCIAGpXSZIdbERR6pZUq8GLEe1T9e+sO6H24w2F19
AgMBAAGjgYUwgYIwNAYDVR0fBC0wKzApoCegJYYjbGRhcDovL2xkYXAuaG9zdC5m
b3IuY3JsZHAvbWFpbi5jcmwwSgYIKwYBBQUHAQEEPjA8MDoGCCsGAQUFBzAChi5s
ZGFwOi8vbGRhcC5ob3N0LmZvci5haWEvZGM9Um9vdD9jQUNlcnRpZmljYXRlMA0G
CSqGSIb3DQEBDQUAA4IBAQBWDfZHpuUx0yn5d3+BuztFqoks1MkGdk+USlH0TB1/
gWWBd+4S4PCKlpSur0gj2rMW4fP5HQfNlHci8JV8/bG4KuKRAXW56dg1818Hl3pc
iIrUSRn8uUjH3p9qb+Rb/u3mmVQRyJjN2t/zceNsO8/+Dd808OB9aEwGs8lMT0nn
ZYaaAqYz1GIY/Ecyx1vfEZEQ1ljo6i/r70C3igbypBUShxSiGsleiVTLOGNA+MN1
/a/Qh0bkaQyTGqK3bwvzzMeQVqWu2EWTBD/PmND5ExkpRICdv8LBVXfLnpoBr4lL
hnxn9+e0Ah+t8dS5EKfn44w5bI5PCu2bqxs6RCTxNjcY
-----END CERTIFICATE-----""";
public static void main(String[] args) throws Exception {
String extension = args[0];
String targetHost = args[1];
Security.setProperty("com.sun.security.allowedAIALocations",
"ldap://" + targetHost + "/dc=Root");
X509Certificate trustedCert = loadCertificate(CA_CERT);
X509Certificate eeCert = loadCertificate(EE_CERT);
Set<TrustAnchor> trustedCertsSet = new HashSet<>();
trustedCertsSet.add(new TrustAnchor(trustedCert, null));
CertPath cp = (CertPath) CertificateFactory.getInstance("X509")
.generateCertPath(Arrays.asList(eeCert));
CertPath cp = CertificateFactory.getInstance("X509")
.generateCertPath(List.of(eeCert));
// CertPath validator should try to parse CRLDP and AIA extensions,
// and load CRLs/certs which they point to.
@ -151,7 +155,7 @@ public class ExtensionsWithLDAP {
= (InetSocketAddress) socket.getRemoteSocketAddress();
hosts.add(remoteAddress.getHostName());
};
try (SocksProxy proxy = SocksProxy.startProxy(socketConsumer)) {
try (SocksProxy _ = SocksProxy.startProxy(socketConsumer)) {
CertPathValidator.getInstance("PKIX").validate(cp,
new PKIXParameters(trustedCertsSet));
throw new RuntimeException("CertPathValidatorException not thrown");