1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.provider.certpath.ldap; 27 28 import java.io.ByteArrayInputStream; 29 import java.io.IOException; 30 import java.util.*; 31 import javax.naming.Context; 32 import javax.naming.NamingEnumeration; 33 import javax.naming.NamingException; 34 import javax.naming.NameNotFoundException; 35 import javax.naming.directory.Attribute; 36 import javax.naming.directory.Attributes; 37 import javax.naming.directory.BasicAttributes; 38 39 import java.security.*; 40 import java.security.cert.Certificate; 41 import java.security.cert.*; 42 import javax.naming.CommunicationException; 43 import javax.naming.ldap.InitialLdapContext; 44 import javax.naming.ldap.LdapContext; 45 import javax.security.auth.x500.X500Principal; 46 47 import sun.security.util.HexDumpEncoder; 48 import sun.security.provider.certpath.X509CertificatePair; 49 import sun.security.util.Cache; 50 import sun.security.util.Debug; 51 import sun.security.x509.X500Name; 52 53 /** 54 * Core implementation of a LDAP Cert Store. 55 * @see java.security.cert.CertStore 56 * 57 * @since 9 58 */ 59 final class LDAPCertStoreImpl { 60 61 private static final Debug debug = Debug.getInstance("certpath"); 62 63 private final static boolean DEBUG = false; 64 65 /** 66 * LDAP attribute identifiers. 67 */ 68 private static final String USER_CERT = "userCertificate;binary"; 69 private static final String CA_CERT = "cACertificate;binary"; 70 private static final String CROSS_CERT = "crossCertificatePair;binary"; 71 private static final String CRL = "certificateRevocationList;binary"; 72 private static final String ARL = "authorityRevocationList;binary"; 73 private static final String DELTA_CRL = "deltaRevocationList;binary"; 74 75 // Constants for various empty values 76 private final static String[] STRING0 = new String[0]; 77 78 private final static byte[][] BB0 = new byte[0][]; 79 80 private final static Attributes EMPTY_ATTRIBUTES = new BasicAttributes(); 81 82 // cache related constants 83 private final static int DEFAULT_CACHE_SIZE = 750; 84 private final static int DEFAULT_CACHE_LIFETIME = 30; 85 86 private final static int LIFETIME; 87 88 private final static String PROP_LIFETIME = 89 "sun.security.certpath.ldap.cache.lifetime"; 90 91 /* 92 * Internal system property, that when set to "true", disables the 93 * JNDI application resource files lookup to prevent recursion issues 94 * when validating signed JARs with LDAP URLs in certificates. 95 */ 96 private final static String PROP_DISABLE_APP_RESOURCE_FILES = 97 "sun.security.certpath.ldap.disable.app.resource.files"; 98 99 static { 100 String s = AccessController.doPrivileged( 101 (PrivilegedAction<String>) () -> System.getProperty(PROP_LIFETIME)); 102 if (s != null) { 103 LIFETIME = Integer.parseInt(s); // throws NumberFormatException 104 } else { 105 LIFETIME = DEFAULT_CACHE_LIFETIME; 106 } 107 } 108 109 /** 110 * The CertificateFactory used to decode certificates from 111 * their binary stored form. 112 */ 113 private CertificateFactory cf; 114 /** 115 * The JNDI directory context. 116 */ 117 private LdapContext ctx; 118 119 /** 120 * Flag indicating that communication error occurred. 121 */ 122 private boolean communicationError = false; 123 124 /** 125 * Flag indicating whether we should prefetch CRLs. 126 */ 127 private boolean prefetchCRLs = false; 128 129 private final Cache<String, byte[][]> valueCache; 130 131 private int cacheHits = 0; 132 private int cacheMisses = 0; 133 private int requests = 0; 134 135 /** 136 * Creates a <code>CertStore</code> with the specified parameters. 137 */ 138 LDAPCertStoreImpl(String serverName, int port) 139 throws InvalidAlgorithmParameterException { 140 createInitialDirContext(serverName, port); 141 // Create CertificateFactory for use later on 142 try { 143 cf = CertificateFactory.getInstance("X.509"); 144 } catch (CertificateException e) { 145 throw new InvalidAlgorithmParameterException( 146 "unable to create CertificateFactory for X.509"); 147 } 148 if (LIFETIME == 0) { 149 valueCache = Cache.newNullCache(); 150 } else if (LIFETIME < 0) { 151 valueCache = Cache.newSoftMemoryCache(DEFAULT_CACHE_SIZE); 152 } else { 153 valueCache = Cache.newSoftMemoryCache(DEFAULT_CACHE_SIZE, LIFETIME); 154 } 155 } 156 157 /** 158 * Create InitialDirContext. 159 * 160 * @param server Server DNS name hosting LDAP service 161 * @param port Port at which server listens for requests 162 * @throws InvalidAlgorithmParameterException if creation fails 163 */ 164 private void createInitialDirContext(String server, int port) 165 throws InvalidAlgorithmParameterException { 166 String url = "ldap://" + server + ":" + port; 167 Hashtable<String,Object> env = new Hashtable<>(); 168 env.put(Context.INITIAL_CONTEXT_FACTORY, 169 "com.sun.jndi.ldap.LdapCtxFactory"); 170 env.put(Context.PROVIDER_URL, url); 171 172 // If property is set to true, disable application resource file lookup. 173 boolean disableAppResourceFiles = AccessController.doPrivileged( 174 (PrivilegedAction<Boolean>) () -> Boolean.getBoolean(PROP_DISABLE_APP_RESOURCE_FILES)); 175 if (disableAppResourceFiles) { 176 if (debug != null) { 177 debug.println("LDAPCertStore disabling app resource files"); 178 } 179 env.put("com.sun.naming.disable.app.resource.files", "true"); 180 } 181 182 try { 183 ctx = new InitialLdapContext(env, null); 184 /* 185 * By default, follow referrals unless application has 186 * overridden property in an application resource file. 187 */ 188 Hashtable<?,?> currentEnv = ctx.getEnvironment(); 189 if (currentEnv.get(Context.REFERRAL) == null) { 190 ctx.addToEnvironment(Context.REFERRAL, "follow"); 191 } 192 } catch (NamingException e) { 193 if (debug != null) { 194 debug.println("LDAPCertStore.engineInit about to throw " 195 + "InvalidAlgorithmParameterException"); 196 e.printStackTrace(); 197 } 198 Exception ee = new InvalidAlgorithmParameterException 199 ("unable to create InitialDirContext using supplied parameters"); 200 ee.initCause(e); 201 throw (InvalidAlgorithmParameterException)ee; 202 } 203 } 204 205 /** 206 * Private class encapsulating the actual LDAP operations and cache 207 * handling. Use: 208 * 209 * LDAPRequest request = new LDAPRequest(dn); 210 * request.addRequestedAttribute(CROSS_CERT); 211 * request.addRequestedAttribute(CA_CERT); 212 * byte[][] crossValues = request.getValues(CROSS_CERT); 213 * byte[][] caValues = request.getValues(CA_CERT); 214 * 215 * At most one LDAP request is sent for each instance created. If all 216 * getValues() calls can be satisfied from the cache, no request 217 * is sent at all. If a request is sent, all requested attributes 218 * are always added to the cache irrespective of whether the getValues() 219 * method is called. 220 */ 221 private class LDAPRequest { 222 223 private final String name; 224 private Map<String, byte[][]> valueMap; 225 private final List<String> requestedAttributes; 226 227 LDAPRequest(String name) { 228 this.name = name; 229 requestedAttributes = new ArrayList<>(5); 230 } 231 232 String getName() { 233 return name; 234 } 235 236 void addRequestedAttribute(String attrId) { 237 if (valueMap != null) { 238 throw new IllegalStateException("Request already sent"); 239 } 240 requestedAttributes.add(attrId); 241 } 242 243 /** 244 * Gets one or more binary values from an attribute. 245 * 246 * @param name the location holding the attribute 247 * @param attrId the attribute identifier 248 * @return an array of binary values (byte arrays) 249 * @throws NamingException if a naming exception occurs 250 */ 251 byte[][] getValues(String attrId) throws NamingException { 252 if (DEBUG && ((cacheHits + cacheMisses) % 50 == 0)) { 253 System.out.println("Cache hits: " + cacheHits + "; misses: " 254 + cacheMisses); 255 } 256 String cacheKey = name + "|" + attrId; 257 byte[][] values = valueCache.get(cacheKey); 258 if (values != null) { 259 cacheHits++; 260 return values; 261 } 262 cacheMisses++; 263 Map<String, byte[][]> attrs = getValueMap(); 264 values = attrs.get(attrId); 265 return values; 266 } 267 268 /** 269 * Get a map containing the values for this request. The first time 270 * this method is called on an object, the LDAP request is sent, 271 * the results parsed and added to a private map and also to the 272 * cache of this LDAPCertStore. Subsequent calls return the private 273 * map immediately. 274 * 275 * The map contains an entry for each requested attribute. The 276 * attribute name is the key, values are byte[][]. If there are no 277 * values for that attribute, values are byte[0][]. 278 * 279 * @return the value Map 280 * @throws NamingException if a naming exception occurs 281 */ 282 private Map<String, byte[][]> getValueMap() throws NamingException { 283 if (valueMap != null) { 284 return valueMap; 285 } 286 if (DEBUG) { 287 System.out.println("Request: " + name + ":" + requestedAttributes); 288 requests++; 289 if (requests % 5 == 0) { 290 System.out.println("LDAP requests: " + requests); 291 } 292 } 293 valueMap = new HashMap<>(8); 294 String[] attrIds = requestedAttributes.toArray(STRING0); 295 Attributes attrs; 296 297 if (communicationError) { 298 ctx.reconnect(null); 299 communicationError = false; 300 } 301 302 try { 303 attrs = ctx.getAttributes(name, attrIds); 304 } catch (CommunicationException ce) { 305 communicationError = true; 306 throw ce; 307 } catch (NameNotFoundException e) { 308 // name does not exist on this LDAP server 309 // treat same as not attributes found 310 attrs = EMPTY_ATTRIBUTES; 311 } 312 for (String attrId : requestedAttributes) { 313 Attribute attr = attrs.get(attrId); 314 byte[][] values = getAttributeValues(attr); 315 cacheAttribute(attrId, values); 316 valueMap.put(attrId, values); 317 } 318 return valueMap; 319 } 320 321 /** 322 * Add the values to the cache. 323 */ 324 private void cacheAttribute(String attrId, byte[][] values) { 325 String cacheKey = name + "|" + attrId; 326 valueCache.put(cacheKey, values); 327 } 328 329 /** 330 * Get the values for the given attribute. If the attribute is null 331 * or does not contain any values, a zero length byte array is 332 * returned. NOTE that it is assumed that all values are byte arrays. 333 */ 334 private byte[][] getAttributeValues(Attribute attr) 335 throws NamingException { 336 byte[][] values; 337 if (attr == null) { 338 values = BB0; 339 } else { 340 values = new byte[attr.size()][]; 341 int i = 0; 342 NamingEnumeration<?> enum_ = attr.getAll(); 343 while (enum_.hasMore()) { 344 Object obj = enum_.next(); 345 if (debug != null) { 346 if (obj instanceof String) { 347 debug.println("LDAPCertStore.getAttrValues() " 348 + "enum.next is a string!: " + obj); 349 } 350 } 351 byte[] value = (byte[])obj; 352 values[i++] = value; 353 } 354 } 355 return values; 356 } 357 358 } 359 360 /* 361 * Gets certificates from an attribute id and location in the LDAP 362 * directory. Returns a Collection containing only the Certificates that 363 * match the specified CertSelector. 364 * 365 * @param name the location holding the attribute 366 * @param id the attribute identifier 367 * @param sel a CertSelector that the Certificates must match 368 * @return a Collection of Certificates found 369 * @throws CertStoreException if an exception occurs 370 */ 371 private Collection<X509Certificate> getCertificates(LDAPRequest request, 372 String id, X509CertSelector sel) throws CertStoreException { 373 374 /* fetch encoded certs from storage */ 375 byte[][] encodedCert; 376 try { 377 encodedCert = request.getValues(id); 378 } catch (NamingException namingEx) { 379 throw new CertStoreException(namingEx); 380 } 381 382 int n = encodedCert.length; 383 if (n == 0) { 384 return Collections.emptySet(); 385 } 386 387 List<X509Certificate> certs = new ArrayList<>(n); 388 /* decode certs and check if they satisfy selector */ 389 for (int i = 0; i < n; i++) { 390 ByteArrayInputStream bais = new ByteArrayInputStream(encodedCert[i]); 391 try { 392 Certificate cert = cf.generateCertificate(bais); 393 if (sel.match(cert)) { 394 certs.add((X509Certificate)cert); 395 } 396 } catch (CertificateException e) { 397 if (debug != null) { 398 debug.println("LDAPCertStore.getCertificates() encountered " 399 + "exception while parsing cert, skipping the bad data: "); 400 HexDumpEncoder encoder = new HexDumpEncoder(); 401 debug.println( 402 "[ " + encoder.encodeBuffer(encodedCert[i]) + " ]"); 403 } 404 } 405 } 406 407 return certs; 408 } 409 410 /* 411 * Gets certificate pairs from an attribute id and location in the LDAP 412 * directory. 413 * 414 * @param name the location holding the attribute 415 * @param id the attribute identifier 416 * @return a Collection of X509CertificatePairs found 417 * @throws CertStoreException if an exception occurs 418 */ 419 private Collection<X509CertificatePair> getCertPairs( 420 LDAPRequest request, String id) throws CertStoreException { 421 422 /* fetch the encoded cert pairs from storage */ 423 byte[][] encodedCertPair; 424 try { 425 encodedCertPair = request.getValues(id); 426 } catch (NamingException namingEx) { 427 throw new CertStoreException(namingEx); 428 } 429 430 int n = encodedCertPair.length; 431 if (n == 0) { 432 return Collections.emptySet(); 433 } 434 435 List<X509CertificatePair> certPairs = new ArrayList<>(n); 436 /* decode each cert pair and add it to the Collection */ 437 for (int i = 0; i < n; i++) { 438 try { 439 X509CertificatePair certPair = 440 X509CertificatePair.generateCertificatePair(encodedCertPair[i]); 441 certPairs.add(certPair); 442 } catch (CertificateException e) { 443 if (debug != null) { 444 debug.println( 445 "LDAPCertStore.getCertPairs() encountered exception " 446 + "while parsing cert, skipping the bad data: "); 447 HexDumpEncoder encoder = new HexDumpEncoder(); 448 debug.println( 449 "[ " + encoder.encodeBuffer(encodedCertPair[i]) + " ]"); 450 } 451 } 452 } 453 454 return certPairs; 455 } 456 457 /* 458 * Looks at certificate pairs stored in the crossCertificatePair attribute 459 * at the specified location in the LDAP directory. Returns a Collection 460 * containing all X509Certificates stored in the forward component that match 461 * the forward X509CertSelector and all Certificates stored in the reverse 462 * component that match the reverse X509CertSelector. 463 * <p> 464 * If either forward or reverse is null, all certificates from the 465 * corresponding component will be rejected. 466 * 467 * @param name the location to look in 468 * @param forward the forward X509CertSelector (or null) 469 * @param reverse the reverse X509CertSelector (or null) 470 * @return a Collection of X509Certificates found 471 * @throws CertStoreException if an exception occurs 472 */ 473 private Collection<X509Certificate> getMatchingCrossCerts( 474 LDAPRequest request, X509CertSelector forward, 475 X509CertSelector reverse) 476 throws CertStoreException { 477 // Get the cert pairs 478 Collection<X509CertificatePair> certPairs = 479 getCertPairs(request, CROSS_CERT); 480 481 // Find Certificates that match and put them in a list 482 ArrayList<X509Certificate> matchingCerts = new ArrayList<>(); 483 for (X509CertificatePair certPair : certPairs) { 484 X509Certificate cert; 485 if (forward != null) { 486 cert = certPair.getForward(); 487 if ((cert != null) && forward.match(cert)) { 488 matchingCerts.add(cert); 489 } 490 } 491 if (reverse != null) { 492 cert = certPair.getReverse(); 493 if ((cert != null) && reverse.match(cert)) { 494 matchingCerts.add(cert); 495 } 496 } 497 } 498 return matchingCerts; 499 } 500 501 /** 502 * Returns a <code>Collection</code> of <code>X509Certificate</code>s that 503 * match the specified selector. If no <code>X509Certificate</code>s 504 * match the selector, an empty <code>Collection</code> will be returned. 505 * <p> 506 * It is not practical to search every entry in the LDAP database for 507 * matching <code>X509Certificate</code>s. Instead, the 508 * <code>X509CertSelector</code> is examined in order to determine where 509 * matching <code>Certificate</code>s are likely to be found (according 510 * to the PKIX LDAPv2 schema, RFC 2587). 511 * If the subject is specified, its directory entry is searched. If the 512 * issuer is specified, its directory entry is searched. If neither the 513 * subject nor the issuer are specified (or the selector is not an 514 * <code>X509CertSelector</code>), a <code>CertStoreException</code> is 515 * thrown. 516 * 517 * @param selector a <code>X509CertSelector</code> used to select which 518 * <code>Certificate</code>s should be returned. 519 * @return a <code>Collection</code> of <code>X509Certificate</code>s that 520 * match the specified selector 521 * @throws CertStoreException if an exception occurs 522 */ 523 synchronized Collection<X509Certificate> getCertificates 524 (X509CertSelector xsel, String ldapDN) throws CertStoreException { 525 526 if (ldapDN == null) { 527 ldapDN = xsel.getSubjectAsString(); 528 } 529 int basicConstraints = xsel.getBasicConstraints(); 530 String issuer = xsel.getIssuerAsString(); 531 HashSet<X509Certificate> certs = new HashSet<>(); 532 if (debug != null) { 533 debug.println("LDAPCertStore.engineGetCertificates() basicConstraints: " 534 + basicConstraints); 535 } 536 537 // basicConstraints: 538 // -2: only EE certs accepted 539 // -1: no check is done 540 // 0: any CA certificate accepted 541 // >1: certificate's basicConstraints extension pathlen must match 542 if (ldapDN != null) { 543 if (debug != null) { 544 debug.println("LDAPCertStore.engineGetCertificates() " 545 + " subject is not null"); 546 } 547 LDAPRequest request = new LDAPRequest(ldapDN); 548 if (basicConstraints > -2) { 549 request.addRequestedAttribute(CROSS_CERT); 550 request.addRequestedAttribute(CA_CERT); 551 request.addRequestedAttribute(ARL); 552 if (prefetchCRLs) { 553 request.addRequestedAttribute(CRL); 554 } 555 } 556 if (basicConstraints < 0) { 557 request.addRequestedAttribute(USER_CERT); 558 } 559 560 if (basicConstraints > -2) { 561 certs.addAll(getMatchingCrossCerts(request, xsel, null)); 562 if (debug != null) { 563 debug.println("LDAPCertStore.engineGetCertificates() after " 564 + "getMatchingCrossCerts(subject,xsel,null),certs.size(): " 565 + certs.size()); 566 } 567 certs.addAll(getCertificates(request, CA_CERT, xsel)); 568 if (debug != null) { 569 debug.println("LDAPCertStore.engineGetCertificates() after " 570 + "getCertificates(subject,CA_CERT,xsel),certs.size(): " 571 + certs.size()); 572 } 573 } 574 if (basicConstraints < 0) { 575 certs.addAll(getCertificates(request, USER_CERT, xsel)); 576 if (debug != null) { 577 debug.println("LDAPCertStore.engineGetCertificates() after " 578 + "getCertificates(subject,USER_CERT, xsel),certs.size(): " 579 + certs.size()); 580 } 581 } 582 } else { 583 if (debug != null) { 584 debug.println 585 ("LDAPCertStore.engineGetCertificates() subject is null"); 586 } 587 if (basicConstraints == -2) { 588 throw new CertStoreException("need subject to find EE certs"); 589 } 590 if (issuer == null) { 591 throw new CertStoreException("need subject or issuer to find certs"); 592 } 593 } 594 if (debug != null) { 595 debug.println("LDAPCertStore.engineGetCertificates() about to " 596 + "getMatchingCrossCerts..."); 597 } 598 if ((issuer != null) && (basicConstraints > -2)) { 599 LDAPRequest request = new LDAPRequest(issuer); 600 request.addRequestedAttribute(CROSS_CERT); 601 request.addRequestedAttribute(CA_CERT); 602 request.addRequestedAttribute(ARL); 603 if (prefetchCRLs) { 604 request.addRequestedAttribute(CRL); 605 } 606 607 certs.addAll(getMatchingCrossCerts(request, null, xsel)); 608 if (debug != null) { 609 debug.println("LDAPCertStore.engineGetCertificates() after " 610 + "getMatchingCrossCerts(issuer,null,xsel),certs.size(): " 611 + certs.size()); 612 } 613 certs.addAll(getCertificates(request, CA_CERT, xsel)); 614 if (debug != null) { 615 debug.println("LDAPCertStore.engineGetCertificates() after " 616 + "getCertificates(issuer,CA_CERT,xsel),certs.size(): " 617 + certs.size()); 618 } 619 } 620 if (debug != null) { 621 debug.println("LDAPCertStore.engineGetCertificates() returning certs"); 622 } 623 return certs; 624 } 625 626 /* 627 * Gets CRLs from an attribute id and location in the LDAP directory. 628 * Returns a Collection containing only the CRLs that match the 629 * specified X509CRLSelector. 630 * 631 * @param name the location holding the attribute 632 * @param id the attribute identifier 633 * @param sel a X509CRLSelector that the CRLs must match 634 * @return a Collection of CRLs found 635 * @throws CertStoreException if an exception occurs 636 */ 637 private Collection<X509CRL> getCRLs(LDAPRequest request, String id, 638 X509CRLSelector sel) throws CertStoreException { 639 640 /* fetch the encoded crls from storage */ 641 byte[][] encodedCRL; 642 try { 643 encodedCRL = request.getValues(id); 644 } catch (NamingException namingEx) { 645 throw new CertStoreException(namingEx); 646 } 647 648 int n = encodedCRL.length; 649 if (n == 0) { 650 return Collections.emptySet(); 651 } 652 653 List<X509CRL> crls = new ArrayList<>(n); 654 /* decode each crl and check if it matches selector */ 655 for (int i = 0; i < n; i++) { 656 try { 657 CRL crl = cf.generateCRL(new ByteArrayInputStream(encodedCRL[i])); 658 if (sel.match(crl)) { 659 crls.add((X509CRL)crl); 660 } 661 } catch (CRLException e) { 662 if (debug != null) { 663 debug.println("LDAPCertStore.getCRLs() encountered exception" 664 + " while parsing CRL, skipping the bad data: "); 665 HexDumpEncoder encoder = new HexDumpEncoder(); 666 debug.println("[ " + encoder.encodeBuffer(encodedCRL[i]) + " ]"); 667 } 668 } 669 } 670 671 return crls; 672 } 673 674 /** 675 * Returns a <code>Collection</code> of <code>X509CRL</code>s that 676 * match the specified selector. If no <code>X509CRL</code>s 677 * match the selector, an empty <code>Collection</code> will be returned. 678 * <p> 679 * It is not practical to search every entry in the LDAP database for 680 * matching <code>X509CRL</code>s. Instead, the <code>X509CRLSelector</code> 681 * is examined in order to determine where matching <code>X509CRL</code>s 682 * are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587). 683 * If issuerNames or certChecking are specified, the issuer's directory 684 * entry is searched. If neither issuerNames or certChecking are specified 685 * (or the selector is not an <code>X509CRLSelector</code>), a 686 * <code>CertStoreException</code> is thrown. 687 * 688 * @param selector A <code>X509CRLSelector</code> used to select which 689 * <code>CRL</code>s should be returned. Specify <code>null</code> 690 * to return all <code>CRL</code>s. 691 * @return A <code>Collection</code> of <code>X509CRL</code>s that 692 * match the specified selector 693 * @throws CertStoreException if an exception occurs 694 */ 695 synchronized Collection<X509CRL> getCRLs(X509CRLSelector xsel, 696 String ldapDN) throws CertStoreException { 697 698 HashSet<X509CRL> crls = new HashSet<>(); 699 700 // Look in directory entry for issuer of cert we're checking. 701 Collection<Object> issuerNames; 702 X509Certificate certChecking = xsel.getCertificateChecking(); 703 if (certChecking != null) { 704 issuerNames = new HashSet<>(); 705 X500Principal issuer = certChecking.getIssuerX500Principal(); 706 issuerNames.add(issuer.getName(X500Principal.RFC2253)); 707 } else { 708 // But if we don't know which cert we're checking, try the directory 709 // entries of all acceptable CRL issuers 710 if (ldapDN != null) { 711 issuerNames = new HashSet<>(); 712 issuerNames.add(ldapDN); 713 } else { 714 issuerNames = xsel.getIssuerNames(); 715 if (issuerNames == null) { 716 throw new CertStoreException("need issuerNames or" 717 + " certChecking to find CRLs"); 718 } 719 } 720 } 721 for (Object nameObject : issuerNames) { 722 String issuerName; 723 if (nameObject instanceof byte[]) { 724 try { 725 X500Principal issuer = new X500Principal((byte[])nameObject); 726 issuerName = issuer.getName(X500Principal.RFC2253); 727 } catch (IllegalArgumentException e) { 728 continue; 729 } 730 } else { 731 issuerName = (String)nameObject; 732 } 733 // If all we want is CA certs, try to get the (probably shorter) ARL 734 Collection<X509CRL> entryCRLs = Collections.emptySet(); 735 if (certChecking == null || certChecking.getBasicConstraints() != -1) { 736 LDAPRequest request = new LDAPRequest(issuerName); 737 request.addRequestedAttribute(CROSS_CERT); 738 request.addRequestedAttribute(CA_CERT); 739 request.addRequestedAttribute(ARL); 740 if (prefetchCRLs) { 741 request.addRequestedAttribute(CRL); 742 } 743 try { 744 entryCRLs = getCRLs(request, ARL, xsel); 745 if (entryCRLs.isEmpty()) { 746 // no ARLs found. We assume that means that there are 747 // no ARLs on this server at all and prefetch the CRLs. 748 prefetchCRLs = true; 749 } else { 750 crls.addAll(entryCRLs); 751 } 752 } catch (CertStoreException e) { 753 if (debug != null) { 754 debug.println("LDAPCertStore.engineGetCRLs non-fatal error " 755 + "retrieving ARLs:" + e); 756 e.printStackTrace(); 757 } 758 } 759 } 760 // Otherwise, get the CRL 761 // if certChecking is null, we don't know if we should look in ARL or CRL 762 // attribute, so check both for matching CRLs. 763 if (entryCRLs.isEmpty() || certChecking == null) { 764 LDAPRequest request = new LDAPRequest(issuerName); 765 request.addRequestedAttribute(CRL); 766 entryCRLs = getCRLs(request, CRL, xsel); 767 crls.addAll(entryCRLs); 768 } 769 } 770 return crls; 771 } 772 }