| /* |
| * Copyright (c) 2015, 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 |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.security.provider.certpath.ldap; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.IOException; |
| import java.util.*; |
| import javax.naming.Context; |
| import javax.naming.NamingEnumeration; |
| import javax.naming.NamingException; |
| import javax.naming.NameNotFoundException; |
| import javax.naming.directory.Attribute; |
| import javax.naming.directory.Attributes; |
| import javax.naming.directory.BasicAttributes; |
| |
| import java.security.*; |
| import java.security.cert.Certificate; |
| import java.security.cert.*; |
| import javax.naming.CommunicationException; |
| import javax.naming.ldap.InitialLdapContext; |
| import javax.naming.ldap.LdapContext; |
| import javax.security.auth.x500.X500Principal; |
| |
| import sun.security.util.HexDumpEncoder; |
| import sun.security.provider.certpath.X509CertificatePair; |
| import sun.security.util.Cache; |
| import sun.security.util.Debug; |
| |
| /** |
| * Core implementation of a LDAP Cert Store. |
| * @see java.security.cert.CertStore |
| * |
| * @since 9 |
| */ |
| final class LDAPCertStoreImpl { |
| |
| private static final Debug debug = Debug.getInstance("certpath"); |
| |
| private final static boolean DEBUG = false; |
| |
| /** |
| * LDAP attribute identifiers. |
| */ |
| private static final String USER_CERT = "userCertificate;binary"; |
| private static final String CA_CERT = "cACertificate;binary"; |
| private static final String CROSS_CERT = "crossCertificatePair;binary"; |
| private static final String CRL = "certificateRevocationList;binary"; |
| private static final String ARL = "authorityRevocationList;binary"; |
| private static final String DELTA_CRL = "deltaRevocationList;binary"; |
| |
| // Constants for various empty values |
| private final static String[] STRING0 = new String[0]; |
| |
| private final static byte[][] BB0 = new byte[0][]; |
| |
| private final static Attributes EMPTY_ATTRIBUTES = new BasicAttributes(); |
| |
| // cache related constants |
| private final static int DEFAULT_CACHE_SIZE = 750; |
| private final static int DEFAULT_CACHE_LIFETIME = 30; |
| |
| private final static int LIFETIME; |
| |
| private final static String PROP_LIFETIME = |
| "sun.security.certpath.ldap.cache.lifetime"; |
| |
| /* |
| * Internal system property, that when set to "true", disables the |
| * JNDI application resource files lookup to prevent recursion issues |
| * when validating signed JARs with LDAP URLs in certificates. |
| */ |
| private final static String PROP_DISABLE_APP_RESOURCE_FILES = |
| "sun.security.certpath.ldap.disable.app.resource.files"; |
| |
| static { |
| String s = AccessController.doPrivileged( |
| (PrivilegedAction<String>) () -> System.getProperty(PROP_LIFETIME)); |
| if (s != null) { |
| LIFETIME = Integer.parseInt(s); // throws NumberFormatException |
| } else { |
| LIFETIME = DEFAULT_CACHE_LIFETIME; |
| } |
| } |
| |
| /** |
| * The CertificateFactory used to decode certificates from |
| * their binary stored form. |
| */ |
| private CertificateFactory cf; |
| /** |
| * The JNDI directory context. |
| */ |
| private LdapContext ctx; |
| |
| /** |
| * Flag indicating that communication error occurred. |
| */ |
| private boolean communicationError = false; |
| |
| /** |
| * Flag indicating whether we should prefetch CRLs. |
| */ |
| private boolean prefetchCRLs = false; |
| |
| private final Cache<String, byte[][]> valueCache; |
| |
| private int cacheHits = 0; |
| private int cacheMisses = 0; |
| private int requests = 0; |
| |
| /** |
| * Creates a <code>CertStore</code> with the specified parameters. |
| */ |
| LDAPCertStoreImpl(String serverName, int port) |
| throws InvalidAlgorithmParameterException { |
| createInitialDirContext(serverName, port); |
| // Create CertificateFactory for use later on |
| try { |
| cf = CertificateFactory.getInstance("X.509"); |
| } catch (CertificateException e) { |
| throw new InvalidAlgorithmParameterException( |
| "unable to create CertificateFactory for X.509"); |
| } |
| if (LIFETIME == 0) { |
| valueCache = Cache.newNullCache(); |
| } else if (LIFETIME < 0) { |
| valueCache = Cache.newSoftMemoryCache(DEFAULT_CACHE_SIZE); |
| } else { |
| valueCache = Cache.newSoftMemoryCache(DEFAULT_CACHE_SIZE, LIFETIME); |
| } |
| } |
| |
| /** |
| * Create InitialDirContext. |
| * |
| * @param server Server DNS name hosting LDAP service |
| * @param port Port at which server listens for requests |
| * @throws InvalidAlgorithmParameterException if creation fails |
| */ |
| private void createInitialDirContext(String server, int port) |
| throws InvalidAlgorithmParameterException { |
| String url = "ldap://" + server + ":" + port; |
| Hashtable<String,Object> env = new Hashtable<>(); |
| env.put(Context.INITIAL_CONTEXT_FACTORY, |
| "com.sun.jndi.ldap.LdapCtxFactory"); |
| env.put(Context.PROVIDER_URL, url); |
| |
| // If property is set to true, disable application resource file lookup. |
| boolean disableAppResourceFiles = AccessController.doPrivileged( |
| (PrivilegedAction<Boolean>) () -> Boolean.getBoolean(PROP_DISABLE_APP_RESOURCE_FILES)); |
| if (disableAppResourceFiles) { |
| if (debug != null) { |
| debug.println("LDAPCertStore disabling app resource files"); |
| } |
| env.put("com.sun.naming.disable.app.resource.files", "true"); |
| } |
| |
| try { |
| ctx = new InitialLdapContext(env, null); |
| /* |
| * By default, follow referrals unless application has |
| * overridden property in an application resource file. |
| */ |
| Hashtable<?,?> currentEnv = ctx.getEnvironment(); |
| if (currentEnv.get(Context.REFERRAL) == null) { |
| ctx.addToEnvironment(Context.REFERRAL, "follow"); |
| } |
| } catch (NamingException e) { |
| if (debug != null) { |
| debug.println("LDAPCertStore.engineInit about to throw " |
| + "InvalidAlgorithmParameterException"); |
| e.printStackTrace(); |
| } |
| Exception ee = new InvalidAlgorithmParameterException |
| ("unable to create InitialDirContext using supplied parameters"); |
| ee.initCause(e); |
| throw (InvalidAlgorithmParameterException)ee; |
| } |
| } |
| |
| /** |
| * Private class encapsulating the actual LDAP operations and cache |
| * handling. Use: |
| * |
| * LDAPRequest request = new LDAPRequest(dn); |
| * request.addRequestedAttribute(CROSS_CERT); |
| * request.addRequestedAttribute(CA_CERT); |
| * byte[][] crossValues = request.getValues(CROSS_CERT); |
| * byte[][] caValues = request.getValues(CA_CERT); |
| * |
| * At most one LDAP request is sent for each instance created. If all |
| * getValues() calls can be satisfied from the cache, no request |
| * is sent at all. If a request is sent, all requested attributes |
| * are always added to the cache irrespective of whether the getValues() |
| * method is called. |
| */ |
| private class LDAPRequest { |
| |
| private final String name; |
| private Map<String, byte[][]> valueMap; |
| private final List<String> requestedAttributes; |
| |
| LDAPRequest(String name) { |
| this.name = name; |
| requestedAttributes = new ArrayList<>(5); |
| } |
| |
| String getName() { |
| return name; |
| } |
| |
| void addRequestedAttribute(String attrId) { |
| if (valueMap != null) { |
| throw new IllegalStateException("Request already sent"); |
| } |
| requestedAttributes.add(attrId); |
| } |
| |
| /** |
| * Gets one or more binary values from an attribute. |
| * |
| * @param name the location holding the attribute |
| * @param attrId the attribute identifier |
| * @return an array of binary values (byte arrays) |
| * @throws NamingException if a naming exception occurs |
| */ |
| byte[][] getValues(String attrId) throws NamingException { |
| if (DEBUG && ((cacheHits + cacheMisses) % 50 == 0)) { |
| System.out.println("Cache hits: " + cacheHits + "; misses: " |
| + cacheMisses); |
| } |
| String cacheKey = name + "|" + attrId; |
| byte[][] values = valueCache.get(cacheKey); |
| if (values != null) { |
| cacheHits++; |
| return values; |
| } |
| cacheMisses++; |
| Map<String, byte[][]> attrs = getValueMap(); |
| values = attrs.get(attrId); |
| return values; |
| } |
| |
| /** |
| * Get a map containing the values for this request. The first time |
| * this method is called on an object, the LDAP request is sent, |
| * the results parsed and added to a private map and also to the |
| * cache of this LDAPCertStore. Subsequent calls return the private |
| * map immediately. |
| * |
| * The map contains an entry for each requested attribute. The |
| * attribute name is the key, values are byte[][]. If there are no |
| * values for that attribute, values are byte[0][]. |
| * |
| * @return the value Map |
| * @throws NamingException if a naming exception occurs |
| */ |
| private Map<String, byte[][]> getValueMap() throws NamingException { |
| if (valueMap != null) { |
| return valueMap; |
| } |
| if (DEBUG) { |
| System.out.println("Request: " + name + ":" + requestedAttributes); |
| requests++; |
| if (requests % 5 == 0) { |
| System.out.println("LDAP requests: " + requests); |
| } |
| } |
| valueMap = new HashMap<>(8); |
| String[] attrIds = requestedAttributes.toArray(STRING0); |
| Attributes attrs; |
| |
| if (communicationError) { |
| ctx.reconnect(null); |
| communicationError = false; |
| } |
| |
| try { |
| attrs = ctx.getAttributes(name, attrIds); |
| } catch (CommunicationException ce) { |
| communicationError = true; |
| throw ce; |
| } catch (NameNotFoundException e) { |
| // name does not exist on this LDAP server |
| // treat same as not attributes found |
| attrs = EMPTY_ATTRIBUTES; |
| } |
| for (String attrId : requestedAttributes) { |
| Attribute attr = attrs.get(attrId); |
| byte[][] values = getAttributeValues(attr); |
| cacheAttribute(attrId, values); |
| valueMap.put(attrId, values); |
| } |
| return valueMap; |
| } |
| |
| /** |
| * Add the values to the cache. |
| */ |
| private void cacheAttribute(String attrId, byte[][] values) { |
| String cacheKey = name + "|" + attrId; |
| valueCache.put(cacheKey, values); |
| } |
| |
| /** |
| * Get the values for the given attribute. If the attribute is null |
| * or does not contain any values, a zero length byte array is |
| * returned. NOTE that it is assumed that all values are byte arrays. |
| */ |
| private byte[][] getAttributeValues(Attribute attr) |
| throws NamingException { |
| byte[][] values; |
| if (attr == null) { |
| values = BB0; |
| } else { |
| values = new byte[attr.size()][]; |
| int i = 0; |
| NamingEnumeration<?> enum_ = attr.getAll(); |
| while (enum_.hasMore()) { |
| Object obj = enum_.next(); |
| if (debug != null) { |
| if (obj instanceof String) { |
| debug.println("LDAPCertStore.getAttrValues() " |
| + "enum.next is a string!: " + obj); |
| } |
| } |
| byte[] value = (byte[])obj; |
| values[i++] = value; |
| } |
| } |
| return values; |
| } |
| |
| } |
| |
| /* |
| * Gets certificates from an attribute id and location in the LDAP |
| * directory. Returns a Collection containing only the Certificates that |
| * match the specified CertSelector. |
| * |
| * @param name the location holding the attribute |
| * @param id the attribute identifier |
| * @param sel a CertSelector that the Certificates must match |
| * @return a Collection of Certificates found |
| * @throws CertStoreException if an exception occurs |
| */ |
| private Collection<X509Certificate> getCertificates(LDAPRequest request, |
| String id, X509CertSelector sel) throws CertStoreException { |
| |
| /* fetch encoded certs from storage */ |
| byte[][] encodedCert; |
| try { |
| encodedCert = request.getValues(id); |
| } catch (NamingException namingEx) { |
| throw new CertStoreException(namingEx); |
| } |
| |
| int n = encodedCert.length; |
| if (n == 0) { |
| return Collections.emptySet(); |
| } |
| |
| List<X509Certificate> certs = new ArrayList<>(n); |
| /* decode certs and check if they satisfy selector */ |
| for (int i = 0; i < n; i++) { |
| ByteArrayInputStream bais = new ByteArrayInputStream(encodedCert[i]); |
| try { |
| Certificate cert = cf.generateCertificate(bais); |
| if (sel.match(cert)) { |
| certs.add((X509Certificate)cert); |
| } |
| } catch (CertificateException e) { |
| if (debug != null) { |
| debug.println("LDAPCertStore.getCertificates() encountered " |
| + "exception while parsing cert, skipping the bad data: "); |
| HexDumpEncoder encoder = new HexDumpEncoder(); |
| debug.println( |
| "[ " + encoder.encodeBuffer(encodedCert[i]) + " ]"); |
| } |
| } |
| } |
| |
| return certs; |
| } |
| |
| /* |
| * Gets certificate pairs from an attribute id and location in the LDAP |
| * directory. |
| * |
| * @param name the location holding the attribute |
| * @param id the attribute identifier |
| * @return a Collection of X509CertificatePairs found |
| * @throws CertStoreException if an exception occurs |
| */ |
| private Collection<X509CertificatePair> getCertPairs( |
| LDAPRequest request, String id) throws CertStoreException { |
| |
| /* fetch the encoded cert pairs from storage */ |
| byte[][] encodedCertPair; |
| try { |
| encodedCertPair = request.getValues(id); |
| } catch (NamingException namingEx) { |
| throw new CertStoreException(namingEx); |
| } |
| |
| int n = encodedCertPair.length; |
| if (n == 0) { |
| return Collections.emptySet(); |
| } |
| |
| List<X509CertificatePair> certPairs = new ArrayList<>(n); |
| /* decode each cert pair and add it to the Collection */ |
| for (int i = 0; i < n; i++) { |
| try { |
| X509CertificatePair certPair = |
| X509CertificatePair.generateCertificatePair(encodedCertPair[i]); |
| certPairs.add(certPair); |
| } catch (CertificateException e) { |
| if (debug != null) { |
| debug.println( |
| "LDAPCertStore.getCertPairs() encountered exception " |
| + "while parsing cert, skipping the bad data: "); |
| HexDumpEncoder encoder = new HexDumpEncoder(); |
| debug.println( |
| "[ " + encoder.encodeBuffer(encodedCertPair[i]) + " ]"); |
| } |
| } |
| } |
| |
| return certPairs; |
| } |
| |
| /* |
| * Looks at certificate pairs stored in the crossCertificatePair attribute |
| * at the specified location in the LDAP directory. Returns a Collection |
| * containing all X509Certificates stored in the forward component that match |
| * the forward X509CertSelector and all Certificates stored in the reverse |
| * component that match the reverse X509CertSelector. |
| * <p> |
| * If either forward or reverse is null, all certificates from the |
| * corresponding component will be rejected. |
| * |
| * @param name the location to look in |
| * @param forward the forward X509CertSelector (or null) |
| * @param reverse the reverse X509CertSelector (or null) |
| * @return a Collection of X509Certificates found |
| * @throws CertStoreException if an exception occurs |
| */ |
| private Collection<X509Certificate> getMatchingCrossCerts( |
| LDAPRequest request, X509CertSelector forward, |
| X509CertSelector reverse) |
| throws CertStoreException { |
| // Get the cert pairs |
| Collection<X509CertificatePair> certPairs = |
| getCertPairs(request, CROSS_CERT); |
| |
| // Find Certificates that match and put them in a list |
| ArrayList<X509Certificate> matchingCerts = new ArrayList<>(); |
| for (X509CertificatePair certPair : certPairs) { |
| X509Certificate cert; |
| if (forward != null) { |
| cert = certPair.getForward(); |
| if ((cert != null) && forward.match(cert)) { |
| matchingCerts.add(cert); |
| } |
| } |
| if (reverse != null) { |
| cert = certPair.getReverse(); |
| if ((cert != null) && reverse.match(cert)) { |
| matchingCerts.add(cert); |
| } |
| } |
| } |
| return matchingCerts; |
| } |
| |
| /** |
| * Returns a <code>Collection</code> of <code>X509Certificate</code>s that |
| * match the specified selector. If no <code>X509Certificate</code>s |
| * match the selector, an empty <code>Collection</code> will be returned. |
| * <p> |
| * It is not practical to search every entry in the LDAP database for |
| * matching <code>X509Certificate</code>s. Instead, the |
| * <code>X509CertSelector</code> is examined in order to determine where |
| * matching <code>Certificate</code>s are likely to be found (according |
| * to the PKIX LDAPv2 schema, RFC 2587). |
| * If the subject is specified, its directory entry is searched. If the |
| * issuer is specified, its directory entry is searched. If neither the |
| * subject nor the issuer are specified (or the selector is not an |
| * <code>X509CertSelector</code>), a <code>CertStoreException</code> is |
| * thrown. |
| * |
| * @param selector a <code>X509CertSelector</code> used to select which |
| * <code>Certificate</code>s should be returned. |
| * @return a <code>Collection</code> of <code>X509Certificate</code>s that |
| * match the specified selector |
| * @throws CertStoreException if an exception occurs |
| */ |
| synchronized Collection<X509Certificate> getCertificates |
| (X509CertSelector xsel, String ldapDN) throws CertStoreException { |
| |
| if (ldapDN == null) { |
| ldapDN = xsel.getSubjectAsString(); |
| } |
| int basicConstraints = xsel.getBasicConstraints(); |
| String issuer = xsel.getIssuerAsString(); |
| HashSet<X509Certificate> certs = new HashSet<>(); |
| if (debug != null) { |
| debug.println("LDAPCertStore.engineGetCertificates() basicConstraints: " |
| + basicConstraints); |
| } |
| |
| // basicConstraints: |
| // -2: only EE certs accepted |
| // -1: no check is done |
| // 0: any CA certificate accepted |
| // >1: certificate's basicConstraints extension pathlen must match |
| if (ldapDN != null) { |
| if (debug != null) { |
| debug.println("LDAPCertStore.engineGetCertificates() " |
| + " subject is not null"); |
| } |
| LDAPRequest request = new LDAPRequest(ldapDN); |
| if (basicConstraints > -2) { |
| request.addRequestedAttribute(CROSS_CERT); |
| request.addRequestedAttribute(CA_CERT); |
| request.addRequestedAttribute(ARL); |
| if (prefetchCRLs) { |
| request.addRequestedAttribute(CRL); |
| } |
| } |
| if (basicConstraints < 0) { |
| request.addRequestedAttribute(USER_CERT); |
| } |
| |
| if (basicConstraints > -2) { |
| certs.addAll(getMatchingCrossCerts(request, xsel, null)); |
| if (debug != null) { |
| debug.println("LDAPCertStore.engineGetCertificates() after " |
| + "getMatchingCrossCerts(subject,xsel,null),certs.size(): " |
| + certs.size()); |
| } |
| certs.addAll(getCertificates(request, CA_CERT, xsel)); |
| if (debug != null) { |
| debug.println("LDAPCertStore.engineGetCertificates() after " |
| + "getCertificates(subject,CA_CERT,xsel),certs.size(): " |
| + certs.size()); |
| } |
| } |
| if (basicConstraints < 0) { |
| certs.addAll(getCertificates(request, USER_CERT, xsel)); |
| if (debug != null) { |
| debug.println("LDAPCertStore.engineGetCertificates() after " |
| + "getCertificates(subject,USER_CERT, xsel),certs.size(): " |
| + certs.size()); |
| } |
| } |
| } else { |
| if (debug != null) { |
| debug.println |
| ("LDAPCertStore.engineGetCertificates() subject is null"); |
| } |
| if (basicConstraints == -2) { |
| throw new CertStoreException("need subject to find EE certs"); |
| } |
| if (issuer == null) { |
| throw new CertStoreException("need subject or issuer to find certs"); |
| } |
| } |
| if (debug != null) { |
| debug.println("LDAPCertStore.engineGetCertificates() about to " |
| + "getMatchingCrossCerts..."); |
| } |
| if ((issuer != null) && (basicConstraints > -2)) { |
| LDAPRequest request = new LDAPRequest(issuer); |
| request.addRequestedAttribute(CROSS_CERT); |
| request.addRequestedAttribute(CA_CERT); |
| request.addRequestedAttribute(ARL); |
| if (prefetchCRLs) { |
| request.addRequestedAttribute(CRL); |
| } |
| |
| certs.addAll(getMatchingCrossCerts(request, null, xsel)); |
| if (debug != null) { |
| debug.println("LDAPCertStore.engineGetCertificates() after " |
| + "getMatchingCrossCerts(issuer,null,xsel),certs.size(): " |
| + certs.size()); |
| } |
| certs.addAll(getCertificates(request, CA_CERT, xsel)); |
| if (debug != null) { |
| debug.println("LDAPCertStore.engineGetCertificates() after " |
| + "getCertificates(issuer,CA_CERT,xsel),certs.size(): " |
| + certs.size()); |
| } |
| } |
| if (debug != null) { |
| debug.println("LDAPCertStore.engineGetCertificates() returning certs"); |
| } |
| return certs; |
| } |
| |
| /* |
| * Gets CRLs from an attribute id and location in the LDAP directory. |
| * Returns a Collection containing only the CRLs that match the |
| * specified X509CRLSelector. |
| * |
| * @param name the location holding the attribute |
| * @param id the attribute identifier |
| * @param sel a X509CRLSelector that the CRLs must match |
| * @return a Collection of CRLs found |
| * @throws CertStoreException if an exception occurs |
| */ |
| private Collection<X509CRL> getCRLs(LDAPRequest request, String id, |
| X509CRLSelector sel) throws CertStoreException { |
| |
| /* fetch the encoded crls from storage */ |
| byte[][] encodedCRL; |
| try { |
| encodedCRL = request.getValues(id); |
| } catch (NamingException namingEx) { |
| throw new CertStoreException(namingEx); |
| } |
| |
| int n = encodedCRL.length; |
| if (n == 0) { |
| return Collections.emptySet(); |
| } |
| |
| List<X509CRL> crls = new ArrayList<>(n); |
| /* decode each crl and check if it matches selector */ |
| for (int i = 0; i < n; i++) { |
| try { |
| CRL crl = cf.generateCRL(new ByteArrayInputStream(encodedCRL[i])); |
| if (sel.match(crl)) { |
| crls.add((X509CRL)crl); |
| } |
| } catch (CRLException e) { |
| if (debug != null) { |
| debug.println("LDAPCertStore.getCRLs() encountered exception" |
| + " while parsing CRL, skipping the bad data: "); |
| HexDumpEncoder encoder = new HexDumpEncoder(); |
| debug.println("[ " + encoder.encodeBuffer(encodedCRL[i]) + " ]"); |
| } |
| } |
| } |
| |
| return crls; |
| } |
| |
| /** |
| * Returns a <code>Collection</code> of <code>X509CRL</code>s that |
| * match the specified selector. If no <code>X509CRL</code>s |
| * match the selector, an empty <code>Collection</code> will be returned. |
| * <p> |
| * It is not practical to search every entry in the LDAP database for |
| * matching <code>X509CRL</code>s. Instead, the <code>X509CRLSelector</code> |
| * is examined in order to determine where matching <code>X509CRL</code>s |
| * are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587). |
| * If issuerNames or certChecking are specified, the issuer's directory |
| * entry is searched. If neither issuerNames or certChecking are specified |
| * (or the selector is not an <code>X509CRLSelector</code>), a |
| * <code>CertStoreException</code> is thrown. |
| * |
| * @param selector A <code>X509CRLSelector</code> used to select which |
| * <code>CRL</code>s should be returned. Specify <code>null</code> |
| * to return all <code>CRL</code>s. |
| * @return A <code>Collection</code> of <code>X509CRL</code>s that |
| * match the specified selector |
| * @throws CertStoreException if an exception occurs |
| */ |
| synchronized Collection<X509CRL> getCRLs(X509CRLSelector xsel, |
| String ldapDN) throws CertStoreException { |
| |
| HashSet<X509CRL> crls = new HashSet<>(); |
| |
| // Look in directory entry for issuer of cert we're checking. |
| Collection<Object> issuerNames; |
| X509Certificate certChecking = xsel.getCertificateChecking(); |
| if (certChecking != null) { |
| issuerNames = new HashSet<>(); |
| X500Principal issuer = certChecking.getIssuerX500Principal(); |
| issuerNames.add(issuer.getName(X500Principal.RFC2253)); |
| } else { |
| // But if we don't know which cert we're checking, try the directory |
| // entries of all acceptable CRL issuers |
| if (ldapDN != null) { |
| issuerNames = new HashSet<>(); |
| issuerNames.add(ldapDN); |
| } else { |
| issuerNames = xsel.getIssuerNames(); |
| if (issuerNames == null) { |
| throw new CertStoreException("need issuerNames or" |
| + " certChecking to find CRLs"); |
| } |
| } |
| } |
| for (Object nameObject : issuerNames) { |
| String issuerName; |
| if (nameObject instanceof byte[]) { |
| try { |
| X500Principal issuer = new X500Principal((byte[])nameObject); |
| issuerName = issuer.getName(X500Principal.RFC2253); |
| } catch (IllegalArgumentException e) { |
| continue; |
| } |
| } else { |
| issuerName = (String)nameObject; |
| } |
| // If all we want is CA certs, try to get the (probably shorter) ARL |
| Collection<X509CRL> entryCRLs = Collections.emptySet(); |
| if (certChecking == null || certChecking.getBasicConstraints() != -1) { |
| LDAPRequest request = new LDAPRequest(issuerName); |
| request.addRequestedAttribute(CROSS_CERT); |
| request.addRequestedAttribute(CA_CERT); |
| request.addRequestedAttribute(ARL); |
| if (prefetchCRLs) { |
| request.addRequestedAttribute(CRL); |
| } |
| try { |
| entryCRLs = getCRLs(request, ARL, xsel); |
| if (entryCRLs.isEmpty()) { |
| // no ARLs found. We assume that means that there are |
| // no ARLs on this server at all and prefetch the CRLs. |
| prefetchCRLs = true; |
| } else { |
| crls.addAll(entryCRLs); |
| } |
| } catch (CertStoreException e) { |
| if (debug != null) { |
| debug.println("LDAPCertStore.engineGetCRLs non-fatal error " |
| + "retrieving ARLs:" + e); |
| e.printStackTrace(); |
| } |
| } |
| } |
| // Otherwise, get the CRL |
| // if certChecking is null, we don't know if we should look in ARL or CRL |
| // attribute, so check both for matching CRLs. |
| if (entryCRLs.isEmpty() || certChecking == null) { |
| LDAPRequest request = new LDAPRequest(issuerName); |
| request.addRequestedAttribute(CRL); |
| entryCRLs = getCRLs(request, CRL, xsel); |
| crls.addAll(entryCRLs); |
| } |
| } |
| return crls; |
| } |
| } |