J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. |
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | * |
| 5 | * This code is free software; you can redistribute it and/or modify it |
| 6 | * under the terms of the GNU General Public License version 2 only, as |
| 7 | * published by the Free Software Foundation. Sun designates this |
| 8 | * particular file as subject to the "Classpath" exception as provided |
| 9 | * by Sun in the LICENSE file that accompanied this code. |
| 10 | * |
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 14 | * version 2 for more details (a copy is included in the LICENSE file that |
| 15 | * accompanied this code). |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License version |
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 20 | * |
| 21 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 23 | * have any questions. |
| 24 | */ |
| 25 | |
| 26 | package sun.security.provider.certpath; |
| 27 | |
| 28 | import java.io.ByteArrayInputStream; |
| 29 | import java.io.IOException; |
| 30 | import java.math.BigInteger; |
| 31 | import java.net.URI; |
| 32 | import java.util.*; |
| 33 | import javax.naming.Context; |
| 34 | import javax.naming.NamingEnumeration; |
| 35 | import javax.naming.NamingException; |
| 36 | import javax.naming.NameNotFoundException; |
| 37 | import javax.naming.directory.Attribute; |
| 38 | import javax.naming.directory.Attributes; |
| 39 | import javax.naming.directory.BasicAttributes; |
| 40 | import javax.naming.directory.DirContext; |
| 41 | import javax.naming.directory.InitialDirContext; |
| 42 | |
| 43 | import java.security.*; |
| 44 | import java.security.cert.Certificate; |
| 45 | import java.security.cert.*; |
| 46 | import javax.security.auth.x500.X500Principal; |
| 47 | |
| 48 | import sun.misc.HexDumpEncoder; |
| 49 | import sun.security.util.Cache; |
| 50 | import sun.security.util.Debug; |
| 51 | import sun.security.x509.X500Name; |
| 52 | import sun.security.action.GetPropertyAction; |
| 53 | |
| 54 | /** |
| 55 | * A <code>CertStore</code> that retrieves <code>Certificates</code> and |
| 56 | * <code>CRL</code>s from an LDAP directory, using the PKIX LDAP V2 Schema |
| 57 | * (RFC 2587): |
| 58 | * <a href="http://www.ietf.org/rfc/rfc2587.txt"> |
| 59 | * http://www.ietf.org/rfc/rfc2587.txt</a>. |
| 60 | * <p> |
| 61 | * Before calling the {@link #engineGetCertificates engineGetCertificates} or |
| 62 | * {@link #engineGetCRLs engineGetCRLs} methods, the |
| 63 | * {@link #LDAPCertStore(CertStoreParameters) |
| 64 | * LDAPCertStore(CertStoreParameters)} constructor is called to create the |
| 65 | * <code>CertStore</code> and establish the DNS name and port of the LDAP |
| 66 | * server from which <code>Certificate</code>s and <code>CRL</code>s will be |
| 67 | * retrieved. |
| 68 | * <p> |
| 69 | * <b>Concurrent Access</b> |
| 70 | * <p> |
| 71 | * As described in the javadoc for <code>CertStoreSpi</code>, the |
| 72 | * <code>engineGetCertificates</code> and <code>engineGetCRLs</code> methods |
| 73 | * must be thread-safe. That is, multiple threads may concurrently |
| 74 | * invoke these methods on a single <code>LDAPCertStore</code> object |
| 75 | * (or more than one) with no ill effects. This allows a |
| 76 | * <code>CertPathBuilder</code> to search for a CRL while simultaneously |
| 77 | * searching for further certificates, for instance. |
| 78 | * <p> |
| 79 | * This is achieved by adding the <code>synchronized</code> keyword to the |
| 80 | * <code>engineGetCertificates</code> and <code>engineGetCRLs</code> methods. |
| 81 | * <p> |
| 82 | * This classes uses caching and requests multiple attributes at once to |
| 83 | * minimize LDAP round trips. The cache is associated with the CertStore |
| 84 | * instance. It uses soft references to hold the values to minimize impact |
| 85 | * on footprint and currently has a maximum size of 750 attributes and a |
| 86 | * 30 second default lifetime. |
| 87 | * <p> |
| 88 | * We always request CA certificates, cross certificate pairs, and ARLs in |
| 89 | * a single LDAP request when any one of them is needed. The reason is that |
| 90 | * we typically need all of them anyway and requesting them in one go can |
| 91 | * reduce the number of requests to a third. Even if we don't need them, |
| 92 | * these attributes are typically small enough not to cause a noticeable |
| 93 | * overhead. In addition, when the prefetchCRLs flag is true, we also request |
| 94 | * the full CRLs. It is currently false initially but set to true once any |
| 95 | * request for an ARL to the server returns an null value. The reason is |
| 96 | * that CRLs could be rather large but are rarely used. This implementation |
| 97 | * should improve performance in most cases. |
| 98 | * |
| 99 | * @see java.security.cert.CertStore |
| 100 | * |
| 101 | * @since 1.4 |
| 102 | * @author Steve Hanna |
| 103 | * @author Andreas Sterbenz |
| 104 | */ |
| 105 | public class LDAPCertStore extends CertStoreSpi { |
| 106 | |
| 107 | private static final Debug debug = Debug.getInstance("certpath"); |
| 108 | |
| 109 | private final static boolean DEBUG = false; |
| 110 | |
| 111 | /** |
| 112 | * LDAP attribute identifiers. |
| 113 | */ |
| 114 | private static final String USER_CERT = "userCertificate;binary"; |
| 115 | private static final String CA_CERT = "cACertificate;binary"; |
| 116 | private static final String CROSS_CERT = "crossCertificatePair;binary"; |
| 117 | private static final String CRL = "certificateRevocationList;binary"; |
| 118 | private static final String ARL = "authorityRevocationList;binary"; |
| 119 | private static final String DELTA_CRL = "deltaRevocationList;binary"; |
| 120 | |
| 121 | // Constants for various empty values |
| 122 | private final static String[] STRING0 = new String[0]; |
| 123 | |
| 124 | private final static byte[][] BB0 = new byte[0][]; |
| 125 | |
| 126 | private final static Attributes EMPTY_ATTRIBUTES = new BasicAttributes(); |
| 127 | |
| 128 | // cache related constants |
| 129 | private final static int DEFAULT_CACHE_SIZE = 750; |
| 130 | private final static int DEFAULT_CACHE_LIFETIME = 30; |
| 131 | |
| 132 | private final static int LIFETIME; |
| 133 | |
| 134 | private final static String PROP_LIFETIME = |
| 135 | "sun.security.certpath.ldap.cache.lifetime"; |
| 136 | |
| 137 | static { |
| 138 | String s = AccessController.doPrivileged( |
| 139 | new GetPropertyAction(PROP_LIFETIME)); |
| 140 | if (s != null) { |
| 141 | LIFETIME = Integer.parseInt(s); // throws NumberFormatException |
| 142 | } else { |
| 143 | LIFETIME = DEFAULT_CACHE_LIFETIME; |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | /** |
| 148 | * The CertificateFactory used to decode certificates from |
| 149 | * their binary stored form. |
| 150 | */ |
| 151 | private CertificateFactory cf; |
| 152 | /** |
| 153 | * The JNDI directory context. |
| 154 | */ |
| 155 | private DirContext ctx; |
| 156 | |
| 157 | /** |
| 158 | * Flag indicating whether we should prefetch CRLs. |
| 159 | */ |
| 160 | private boolean prefetchCRLs = false; |
| 161 | |
| 162 | private final Cache valueCache; |
| 163 | |
| 164 | private int cacheHits = 0; |
| 165 | private int cacheMisses = 0; |
| 166 | private int requests = 0; |
| 167 | |
| 168 | /** |
| 169 | * Creates a <code>CertStore</code> with the specified parameters. |
| 170 | * For this class, the parameters object must be an instance of |
| 171 | * <code>LDAPCertStoreParameters</code>. |
| 172 | * |
| 173 | * @param params the algorithm parameters |
| 174 | * @exception InvalidAlgorithmParameterException if params is not an |
| 175 | * instance of <code>LDAPCertStoreParameters</code> |
| 176 | */ |
| 177 | public LDAPCertStore(CertStoreParameters params) |
| 178 | throws InvalidAlgorithmParameterException { |
| 179 | super(params); |
| 180 | if (!(params instanceof LDAPCertStoreParameters)) |
| 181 | throw new InvalidAlgorithmParameterException( |
| 182 | "parameters must be LDAPCertStoreParameters"); |
| 183 | |
| 184 | LDAPCertStoreParameters lparams = (LDAPCertStoreParameters) params; |
| 185 | |
| 186 | // Create InitialDirContext needed to communicate with the server |
| 187 | createInitialDirContext(lparams.getServerName(), lparams.getPort()); |
| 188 | |
| 189 | // Create CertificateFactory for use later on |
| 190 | try { |
| 191 | cf = CertificateFactory.getInstance("X.509"); |
| 192 | } catch (CertificateException e) { |
| 193 | throw new InvalidAlgorithmParameterException( |
| 194 | "unable to create CertificateFactory for X.509"); |
| 195 | } |
| 196 | if (LIFETIME == 0) { |
| 197 | valueCache = Cache.newNullCache(); |
| 198 | } else if (LIFETIME < 0) { |
| 199 | valueCache = Cache.newSoftMemoryCache(DEFAULT_CACHE_SIZE); |
| 200 | } else { |
| 201 | valueCache = Cache.newSoftMemoryCache(DEFAULT_CACHE_SIZE, LIFETIME); |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | /** |
| 206 | * Returns an LDAP CertStore. This method consults a cache of |
| 207 | * CertStores (shared per JVM) using the LDAP server/port as a key. |
| 208 | */ |
| 209 | private static final Cache certStoreCache = Cache.newSoftMemoryCache(185); |
| 210 | static synchronized CertStore getInstance(LDAPCertStoreParameters params) |
| 211 | throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { |
| 212 | CertStore lcs = (CertStore) certStoreCache.get(params); |
| 213 | if (lcs == null) { |
| 214 | lcs = CertStore.getInstance("LDAP", params); |
| 215 | certStoreCache.put(params, lcs); |
| 216 | } else { |
| 217 | if (debug != null) { |
| 218 | debug.println("LDAPCertStore.getInstance: cache hit"); |
| 219 | } |
| 220 | } |
| 221 | return lcs; |
| 222 | } |
| 223 | |
| 224 | /** |
| 225 | * Create InitialDirContext. |
| 226 | * |
| 227 | * @param server Server DNS name hosting LDAP service |
| 228 | * @param port Port at which server listens for requests |
| 229 | * @throws InvalidAlgorithmParameterException if creation fails |
| 230 | */ |
| 231 | private void createInitialDirContext(String server, int port) |
| 232 | throws InvalidAlgorithmParameterException { |
| 233 | String url = "ldap://" + server + ":" + port; |
| 234 | Hashtable<String,Object> env = new Hashtable<String,Object>(); |
| 235 | env.put(Context.INITIAL_CONTEXT_FACTORY, |
| 236 | "com.sun.jndi.ldap.LdapCtxFactory"); |
| 237 | env.put(Context.PROVIDER_URL, url); |
| 238 | try { |
| 239 | ctx = new InitialDirContext(env); |
| 240 | /* |
| 241 | * By default, follow referrals unless application has |
| 242 | * overridden property in an application resource file. |
| 243 | */ |
| 244 | Hashtable<?,?> currentEnv = ctx.getEnvironment(); |
| 245 | if (currentEnv.get(Context.REFERRAL) == null) { |
| 246 | ctx.addToEnvironment(Context.REFERRAL, "follow"); |
| 247 | } |
| 248 | } catch (NamingException e) { |
| 249 | if (debug != null) { |
| 250 | debug.println("LDAPCertStore.engineInit about to throw " |
| 251 | + "InvalidAlgorithmParameterException"); |
| 252 | e.printStackTrace(); |
| 253 | } |
| 254 | Exception ee = new InvalidAlgorithmParameterException |
| 255 | ("unable to create InitialDirContext using supplied parameters"); |
| 256 | ee.initCause(e); |
| 257 | throw (InvalidAlgorithmParameterException)ee; |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | /** |
| 262 | * Private class encapsulating the actual LDAP operations and cache |
| 263 | * handling. Use: |
| 264 | * |
| 265 | * LDAPRequest request = new LDAPRequest(dn); |
| 266 | * request.addRequestedAttribute(CROSS_CERT); |
| 267 | * request.addRequestedAttribute(CA_CERT); |
| 268 | * byte[][] crossValues = request.getValues(CROSS_CERT); |
| 269 | * byte[][] caValues = request.getValues(CA_CERT); |
| 270 | * |
| 271 | * At most one LDAP request is sent for each instance created. If all |
| 272 | * getValues() calls can be satisfied from the cache, no request |
| 273 | * is sent at all. If a request is sent, all requested attributes |
| 274 | * are always added to the cache irrespective of whether the getValues() |
| 275 | * method is called. |
| 276 | */ |
| 277 | private class LDAPRequest { |
| 278 | |
| 279 | private final String name; |
| 280 | private Map<String, byte[][]> valueMap; |
| 281 | private final List<String> requestedAttributes; |
| 282 | |
| 283 | LDAPRequest(String name) { |
| 284 | this.name = name; |
| 285 | requestedAttributes = new ArrayList<String>(5); |
| 286 | } |
| 287 | |
| 288 | String getName() { |
| 289 | return name; |
| 290 | } |
| 291 | |
| 292 | void addRequestedAttribute(String attrId) { |
| 293 | if (valueMap != null) { |
| 294 | throw new IllegalStateException("Request already sent"); |
| 295 | } |
| 296 | requestedAttributes.add(attrId); |
| 297 | } |
| 298 | |
| 299 | /** |
| 300 | * Gets one or more binary values from an attribute. |
| 301 | * |
| 302 | * @param name the location holding the attribute |
| 303 | * @param attrId the attribute identifier |
| 304 | * @return an array of binary values (byte arrays) |
| 305 | * @throws NamingException if a naming exception occurs |
| 306 | */ |
| 307 | byte[][] getValues(String attrId) throws NamingException { |
| 308 | if (DEBUG && ((cacheHits + cacheMisses) % 50 == 0)) { |
| 309 | System.out.println("Cache hits: " + cacheHits + "; misses: " |
| 310 | + cacheMisses); |
| 311 | } |
| 312 | String cacheKey = name + "|" + attrId; |
| 313 | byte[][] values = (byte[][])valueCache.get(cacheKey); |
| 314 | if (values != null) { |
| 315 | cacheHits++; |
| 316 | return values; |
| 317 | } |
| 318 | cacheMisses++; |
| 319 | Map<String, byte[][]> attrs = getValueMap(); |
| 320 | values = attrs.get(attrId); |
| 321 | return values; |
| 322 | } |
| 323 | |
| 324 | /** |
| 325 | * Get a map containing the values for this request. The first time |
| 326 | * this method is called on an object, the LDAP request is sent, |
| 327 | * the results parsed and added to a private map and also to the |
| 328 | * cache of this LDAPCertStore. Subsequent calls return the private |
| 329 | * map immediately. |
| 330 | * |
| 331 | * The map contains an entry for each requested attribute. The |
| 332 | * attribute name is the key, values are byte[][]. If there are no |
| 333 | * values for that attribute, values are byte[0][]. |
| 334 | * |
| 335 | * @return the value Map |
| 336 | * @throws NamingException if a naming exception occurs |
| 337 | */ |
| 338 | private Map<String, byte[][]> getValueMap() throws NamingException { |
| 339 | if (valueMap != null) { |
| 340 | return valueMap; |
| 341 | } |
| 342 | if (DEBUG) { |
| 343 | System.out.println("Request: " + name + ":" + requestedAttributes); |
| 344 | requests++; |
| 345 | if (requests % 5 == 0) { |
| 346 | System.out.println("LDAP requests: " + requests); |
| 347 | } |
| 348 | } |
| 349 | valueMap = new HashMap<String, byte[][]>(8); |
| 350 | String[] attrIds = requestedAttributes.toArray(STRING0); |
| 351 | Attributes attrs; |
| 352 | try { |
| 353 | attrs = ctx.getAttributes(name, attrIds); |
| 354 | } catch (NameNotFoundException e) { |
| 355 | // name does not exist on this LDAP server |
| 356 | // treat same as not attributes found |
| 357 | attrs = EMPTY_ATTRIBUTES; |
| 358 | } |
| 359 | for (String attrId : requestedAttributes) { |
| 360 | Attribute attr = attrs.get(attrId); |
| 361 | byte[][] values = getAttributeValues(attr); |
| 362 | cacheAttribute(attrId, values); |
| 363 | valueMap.put(attrId, values); |
| 364 | } |
| 365 | return valueMap; |
| 366 | } |
| 367 | |
| 368 | /** |
| 369 | * Add the values to the cache. |
| 370 | */ |
| 371 | private void cacheAttribute(String attrId, byte[][] values) { |
| 372 | String cacheKey = name + "|" + attrId; |
| 373 | valueCache.put(cacheKey, values); |
| 374 | } |
| 375 | |
| 376 | /** |
| 377 | * Get the values for the given attribute. If the attribute is null |
| 378 | * or does not contain any values, a zero length byte array is |
| 379 | * returned. NOTE that it is assumed that all values are byte arrays. |
| 380 | */ |
| 381 | private byte[][] getAttributeValues(Attribute attr) |
| 382 | throws NamingException { |
| 383 | byte[][] values; |
| 384 | if (attr == null) { |
| 385 | values = BB0; |
| 386 | } else { |
| 387 | values = new byte[attr.size()][]; |
| 388 | int i = 0; |
| 389 | NamingEnumeration<?> enum_ = attr.getAll(); |
| 390 | while (enum_.hasMore()) { |
| 391 | Object obj = enum_.next(); |
| 392 | if (debug != null) { |
| 393 | if (obj instanceof String) { |
| 394 | debug.println("LDAPCertStore.getAttrValues() " |
| 395 | + "enum.next is a string!: " + obj); |
| 396 | } |
| 397 | } |
| 398 | byte[] value = (byte[])obj; |
| 399 | values[i++] = value; |
| 400 | } |
| 401 | } |
| 402 | return values; |
| 403 | } |
| 404 | |
| 405 | } |
| 406 | |
| 407 | /* |
| 408 | * Gets certificates from an attribute id and location in the LDAP |
| 409 | * directory. Returns a Collection containing only the Certificates that |
| 410 | * match the specified CertSelector. |
| 411 | * |
| 412 | * @param name the location holding the attribute |
| 413 | * @param id the attribute identifier |
| 414 | * @param sel a CertSelector that the Certificates must match |
| 415 | * @return a Collection of Certificates found |
| 416 | * @throws CertStoreException if an exception occurs |
| 417 | */ |
| 418 | private Collection<X509Certificate> getCertificates(LDAPRequest request, |
| 419 | String id, X509CertSelector sel) throws CertStoreException { |
| 420 | |
| 421 | /* fetch encoded certs from storage */ |
| 422 | byte[][] encodedCert; |
| 423 | try { |
| 424 | encodedCert = request.getValues(id); |
| 425 | } catch (NamingException namingEx) { |
| 426 | throw new CertStoreException(namingEx); |
| 427 | } |
| 428 | |
| 429 | int n = encodedCert.length; |
| 430 | if (n == 0) { |
| 431 | return Collections.<X509Certificate>emptySet(); |
| 432 | } |
| 433 | |
| 434 | List<X509Certificate> certs = new ArrayList<X509Certificate>(n); |
| 435 | /* decode certs and check if they satisfy selector */ |
| 436 | for (int i = 0; i < n; i++) { |
| 437 | ByteArrayInputStream bais = new ByteArrayInputStream(encodedCert[i]); |
| 438 | try { |
| 439 | Certificate cert = cf.generateCertificate(bais); |
| 440 | if (sel.match(cert)) { |
| 441 | certs.add((X509Certificate)cert); |
| 442 | } |
| 443 | } catch (CertificateException e) { |
| 444 | if (debug != null) { |
| 445 | debug.println("LDAPCertStore.getCertificates() encountered " |
| 446 | + "exception while parsing cert, skipping the bad data: "); |
| 447 | HexDumpEncoder encoder = new HexDumpEncoder(); |
| 448 | debug.println( |
| 449 | "[ " + encoder.encodeBuffer(encodedCert[i]) + " ]"); |
| 450 | } |
| 451 | } |
| 452 | } |
| 453 | |
| 454 | return certs; |
| 455 | } |
| 456 | |
| 457 | /* |
| 458 | * Gets certificate pairs from an attribute id and location in the LDAP |
| 459 | * directory. |
| 460 | * |
| 461 | * @param name the location holding the attribute |
| 462 | * @param id the attribute identifier |
| 463 | * @return a Collection of X509CertificatePairs found |
| 464 | * @throws CertStoreException if an exception occurs |
| 465 | */ |
| 466 | private Collection<X509CertificatePair> getCertPairs( |
| 467 | LDAPRequest request, String id) throws CertStoreException { |
| 468 | |
| 469 | /* fetch the encoded cert pairs from storage */ |
| 470 | byte[][] encodedCertPair; |
| 471 | try { |
| 472 | encodedCertPair = request.getValues(id); |
| 473 | } catch (NamingException namingEx) { |
| 474 | throw new CertStoreException(namingEx); |
| 475 | } |
| 476 | |
| 477 | int n = encodedCertPair.length; |
| 478 | if (n == 0) { |
| 479 | return Collections.<X509CertificatePair>emptySet(); |
| 480 | } |
| 481 | |
| 482 | List<X509CertificatePair> certPairs = |
| 483 | new ArrayList<X509CertificatePair>(n); |
| 484 | /* decode each cert pair and add it to the Collection */ |
| 485 | for (int i = 0; i < n; i++) { |
| 486 | try { |
| 487 | X509CertificatePair certPair = |
| 488 | X509CertificatePair.generateCertificatePair(encodedCertPair[i]); |
| 489 | certPairs.add(certPair); |
| 490 | } catch (CertificateException e) { |
| 491 | if (debug != null) { |
| 492 | debug.println( |
| 493 | "LDAPCertStore.getCertPairs() encountered exception " |
| 494 | + "while parsing cert, skipping the bad data: "); |
| 495 | HexDumpEncoder encoder = new HexDumpEncoder(); |
| 496 | debug.println( |
| 497 | "[ " + encoder.encodeBuffer(encodedCertPair[i]) + " ]"); |
| 498 | } |
| 499 | } |
| 500 | } |
| 501 | |
| 502 | return certPairs; |
| 503 | } |
| 504 | |
| 505 | /* |
| 506 | * Looks at certificate pairs stored in the crossCertificatePair attribute |
| 507 | * at the specified location in the LDAP directory. Returns a Collection |
| 508 | * containing all Certificates stored in the forward component that match |
| 509 | * the forward CertSelector and all Certificates stored in the reverse |
| 510 | * component that match the reverse CertSelector. |
| 511 | * <p> |
| 512 | * If either forward or reverse is null, all certificates from the |
| 513 | * corresponding component will be rejected. |
| 514 | * |
| 515 | * @param name the location to look in |
| 516 | * @param forward the forward CertSelector (or null) |
| 517 | * @param reverse the reverse CertSelector (or null) |
| 518 | * @return a Collection of Certificates found |
| 519 | * @throws CertStoreException if an exception occurs |
| 520 | */ |
| 521 | private Collection<X509Certificate> getMatchingCrossCerts( |
| 522 | LDAPRequest request, X509CertSelector forward, |
| 523 | X509CertSelector reverse) |
| 524 | throws CertStoreException { |
| 525 | // Get the cert pairs |
| 526 | Collection<X509CertificatePair> certPairs = |
| 527 | getCertPairs(request, CROSS_CERT); |
| 528 | |
| 529 | // Find Certificates that match and put them in a list |
| 530 | ArrayList<X509Certificate> matchingCerts = |
| 531 | new ArrayList<X509Certificate>(); |
| 532 | for (X509CertificatePair certPair : certPairs) { |
| 533 | X509Certificate cert; |
| 534 | if (forward != null) { |
| 535 | cert = certPair.getForward(); |
| 536 | if ((cert != null) && forward.match(cert)) { |
| 537 | matchingCerts.add(cert); |
| 538 | } |
| 539 | } |
| 540 | if (reverse != null) { |
| 541 | cert = certPair.getReverse(); |
| 542 | if ((cert != null) && reverse.match(cert)) { |
| 543 | matchingCerts.add(cert); |
| 544 | } |
| 545 | } |
| 546 | } |
| 547 | return matchingCerts; |
| 548 | } |
| 549 | |
| 550 | /** |
| 551 | * Returns a <code>Collection</code> of <code>Certificate</code>s that |
| 552 | * match the specified selector. If no <code>Certificate</code>s |
| 553 | * match the selector, an empty <code>Collection</code> will be returned. |
| 554 | * <p> |
| 555 | * It is not practical to search every entry in the LDAP database for |
| 556 | * matching <code>Certificate</code>s. Instead, the <code>CertSelector</code> |
| 557 | * is examined in order to determine where matching <code>Certificate</code>s |
| 558 | * are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587). |
| 559 | * If the subject is specified, its directory entry is searched. If the |
| 560 | * issuer is specified, its directory entry is searched. If neither the |
| 561 | * subject nor the issuer are specified (or the selector is not an |
| 562 | * <code>X509CertSelector</code>), a <code>CertStoreException</code> is |
| 563 | * thrown. |
| 564 | * |
| 565 | * @param selector a <code>CertSelector</code> used to select which |
| 566 | * <code>Certificate</code>s should be returned. |
| 567 | * @return a <code>Collection</code> of <code>Certificate</code>s that |
| 568 | * match the specified selector |
| 569 | * @throws CertStoreException if an exception occurs |
| 570 | */ |
| 571 | public synchronized Collection<X509Certificate> engineGetCertificates |
| 572 | (CertSelector selector) throws CertStoreException { |
| 573 | if (debug != null) { |
| 574 | debug.println("LDAPCertStore.engineGetCertificates() selector: " |
| 575 | + String.valueOf(selector)); |
| 576 | } |
| 577 | |
| 578 | if (selector == null) { |
| 579 | selector = new X509CertSelector(); |
| 580 | } |
| 581 | if (!(selector instanceof X509CertSelector)) { |
| 582 | throw new CertStoreException("LDAPCertStore needs an X509CertSelector " + |
| 583 | "to find certs"); |
| 584 | } |
| 585 | X509CertSelector xsel = (X509CertSelector) selector; |
| 586 | int basicConstraints = xsel.getBasicConstraints(); |
| 587 | String subject = xsel.getSubjectAsString(); |
| 588 | String issuer = xsel.getIssuerAsString(); |
| 589 | HashSet<X509Certificate> certs = new HashSet<X509Certificate>(); |
| 590 | if (debug != null) { |
| 591 | debug.println("LDAPCertStore.engineGetCertificates() basicConstraints: " |
| 592 | + basicConstraints); |
| 593 | } |
| 594 | |
| 595 | // basicConstraints: |
| 596 | // -2: only EE certs accepted |
| 597 | // -1: no check is done |
| 598 | // 0: any CA certificate accepted |
| 599 | // >1: certificate's basicConstraints extension pathlen must match |
| 600 | if (subject != null) { |
| 601 | if (debug != null) { |
| 602 | debug.println("LDAPCertStore.engineGetCertificates() " |
| 603 | + "subject is not null"); |
| 604 | } |
| 605 | LDAPRequest request = new LDAPRequest(subject); |
| 606 | if (basicConstraints > -2) { |
| 607 | request.addRequestedAttribute(CROSS_CERT); |
| 608 | request.addRequestedAttribute(CA_CERT); |
| 609 | request.addRequestedAttribute(ARL); |
| 610 | if (prefetchCRLs) { |
| 611 | request.addRequestedAttribute(CRL); |
| 612 | } |
| 613 | } |
| 614 | if (basicConstraints < 0) { |
| 615 | request.addRequestedAttribute(USER_CERT); |
| 616 | } |
| 617 | |
| 618 | if (basicConstraints > -2) { |
| 619 | certs.addAll(getMatchingCrossCerts(request, xsel, null)); |
| 620 | if (debug != null) { |
| 621 | debug.println("LDAPCertStore.engineGetCertificates() after " |
| 622 | + "getMatchingCrossCerts(subject,xsel,null),certs.size(): " |
| 623 | + certs.size()); |
| 624 | } |
| 625 | certs.addAll(getCertificates(request, CA_CERT, xsel)); |
| 626 | if (debug != null) { |
| 627 | debug.println("LDAPCertStore.engineGetCertificates() after " |
| 628 | + "getCertificates(subject,CA_CERT,xsel),certs.size(): " |
| 629 | + certs.size()); |
| 630 | } |
| 631 | } |
| 632 | if (basicConstraints < 0) { |
| 633 | certs.addAll(getCertificates(request, USER_CERT, xsel)); |
| 634 | if (debug != null) { |
| 635 | debug.println("LDAPCertStore.engineGetCertificates() after " |
| 636 | + "getCertificates(subject,USER_CERT, xsel),certs.size(): " |
| 637 | + certs.size()); |
| 638 | } |
| 639 | } |
| 640 | } else { |
| 641 | if (debug != null) { |
| 642 | debug.println |
| 643 | ("LDAPCertStore.engineGetCertificates() subject is null"); |
| 644 | } |
| 645 | if (basicConstraints == -2) { |
| 646 | throw new CertStoreException("need subject to find EE certs"); |
| 647 | } |
| 648 | if (issuer == null) { |
| 649 | throw new CertStoreException("need subject or issuer to find certs"); |
| 650 | } |
| 651 | } |
| 652 | if (debug != null) { |
| 653 | debug.println("LDAPCertStore.engineGetCertificates() about to " |
| 654 | + "getMatchingCrossCerts..."); |
| 655 | } |
| 656 | if ((issuer != null) && (basicConstraints > -2)) { |
| 657 | LDAPRequest request = new LDAPRequest(issuer); |
| 658 | request.addRequestedAttribute(CROSS_CERT); |
| 659 | request.addRequestedAttribute(CA_CERT); |
| 660 | request.addRequestedAttribute(ARL); |
| 661 | if (prefetchCRLs) { |
| 662 | request.addRequestedAttribute(CRL); |
| 663 | } |
| 664 | |
| 665 | certs.addAll(getMatchingCrossCerts(request, null, xsel)); |
| 666 | if (debug != null) { |
| 667 | debug.println("LDAPCertStore.engineGetCertificates() after " |
| 668 | + "getMatchingCrossCerts(issuer,null,xsel),certs.size(): " |
| 669 | + certs.size()); |
| 670 | } |
| 671 | certs.addAll(getCertificates(request, CA_CERT, xsel)); |
| 672 | if (debug != null) { |
| 673 | debug.println("LDAPCertStore.engineGetCertificates() after " |
| 674 | + "getCertificates(issuer,CA_CERT,xsel),certs.size(): " |
| 675 | + certs.size()); |
| 676 | } |
| 677 | } |
| 678 | if (debug != null) { |
| 679 | debug.println("LDAPCertStore.engineGetCertificates() returning certs"); |
| 680 | } |
| 681 | return certs; |
| 682 | } |
| 683 | |
| 684 | /* |
| 685 | * Gets CRLs from an attribute id and location in the LDAP directory. |
| 686 | * Returns a Collection containing only the CRLs that match the |
| 687 | * specified CRLSelector. |
| 688 | * |
| 689 | * @param name the location holding the attribute |
| 690 | * @param id the attribute identifier |
| 691 | * @param sel a CRLSelector that the CRLs must match |
| 692 | * @return a Collection of CRLs found |
| 693 | * @throws CertStoreException if an exception occurs |
| 694 | */ |
| 695 | private Collection<X509CRL> getCRLs(LDAPRequest request, String id, |
| 696 | X509CRLSelector sel) throws CertStoreException { |
| 697 | |
| 698 | /* fetch the encoded crls from storage */ |
| 699 | byte[][] encodedCRL; |
| 700 | try { |
| 701 | encodedCRL = request.getValues(id); |
| 702 | } catch (NamingException namingEx) { |
| 703 | throw new CertStoreException(namingEx); |
| 704 | } |
| 705 | |
| 706 | int n = encodedCRL.length; |
| 707 | if (n == 0) { |
| 708 | return Collections.<X509CRL>emptySet(); |
| 709 | } |
| 710 | |
| 711 | List<X509CRL> crls = new ArrayList<X509CRL>(n); |
| 712 | /* decode each crl and check if it matches selector */ |
| 713 | for (int i = 0; i < n; i++) { |
| 714 | try { |
| 715 | CRL crl = cf.generateCRL(new ByteArrayInputStream(encodedCRL[i])); |
| 716 | if (sel.match(crl)) { |
| 717 | crls.add((X509CRL)crl); |
| 718 | } |
| 719 | } catch (CRLException e) { |
| 720 | if (debug != null) { |
| 721 | debug.println("LDAPCertStore.getCRLs() encountered exception" |
| 722 | + " while parsing CRL, skipping the bad data: "); |
| 723 | HexDumpEncoder encoder = new HexDumpEncoder(); |
| 724 | debug.println("[ " + encoder.encodeBuffer(encodedCRL[i]) + " ]"); |
| 725 | } |
| 726 | } |
| 727 | } |
| 728 | |
| 729 | return crls; |
| 730 | } |
| 731 | |
| 732 | /** |
| 733 | * Returns a <code>Collection</code> of <code>CRL</code>s that |
| 734 | * match the specified selector. If no <code>CRL</code>s |
| 735 | * match the selector, an empty <code>Collection</code> will be returned. |
| 736 | * <p> |
| 737 | * It is not practical to search every entry in the LDAP database for |
| 738 | * matching <code>CRL</code>s. Instead, the <code>CRLSelector</code> |
| 739 | * is examined in order to determine where matching <code>CRL</code>s |
| 740 | * are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587). |
| 741 | * If issuerNames or certChecking are specified, the issuer's directory |
| 742 | * entry is searched. If neither issuerNames or certChecking are specified |
| 743 | * (or the selector is not an <code>X509CRLSelector</code>), a |
| 744 | * <code>CertStoreException</code> is thrown. |
| 745 | * |
| 746 | * @param selector A <code>CRLSelector</code> used to select which |
| 747 | * <code>CRL</code>s should be returned. Specify <code>null</code> |
| 748 | * to return all <code>CRL</code>s. |
| 749 | * @return A <code>Collection</code> of <code>CRL</code>s that |
| 750 | * match the specified selector |
| 751 | * @throws CertStoreException if an exception occurs |
| 752 | */ |
| 753 | public synchronized Collection<X509CRL> engineGetCRLs(CRLSelector selector) |
| 754 | throws CertStoreException { |
| 755 | if (debug != null) { |
| 756 | debug.println("LDAPCertStore.engineGetCRLs() selector: " |
| 757 | + selector); |
| 758 | } |
| 759 | // Set up selector and collection to hold CRLs |
| 760 | if (selector == null) { |
| 761 | selector = new X509CRLSelector(); |
| 762 | } |
| 763 | if (!(selector instanceof X509CRLSelector)) { |
| 764 | throw new CertStoreException("need X509CRLSelector to find CRLs"); |
| 765 | } |
| 766 | X509CRLSelector xsel = (X509CRLSelector) selector; |
| 767 | HashSet<X509CRL> crls = new HashSet<X509CRL>(); |
| 768 | |
| 769 | // Look in directory entry for issuer of cert we're checking. |
| 770 | Collection<Object> issuerNames; |
| 771 | X509Certificate certChecking = xsel.getCertificateChecking(); |
| 772 | if (certChecking != null) { |
| 773 | issuerNames = new HashSet<Object>(); |
| 774 | X500Principal issuer = certChecking.getIssuerX500Principal(); |
| 775 | issuerNames.add(issuer.getName(X500Principal.RFC2253)); |
| 776 | } else { |
| 777 | // But if we don't know which cert we're checking, try the directory |
| 778 | // entries of all acceptable CRL issuers |
| 779 | issuerNames = xsel.getIssuerNames(); |
| 780 | if (issuerNames == null) { |
| 781 | throw new CertStoreException("need issuerNames or certChecking to " |
| 782 | + "find CRLs"); |
| 783 | } |
| 784 | } |
| 785 | for (Object nameObject : issuerNames) { |
| 786 | String issuerName; |
| 787 | if (nameObject instanceof byte[]) { |
| 788 | try { |
| 789 | X500Principal issuer = new X500Principal((byte[])nameObject); |
| 790 | issuerName = issuer.getName(X500Principal.RFC2253); |
| 791 | } catch (IllegalArgumentException e) { |
| 792 | continue; |
| 793 | } |
| 794 | } else { |
| 795 | issuerName = (String)nameObject; |
| 796 | } |
| 797 | // If all we want is CA certs, try to get the (probably shorter) ARL |
| 798 | Collection<X509CRL> entryCRLs = Collections.<X509CRL>emptySet(); |
| 799 | if (certChecking == null || certChecking.getBasicConstraints() != -1) { |
| 800 | LDAPRequest request = new LDAPRequest(issuerName); |
| 801 | request.addRequestedAttribute(CROSS_CERT); |
| 802 | request.addRequestedAttribute(CA_CERT); |
| 803 | request.addRequestedAttribute(ARL); |
| 804 | if (prefetchCRLs) { |
| 805 | request.addRequestedAttribute(CRL); |
| 806 | } |
| 807 | try { |
| 808 | entryCRLs = getCRLs(request, ARL, xsel); |
| 809 | if (entryCRLs.isEmpty()) { |
| 810 | // no ARLs found. We assume that means that there are |
| 811 | // no ARLs on this server at all and prefetch the CRLs. |
| 812 | prefetchCRLs = true; |
| 813 | } else { |
| 814 | crls.addAll(entryCRLs); |
| 815 | } |
| 816 | } catch (CertStoreException e) { |
| 817 | if (debug != null) { |
| 818 | debug.println("LDAPCertStore.engineGetCRLs non-fatal error " |
| 819 | + "retrieving ARLs:" + e); |
| 820 | e.printStackTrace(); |
| 821 | } |
| 822 | } |
| 823 | } |
| 824 | // Otherwise, get the CRL |
| 825 | // if certChecking is null, we don't know if we should look in ARL or CRL |
| 826 | // attribute, so check both for matching CRLs. |
| 827 | if (entryCRLs.isEmpty() || certChecking == null) { |
| 828 | LDAPRequest request = new LDAPRequest(issuerName); |
| 829 | request.addRequestedAttribute(CRL); |
| 830 | entryCRLs = getCRLs(request, CRL, xsel); |
| 831 | crls.addAll(entryCRLs); |
| 832 | } |
| 833 | } |
| 834 | return crls; |
| 835 | } |
| 836 | |
| 837 | // converts an LDAP URI into LDAPCertStoreParameters |
| 838 | static LDAPCertStoreParameters getParameters(URI uri) { |
| 839 | String host = uri.getHost(); |
| 840 | if (host == null) { |
| 841 | return new SunLDAPCertStoreParameters(); |
| 842 | } else { |
| 843 | int port = uri.getPort(); |
| 844 | return (port == -1 |
| 845 | ? new SunLDAPCertStoreParameters(host) |
| 846 | : new SunLDAPCertStoreParameters(host, port)); |
| 847 | } |
| 848 | } |
| 849 | |
| 850 | /* |
| 851 | * Subclass of LDAPCertStoreParameters with overridden equals/hashCode |
| 852 | * methods. This is necessary because the parameters are used as |
| 853 | * keys in the LDAPCertStore cache. |
| 854 | */ |
| 855 | private static class SunLDAPCertStoreParameters |
| 856 | extends LDAPCertStoreParameters { |
| 857 | |
| 858 | private volatile int hashCode = 0; |
| 859 | |
| 860 | SunLDAPCertStoreParameters(String serverName, int port) { |
| 861 | super(serverName, port); |
| 862 | } |
| 863 | SunLDAPCertStoreParameters(String serverName) { |
| 864 | super(serverName); |
| 865 | } |
| 866 | SunLDAPCertStoreParameters() { |
| 867 | super(); |
| 868 | } |
| 869 | public boolean equals(Object obj) { |
| 870 | if (!(obj instanceof LDAPCertStoreParameters)) { |
| 871 | return false; |
| 872 | } |
| 873 | LDAPCertStoreParameters params = (LDAPCertStoreParameters) obj; |
| 874 | return (getPort() == params.getPort() && |
| 875 | getServerName().equalsIgnoreCase(params.getServerName())); |
| 876 | } |
| 877 | public int hashCode() { |
| 878 | if (hashCode == 0) { |
| 879 | int result = 17; |
| 880 | result = 37*result + getPort(); |
| 881 | result = 37*result + getServerName().toLowerCase().hashCode(); |
| 882 | hashCode = result; |
| 883 | } |
| 884 | return hashCode; |
| 885 | } |
| 886 | } |
| 887 | |
| 888 | /* |
| 889 | * This inner class wraps an existing X509CertSelector and adds |
| 890 | * additional criteria to match on when the certificate's subject is |
| 891 | * different than the LDAP Distinguished Name entry. The LDAPCertStore |
| 892 | * implementation uses the subject DN as the directory entry for |
| 893 | * looking up certificates. This can be problematic if the certificates |
| 894 | * that you want to fetch have a different subject DN than the entry |
| 895 | * where they are stored. You could set the selector's subject to the |
| 896 | * LDAP DN entry, but then the resulting match would fail to find the |
| 897 | * desired certificates because the subject DNs would not match. This |
| 898 | * class avoids that problem by introducing a certSubject which should |
| 899 | * be set to the certificate's subject DN when it is different than |
| 900 | * the LDAP DN. |
| 901 | */ |
| 902 | static class LDAPCertSelector extends X509CertSelector { |
| 903 | |
| 904 | private X500Principal certSubject; |
| 905 | private X509CertSelector selector; |
| 906 | private X500Principal subject; |
| 907 | |
| 908 | /** |
| 909 | * Creates an LDAPCertSelector. |
| 910 | * |
| 911 | * @param selector the X509CertSelector to wrap |
| 912 | * @param certSubject the subject DN of the certificate that you want |
| 913 | * to retrieve via LDAP |
| 914 | * @param ldapDN the LDAP DN where the certificate is stored |
| 915 | */ |
| 916 | LDAPCertSelector(X509CertSelector selector, X500Principal certSubject, |
| 917 | String ldapDN) throws IOException { |
| 918 | this.selector = selector == null ? new X509CertSelector() : selector; |
| 919 | this.certSubject = certSubject; |
| 920 | this.subject = new X500Name(ldapDN).asX500Principal(); |
| 921 | } |
| 922 | |
| 923 | // we only override the get (accessor methods) since the set methods |
| 924 | // will not be invoked by the code that uses this LDAPCertSelector. |
| 925 | public X509Certificate getCertificate() { |
| 926 | return selector.getCertificate(); |
| 927 | } |
| 928 | public BigInteger getSerialNumber() { |
| 929 | return selector.getSerialNumber(); |
| 930 | } |
| 931 | public X500Principal getIssuer() { |
| 932 | return selector.getIssuer(); |
| 933 | } |
| 934 | public String getIssuerAsString() { |
| 935 | return selector.getIssuerAsString(); |
| 936 | } |
| 937 | public byte[] getIssuerAsBytes() throws IOException { |
| 938 | return selector.getIssuerAsBytes(); |
| 939 | } |
| 940 | public X500Principal getSubject() { |
| 941 | // return the ldap DN |
| 942 | return subject; |
| 943 | } |
| 944 | public String getSubjectAsString() { |
| 945 | // return the ldap DN |
| 946 | return subject.getName(); |
| 947 | } |
| 948 | public byte[] getSubjectAsBytes() throws IOException { |
| 949 | // return the encoded ldap DN |
| 950 | return subject.getEncoded(); |
| 951 | } |
| 952 | public byte[] getSubjectKeyIdentifier() { |
| 953 | return selector.getSubjectKeyIdentifier(); |
| 954 | } |
| 955 | public byte[] getAuthorityKeyIdentifier() { |
| 956 | return selector.getAuthorityKeyIdentifier(); |
| 957 | } |
| 958 | public Date getCertificateValid() { |
| 959 | return selector.getCertificateValid(); |
| 960 | } |
| 961 | public Date getPrivateKeyValid() { |
| 962 | return selector.getPrivateKeyValid(); |
| 963 | } |
| 964 | public String getSubjectPublicKeyAlgID() { |
| 965 | return selector.getSubjectPublicKeyAlgID(); |
| 966 | } |
| 967 | public PublicKey getSubjectPublicKey() { |
| 968 | return selector.getSubjectPublicKey(); |
| 969 | } |
| 970 | public boolean[] getKeyUsage() { |
| 971 | return selector.getKeyUsage(); |
| 972 | } |
| 973 | public Set<String> getExtendedKeyUsage() { |
| 974 | return selector.getExtendedKeyUsage(); |
| 975 | } |
| 976 | public boolean getMatchAllSubjectAltNames() { |
| 977 | return selector.getMatchAllSubjectAltNames(); |
| 978 | } |
| 979 | public Collection<List<?>> getSubjectAlternativeNames() { |
| 980 | return selector.getSubjectAlternativeNames(); |
| 981 | } |
| 982 | public byte[] getNameConstraints() { |
| 983 | return selector.getNameConstraints(); |
| 984 | } |
| 985 | public int getBasicConstraints() { |
| 986 | return selector.getBasicConstraints(); |
| 987 | } |
| 988 | public Set<String> getPolicy() { |
| 989 | return selector.getPolicy(); |
| 990 | } |
| 991 | public Collection<List<?>> getPathToNames() { |
| 992 | return selector.getPathToNames(); |
| 993 | } |
| 994 | |
| 995 | public boolean match(Certificate cert) { |
| 996 | // temporarily set the subject criterion to the certSubject |
| 997 | // so that match will not reject the desired certificates |
| 998 | selector.setSubject(certSubject); |
| 999 | boolean match = selector.match(cert); |
| 1000 | selector.setSubject(subject); |
| 1001 | return match; |
| 1002 | } |
| 1003 | } |
| 1004 | |
| 1005 | /** |
| 1006 | * This class has the same purpose as LDAPCertSelector except it is for |
| 1007 | * X.509 CRLs. |
| 1008 | */ |
| 1009 | static class LDAPCRLSelector extends X509CRLSelector { |
| 1010 | |
| 1011 | private X509CRLSelector selector; |
| 1012 | private Collection<X500Principal> certIssuers; |
| 1013 | private Collection<X500Principal> issuers; |
| 1014 | private HashSet<Object> issuerNames; |
| 1015 | |
| 1016 | /** |
| 1017 | * Creates an LDAPCRLSelector. |
| 1018 | * |
| 1019 | * @param selector the X509CRLSelector to wrap |
| 1020 | * @param certIssuers the issuer DNs of the CRLs that you want |
| 1021 | * to retrieve via LDAP |
| 1022 | * @param ldapDN the LDAP DN where the CRL is stored |
| 1023 | */ |
| 1024 | LDAPCRLSelector(X509CRLSelector selector, |
| 1025 | Collection<X500Principal> certIssuers, String ldapDN) |
| 1026 | throws IOException { |
| 1027 | this.selector = selector == null ? new X509CRLSelector() : selector; |
| 1028 | this.certIssuers = certIssuers; |
| 1029 | issuerNames = new HashSet<Object>(); |
| 1030 | issuerNames.add(ldapDN); |
| 1031 | issuers = new HashSet<X500Principal>(); |
| 1032 | issuers.add(new X500Name(ldapDN).asX500Principal()); |
| 1033 | } |
| 1034 | // we only override the get (accessor methods) since the set methods |
| 1035 | // will not be invoked by the code that uses this LDAPCRLSelector. |
| 1036 | public Collection<X500Principal> getIssuers() { |
| 1037 | // return the ldap DN |
| 1038 | return Collections.unmodifiableCollection(issuers); |
| 1039 | } |
| 1040 | public Collection<Object> getIssuerNames() { |
| 1041 | // return the ldap DN |
| 1042 | return Collections.unmodifiableCollection(issuerNames); |
| 1043 | } |
| 1044 | public BigInteger getMinCRL() { |
| 1045 | return selector.getMinCRL(); |
| 1046 | } |
| 1047 | public BigInteger getMaxCRL() { |
| 1048 | return selector.getMaxCRL(); |
| 1049 | } |
| 1050 | public Date getDateAndTime() { |
| 1051 | return selector.getDateAndTime(); |
| 1052 | } |
| 1053 | public X509Certificate getCertificateChecking() { |
| 1054 | return selector.getCertificateChecking(); |
| 1055 | } |
| 1056 | public boolean match(CRL crl) { |
| 1057 | // temporarily set the issuer criterion to the certIssuers |
| 1058 | // so that match will not reject the desired CRL |
| 1059 | selector.setIssuers(certIssuers); |
| 1060 | boolean match = selector.match(crl); |
| 1061 | selector.setIssuers(issuers); |
| 1062 | return match; |
| 1063 | } |
| 1064 | } |
| 1065 | } |