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