X509CertSelector.java revision 7365de1056414750d0a7d1fdd26025fd247f0d04
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.security.cert; 19 20import java.io.IOException; 21import java.math.BigInteger; 22import java.security.PublicKey; 23import java.util.ArrayList; 24import java.util.Arrays; 25import java.util.Collection; 26import java.util.Collections; 27import java.util.Date; 28import java.util.HashSet; 29import java.util.Iterator; 30import java.util.List; 31import java.util.Set; 32import javax.security.auth.x500.X500Principal; 33import org.apache.harmony.security.asn1.ASN1OctetString; 34import org.apache.harmony.security.x509.AlgorithmIdentifier; 35import org.apache.harmony.security.x509.CertificatePolicies; 36import org.apache.harmony.security.x509.GeneralName; 37import org.apache.harmony.security.x509.GeneralNames; 38import org.apache.harmony.security.x509.NameConstraints; 39import org.apache.harmony.security.x509.PolicyInformation; 40import org.apache.harmony.security.x509.PrivateKeyUsagePeriod; 41import org.apache.harmony.security.x509.SubjectPublicKeyInfo; 42 43 44 45/** 46 * A certificate selector ({@code CertSelector} for selecting {@code 47 * X509Certificate}s that match the specified criteria. 48 */ 49public class X509CertSelector implements CertSelector { 50 51 // match criteria 52 private X509Certificate certificateEquals; 53 private BigInteger serialNumber; 54 private X500Principal issuer; 55 private X500Principal subject; 56 private byte[] subjectKeyIdentifier; 57 private byte[] authorityKeyIdentifier; 58 private Date certificateValid; 59 private String subjectPublicKeyAlgID; 60 private Date privateKeyValid; 61 private byte[] subjectPublicKey; 62 private boolean[] keyUsage; 63 private Set extendedKeyUsage; 64 private boolean matchAllNames = true; 65 private int pathLen = -1; 66 private List[] subjectAltNames; 67 private NameConstraints nameConstraints; 68 private Set policies; 69 private ArrayList pathToNames; 70 71 // needed to avoid needless encoding/decoding work 72 private PublicKey subjectPublicKeyImpl; 73 private String issuerName; 74 private byte[] issuerBytes; 75 76 /** 77 * Creates a new {@code X509CertSelector}. 78 */ 79 public X509CertSelector() {} 80 81 /** 82 * Sets the certificate that a matching certificate must be equal to. 83 * 84 * @param certificate 85 * the certificate to match, or null to not check this criteria. 86 */ 87 public void setCertificate(X509Certificate certificate) { 88 certificateEquals = certificate; 89 } 90 91 /** 92 * Returns the certificate that a matching certificate must be equal to. 93 * 94 * @return the certificate to match, or null if this criteria is not 95 * checked. 96 */ 97 public X509Certificate getCertificate() { 98 return certificateEquals; 99 } 100 101 /** 102 * Sets the serial number that a certificate must match. 103 * 104 * @param serialNumber 105 * the serial number to match, or {@code null} to not check the 106 * serial number. 107 */ 108 public void setSerialNumber(BigInteger serialNumber) { 109 this.serialNumber = serialNumber; 110 } 111 112 /** 113 * Returns the serial number that a certificate must match. 114 * 115 * @return the serial number to match, or {@code null} if the serial number 116 * is not to be checked. 117 */ 118 public BigInteger getSerialNumber() { 119 return serialNumber; 120 } 121 122 /** 123 * Sets the issuer that a certificate must match. 124 * 125 * @param issuer 126 * the issuer to match, or {@code null} if the issuer is not to 127 * be checked. 128 */ 129 public void setIssuer(X500Principal issuer) { 130 this.issuer = issuer; 131 this.issuerName = null; 132 this.issuerBytes = null; 133 } 134 135 /** 136 * Returns the issuer that a certificate must match. 137 * 138 * @return the issuer that a certificate must match, or {@code null} if the 139 * issuer is not to be checked. 140 */ 141 public X500Principal getIssuer() { 142 return issuer; 143 } 144 145 /** 146 * <b>Do not use</b>, use {@link #getIssuer()} or 147 * {@link #getIssuerAsBytes()} instead. Sets the issuer that a certificate 148 * must match. 149 * 150 * @param issuerName 151 * the issuer in a RFC 2253 format string, or {@code null} to not 152 * check the issuer. 153 * @throws IOException 154 * if parsing the issuer fails. 155 */ 156 public void setIssuer(String issuerName) throws IOException { 157 if (issuerName == null) { 158 this.issuer = null; 159 this.issuerName = null; 160 this.issuerBytes = null; 161 return; 162 } 163 try { 164 this.issuer = new X500Principal(issuerName); 165 this.issuerName = issuerName; 166 this.issuerBytes = null; 167 } catch (IllegalArgumentException e) { 168 throw new IOException(e.getMessage()); 169 } 170 } 171 172 /** 173 * <b>Do not use</b>, use {@link #getIssuer()} or 174 * {@link #getIssuerAsBytes()} instead. Returns the issuer that a 175 * certificate must match in a RFC 2253 format string. 176 * 177 * @return the issuer in a RFC 2253 format string, or {@code null} if the 178 * issuer is not to be checked. 179 */ 180 public String getIssuerAsString() { 181 if (issuer == null) { 182 return null; 183 } 184 if (issuerName == null) { 185 issuerName = issuer.getName(); 186 } 187 return issuerName; 188 } 189 190 /** 191 * Sets the issuer that a certificate must match. 192 * 193 * @param issuerDN 194 * the distinguished issuer name in ASN.1 DER encoded format, or 195 * {@code null} to not check the issuer. 196 * @throws IOException 197 * if decoding the issuer fail. 198 */ 199 public void setIssuer(byte[] issuerDN) throws IOException { 200 if (issuerDN == null) { 201 issuer = null; 202 return; 203 } 204 try { 205 issuer = new X500Principal(issuerDN); 206 this.issuerName = null; 207 this.issuerBytes = new byte[issuerDN.length]; 208 System.arraycopy(issuerDN, 0, this.issuerBytes, 0, issuerDN.length); 209 } catch (IllegalArgumentException e) { 210 throw new IOException(e.getMessage()); 211 } 212 } 213 214 /** 215 * Returns the issuer that a certificate must match. 216 * 217 * @return the distinguished issuer name in ASN.1 DER encoded format, or 218 * {@code null} if the issuer is not to be checked. 219 * @throws IOException 220 * if encoding the issuer fails. 221 */ 222 public byte[] getIssuerAsBytes() throws IOException { 223 if (issuer == null) { 224 return null; 225 } 226 if (issuerBytes == null) { 227 issuerBytes = issuer.getEncoded(); 228 } 229 byte[] result = new byte[issuerBytes.length]; 230 System.arraycopy(issuerBytes, 0, result, 0, issuerBytes.length); 231 return result; 232 } 233 234 /** 235 * Set the subject that a certificate must match. 236 * 237 * @param subject 238 * the subject distinguished name or {@code null} to not check 239 * the subject. 240 */ 241 public void setSubject(X500Principal subject) { 242 this.subject = subject; 243 } 244 245 /** 246 * Returns the subject that a certificate must match. 247 * 248 * @return the subject distinguished name, or null if the subject is not to 249 * be checked. 250 */ 251 public X500Principal getSubject() { 252 return subject; 253 } 254 255 /** 256 * <b>Do not use</b>, use {@link #setSubject(byte[])} or 257 * {@link #setSubject(X500Principal)} instead. Returns the subject that a 258 * certificate must match. 259 * 260 * @param subjectDN 261 * the subject distinguished name in RFC 2253 format or {@code 262 * null} to not check the subject. 263 * @throws IOException 264 * if decoding the subject fails. 265 */ 266 public void setSubject(String subjectDN) throws IOException { 267 if (subjectDN == null) { 268 subject = null; 269 return; 270 } 271 try { 272 subject = new X500Principal(subjectDN); 273 } catch (IllegalArgumentException e) { 274 throw new IOException(e.getMessage()); 275 } 276 } 277 278 /** 279 * <b>Do not use</b>, use {@link #getSubject()} or 280 * {@link #getSubjectAsBytes()} instead. Returns the subject that a 281 * certificate must match. 282 * 283 * @return the subject distinguished name in RFC 2253 format, or {@code 284 * null} if the subject is not to be checked. 285 */ 286 public String getSubjectAsString() { 287 if (subject == null) { 288 return null; 289 } 290 return subject.getName(); 291 } 292 293 /** 294 * Sets the subject that a certificate must match. 295 * 296 * @param subjectDN 297 * the subject distinguished name in ASN.1 DER format, or {@code 298 * null} to not check the subject. 299 * @throws IOException 300 * if decoding the subject fails. 301 */ 302 public void setSubject(byte[] subjectDN) throws IOException { 303 if (subjectDN == null) { 304 subject = null; 305 return; 306 } 307 try { 308 subject = new X500Principal(subjectDN); 309 } catch (IllegalArgumentException e) { 310 throw new IOException(e.getMessage()); 311 } 312 } 313 314 /** 315 * Returns the subject that a certificate must match. 316 * 317 * @return the subject distinguished name in ASN.1 DER format, or {@code 318 * null} if the subject is not to be checked. 319 * @throws IOException 320 * if encoding the subject fails. 321 */ 322 public byte[] getSubjectAsBytes() throws IOException { 323 if (subject == null) { 324 return null; 325 } 326 return subject.getEncoded(); 327 } 328 329 /** 330 * Sets the criterion for the {@literal SubjectKeyIdentifier} extension. 331 * <p> 332 * The {@code subjectKeyIdentifier} should be a single DER encoded value. 333 * 334 * @param subjectKeyIdentifier 335 * the subject key identifier or {@code null} to disable this 336 * check. 337 */ 338 public void setSubjectKeyIdentifier(byte[] subjectKeyIdentifier) { 339 if (subjectKeyIdentifier == null) { 340 this.subjectKeyIdentifier = null; 341 return; 342 } 343 this.subjectKeyIdentifier = new byte[subjectKeyIdentifier.length]; 344 System.arraycopy(subjectKeyIdentifier, 0, this.subjectKeyIdentifier, 0, 345 subjectKeyIdentifier.length); 346 } 347 348 /** 349 * Returns the criterion for the {@literal SubjectKeyIdentifier} extension. 350 * 351 * @return the subject key identifier or {@code null} if it is not to be 352 * checked. 353 */ 354 public byte[] getSubjectKeyIdentifier() { 355 if (subjectKeyIdentifier == null) { 356 return null; 357 } 358 byte[] res = new byte[subjectKeyIdentifier.length]; 359 System.arraycopy(subjectKeyIdentifier, 0, res, 0, res.length); 360 return res; 361 } 362 363 /** 364 * Sets the criterion for the {@literal AuthorityKeyIdentifier} extension. 365 * 366 * @param authorityKeyIdentifier 367 * the authority key identifier, or {@code null} to disable this 368 * check. 369 */ 370 public void setAuthorityKeyIdentifier(byte[] authorityKeyIdentifier) { 371 if (authorityKeyIdentifier == null) { 372 this.authorityKeyIdentifier = null; 373 return; 374 } 375 this.authorityKeyIdentifier = new byte[authorityKeyIdentifier.length]; 376 System.arraycopy(authorityKeyIdentifier, 0, 377 this.authorityKeyIdentifier, 0, 378 authorityKeyIdentifier.length); 379 } 380 381 /** 382 * Returns the criterion for the {@literal AuthorityKeyIdentifier} 383 * extension. 384 * 385 * @return the authority key identifier, or {@code null} if it is not to be 386 * checked. 387 */ 388 public byte[] getAuthorityKeyIdentifier() { 389 if (authorityKeyIdentifier == null) { 390 return null; 391 } 392 byte[] res = new byte[authorityKeyIdentifier.length]; 393 System.arraycopy(authorityKeyIdentifier, 0, res, 0, res.length); 394 return res; 395 } 396 397 /** 398 * Sets the criterion for the validity date of the certificate. 399 * <p> 400 * The certificate must be valid at the specified date. 401 * @param certificateValid 402 * the validity date or {@code null} to not check the date. 403 */ 404 public void setCertificateValid(Date certificateValid) { 405 this.certificateValid = (certificateValid == null) 406 ? null 407 : (Date) certificateValid.clone(); 408 } 409 410 /** 411 * Returns the criterion for the validity date of the certificate. 412 * 413 * @return the validity date or {@code null} if the date is not to be 414 * checked. 415 */ 416 public Date getCertificateValid() { 417 return (certificateValid == null) 418 ? null 419 : (Date) certificateValid.clone(); 420 } 421 422 /** 423 * Sets the criterion for the validity date of the private key. 424 * <p> 425 * The private key must be valid at the specified date. 426 * 427 * @param privateKeyValid 428 * the validity date or {@code null} to not check the date. 429 */ 430 public void setPrivateKeyValid(Date privateKeyValid) { 431 if (privateKeyValid == null) { 432 this.privateKeyValid = null; 433 return; 434 } 435 this.privateKeyValid = (Date) privateKeyValid.clone(); 436 } 437 438 /** 439 * Returns the criterion for the validity date of the private key. 440 * <p> 441 * The private key must be valid at the specified date. 442 * 443 * @return the validity date or {@code null} if the date is not to be 444 * checked. 445 */ 446 public Date getPrivateKeyValid() { 447 if (privateKeyValid != null) { 448 return (Date) privateKeyValid.clone(); 449 } 450 return null; 451 } 452 453 private void checkOID(String oid) throws IOException { 454 int beg = 0; 455 int end = oid.indexOf('.', beg); 456 try { 457 int comp = Integer.parseInt(oid.substring(beg, end)); 458 beg = end + 1; 459 if ((comp < 0) || (comp > 2)) { 460 throw new IOException("Bad OID: " + oid); 461 } 462 end = oid.indexOf('.', beg); 463 comp = Integer.parseInt(oid.substring(beg, end)); 464 if ((comp < 0) || (comp > 39)) { 465 throw new IOException("Bad OID: " + oid); 466 } 467 } catch (IndexOutOfBoundsException e) { 468 throw new IOException("Bad OID: " + oid); 469 } catch (NumberFormatException e) { 470 throw new IOException("Bad OID: " + oid); 471 } 472 } 473 474 /** 475 * Sets the criterion for the subject public key signature algorithm. 476 * <p> 477 * The certificate must contain a subject public key with the algorithm 478 * specified. 479 * 480 * @param oid 481 * the OID (object identifier) of the signature algorithm or 482 * {@code null} to not check the OID. 483 * @throws IOException 484 * if the specified object identifier is invalid. 485 */ 486 public void setSubjectPublicKeyAlgID(String oid) throws IOException { 487 if (oid == null) { 488 subjectPublicKeyAlgID = null; 489 return; 490 } 491 checkOID(oid); 492 subjectPublicKeyAlgID = oid; 493 } 494 495 /** 496 * Returns the criterion for the subject public key signature algorithm. 497 * 498 * @return the OID (object identifier) or the signature algorithm or {@code 499 * null} if it's not to be checked. 500 */ 501 public String getSubjectPublicKeyAlgID() { 502 return subjectPublicKeyAlgID; 503 } 504 505 /** 506 * Sets the criterion for the subject public key. 507 * 508 * @param key 509 * the subject public key or {@code null} to not check the key. 510 */ 511 public void setSubjectPublicKey(PublicKey key) { 512 subjectPublicKey = (key == null) ? null : key.getEncoded(); 513 subjectPublicKeyImpl = key; 514 } 515 516 /** 517 * Sets the criterion for the subject public key. 518 * 519 * @param key 520 * the subject public key in ASN.1 DER encoded format or {@code null} to 521 * not check the key. 522 * @throws IOException 523 * if decoding the the public key fails. 524 */ 525 public void setSubjectPublicKey(byte[] key) throws IOException { 526 if (key == null) { 527 subjectPublicKey = null; 528 subjectPublicKeyImpl = null; 529 return; 530 } 531 subjectPublicKey = new byte[key.length]; 532 System.arraycopy(key, 0, subjectPublicKey, 0, key.length); 533 subjectPublicKeyImpl = 534 ((SubjectPublicKeyInfo) SubjectPublicKeyInfo.ASN1.decode(key)) 535 .getPublicKey(); 536 } 537 538 /** 539 * Returns the criterion for the subject public key. 540 * 541 * @return the subject public key or {@code null} if the key is not to be 542 * checked. 543 */ 544 public PublicKey getSubjectPublicKey() { 545 return subjectPublicKeyImpl; 546 } 547 548 /** 549 * Sets the criterion for the {@literal KeyUsage} extension. 550 * 551 * @param keyUsage 552 * the boolean array in the format as returned by 553 * {@link X509Certificate#getKeyUsage()}, or {@code null} to not 554 * check the key usage. 555 */ 556 public void setKeyUsage(boolean[] keyUsage) { 557 if (keyUsage == null) { 558 this.keyUsage = null; 559 return; 560 } 561 this.keyUsage = new boolean[keyUsage.length]; 562 System.arraycopy(keyUsage, 0, this.keyUsage, 0, keyUsage.length); 563 } 564 565 /** 566 * Returns the criterion for the {@literal KeyUsage} extension. 567 * 568 * @return the boolean array in the format as returned by 569 * {@link X509Certificate#getKeyUsage()}, or {@code null} if the key 570 * usage is not to be checked. 571 */ 572 public boolean[] getKeyUsage() { 573 if (keyUsage == null) { 574 return null; 575 } 576 boolean[] result = new boolean[keyUsage.length]; 577 System.arraycopy(keyUsage, 0, result, 0, keyUsage.length); 578 return result; 579 } 580 581 /** 582 * Sets the criterion for the {@literal ExtendedKeyUsage} extension. 583 * 584 * @param keyUsage 585 * the set of key usage OIDs, or {@code null} to not check it. 586 * @throws IOException 587 * if one of the OIDs is invalid. 588 */ 589 public void setExtendedKeyUsage(Set<String> keyUsage) 590 throws IOException { 591 extendedKeyUsage = null; 592 if ((keyUsage == null) || (keyUsage.size() == 0)) { 593 return; 594 } 595 HashSet key_u = new HashSet(); 596 Iterator it = keyUsage.iterator(); 597 while (it.hasNext()) { 598 String usage = (String) it.next(); 599 checkOID(usage); 600 key_u.add(usage); 601 } 602 extendedKeyUsage = Collections.unmodifiableSet(key_u); 603 } 604 605 /** 606 * Returns the criterion for the {@literal ExtendedKeyUsage} extension. 607 * 608 * @return the set of key usage OIDs, or {@code null} if it's not to be 609 * checked. 610 */ 611 public Set<String> getExtendedKeyUsage() { 612 return extendedKeyUsage; 613 } 614 615 /** 616 * Sets the flag for the matching behavior for subject alternative names. 617 * <p> 618 * The flag indicates whether a certificate must contain all or at least one 619 * of the subject alternative names specified by {@link 620 * #setSubjectAlternativeNames} or {@link #addSubjectAlternativeName}. 621 * 622 * @param matchAllNames 623 * {@code true} if a certificate must contain all of the 624 * specified subject alternative names, otherwise {@code false}. 625 */ 626 public void setMatchAllSubjectAltNames(boolean matchAllNames) { 627 this.matchAllNames = matchAllNames; 628 } 629 630 /** 631 * Returns the flag for the matching behavior for subject alternative names. 632 * <p> 633 * The flag indicates whether a certificate must contain all or at least one 634 * of the subject alternative names specified by {@link 635 * #setSubjectAlternativeNames} or {@link #addSubjectAlternativeName}. 636 * 637 * @return {@code true} if a certificate must contain all of the specified 638 * subject alternative names, otherwise {@code false}. 639 */ 640 public boolean getMatchAllSubjectAltNames() { 641 return matchAllNames; 642 } 643 644 /** 645 * Sets the criterion for subject alternative names. 646 * <p> 647 * the certificate must contain all or at least one of the specified subject 648 * alternative names. The behavior is specified by 649 * {@link #getMatchAllSubjectAltNames}. 650 * <p> 651 * The specified parameter {@code names} is a collection with an entry for 652 * each name to be included in the criterion. The name is specified as a 653 * {@code List}, the first entry must be an {@code Integer} specifying the 654 * name type (0-8), the second entry must be a {@code String} or a byte 655 * array specifying the name (in string or ASN.1 DER encoded form) 656 * 657 * @param names 658 * the names collection or {@code null} to not perform this check. 659 * @throws IOException 660 * if the decoding of a name fails. 661 */ 662 public void setSubjectAlternativeNames(Collection<List<?>> names) 663 throws IOException { 664 subjectAltNames = null; 665 if ((names == null) || (names.size() == 0)) { 666 return; 667 } 668 Iterator it = names.iterator(); 669 while (it.hasNext()) { 670 List name = (List) it.next(); 671 int tag = ((Integer) name.get(0)).intValue(); 672 Object value = name.get(1); 673 if (value instanceof String) { 674 addSubjectAlternativeName(tag, (String) value); 675 } else if (value instanceof byte[]) { 676 addSubjectAlternativeName(tag, (byte[]) value); 677 } else { 678 throw new IOException("name neither a String nor a byte[]"); 679 } 680 } 681 } 682 683 /** 684 * Adds a subject alternative name to the respective criterion. 685 * 686 * @param tag 687 * the type of the name 688 * @param name 689 * the name in string format. 690 * @throws IOException 691 * if parsing the name fails. 692 */ 693 public void addSubjectAlternativeName(int tag, String name) 694 throws IOException { 695 GeneralName alt_name = new GeneralName(tag, name); 696 // create only if there was not any errors 697 if (subjectAltNames == null) { 698 subjectAltNames = new ArrayList[9]; 699 } 700 if (subjectAltNames[tag] == null) { 701 subjectAltNames[tag] = new ArrayList(); 702 } 703 subjectAltNames[tag].add(alt_name); 704 } 705 706 /** 707 * Adds a subject alternative name to the respective criterion. 708 * 709 * @param tag 710 * the type of the name. 711 * @param name 712 * the name in ASN.1 DER encoded form. 713 * @throws IOException 714 * if the decoding of the name fails. 715 */ 716 public void addSubjectAlternativeName(int tag, byte[] name) 717 throws IOException { 718 GeneralName alt_name = new GeneralName(tag, name); 719 // create only if there was not any errors 720 if (subjectAltNames == null) { 721 subjectAltNames = new ArrayList[9]; 722 } 723 if (subjectAltNames[tag] == null) { 724 subjectAltNames[tag] = new ArrayList(); 725 } 726 subjectAltNames[tag].add(alt_name); 727 } 728 729 /** 730 * Returns the criterion for subject alternative names. 731 * <p> 732 * the certificate must contain all or at least one of the specified subject 733 * alternative names. The behavior is specified by 734 * {@link #getMatchAllSubjectAltNames}. 735 * <p> 736 * The subject alternative names is a collection with an entry for each name 737 * included in the criterion. The name is specified as a {@code List}, the 738 * first entry is an {@code Integer} specifying the name type (0-8), the 739 * second entry is byte array specifying the name in ASN.1 DER encoded form) 740 * 741 * @return the names collection or {@code null} if none specified. 742 */ 743 public Collection<List<?>> getSubjectAlternativeNames() { 744 if (subjectAltNames == null) { 745 return null; 746 } 747 ArrayList result = new ArrayList(); 748 for (int tag=0; tag<9; tag++) { 749 if (subjectAltNames[tag] != null) { 750 for (int name=0; name<subjectAltNames[tag].size(); name++) { 751 Object neim = subjectAltNames[tag].get(name); 752 if (neim instanceof byte[]) { 753 byte[] arr_neim = (byte[]) neim; 754 neim = new byte[arr_neim.length]; 755 System.arraycopy(arr_neim, 0, neim, 0, arr_neim.length); 756 } 757 List list = new ArrayList(2); 758 list.add(Integer.valueOf(tag)); // android-changed 759 list.add(neim); 760 result.add(list); 761 } 762 } 763 } 764 return result; 765 } 766 767 /** 768 * Sets the criterion for the name constraints. 769 * <p> 770 * The certificate must constraint subject and subject alternative names 771 * that match the specified name constraints. 772 * <p> 773 * The name constraints in ASN.1: 774 * 775 * <pre> 776 * NameConstraints ::= SEQUENCE { 777 * permittedSubtrees [0] GeneralSubtrees OPTIONAL, 778 * excludedSubtrees [1] GeneralSubtrees OPTIONAL } 779 * 780 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree 781 * 782 * GeneralSubtree ::= SEQUENCE { 783 * base GeneralName, 784 * minimum [0] BaseDistance DEFAULT 0, 785 * maximum [1] BaseDistance OPTIONAL } 786 * 787 * BaseDistance ::= INTEGER (0..MAX) 788 * 789 * GeneralName ::= CHOICE { 790 * otherName [0] OtherName, 791 * rfc822Name [1] IA5String, 792 * dNSName [2] IA5String, 793 * x400Address [3] ORAddress, 794 * directoryName [4] Name, 795 * ediPartyName [5] EDIPartyName, 796 * uniformResourceIdentifier [6] IA5String, 797 * iPAddress [7] OCTET STRING, 798 * registeredID [8] OBJECT IDENTIFIER} 799 * 800 * </pre> 801 * 802 * @param bytes 803 * the name constraints in ASN.1 DER encoded format, or null to 804 * not check any constraints. 805 * @throws IOException 806 * if decoding the name constraints fail. 807 */ 808 public void setNameConstraints(byte[] bytes) throws IOException { 809 this.nameConstraints = (bytes == null) 810 ? null 811 : (NameConstraints) NameConstraints.ASN1.decode(bytes); 812 } 813 814 /** 815 * Returns the criterion for the name constraints. 816 * 817 * @return the name constraints or {@code null} if none specified. 818 * @see #setNameConstraints 819 */ 820 public byte[] getNameConstraints() { 821 return (nameConstraints == null) 822 ? null 823 : nameConstraints.getEncoded(); 824 } 825 826 /** 827 * Sets the criterion for the basic constraints extension. 828 * <p> 829 * A value greater than or equal to zero indicates that a certificate must 830 * include a basic constraints extension with a path length of a least that 831 * value. A value of {@code -2} indicates that only end-entity certificates 832 * are accepted. A value of {@code -1} indicates that no check is done. 833 * 834 * @param pathLen 835 * the value specifying the criterion. 836 * @throws IllegalArgumentException 837 * if {@code pathLen} is less than {@code -2}. 838 */ 839 public void setBasicConstraints(int pathLen) { 840 if (pathLen < -2) { 841 throw new IllegalArgumentException("pathLen < -2"); 842 } 843 this.pathLen = pathLen; 844 } 845 846 /** 847 * Returns the criterion for the basic constraints extension. 848 * <p> 849 * A value greater than or equal to zero indicates that a certificate must 850 * include a basic constraints extension with a path length of a least that 851 * value. A value of {@code -2} indicates that only end-entity certificates 852 * are accepted. A value of {@code -1} indicates that no check is done. 853 * 854 * @return the value of the criterion. 855 */ 856 public int getBasicConstraints() { 857 return pathLen; 858 } 859 860 /** 861 * Sets the criterion for the policy constraint. 862 * <p> 863 * The certificate must have at least one of the specified certificate 864 * policy extensions. For an empty set the certificate must have at least 865 * some policies in its policy extension. 866 * 867 * @param policies 868 * the certificate policy OIDs, an empty set, or {@code null} to 869 * not perform this check. 870 * @throws IOException 871 * if parsing the specified OIDs fails. 872 */ 873 public void setPolicy(Set<String> policies) throws IOException { 874 if (policies == null) { 875 this.policies = null; 876 return; 877 } 878 HashSet pols = new HashSet(policies.size()); 879 Iterator it = policies.iterator(); 880 while (it.hasNext()) { 881 String certPolicyId = (String) it.next(); 882 checkOID(certPolicyId); 883 pols.add(certPolicyId); 884 } 885 this.policies = Collections.unmodifiableSet(pols); 886 } 887 888 /** 889 * Returns the criterion for the policy constraint. 890 * <p> 891 * The certificate must have at least one of the certificate policy 892 * extensions. For an empty set the certificate must have at least some 893 * policies in its policy extension. 894 * 895 * @return the certificate policy OIDs, an empty set, or {@code null} if not 896 * to be checked. 897 */ 898 public Set<String> getPolicy() { 899 return policies; 900 } 901 902 /** 903 * Sets the criterion for the pathToNames constraint. 904 * <p> 905 * This allows to specify the complete set of names, a certificate's name 906 * constraints must permit. 907 * <p> 908 * The specified parameter {@code names} is a collection with an entry for 909 * each name to be included in the criterion. The name is specified as a 910 * {@code List}, the first entry must be an {@code Integer} specifying the 911 * name type (0-8), the second entry must be a {@code String} or a byte 912 * array specifying the name (in string or ASN.1 DER encoded form) 913 * 914 * @param names 915 * the names collection or {@code null} to not perform this 916 * check. 917 * @throws IOException 918 * if decoding fails. 919 */ 920 public void setPathToNames(Collection<List<?>> names) 921 throws IOException { 922 pathToNames = null; 923 if ((names == null) || (names.size() == 0)) { 924 return; 925 } 926 Iterator it = names.iterator(); 927 while (it.hasNext()) { 928 List name = (List) it.next(); 929 int tag = ((Integer) name.get(0)).intValue(); 930 Object value = name.get(1); 931 if (value instanceof String) { 932 addPathToName(tag, (String) value); 933 } else if (value instanceof byte[]) { 934 addPathToName(tag, (byte[]) value); 935 } else { 936 throw new IOException("name neither a String nor a byte[]"); 937 } 938 } 939 } 940 941 /** 942 * Adds a {@literal "pathToName"} to the respective criterion. 943 * 944 * @param type 945 * the type of the name. 946 * @param name 947 * the name in string format. 948 * @throws IOException 949 * if parsing fails. 950 * @see #setPathToNames 951 */ 952 public void addPathToName(int type, String name) throws IOException { 953 GeneralName path_name = new GeneralName(type, name); 954 // create only if there was not any errors 955 if (pathToNames == null) { 956 pathToNames = new ArrayList(); 957 } 958 pathToNames.add(path_name); 959 } 960 961 /** 962 * Adds a {@literal "pathToName"} to the respective criterion. 963 * 964 * @param type 965 * the type of the name 966 * @param name 967 * the name in ASN.1 DER encoded form. 968 * @throws IOException 969 * if decoding fails. 970 * @see #setPathToNames 971 */ 972 public void addPathToName(int type, byte[] name) throws IOException { 973 GeneralName path_name= new GeneralName(type, name); 974 // create only if there was not any errors 975 if (pathToNames == null) { 976 pathToNames = new ArrayList(); 977 } 978 pathToNames.add(path_name); 979 } 980 981 /** 982 * Returns the criterion for the pathToNames constraint. 983 * <p> 984 * The constraint is a collection with an entry for each name to be included 985 * in the criterion. The name is specified as a {@code List}, the first 986 * entry is an {@code Integer} specifying the name type (0-8), the second 987 * entry is a byte array specifying the name in ASN.1 DER encoded form. 988 * 989 * @return the pathToNames constraint or {@code null} if none specified. 990 */ 991 public Collection<List<?>> getPathToNames() { 992 if (pathToNames == null) { 993 return null; 994 } 995 ArrayList result = new ArrayList(); 996 Iterator it = pathToNames.iterator(); 997 while (it.hasNext()) { 998 GeneralName name = (GeneralName) it.next(); 999 result.add(name.getAsList()); 1000 } 1001 return result; 1002 } 1003 1004 /** 1005 * Returns a string representation of this {@code X509CertSelector} 1006 * instance. 1007 * 1008 * @return a string representation of this {@code X509CertSelector} 1009 * instance. 1010 */ 1011 public String toString() { 1012 // For convenient reading of the string representation 1013 // all of the fields named according to the rfc 3280 1014 // (http://www.ietf.org/rfc/rfc3280.txt). 1015 1016 StringBuilder result = new StringBuilder(); 1017 result.append("X509CertSelector: \n["); 1018 if (this.certificateEquals != null) { 1019 result.append("\n certificateEquals: " + certificateEquals); 1020 } 1021 if (this.serialNumber != null) { 1022 //FIXME: needs DRL's BigInteger.toString implementation 1023 //result.append("\n serialNumber: " + serialNumber); 1024 } 1025 if (this.issuer != null) { 1026 result.append("\n issuer: " + issuer); 1027 } 1028 if (this.subject != null) { 1029 result.append("\n subject: " + subject); 1030 } 1031 if (this.subjectKeyIdentifier != null) { 1032 result.append("\n subjectKeyIdentifier: " 1033 + getBytesAsString(subjectKeyIdentifier)); 1034 } 1035 if (this.authorityKeyIdentifier != null) { 1036 result.append("\n authorityKeyIdentifier: " 1037 + getBytesAsString(authorityKeyIdentifier)); 1038 } 1039 if (this.certificateValid != null) { 1040 result.append("\n certificateValid: " + certificateValid); 1041 } 1042 if (this.subjectPublicKeyAlgID != null) { 1043 result.append("\n subjectPublicKeyAlgID: " 1044 + subjectPublicKeyAlgID); 1045 } 1046 if (this.privateKeyValid != null) { 1047 result.append("\n privateKeyValid: " + privateKeyValid); 1048 } 1049 if (this.subjectPublicKey != null) { 1050 result.append("\n subjectPublicKey: " 1051 + getBytesAsString(subjectPublicKey)); 1052 } 1053 if (this.keyUsage != null) { 1054 result.append("\n keyUsage: \n ["); 1055 String[] kuNames = new String[] { 1056 "digitalSignature", "nonRepudiation", "keyEncipherment", 1057 "dataEncipherment", "keyAgreement", "keyCertSign", "cRLSign", 1058 "encipherOnly", "decipherOnly" 1059 }; 1060 for (int i=0; i<9; i++) { 1061 if (keyUsage[i]) { 1062 result.append("\n " + kuNames[i]); 1063 } 1064 } 1065 result.append("\n ]"); 1066 } 1067 if (this.extendedKeyUsage != null) { 1068 result.append("\n extendedKeyUsage: " 1069 + extendedKeyUsage.toString()); 1070 } 1071 result.append("\n matchAllNames: " + matchAllNames); 1072 result.append("\n pathLen: " + pathLen); 1073 if (this.subjectAltNames != null) { 1074 result.append("\n subjectAltNames: \n ["); 1075 for (int i=0; i<9; i++) { 1076 List names = this.subjectAltNames[i]; 1077 if (names != null) { 1078 int size = names.size(); 1079 for (int j=0; j<size; j++) { 1080 result.append("\n " 1081 + ((GeneralName)names.get(j)).toString()); 1082 } 1083 } 1084 } 1085 result.append("\n ]"); 1086 } 1087 if (this.nameConstraints != null) { 1088 } 1089 if (this.policies != null) { 1090 result.append("\n policies: " + policies.toString()); 1091 } 1092 if (this.pathToNames != null) { 1093 result.append("\n pathToNames: \n ["); 1094 int size = pathToNames.size(); 1095 for (int i = 0; i < size; i++) { 1096 result.append("\n " 1097 + ((GeneralName)pathToNames.get(i)).toString()); 1098 } 1099 } 1100 result.append("\n]"); 1101 return result.toString(); 1102 } 1103 1104 private String getBytesAsString(byte[] data) { 1105 String result = ""; 1106 for (int i=0; i<data.length; i++) { 1107 String tail = Integer.toHexString(0x00ff & data[i]); 1108 if (tail.length() == 1) { 1109 tail = "0" + tail; 1110 } 1111 result += tail + " "; 1112 } 1113 return result; 1114 } 1115 1116 private byte[] getExtensionValue(X509Certificate cert, String oid) { 1117 try { 1118 byte[] bytes = cert.getExtensionValue(oid); 1119 if (bytes == null) { 1120 return null; 1121 } 1122 return (byte[]) ASN1OctetString.getInstance().decode(bytes); 1123 } catch (IOException e) { 1124 return null; 1125 } 1126 } 1127 1128 /** 1129 * Returns whether the specified certificate matches all the criteria 1130 * collected in this instance. 1131 * 1132 * @param certificate 1133 * the certificate to check. 1134 * @return {@code true} if the certificate matches all the criteria, 1135 * otherwise {@code false}. 1136 */ 1137 public boolean match(Certificate certificate) { 1138 if (! (certificate instanceof X509Certificate)) { 1139 return false; 1140 } 1141 1142 X509Certificate cert = (X509Certificate) certificate; 1143 if ((certificateEquals != null) && 1144 !certificateEquals.equals(cert)) { 1145 return false; 1146 } 1147 if ((serialNumber != null) && 1148 !serialNumber.equals(cert.getSerialNumber())) { 1149 return false; 1150 } 1151 if ((issuer != null) && 1152 !issuer.equals(cert.getIssuerX500Principal())) { 1153 return false; 1154 } 1155 if ((subject != null) && 1156 !subject.equals(cert.getSubjectX500Principal())) { 1157 return false; 1158 } 1159 if ((subjectKeyIdentifier != null) && 1160 !Arrays.equals(subjectKeyIdentifier, 1161 // Here and later all of the extension OIDs 1162 // are taken from rfc 3280 (http://www.ietf.org/rfc/rfc3280.txt) 1163 getExtensionValue(cert, "2.5.29.14"))) { 1164 return false; 1165 } 1166 if ((authorityKeyIdentifier != null) && 1167 !Arrays.equals(authorityKeyIdentifier, 1168 getExtensionValue(cert, "2.5.29.35"))) { 1169 return false; 1170 } 1171 if (certificateValid != null) { 1172 try { 1173 cert.checkValidity(certificateValid); 1174 } catch(CertificateExpiredException e) { 1175 return false; 1176 } catch(CertificateNotYetValidException e) { 1177 return false; 1178 } 1179 } 1180 if (privateKeyValid != null) { 1181 try { 1182 byte[] bytes = getExtensionValue(cert, "2.5.29.16"); 1183 if (bytes == null) { 1184 return false; 1185 } 1186 PrivateKeyUsagePeriod pkup = (PrivateKeyUsagePeriod) 1187 PrivateKeyUsagePeriod.ASN1.decode(bytes); 1188 Date notBefore = pkup.getNotBefore(); 1189 Date notAfter = pkup.getNotAfter(); 1190 if ((notBefore == null) && (notAfter == null)) { 1191 return false; 1192 } 1193 if ((notBefore != null) 1194 && notBefore.compareTo(privateKeyValid) > 0) { 1195 return false; 1196 } 1197 if ((notAfter != null) 1198 && notAfter.compareTo(privateKeyValid) < 0) { 1199 return false; 1200 } 1201 } catch (IOException e) { 1202 return false; 1203 } 1204 } 1205 if (subjectPublicKeyAlgID != null) { 1206 try { 1207 byte[] encoding = cert.getPublicKey().getEncoded(); 1208 AlgorithmIdentifier ai = ((SubjectPublicKeyInfo) 1209 SubjectPublicKeyInfo.ASN1.decode(encoding)) 1210 .getAlgorithmIdentifier(); 1211 if (!subjectPublicKeyAlgID.equals(ai.getAlgorithm())) { 1212 return false; 1213 } 1214 } catch (IOException e) { 1215 e.printStackTrace(); 1216 return false; 1217 } 1218 } 1219 if (subjectPublicKey != null) { 1220 if (!Arrays.equals(subjectPublicKey, 1221 cert.getPublicKey().getEncoded())) { 1222 return false; 1223 } 1224 } 1225 if (keyUsage != null) { 1226 boolean[] ku = cert.getKeyUsage(); 1227 if (ku != null) { 1228 int i = 0; 1229 int min_length = (ku.length < keyUsage.length) ? ku.length 1230 : keyUsage.length; 1231 for (; i < min_length; i++) { 1232 if (keyUsage[i] && !ku[i]) { 1233 // the specified keyUsage allows, 1234 // but certificate does not. 1235 return false; 1236 } 1237 } 1238 for (; i<keyUsage.length; i++) { 1239 if (keyUsage[i]) { 1240 return false; 1241 } 1242 } 1243 } 1244 } 1245 if (extendedKeyUsage != null) { 1246 try { 1247 List keyUsage = cert.getExtendedKeyUsage(); 1248 if (keyUsage != null) { 1249 if (!keyUsage.containsAll(extendedKeyUsage)) { 1250 return false; 1251 } 1252 } 1253 } catch (CertificateParsingException e) { 1254 return false; 1255 } 1256 } 1257 if (pathLen != -1) { 1258 int p_len = cert.getBasicConstraints(); 1259 if ((pathLen < 0) && (p_len >= 0)) { 1260 // need end-entity but got CA 1261 return false; 1262 } 1263 if ((pathLen > 0) && (pathLen > p_len)) { 1264 // allowed _pathLen is small 1265 return false; 1266 } 1267 } 1268 if (subjectAltNames != null) { 1269 PASSED: 1270 try { 1271 byte[] bytes = getExtensionValue(cert, "2.5.29.17"); 1272 if (bytes == null) { 1273 return false; 1274 } 1275 List sans = ((GeneralNames) GeneralNames.ASN1.decode(bytes)) 1276 .getNames(); 1277 if ((sans == null) || (sans.size() == 0)) { 1278 return false; 1279 } 1280 boolean[][] map = new boolean[9][]; 1281 // initialize the check map 1282 for (int i=0; i<9; i++) { 1283 map[i] = (subjectAltNames[i] == null) 1284 ? new boolean[0] 1285 : new boolean[subjectAltNames[i].size()]; 1286 } 1287 Iterator it = sans.iterator(); 1288 while (it.hasNext()) { 1289 GeneralName name = (GeneralName) it.next(); 1290 int tag = name.getTag(); 1291 for (int i=0; i<map[tag].length; i++) { 1292 if (((GeneralName) subjectAltNames[tag].get(i)) 1293 .equals(name)) { 1294 if (!matchAllNames) { 1295 break PASSED; 1296 } 1297 map[tag][i] = true; 1298 } 1299 } 1300 } 1301 if (!matchAllNames) { 1302 // there was not any match 1303 return false; 1304 } 1305 // else check the map 1306 for (int tag=0; tag<9; tag++) { 1307 for (int name=0; name<map[tag].length; name++) { 1308 if (!map[tag][name]) { 1309 return false; 1310 } 1311 } 1312 } 1313 } catch (IOException e) { 1314 e.printStackTrace(); 1315 return false; 1316 } 1317 } 1318 if (nameConstraints != null) { 1319 if (!nameConstraints.isAcceptable(cert)) { 1320 return false; 1321 } 1322 } 1323 if (policies != null) { 1324 byte[] bytes = getExtensionValue(cert, "2.5.29.32"); 1325 if (bytes == null) { 1326 return false; 1327 } 1328 if (policies.size() == 0) { 1329 // if certificate has such extension than it has at least 1330 // one policy in it. 1331 return true; 1332 } 1333 PASSED: 1334 try { 1335 List policyInformations = ((CertificatePolicies) 1336 CertificatePolicies.ASN1.decode(bytes)) 1337 .getPolicyInformations(); 1338 Iterator it = policyInformations.iterator(); 1339 while (it.hasNext()) { 1340 if (policies.contains(((PolicyInformation) it.next()) 1341 .getPolicyIdentifier())) { 1342 break PASSED; 1343 } 1344 } 1345 return false; 1346 } catch (IOException e) { 1347 // the extension is invalid 1348 return false; 1349 } 1350 } 1351 if (pathToNames != null) { 1352 byte[] bytes = getExtensionValue(cert, "2.5.29.30"); 1353 if (bytes != null) { 1354 NameConstraints nameConstraints; 1355 try { 1356 nameConstraints = 1357 (NameConstraints) NameConstraints.ASN1.decode(bytes); 1358 } catch (IOException e) { 1359 // the extension is invalid; 1360 return false; 1361 } 1362 if (!nameConstraints.isAcceptable(pathToNames)) { 1363 return false; 1364 } 1365 } 1366 } 1367 return true; 1368 } 1369 1370 /** 1371 * Clones this {@code X509CertSelector} instance. 1372 * 1373 * @return the cloned instance. 1374 */ 1375 public Object clone() { 1376 X509CertSelector result; 1377 1378 try { 1379 result = (X509CertSelector) super.clone(); 1380 } catch (CloneNotSupportedException e) { 1381 return null; 1382 } 1383 1384 if (this.subjectKeyIdentifier != null) { 1385 result.subjectKeyIdentifier = 1386 new byte[this.subjectKeyIdentifier.length]; 1387 System.arraycopy(this.subjectKeyIdentifier, 0, 1388 result.subjectKeyIdentifier, 0, 1389 this.subjectKeyIdentifier.length); 1390 } 1391 if (this.authorityKeyIdentifier != null) { 1392 result.authorityKeyIdentifier = 1393 new byte[this.authorityKeyIdentifier.length]; 1394 System.arraycopy(this.authorityKeyIdentifier, 0, 1395 result.authorityKeyIdentifier, 0, 1396 this.authorityKeyIdentifier.length); 1397 } 1398 if (this.subjectPublicKey != null) { 1399 result.subjectPublicKey = new byte[this.subjectPublicKey.length]; 1400 System.arraycopy(this.subjectPublicKey, 0, result.subjectPublicKey, 1401 0, this.subjectPublicKey.length); 1402 } 1403 if (this.keyUsage != null) { 1404 result.keyUsage = new boolean[this.keyUsage.length]; 1405 System.arraycopy(this.keyUsage, 0, result.keyUsage, 0, 1406 this.keyUsage.length); 1407 } 1408 result.extendedKeyUsage = (this.extendedKeyUsage == null) 1409 ? null 1410 : new HashSet(this.extendedKeyUsage); 1411 if (this.subjectAltNames != null) { 1412 result.subjectAltNames = new ArrayList[9]; 1413 for (int i=0; i<9; i++) { 1414 if (this.subjectAltNames[i] != null) { 1415 result.subjectAltNames[i] = 1416 new ArrayList(this.subjectAltNames[i]); 1417 } 1418 } 1419 } 1420 result.policies = (this.policies == null) 1421 ? null 1422 : new HashSet(this.policies); 1423 result.pathToNames = (this.pathToNames == null) 1424 ? null 1425 : new ArrayList(this.pathToNames); 1426 return result; 1427 } 1428} 1429