X509CertSelector.java revision 6186821cb13f4ac7ff50950c813394367e021eae
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.util.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<String> 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<String> pols = new HashSet<String>(policies.size()); 880 Iterator<String> it = policies.iterator(); 881 while (it.hasNext()) { 882 String certPolicyId = 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) 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 ? EmptyArray.BOOLEAN : new boolean[subjectAltNames[i].size()]; 1285 } 1286 Iterator it = sans.iterator(); 1287 while (it.hasNext()) { 1288 GeneralName name = (GeneralName) it.next(); 1289 int tag = name.getTag(); 1290 for (int i=0; i<map[tag].length; i++) { 1291 if (((GeneralName) subjectAltNames[tag].get(i)) 1292 .equals(name)) { 1293 if (!matchAllNames) { 1294 break PASSED; 1295 } 1296 map[tag][i] = true; 1297 } 1298 } 1299 } 1300 if (!matchAllNames) { 1301 // there was not any match 1302 return false; 1303 } 1304 // else check the map 1305 for (int tag=0; tag<9; tag++) { 1306 for (int name=0; name<map[tag].length; name++) { 1307 if (!map[tag][name]) { 1308 return false; 1309 } 1310 } 1311 } 1312 } catch (IOException e) { 1313 e.printStackTrace(); 1314 return false; 1315 } 1316 } 1317 if (nameConstraints != null) { 1318 if (!nameConstraints.isAcceptable(cert)) { 1319 return false; 1320 } 1321 } 1322 if (policies != null) { 1323 byte[] bytes = getExtensionValue(cert, "2.5.29.32"); 1324 if (bytes == null) { 1325 return false; 1326 } 1327 if (policies.size() == 0) { 1328 // if certificate has such extension than it has at least 1329 // one policy in it. 1330 return true; 1331 } 1332 PASSED: 1333 try { 1334 List policyInformations = ((CertificatePolicies) 1335 CertificatePolicies.ASN1.decode(bytes)) 1336 .getPolicyInformations(); 1337 Iterator it = policyInformations.iterator(); 1338 while (it.hasNext()) { 1339 if (policies.contains(((PolicyInformation) it.next()) 1340 .getPolicyIdentifier())) { 1341 break PASSED; 1342 } 1343 } 1344 return false; 1345 } catch (IOException e) { 1346 // the extension is invalid 1347 return false; 1348 } 1349 } 1350 if (pathToNames != null) { 1351 byte[] bytes = getExtensionValue(cert, "2.5.29.30"); 1352 if (bytes != null) { 1353 NameConstraints nameConstraints; 1354 try { 1355 nameConstraints = 1356 (NameConstraints) NameConstraints.ASN1.decode(bytes); 1357 } catch (IOException e) { 1358 // the extension is invalid; 1359 return false; 1360 } 1361 if (!nameConstraints.isAcceptable(pathToNames)) { 1362 return false; 1363 } 1364 } 1365 } 1366 return true; 1367 } 1368 1369 /** 1370 * Clones this {@code X509CertSelector} instance. 1371 * 1372 * @return the cloned instance. 1373 */ 1374 public Object clone() { 1375 X509CertSelector result; 1376 1377 try { 1378 result = (X509CertSelector) super.clone(); 1379 } catch (CloneNotSupportedException e) { 1380 return null; 1381 } 1382 1383 if (this.subjectKeyIdentifier != null) { 1384 result.subjectKeyIdentifier = 1385 new byte[this.subjectKeyIdentifier.length]; 1386 System.arraycopy(this.subjectKeyIdentifier, 0, 1387 result.subjectKeyIdentifier, 0, 1388 this.subjectKeyIdentifier.length); 1389 } 1390 if (this.authorityKeyIdentifier != null) { 1391 result.authorityKeyIdentifier = 1392 new byte[this.authorityKeyIdentifier.length]; 1393 System.arraycopy(this.authorityKeyIdentifier, 0, 1394 result.authorityKeyIdentifier, 0, 1395 this.authorityKeyIdentifier.length); 1396 } 1397 if (this.subjectPublicKey != null) { 1398 result.subjectPublicKey = new byte[this.subjectPublicKey.length]; 1399 System.arraycopy(this.subjectPublicKey, 0, result.subjectPublicKey, 1400 0, this.subjectPublicKey.length); 1401 } 1402 if (this.keyUsage != null) { 1403 result.keyUsage = new boolean[this.keyUsage.length]; 1404 System.arraycopy(this.keyUsage, 0, result.keyUsage, 0, 1405 this.keyUsage.length); 1406 } 1407 result.extendedKeyUsage = (this.extendedKeyUsage == null) 1408 ? null 1409 : new HashSet(this.extendedKeyUsage); 1410 if (this.subjectAltNames != null) { 1411 result.subjectAltNames = new ArrayList[9]; 1412 for (int i=0; i<9; i++) { 1413 if (this.subjectAltNames[i] != null) { 1414 result.subjectAltNames[i] = 1415 new ArrayList(this.subjectAltNames[i]); 1416 } 1417 } 1418 } 1419 result.policies = (this.policies == null) ? null : new HashSet<String>(this.policies); 1420 result.pathToNames = (this.pathToNames == null) 1421 ? null 1422 : new ArrayList(this.pathToNames); 1423 return result; 1424 } 1425} 1426