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 18 package java.security.cert; 19 20 import java.io.IOException; 21 import java.math.BigInteger; 22 import java.security.PublicKey; 23 import java.util.ArrayList; 24 import java.util.Arrays; 25 import java.util.Collection; 26 import java.util.Collections; 27 import java.util.Date; 28 import java.util.HashSet; 29 import java.util.List; 30 import java.util.Set; 31 import javax.security.auth.x500.X500Principal; 32 import libcore.util.EmptyArray; 33 import org.apache.harmony.security.asn1.ASN1OctetString; 34 import org.apache.harmony.security.utils.Array; 35 import org.apache.harmony.security.x509.AlgorithmIdentifier; 36 import org.apache.harmony.security.x509.CertificatePolicies; 37 import org.apache.harmony.security.x509.GeneralName; 38 import org.apache.harmony.security.x509.GeneralNames; 39 import org.apache.harmony.security.x509.NameConstraints; 40 import org.apache.harmony.security.x509.PolicyInformation; 41 import org.apache.harmony.security.x509.PrivateKeyUsagePeriod; 42 import 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 */ 50 public 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<String> extendedKeyUsage; 65 private boolean matchAllNames = true; 66 private int pathLen = -1; 67 private List<GeneralName>[] subjectAltNames; 68 private NameConstraints nameConstraints; 69 private Set<String> policies; 70 private ArrayList<GeneralName> 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<String> key_u = new HashSet<String>(); 597 for (String usage : keyUsage) { 598 checkOID(usage); 599 key_u.add(usage); 600 } 601 extendedKeyUsage = Collections.unmodifiableSet(key_u); 602 } 603 604 /** 605 * Returns the criterion for the {@literal ExtendedKeyUsage} extension. 606 * 607 * @return the set of key usage OIDs, or {@code null} if it's not to be 608 * checked. 609 */ 610 public Set<String> getExtendedKeyUsage() { 611 return extendedKeyUsage; 612 } 613 614 /** 615 * Sets the flag for the matching behavior for subject alternative names. 616 * <p> 617 * The flag indicates whether a certificate must contain all or at least one 618 * of the subject alternative names specified by {@link 619 * #setSubjectAlternativeNames} or {@link #addSubjectAlternativeName}. 620 * 621 * @param matchAllNames 622 * {@code true} if a certificate must contain all of the 623 * specified subject alternative names, otherwise {@code false}. 624 */ 625 public void setMatchAllSubjectAltNames(boolean matchAllNames) { 626 this.matchAllNames = matchAllNames; 627 } 628 629 /** 630 * Returns the flag for the matching behavior for subject alternative names. 631 * <p> 632 * The flag indicates whether a certificate must contain all or at least one 633 * of the subject alternative names specified by {@link 634 * #setSubjectAlternativeNames} or {@link #addSubjectAlternativeName}. 635 * 636 * @return {@code true} if a certificate must contain all of the specified 637 * subject alternative names, otherwise {@code false}. 638 */ 639 public boolean getMatchAllSubjectAltNames() { 640 return matchAllNames; 641 } 642 643 /** 644 * Sets the criterion for subject alternative names. 645 * <p> 646 * the certificate must contain all or at least one of the specified subject 647 * alternative names. The behavior is specified by 648 * {@link #getMatchAllSubjectAltNames}. 649 * <p> 650 * The specified parameter {@code names} is a collection with an entry for 651 * each name to be included in the criterion. The name is specified as a 652 * {@code List}, the first entry must be an {@code Integer} specifying the 653 * name type (0-8), the second entry must be a {@code String} or a byte 654 * array specifying the name (in string or ASN.1 DER encoded form) 655 * 656 * @param names 657 * the names collection or {@code null} to not perform this check. 658 * @throws IOException 659 * if the decoding of a name fails. 660 */ 661 public void setSubjectAlternativeNames(Collection<List<?>> names) throws IOException { 662 subjectAltNames = null; 663 if ((names == null) || (names.size() == 0)) { 664 return; 665 } 666 for (List<?> name : names) { 667 int tag = (Integer) name.get(0); 668 Object value = name.get(1); 669 if (value instanceof String) { 670 addSubjectAlternativeName(tag, (String) value); 671 } else if (value instanceof byte[]) { 672 addSubjectAlternativeName(tag, (byte[]) value); 673 } else { 674 throw new IOException("name neither a String nor a byte[]"); 675 } 676 } 677 } 678 679 /** 680 * Adds a subject alternative name to the respective criterion. 681 * 682 * @param tag 683 * the type of the name 684 * @param name 685 * the name in string format. 686 * @throws IOException 687 * if parsing the name fails. 688 */ 689 public void addSubjectAlternativeName(int tag, String name) 690 throws IOException { 691 GeneralName alt_name = new GeneralName(tag, name); 692 // create only if there was not any errors 693 if (subjectAltNames == null) { 694 subjectAltNames = new ArrayList[9]; 695 } 696 if (subjectAltNames[tag] == null) { 697 subjectAltNames[tag] = new ArrayList<GeneralName>(); 698 } 699 subjectAltNames[tag].add(alt_name); 700 } 701 702 /** 703 * Adds a subject alternative name to the respective criterion. 704 * 705 * @param tag 706 * the type of the name. 707 * @param name 708 * the name in ASN.1 DER encoded form. 709 * @throws IOException 710 * if the decoding of the name fails. 711 */ 712 public void addSubjectAlternativeName(int tag, byte[] name) 713 throws IOException { 714 GeneralName alt_name = new GeneralName(tag, name); 715 // create only if there was not any errors 716 if (subjectAltNames == null) { 717 subjectAltNames = new ArrayList[9]; 718 } 719 if (subjectAltNames[tag] == null) { 720 subjectAltNames[tag] = new ArrayList<GeneralName>(); 721 } 722 subjectAltNames[tag].add(alt_name); 723 } 724 725 /** 726 * Returns the criterion for subject alternative names. 727 * <p> 728 * the certificate must contain all or at least one of the specified subject 729 * alternative names. The behavior is specified by 730 * {@link #getMatchAllSubjectAltNames}. 731 * <p> 732 * The subject alternative names is a collection with an entry for each name 733 * included in the criterion. The name is specified as a {@code List}, the 734 * first entry is an {@code Integer} specifying the name type (0-8), the 735 * second entry is byte array specifying the name in ASN.1 DER encoded form) 736 * 737 * @return the names collection or {@code null} if none specified. 738 */ 739 public Collection<List<?>> getSubjectAlternativeNames() { 740 if (subjectAltNames == null) { 741 return null; 742 } 743 ArrayList<List<?>> result = new ArrayList<List<?>>(); 744 for (int tag=0; tag<9; tag++) { 745 if (subjectAltNames[tag] != null) { 746 for (int name=0; name<subjectAltNames[tag].size(); name++) { 747 List<Object> list = new ArrayList<Object>(2); 748 list.add(tag); 749 list.add(subjectAltNames[tag].get(name)); 750 result.add(list); 751 } 752 } 753 } 754 return result; 755 } 756 757 /** 758 * Sets the criterion for the name constraints. 759 * <p> 760 * The certificate must constraint subject and subject alternative names 761 * that match the specified name constraints. 762 * <p> 763 * The name constraints in ASN.1: 764 * 765 * <pre> 766 * NameConstraints ::= SEQUENCE { 767 * permittedSubtrees [0] GeneralSubtrees OPTIONAL, 768 * excludedSubtrees [1] GeneralSubtrees OPTIONAL } 769 * 770 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree 771 * 772 * GeneralSubtree ::= SEQUENCE { 773 * base GeneralName, 774 * minimum [0] BaseDistance DEFAULT 0, 775 * maximum [1] BaseDistance OPTIONAL } 776 * 777 * BaseDistance ::= INTEGER (0..MAX) 778 * 779 * GeneralName ::= CHOICE { 780 * otherName [0] OtherName, 781 * rfc822Name [1] IA5String, 782 * dNSName [2] IA5String, 783 * x400Address [3] ORAddress, 784 * directoryName [4] Name, 785 * ediPartyName [5] EDIPartyName, 786 * uniformResourceIdentifier [6] IA5String, 787 * iPAddress [7] OCTET STRING, 788 * registeredID [8] OBJECT IDENTIFIER} 789 * 790 * </pre> 791 * 792 * @param bytes 793 * the name constraints in ASN.1 DER encoded format, or null to 794 * not check any constraints. 795 * @throws IOException 796 * if decoding the name constraints fail. 797 */ 798 public void setNameConstraints(byte[] bytes) throws IOException { 799 this.nameConstraints = (bytes == null) 800 ? null 801 : (NameConstraints) NameConstraints.ASN1.decode(bytes); 802 } 803 804 /** 805 * Returns the criterion for the name constraints. 806 * 807 * @return the name constraints or {@code null} if none specified. 808 * @see #setNameConstraints 809 */ 810 public byte[] getNameConstraints() { 811 return (nameConstraints == null) 812 ? null 813 : nameConstraints.getEncoded(); 814 } 815 816 /** 817 * Sets the criterion for the basic constraints extension. 818 * <p> 819 * A value greater than or equal to zero indicates that a certificate must 820 * include a basic constraints extension with a path length of a least that 821 * value. A value of {@code -2} indicates that only end-entity certificates 822 * are accepted. A value of {@code -1} indicates that no check is done. 823 * 824 * @param pathLen 825 * the value specifying the criterion. 826 * @throws IllegalArgumentException 827 * if {@code pathLen} is less than {@code -2}. 828 */ 829 public void setBasicConstraints(int pathLen) { 830 if (pathLen < -2) { 831 throw new IllegalArgumentException("pathLen < -2"); 832 } 833 this.pathLen = pathLen; 834 } 835 836 /** 837 * Returns the criterion for the basic constraints extension. 838 * <p> 839 * A value greater than or equal to zero indicates that a certificate must 840 * include a basic constraints extension with a path length of a least that 841 * value. A value of {@code -2} indicates that only end-entity certificates 842 * are accepted. A value of {@code -1} indicates that no check is done. 843 * 844 * @return the value of the criterion. 845 */ 846 public int getBasicConstraints() { 847 return pathLen; 848 } 849 850 /** 851 * Sets the criterion for the policy constraint. 852 * <p> 853 * The certificate must have at least one of the specified certificate 854 * policy extensions. For an empty set the certificate must have at least 855 * some policies in its policy extension. 856 * 857 * @param policies 858 * the certificate policy OIDs, an empty set, or {@code null} to 859 * not perform this check. 860 * @throws IOException 861 * if parsing the specified OIDs fails. 862 */ 863 public void setPolicy(Set<String> policies) throws IOException { 864 if (policies == null) { 865 this.policies = null; 866 return; 867 } 868 HashSet<String> pols = new HashSet<String>(policies.size()); 869 for (String certPolicyId : policies) { 870 checkOID(certPolicyId); 871 pols.add(certPolicyId); 872 } 873 this.policies = Collections.unmodifiableSet(pols); 874 } 875 876 /** 877 * Returns the criterion for the policy constraint. 878 * <p> 879 * The certificate must have at least one of the certificate policy 880 * extensions. For an empty set the certificate must have at least some 881 * policies in its policy extension. 882 * 883 * @return the certificate policy OIDs, an empty set, or {@code null} if not 884 * to be checked. 885 */ 886 public Set<String> getPolicy() { 887 return policies; 888 } 889 890 /** 891 * Adds a {@literal "pathToName"} to the respective criterion. 892 * 893 * @param type 894 * the type of the name. 895 * @param name 896 * the name in string format. 897 * @throws IOException 898 * if parsing fails. 899 * @see #setPathToNames 900 */ 901 public void addPathToName(int type, String name) throws IOException { 902 GeneralName path_name = new GeneralName(type, name); 903 // create only if there was not any errors 904 if (pathToNames == null) { 905 pathToNames = new ArrayList<GeneralName>(); 906 } 907 pathToNames.add(path_name); 908 } 909 910 /** 911 * Sets the criterion for the pathToNames constraint. 912 * <p> 913 * This allows to specify the complete set of names, a certificate's name 914 * constraints must permit. 915 * <p> 916 * The specified parameter {@code names} is a collection with an entry for 917 * each name to be included in the criterion. The name is specified as a 918 * {@code List}, the first entry must be an {@code Integer} specifying the 919 * name type (0-8), the second entry must be a {@code String} or a byte 920 * array specifying the name (in string or ASN.1 DER encoded form) 921 * 922 * @param names 923 * the names collection or {@code null} to not perform this 924 * check. 925 * @throws IOException 926 * if decoding fails. 927 */ 928 public void setPathToNames(Collection<List<?>> names) throws IOException { 929 pathToNames = null; 930 if ((names == null) || (names.size() == 0)) { 931 return; 932 } 933 for (List<?> name : names) { 934 int tag = (Integer) name.get(0); 935 Object value = name.get(1); 936 if (value instanceof String) { 937 addPathToName(tag, (String) value); 938 } else if (value instanceof byte[]) { 939 addPathToName(tag, (byte[]) value); 940 } else { 941 throw new IOException("name neither a String nor a byte[]"); 942 } 943 } 944 } 945 946 /** 947 * Adds a {@literal "pathToName"} to the respective criterion. 948 * 949 * @param type 950 * the type of the name 951 * @param name 952 * the name in ASN.1 DER encoded form. 953 * @throws IOException 954 * if decoding fails. 955 * @see #setPathToNames 956 */ 957 public void addPathToName(int type, byte[] name) throws IOException { 958 GeneralName path_name= new GeneralName(type, name); 959 // create only if there was not any errors 960 if (pathToNames == null) { 961 pathToNames = new ArrayList<GeneralName>(); 962 } 963 pathToNames.add(path_name); 964 } 965 966 /** 967 * Returns the criterion for the pathToNames constraint. 968 * <p> 969 * The constraint is a collection with an entry for each name to be included 970 * in the criterion. The name is specified as a {@code List}, the first 971 * entry is an {@code Integer} specifying the name type (0-8), the second 972 * entry is a byte array specifying the name in ASN.1 DER encoded form. 973 * 974 * @return the pathToNames constraint or {@code null} if none specified. 975 */ 976 public Collection<List<?>> getPathToNames() { 977 if (pathToNames == null) { 978 return null; 979 } 980 Collection<List<?>> result = new ArrayList<List<?>>(); 981 for (GeneralName name : pathToNames) { 982 result.add(name.getAsList()); 983 } 984 return result; 985 } 986 987 /** 988 * Returns a string representation of this {@code X509CertSelector} 989 * instance. 990 * 991 * @return a string representation of this {@code X509CertSelector} 992 * instance. 993 */ 994 public String toString() { 995 // For convenient reading of the string representation 996 // all of the fields named according to the rfc 3280 997 // (http://www.ietf.org/rfc/rfc3280.txt). 998 999 StringBuilder result = new StringBuilder(); 1000 result.append("X509CertSelector: \n["); 1001 if (this.certificateEquals != null) { 1002 result.append("\n certificateEquals: ").append(certificateEquals); 1003 } 1004 if (this.serialNumber != null) { 1005 result.append("\n serialNumber: ").append(serialNumber); 1006 } 1007 if (this.issuer != null) { 1008 result.append("\n issuer: ").append(issuer); 1009 } 1010 if (this.subject != null) { 1011 result.append("\n subject: ").append(subject); 1012 } 1013 if (this.subjectKeyIdentifier != null) { 1014 result.append("\n subjectKeyIdentifier: ") 1015 .append(Array.getBytesAsString(subjectKeyIdentifier)); 1016 } 1017 if (this.authorityKeyIdentifier != null) { 1018 result.append("\n authorityKeyIdentifier: ") 1019 .append(Array.getBytesAsString(authorityKeyIdentifier)); 1020 } 1021 if (this.certificateValid != null) { 1022 result.append("\n certificateValid: ").append(certificateValid); 1023 } 1024 if (this.subjectPublicKeyAlgID != null) { 1025 result.append("\n subjectPublicKeyAlgID: ").append(subjectPublicKeyAlgID); 1026 } 1027 if (this.privateKeyValid != null) { 1028 result.append("\n privateKeyValid: ").append(privateKeyValid); 1029 } 1030 if (this.subjectPublicKey != null) { 1031 result.append("\n subjectPublicKey: ") 1032 .append(Array.getBytesAsString(subjectPublicKey)); 1033 } 1034 if (this.keyUsage != null) { 1035 result.append("\n keyUsage: \n ["); 1036 String[] kuNames = new String[] { 1037 "digitalSignature", "nonRepudiation", "keyEncipherment", 1038 "dataEncipherment", "keyAgreement", "keyCertSign", "cRLSign", 1039 "encipherOnly", "decipherOnly" 1040 }; 1041 for (int i=0; i<9; i++) { 1042 if (keyUsage[i]) { 1043 result.append("\n ").append(kuNames[i]); 1044 } 1045 } 1046 result.append("\n ]"); 1047 } 1048 if (this.extendedKeyUsage != null) { 1049 result.append("\n extendedKeyUsage: ").append(extendedKeyUsage.toString()); 1050 } 1051 result.append("\n matchAllNames: ").append(matchAllNames); 1052 result.append("\n pathLen: ").append(pathLen); 1053 if (this.subjectAltNames != null) { 1054 result.append("\n subjectAltNames: \n ["); 1055 for (int i=0; i<9; i++) { 1056 List<GeneralName> names = subjectAltNames[i]; 1057 if (names != null) { 1058 int size = names.size(); 1059 for (GeneralName generalName : names) { 1060 result.append("\n ").append(generalName.toString()); 1061 } 1062 } 1063 } 1064 result.append("\n ]"); 1065 } 1066 if (this.nameConstraints != null) { 1067 } 1068 if (this.policies != null) { 1069 result.append("\n policies: ").append(policies.toString()); 1070 } 1071 if (this.pathToNames != null) { 1072 result.append("\n pathToNames: \n ["); 1073 for (GeneralName generalName : pathToNames) { 1074 result.append("\n ").append(generalName.toString()); 1075 } 1076 } 1077 result.append("\n]"); 1078 return result.toString(); 1079 } 1080 1081 private byte[] getExtensionValue(X509Certificate cert, String oid) { 1082 try { 1083 byte[] bytes = cert.getExtensionValue(oid); 1084 if (bytes == null) { 1085 return null; 1086 } 1087 return (byte[]) ASN1OctetString.getInstance().decode(bytes); 1088 } catch (IOException e) { 1089 return null; 1090 } 1091 } 1092 1093 /** 1094 * Returns whether the specified certificate matches all the criteria 1095 * collected in this instance. 1096 * 1097 * @param certificate 1098 * the certificate to check. 1099 * @return {@code true} if the certificate matches all the criteria, 1100 * otherwise {@code false}. 1101 */ 1102 public boolean match(Certificate certificate) { 1103 if (! (certificate instanceof X509Certificate)) { 1104 return false; 1105 } 1106 1107 X509Certificate cert = (X509Certificate) certificate; 1108 if ((certificateEquals != null) && 1109 !certificateEquals.equals(cert)) { 1110 return false; 1111 } 1112 if ((serialNumber != null) && 1113 !serialNumber.equals(cert.getSerialNumber())) { 1114 return false; 1115 } 1116 if ((issuer != null) && 1117 !issuer.equals(cert.getIssuerX500Principal())) { 1118 return false; 1119 } 1120 if ((subject != null) && 1121 !subject.equals(cert.getSubjectX500Principal())) { 1122 return false; 1123 } 1124 if ((subjectKeyIdentifier != null) && 1125 !Arrays.equals(subjectKeyIdentifier, 1126 // Here and later all of the extension OIDs 1127 // are taken from rfc 3280 (http://www.ietf.org/rfc/rfc3280.txt) 1128 getExtensionValue(cert, "2.5.29.14"))) { 1129 return false; 1130 } 1131 if ((authorityKeyIdentifier != null) && 1132 !Arrays.equals(authorityKeyIdentifier, 1133 getExtensionValue(cert, "2.5.29.35"))) { 1134 return false; 1135 } 1136 if (certificateValid != null) { 1137 try { 1138 cert.checkValidity(certificateValid); 1139 } catch(CertificateExpiredException e) { 1140 return false; 1141 } catch(CertificateNotYetValidException e) { 1142 return false; 1143 } 1144 } 1145 if (privateKeyValid != null) { 1146 try { 1147 byte[] bytes = getExtensionValue(cert, "2.5.29.16"); 1148 if (bytes == null) { 1149 return false; 1150 } 1151 PrivateKeyUsagePeriod pkup = (PrivateKeyUsagePeriod) 1152 PrivateKeyUsagePeriod.ASN1.decode(bytes); 1153 Date notBefore = pkup.getNotBefore(); 1154 Date notAfter = pkup.getNotAfter(); 1155 if ((notBefore == null) && (notAfter == null)) { 1156 return false; 1157 } 1158 if ((notBefore != null) 1159 && notBefore.compareTo(privateKeyValid) > 0) { 1160 return false; 1161 } 1162 if ((notAfter != null) 1163 && notAfter.compareTo(privateKeyValid) < 0) { 1164 return false; 1165 } 1166 } catch (IOException e) { 1167 return false; 1168 } 1169 } 1170 if (subjectPublicKeyAlgID != null) { 1171 try { 1172 byte[] encoding = cert.getPublicKey().getEncoded(); 1173 AlgorithmIdentifier ai = ((SubjectPublicKeyInfo) 1174 SubjectPublicKeyInfo.ASN1.decode(encoding)) 1175 .getAlgorithmIdentifier(); 1176 if (!subjectPublicKeyAlgID.equals(ai.getAlgorithm())) { 1177 return false; 1178 } 1179 } catch (IOException e) { 1180 e.printStackTrace(); 1181 return false; 1182 } 1183 } 1184 if (subjectPublicKey != null) { 1185 if (!Arrays.equals(subjectPublicKey, 1186 cert.getPublicKey().getEncoded())) { 1187 return false; 1188 } 1189 } 1190 if (keyUsage != null) { 1191 boolean[] ku = cert.getKeyUsage(); 1192 if (ku != null) { 1193 int i = 0; 1194 int min_length = (ku.length < keyUsage.length) ? ku.length 1195 : keyUsage.length; 1196 for (; i < min_length; i++) { 1197 if (keyUsage[i] && !ku[i]) { 1198 // the specified keyUsage allows, 1199 // but certificate does not. 1200 return false; 1201 } 1202 } 1203 for (; i<keyUsage.length; i++) { 1204 if (keyUsage[i]) { 1205 return false; 1206 } 1207 } 1208 } 1209 } 1210 if (extendedKeyUsage != null) { 1211 try { 1212 List keyUsage = cert.getExtendedKeyUsage(); 1213 if (keyUsage != null) { 1214 if (!keyUsage.containsAll(extendedKeyUsage)) { 1215 return false; 1216 } 1217 } 1218 } catch (CertificateParsingException e) { 1219 return false; 1220 } 1221 } 1222 if (pathLen != -1) { 1223 int p_len = cert.getBasicConstraints(); 1224 if ((pathLen < 0) && (p_len >= 0)) { 1225 // need end-entity but got CA 1226 return false; 1227 } 1228 if ((pathLen > 0) && (pathLen > p_len)) { 1229 // allowed _pathLen is small 1230 return false; 1231 } 1232 } 1233 if (subjectAltNames != null) { 1234 PASSED: 1235 try { 1236 byte[] bytes = getExtensionValue(cert, "2.5.29.17"); 1237 if (bytes == null) { 1238 return false; 1239 } 1240 List<GeneralName> sans = ((GeneralNames) GeneralNames.ASN1.decode(bytes)) 1241 .getNames(); 1242 if ((sans == null) || (sans.size() == 0)) { 1243 return false; 1244 } 1245 boolean[][] map = new boolean[9][]; 1246 // initialize the check map 1247 for (int i=0; i<9; i++) { 1248 map[i] = (subjectAltNames[i] == null) 1249 ? EmptyArray.BOOLEAN : new boolean[subjectAltNames[i].size()]; 1250 } 1251 for (GeneralName name : sans) { 1252 int tag = name.getTag(); 1253 for (int i = 0; i < map[tag].length; i++) { 1254 if (subjectAltNames[tag].get(i).equals(name)) { 1255 if (!matchAllNames) { 1256 break PASSED; 1257 } 1258 map[tag][i] = true; 1259 } 1260 } 1261 } 1262 if (!matchAllNames) { 1263 // there was not any match 1264 return false; 1265 } 1266 // else check the map 1267 for (int tag=0; tag<9; tag++) { 1268 for (int name=0; name<map[tag].length; name++) { 1269 if (!map[tag][name]) { 1270 return false; 1271 } 1272 } 1273 } 1274 } catch (IOException e) { 1275 e.printStackTrace(); 1276 return false; 1277 } 1278 } 1279 if (nameConstraints != null) { 1280 if (!nameConstraints.isAcceptable(cert)) { 1281 return false; 1282 } 1283 } 1284 if (policies != null) { 1285 byte[] bytes = getExtensionValue(cert, "2.5.29.32"); 1286 if (bytes == null) { 1287 return false; 1288 } 1289 if (policies.size() == 0) { 1290 // if certificate has such extension than it has at least 1291 // one policy in it. 1292 return true; 1293 } 1294 PASSED: 1295 try { 1296 List<PolicyInformation> policyInformations 1297 = ((CertificatePolicies) CertificatePolicies.ASN1.decode(bytes)) 1298 .getPolicyInformations(); 1299 for (PolicyInformation policyInformation : policyInformations) { 1300 if (policies.contains(policyInformation.getPolicyIdentifier())) { 1301 break PASSED; 1302 } 1303 } 1304 return false; 1305 } catch (IOException e) { 1306 // the extension is invalid 1307 return false; 1308 } 1309 } 1310 if (pathToNames != null) { 1311 byte[] bytes = getExtensionValue(cert, "2.5.29.30"); 1312 if (bytes != null) { 1313 NameConstraints nameConstraints; 1314 try { 1315 nameConstraints = 1316 (NameConstraints) NameConstraints.ASN1.decode(bytes); 1317 } catch (IOException e) { 1318 // the extension is invalid; 1319 return false; 1320 } 1321 if (!nameConstraints.isAcceptable(pathToNames)) { 1322 return false; 1323 } 1324 } 1325 } 1326 return true; 1327 } 1328 1329 /** 1330 * Clones this {@code X509CertSelector} instance. 1331 * 1332 * @return the cloned instance. 1333 */ 1334 public Object clone() { 1335 X509CertSelector result; 1336 1337 try { 1338 result = (X509CertSelector) super.clone(); 1339 } catch (CloneNotSupportedException e) { 1340 return null; 1341 } 1342 1343 if (this.subjectKeyIdentifier != null) { 1344 result.subjectKeyIdentifier = 1345 new byte[this.subjectKeyIdentifier.length]; 1346 System.arraycopy(this.subjectKeyIdentifier, 0, 1347 result.subjectKeyIdentifier, 0, 1348 this.subjectKeyIdentifier.length); 1349 } 1350 if (this.authorityKeyIdentifier != null) { 1351 result.authorityKeyIdentifier = 1352 new byte[this.authorityKeyIdentifier.length]; 1353 System.arraycopy(this.authorityKeyIdentifier, 0, 1354 result.authorityKeyIdentifier, 0, 1355 this.authorityKeyIdentifier.length); 1356 } 1357 if (this.subjectPublicKey != null) { 1358 result.subjectPublicKey = new byte[this.subjectPublicKey.length]; 1359 System.arraycopy(this.subjectPublicKey, 0, result.subjectPublicKey, 1360 0, this.subjectPublicKey.length); 1361 } 1362 if (this.keyUsage != null) { 1363 result.keyUsage = new boolean[this.keyUsage.length]; 1364 System.arraycopy(this.keyUsage, 0, result.keyUsage, 0, 1365 this.keyUsage.length); 1366 } 1367 result.extendedKeyUsage = (this.extendedKeyUsage == null) 1368 ? null 1369 : new HashSet<String>(this.extendedKeyUsage); 1370 if (this.subjectAltNames != null) { 1371 result.subjectAltNames = new ArrayList[9]; 1372 for (int i=0; i<9; i++) { 1373 if (this.subjectAltNames[i] != null) { 1374 result.subjectAltNames[i] = 1375 new ArrayList<GeneralName>(this.subjectAltNames[i]); 1376 } 1377 } 1378 } 1379 result.policies = (this.policies == null) ? null : new HashSet<String>(this.policies); 1380 result.pathToNames = (this.pathToNames == null) 1381 ? null 1382 : new ArrayList<GeneralName>(this.pathToNames); 1383 return result; 1384 } 1385 } 1386