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