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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 package java.security.cert;
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.Iterator;
30 import java.util.List;
32 import javax.security.auth.x500.X500Principal;
33 import org.apache.harmony.security.asn1.ASN1OctetString;
34 import org.apache.harmony.security.x509.AlgorithmIdentifier;
35 import org.apache.harmony.security.x509.CertificatePolicies;
36 import org.apache.harmony.security.x509.GeneralName;
37 import org.apache.harmony.security.x509.GeneralNames;
38 import org.apache.harmony.security.x509.NameConstraints;
39 import org.apache.harmony.security.x509.PolicyInformation;
40 import org.apache.harmony.security.x509.PrivateKeyUsagePeriod;
41 import org.apache.harmony.security.x509.SubjectPublicKeyInfo;
46 * A certificate selector ({@code CertSelector} for selecting {@code
47 * X509Certificate}s that match the specified criteria.
49 public class X509CertSelector implements CertSelector {
52 private X509Certificate certificateEquals;
53 private BigInteger serialNumber;
54 private X500Principal issuer;
55 private X500Principal subject;
56 private byte[] subjectKeyIdentifier;
57 private byte[] authorityKeyIdentifier;
58 private Date certificateValid;
59 private String subjectPublicKeyAlgID;
60 private Date privateKeyValid;
61 private byte[] subjectPublicKey;
62 private boolean[] keyUsage;
63 private Set extendedKeyUsage;
64 private boolean matchAllNames = true;
65 private int pathLen = -1;
66 private List[] subjectAltNames;
67 private NameConstraints nameConstraints;
69 private ArrayList pathToNames;
71 // needed to avoid needless encoding/decoding work
72 private PublicKey subjectPublicKeyImpl;
73 private String issuerName;
74 private byte[] issuerBytes;
77 * Creates a new {@code X509CertSelector}.
79 public X509CertSelector() {}
82 * Sets the certificate that a matching certificate must be equal to.
85 * the certificate to match, or null to not check this criteria.
87 public void setCertificate(X509Certificate certificate) {
88 certificateEquals = certificate;
92 * Returns the certificate that a matching certificate must be equal to.
94 * @return the certificate to match, or null if this criteria is not
97 public X509Certificate getCertificate() {
98 return certificateEquals;
102 * Sets the serial number that a certificate must match.
104 * @param serialNumber
105 * the serial number to match, or {@code null} to not check the
108 public void setSerialNumber(BigInteger serialNumber) {
109 this.serialNumber = serialNumber;
113 * Returns the serial number that a certificate must match.
115 * @return the serial number to match, or {@code null} if the serial number
116 * is not to be checked.
118 public BigInteger getSerialNumber() {
123 * Sets the issuer that a certificate must match.
126 * the issuer to match, or {@code null} if the issuer is not to
129 public void setIssuer(X500Principal issuer) {
130 this.issuer = issuer;
131 this.issuerName = null;
132 this.issuerBytes = null;
136 * Returns the issuer that a certificate must match.
138 * @return the issuer that a certificate must match, or {@code null} if the
139 * issuer is not to be checked.
141 public X500Principal getIssuer() {
146 * <b>Do not use</b>, use {@link #getIssuer()} or
147 * {@link #getIssuerAsBytes()} instead. Sets the issuer that a certificate
151 * the issuer in a RFC 2253 format string, or {@code null} to not
153 * @throws IOException
154 * if parsing the issuer fails.
156 public void setIssuer(String issuerName) throws IOException {
157 if (issuerName == null) {
159 this.issuerName = null;
160 this.issuerBytes = null;
164 this.issuer = new X500Principal(issuerName);
165 this.issuerName = issuerName;
166 this.issuerBytes = null;
167 } catch (IllegalArgumentException e) {
168 throw new IOException(e.getMessage());
173 * <b>Do not use</b>, use {@link #getIssuer()} or
174 * {@link #getIssuerAsBytes()} instead. Returns the issuer that a
175 * certificate must match in a RFC 2253 format string.
177 * @return the issuer in a RFC 2253 format string, or {@code null} if the
178 * issuer is not to be checked.
180 public String getIssuerAsString() {
181 if (issuer == null) {
184 if (issuerName == null) {
185 issuerName = issuer.getName();
191 * Sets the issuer that a certificate must match.
194 * the distinguished issuer name in ASN.1 DER encoded format, or
195 * {@code null} to not check the issuer.
196 * @throws IOException
197 * if decoding the issuer fail.
199 public void setIssuer(byte[] issuerDN) throws IOException {
200 if (issuerDN == null) {
205 issuer = new X500Principal(issuerDN);
206 this.issuerName = null;
207 this.issuerBytes = new byte[issuerDN.length];
208 System.arraycopy(issuerDN, 0, this.issuerBytes, 0, issuerDN.length);
209 } catch (IllegalArgumentException e) {
210 throw new IOException(e.getMessage());
215 * Returns the issuer that a certificate must match.
217 * @return the distinguished issuer name in ASN.1 DER encoded format, or
218 * {@code null} if the issuer is not to be checked.
219 * @throws IOException
220 * if encoding the issuer fails.
222 public byte[] getIssuerAsBytes() throws IOException {
223 if (issuer == null) {
226 if (issuerBytes == null) {
227 issuerBytes = issuer.getEncoded();
229 byte[] result = new byte[issuerBytes.length];
230 System.arraycopy(issuerBytes, 0, result, 0, issuerBytes.length);
235 * Set the subject that a certificate must match.
238 * the subject distinguished name or {@code null} to not check
241 public void setSubject(X500Principal subject) {
242 this.subject = subject;
246 * Returns the subject that a certificate must match.
248 * @return the subject distinguished name, or null if the subject is not to
251 public X500Principal getSubject() {
256 * <b>Do not use</b>, use {@link #setSubject(byte[])} or
257 * {@link #setSubject(X500Principal)} instead. Returns the subject that a
258 * certificate must match.
261 * the subject distinguished name in RFC 2253 format or {@code
262 * null} to not check the subject.
263 * @throws IOException
264 * if decoding the subject fails.
266 public void setSubject(String subjectDN) throws IOException {
267 if (subjectDN == null) {
272 subject = new X500Principal(subjectDN);
273 } catch (IllegalArgumentException e) {
274 throw new IOException(e.getMessage());
279 * <b>Do not use</b>, use {@link #getSubject()} or
280 * {@link #getSubjectAsBytes()} instead. Returns the subject that a
281 * certificate must match.
283 * @return the subject distinguished name in RFC 2253 format, or {@code
284 * null} if the subject is not to be checked.
286 public String getSubjectAsString() {
287 if (subject == null) {
290 return subject.getName();
294 * Sets the subject that a certificate must match.
297 * the subject distinguished name in ASN.1 DER format, or {@code
298 * null} to not check the subject.
299 * @throws IOException
300 * if decoding the subject fails.
302 public void setSubject(byte[] subjectDN) throws IOException {
303 if (subjectDN == null) {
308 subject = new X500Principal(subjectDN);
309 } catch (IllegalArgumentException e) {
310 throw new IOException(e.getMessage());
315 * Returns the subject that a certificate must match.
317 * @return the subject distinguished name in ASN.1 DER format, or {@code
318 * null} if the subject is not to be checked.
319 * @throws IOException
320 * if encoding the subject fails.
322 public byte[] getSubjectAsBytes() throws IOException {
323 if (subject == null) {
326 return subject.getEncoded();
330 * Sets the criterion for the {@literal SubjectKeyIdentifier} extension.
332 * The {@code subjectKeyIdentifier} should be a single DER encoded value.
334 * @param subjectKeyIdentifier
335 * the subject key identifier or {@code null} to disable this
338 public void setSubjectKeyIdentifier(byte[] subjectKeyIdentifier) {
339 if (subjectKeyIdentifier == null) {
340 this.subjectKeyIdentifier = null;
343 this.subjectKeyIdentifier = new byte[subjectKeyIdentifier.length];
344 System.arraycopy(subjectKeyIdentifier, 0, this.subjectKeyIdentifier, 0,
345 subjectKeyIdentifier.length);
349 * Returns the criterion for the {@literal SubjectKeyIdentifier} extension.
351 * @return the subject key identifier or {@code null} if it is not to be
354 public byte[] getSubjectKeyIdentifier() {
355 if (subjectKeyIdentifier == null) {
358 byte[] res = new byte[subjectKeyIdentifier.length];
359 System.arraycopy(subjectKeyIdentifier, 0, res, 0, res.length);
364 * Sets the criterion for the {@literal AuthorityKeyIdentifier} extension.
366 * @param authorityKeyIdentifier
367 * the authority key identifier, or {@code null} to disable this
370 public void setAuthorityKeyIdentifier(byte[] authorityKeyIdentifier) {
371 if (authorityKeyIdentifier == null) {
372 this.authorityKeyIdentifier = null;
375 this.authorityKeyIdentifier = new byte[authorityKeyIdentifier.length];
376 System.arraycopy(authorityKeyIdentifier, 0,
377 this.authorityKeyIdentifier, 0,
378 authorityKeyIdentifier.length);
382 * Returns the criterion for the {@literal AuthorityKeyIdentifier}
385 * @return the authority key identifier, or {@code null} if it is not to be
388 public byte[] getAuthorityKeyIdentifier() {
389 if (authorityKeyIdentifier == null) {
392 byte[] res = new byte[authorityKeyIdentifier.length];
393 System.arraycopy(authorityKeyIdentifier, 0, res, 0, res.length);
398 * Sets the criterion for the validity date of the certificate.
400 * The certificate must be valid at the specified date.
401 * @param certificateValid
402 * the validity date or {@code null} to not check the date.
404 public void setCertificateValid(Date certificateValid) {
405 this.certificateValid = (certificateValid == null)
407 : (Date) certificateValid.clone();
411 * Returns the criterion for the validity date of the certificate.
413 * @return the validity date or {@code null} if the date is not to be
416 public Date getCertificateValid() {
417 return (certificateValid == null)
419 : (Date) certificateValid.clone();
423 * Sets the criterion for the validity date of the private key.
425 * The private key must be valid at the specified date.
427 * @param privateKeyValid
428 * the validity date or {@code null} to not check the date.
430 public void setPrivateKeyValid(Date privateKeyValid) {
431 if (privateKeyValid == null) {
432 this.privateKeyValid = null;
435 this.privateKeyValid = (Date) privateKeyValid.clone();
439 * Returns the criterion for the validity date of the private key.
441 * The private key must be valid at the specified date.
443 * @return the validity date or {@code null} if the date is not to be
446 public Date getPrivateKeyValid() {
447 if (privateKeyValid != null) {
448 return (Date) privateKeyValid.clone();
453 private void checkOID(String oid) throws IOException {
455 int end = oid.indexOf('.', beg);
457 int comp = Integer.parseInt(oid.substring(beg, end));
459 if ((comp < 0) || (comp > 2)) {
460 throw new IOException("Bad OID: " + oid);
462 end = oid.indexOf('.', beg);
463 comp = Integer.parseInt(oid.substring(beg, end));
464 if ((comp < 0) || (comp > 39)) {
465 throw new IOException("Bad OID: " + oid);
467 } catch (IndexOutOfBoundsException e) {
468 throw new IOException("Bad OID: " + oid);
469 } catch (NumberFormatException e) {
470 throw new IOException("Bad OID: " + oid);
475 * Sets the criterion for the subject public key signature algorithm.
477 * The certificate must contain a subject public key with the algorithm
481 * the OID (object identifier) of the signature algorithm or
482 * {@code null} to not check the OID.
483 * @throws IOException
484 * if the specified object identifier is invalid.
486 public void setSubjectPublicKeyAlgID(String oid) throws IOException {
488 subjectPublicKeyAlgID = null;
492 subjectPublicKeyAlgID = oid;
496 * Returns the criterion for the subject public key signature algorithm.
498 * @return the OID (object identifier) or the signature algorithm or {@code
499 * null} if it's not to be checked.
501 public String getSubjectPublicKeyAlgID() {
502 return subjectPublicKeyAlgID;
506 * Sets the criterion for the subject public key.
509 * the subject public key or {@code null} to not check the key.
511 public void setSubjectPublicKey(PublicKey key) {
512 subjectPublicKey = (key == null) ? null : key.getEncoded();
513 subjectPublicKeyImpl = key;
517 * Sets the criterion for the subject public key.
520 * the subject public key in ASN.1 DER encoded format or {@code null} to
522 * @throws IOException
523 * if decoding the the public key fails.
525 public void setSubjectPublicKey(byte[] key) throws IOException {
527 subjectPublicKey = null;
528 subjectPublicKeyImpl = null;
531 subjectPublicKey = new byte[key.length];
532 System.arraycopy(key, 0, subjectPublicKey, 0, key.length);
533 subjectPublicKeyImpl =
534 ((SubjectPublicKeyInfo) SubjectPublicKeyInfo.ASN1.decode(key))
539 * Returns the criterion for the subject public key.
541 * @return the subject public key or {@code null} if the key is not to be
544 public PublicKey getSubjectPublicKey() {
545 return subjectPublicKeyImpl;
549 * Sets the criterion for the {@literal KeyUsage} extension.
552 * the boolean array in the format as returned by
553 * {@link X509Certificate#getKeyUsage()}, or {@code null} to not
554 * check the key usage.
556 public void setKeyUsage(boolean[] keyUsage) {
557 if (keyUsage == null) {
558 this.keyUsage = null;
561 this.keyUsage = new boolean[keyUsage.length];
562 System.arraycopy(keyUsage, 0, this.keyUsage, 0, keyUsage.length);
566 * Returns the criterion for the {@literal KeyUsage} extension.
568 * @return the boolean array in the format as returned by
569 * {@link X509Certificate#getKeyUsage()}, or {@code null} if the key
570 * usage is not to be checked.
572 public boolean[] getKeyUsage() {
573 if (keyUsage == null) {
576 boolean[] result = new boolean[keyUsage.length];
577 System.arraycopy(keyUsage, 0, result, 0, keyUsage.length);
582 * Sets the criterion for the {@literal ExtendedKeyUsage} extension.
585 * the set of key usage OIDs, or {@code null} to not check it.
586 * @throws IOException
587 * if one of the OIDs is invalid.
589 public void setExtendedKeyUsage(Set<String> keyUsage)
591 extendedKeyUsage = null;
592 if ((keyUsage == null) || (keyUsage.size() == 0)) {
595 HashSet key_u = new HashSet();
596 Iterator it = keyUsage.iterator();
597 while (it.hasNext()) {
598 String usage = (String) it.next();
602 extendedKeyUsage = Collections.unmodifiableSet(key_u);
606 * Returns the criterion for the {@literal ExtendedKeyUsage} extension.
608 * @return the set of key usage OIDs, or {@code null} if it's not to be
611 public Set<String> getExtendedKeyUsage() {
612 return extendedKeyUsage;
616 * Sets the flag for the matching behavior for subject alternative names.
618 * The flag indicates whether a certificate must contain all or at least one
619 * of the subject alternative names specified by {@link
620 * #setSubjectAlternativeNames} or {@link #addSubjectAlternativeName}.
622 * @param matchAllNames
623 * {@code true} if a certificate must contain all of the
624 * specified subject alternative names, otherwise {@code false}.
626 public void setMatchAllSubjectAltNames(boolean matchAllNames) {
627 this.matchAllNames = matchAllNames;
631 * Returns the flag for the matching behavior for subject alternative names.
633 * The flag indicates whether a certificate must contain all or at least one
634 * of the subject alternative names specified by {@link
635 * #setSubjectAlternativeNames} or {@link #addSubjectAlternativeName}.
637 * @return {@code true} if a certificate must contain all of the specified
638 * subject alternative names, otherwise {@code false}.
640 public boolean getMatchAllSubjectAltNames() {
641 return matchAllNames;
645 * Sets the criterion for subject alternative names.
647 * the certificate must contain all or at least one of the specified subject
648 * alternative names. The behavior is specified by
649 * {@link #getMatchAllSubjectAltNames}.
651 * The specified parameter {@code names} is a collection with an entry for
652 * each name to be included in the criterion. The name is specified as a
653 * {@code List}, the first entry must be an {@code Integer} specifying the
654 * name type (0-8), the second entry must be a {@code String} or a byte
655 * array specifying the name (in string or ASN.1 DER encoded form)
658 * the names collection or {@code null} to not perform this check.
659 * @throws IOException
660 * if the decoding of a name fails.
662 public void setSubjectAlternativeNames(Collection<List<?>> names)
664 subjectAltNames = null;
665 if ((names == null) || (names.size() == 0)) {
668 Iterator it = names.iterator();
669 while (it.hasNext()) {
670 List name = (List) it.next();
671 int tag = ((Integer) name.get(0)).intValue();
672 Object value = name.get(1);
673 if (value instanceof String) {
674 addSubjectAlternativeName(tag, (String) value);
675 } else if (value instanceof byte[]) {
676 addSubjectAlternativeName(tag, (byte[]) value);
678 throw new IOException("name neither a String nor a byte[]");
684 * Adds a subject alternative name to the respective criterion.
687 * the type of the name
689 * the name in string format.
690 * @throws IOException
691 * if parsing the name fails.
693 public void addSubjectAlternativeName(int tag, String name)
695 GeneralName alt_name = new GeneralName(tag, name);
696 // create only if there was not any errors
697 if (subjectAltNames == null) {
698 subjectAltNames = new ArrayList[9];
700 if (subjectAltNames[tag] == null) {
701 subjectAltNames[tag] = new ArrayList();
703 subjectAltNames[tag].add(alt_name);
707 * Adds a subject alternative name to the respective criterion.
710 * the type of the name.
712 * the name in ASN.1 DER encoded form.
713 * @throws IOException
714 * if the decoding of the name fails.
716 public void addSubjectAlternativeName(int tag, byte[] name)
718 GeneralName alt_name = new GeneralName(tag, name);
719 // create only if there was not any errors
720 if (subjectAltNames == null) {
721 subjectAltNames = new ArrayList[9];
723 if (subjectAltNames[tag] == null) {
724 subjectAltNames[tag] = new ArrayList();
726 subjectAltNames[tag].add(alt_name);
730 * Returns the criterion for subject alternative names.
732 * the certificate must contain all or at least one of the specified subject
733 * alternative names. The behavior is specified by
734 * {@link #getMatchAllSubjectAltNames}.
736 * The subject alternative names is a collection with an entry for each name
737 * included in the criterion. The name is specified as a {@code List}, the
738 * first entry is an {@code Integer} specifying the name type (0-8), the
739 * second entry is byte array specifying the name in ASN.1 DER encoded form)
741 * @return the names collection or {@code null} if none specified.
743 public Collection<List<?>> getSubjectAlternativeNames() {
744 if (subjectAltNames == null) {
747 ArrayList result = new ArrayList();
748 for (int tag=0; tag<9; tag++) {
749 if (subjectAltNames[tag] != null) {
750 for (int name=0; name<subjectAltNames[tag].size(); name++) {
751 Object neim = subjectAltNames[tag].get(name);
752 if (neim instanceof byte[]) {
753 byte[] arr_neim = (byte[]) neim;
754 neim = new byte[arr_neim.length];
755 System.arraycopy(arr_neim, 0, neim, 0, arr_neim.length);
757 List list = new ArrayList(2);
758 list.add(Integer.valueOf(tag)); // android-changed
768 * Sets the criterion for the name constraints.
770 * The certificate must constraint subject and subject alternative names
771 * that match the specified name constraints.
773 * The name constraints in ASN.1:
776 * NameConstraints ::= SEQUENCE {
777 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
778 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
780 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
782 * GeneralSubtree ::= SEQUENCE {
784 * minimum [0] BaseDistance DEFAULT 0,
785 * maximum [1] BaseDistance OPTIONAL }
787 * BaseDistance ::= INTEGER (0..MAX)
789 * GeneralName ::= CHOICE {
790 * otherName [0] OtherName,
791 * rfc822Name [1] IA5String,
792 * dNSName [2] IA5String,
793 * x400Address [3] ORAddress,
794 * directoryName [4] Name,
795 * ediPartyName [5] EDIPartyName,
796 * uniformResourceIdentifier [6] IA5String,
797 * iPAddress [7] OCTET STRING,
798 * registeredID [8] OBJECT IDENTIFIER}
803 * the name constraints in ASN.1 DER encoded format, or null to
804 * not check any constraints.
805 * @throws IOException
806 * if decoding the name constraints fail.
808 public void setNameConstraints(byte[] bytes) throws IOException {
809 this.nameConstraints = (bytes == null)
811 : (NameConstraints) NameConstraints.ASN1.decode(bytes);
815 * Returns the criterion for the name constraints.
817 * @return the name constraints or {@code null} if none specified.
818 * @see #setNameConstraints
820 public byte[] getNameConstraints() {
821 return (nameConstraints == null)
823 : nameConstraints.getEncoded();
827 * Sets the criterion for the basic constraints extension.
829 * A value greater than or equal to zero indicates that a certificate must
830 * include a basic constraints extension with a path length of a least that
831 * value. A value of {@code -2} indicates that only end-entity certificates
832 * are accepted. A value of {@code -1} indicates that no check is done.
835 * the value specifying the criterion.
836 * @throws IllegalArgumentException
837 * if {@code pathLen} is less than {@code -2}.
839 public void setBasicConstraints(int pathLen) {
841 throw new IllegalArgumentException("pathLen < -2");
843 this.pathLen = pathLen;
847 * Returns the criterion for the basic constraints extension.
849 * A value greater than or equal to zero indicates that a certificate must
850 * include a basic constraints extension with a path length of a least that
851 * value. A value of {@code -2} indicates that only end-entity certificates
852 * are accepted. A value of {@code -1} indicates that no check is done.
854 * @return the value of the criterion.
856 public int getBasicConstraints() {
861 * Sets the criterion for the policy constraint.
863 * The certificate must have at least one of the specified certificate
864 * policy extensions. For an empty set the certificate must have at least
865 * some policies in its policy extension.
868 * the certificate policy OIDs, an empty set, or {@code null} to
869 * not perform this check.
870 * @throws IOException
871 * if parsing the specified OIDs fails.
873 public void setPolicy(Set<String> policies) throws IOException {
874 if (policies == null) {
875 this.policies = null;
878 HashSet pols = new HashSet(policies.size());
879 Iterator it = policies.iterator();
880 while (it.hasNext()) {
881 String certPolicyId = (String) it.next();
882 checkOID(certPolicyId);
883 pols.add(certPolicyId);
885 this.policies = Collections.unmodifiableSet(pols);
889 * Returns the criterion for the policy constraint.
891 * The certificate must have at least one of the certificate policy
892 * extensions. For an empty set the certificate must have at least some
893 * policies in its policy extension.
895 * @return the certificate policy OIDs, an empty set, or {@code null} if not
898 public Set<String> getPolicy() {
903 * Sets the criterion for the pathToNames constraint.
905 * This allows to specify the complete set of names, a certificate's name
906 * constraints must permit.
908 * The specified parameter {@code names} is a collection with an entry for
909 * each name to be included in the criterion. The name is specified as a
910 * {@code List}, the first entry must be an {@code Integer} specifying the
911 * name type (0-8), the second entry must be a {@code String} or a byte
912 * array specifying the name (in string or ASN.1 DER encoded form)
915 * the names collection or {@code null} to not perform this
917 * @throws IOException
920 public void setPathToNames(Collection<List<?>> names)
923 if ((names == null) || (names.size() == 0)) {
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);
936 throw new IOException("name neither a String nor a byte[]");
942 * Adds a {@literal "pathToName"} to the respective criterion.
945 * the type of the name.
947 * the name in string format.
948 * @throws IOException
950 * @see #setPathToNames
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();
958 pathToNames.add(path_name);
962 * Adds a {@literal "pathToName"} to the respective criterion.
965 * the type of the name
967 * the name in ASN.1 DER encoded form.
968 * @throws IOException
970 * @see #setPathToNames
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();
978 pathToNames.add(path_name);
982 * Returns the criterion for the pathToNames constraint.
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.
989 * @return the pathToNames constraint or {@code null} if none specified.
991 public Collection<List<?>> getPathToNames() {
992 if (pathToNames == null) {
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());
1005 * Returns a string representation of this {@code X509CertSelector}
1008 * @return a string representation of this {@code X509CertSelector}
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).
1016 StringBuilder result = new StringBuilder();
1017 result.append("X509CertSelector: \n[");
1018 if (this.certificateEquals != null) {
1019 result.append("\n certificateEquals: " + certificateEquals);
1021 if (this.serialNumber != null) {
1022 //FIXME: needs DRL's BigInteger.toString implementation
1023 //result.append("\n serialNumber: " + serialNumber);
1025 if (this.issuer != null) {
1026 result.append("\n issuer: " + issuer);
1028 if (this.subject != null) {
1029 result.append("\n subject: " + subject);
1031 if (this.subjectKeyIdentifier != null) {
1032 result.append("\n subjectKeyIdentifier: "
1033 + getBytesAsString(subjectKeyIdentifier));
1035 if (this.authorityKeyIdentifier != null) {
1036 result.append("\n authorityKeyIdentifier: "
1037 + getBytesAsString(authorityKeyIdentifier));
1039 if (this.certificateValid != null) {
1040 result.append("\n certificateValid: " + certificateValid);
1042 if (this.subjectPublicKeyAlgID != null) {
1043 result.append("\n subjectPublicKeyAlgID: "
1044 + subjectPublicKeyAlgID);
1046 if (this.privateKeyValid != null) {
1047 result.append("\n privateKeyValid: " + privateKeyValid);
1049 if (this.subjectPublicKey != null) {
1050 result.append("\n subjectPublicKey: "
1051 + getBytesAsString(subjectPublicKey));
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"
1060 for (int i=0; i<9; i++) {
1062 result.append("\n " + kuNames[i]);
1065 result.append("\n ]");
1067 if (this.extendedKeyUsage != null) {
1068 result.append("\n extendedKeyUsage: "
1069 + extendedKeyUsage.toString());
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++) {
1081 + ((GeneralName)names.get(j)).toString());
1085 result.append("\n ]");
1087 if (this.nameConstraints != null) {
1089 if (this.policies != null) {
1090 result.append("\n policies: " + policies.toString());
1092 if (this.pathToNames != null) {
1093 result.append("\n pathToNames: \n [");
1094 int size = pathToNames.size();
1095 for (int i = 0; i < size; i++) {
1097 + ((GeneralName)pathToNames.get(i)).toString());
1100 result.append("\n]");
1101 return result.toString();
1104 private String getBytesAsString(byte[] data) {
1106 for (int i=0; i<data.length; i++) {
1107 String tail = Integer.toHexString(0x00ff & data[i]);
1108 if (tail.length() == 1) {
1111 result += tail + " ";
1116 private byte[] getExtensionValue(X509Certificate cert, String oid) {
1118 byte[] bytes = cert.getExtensionValue(oid);
1119 if (bytes == null) {
1122 return (byte[]) ASN1OctetString.getInstance().decode(bytes);
1123 } catch (IOException e) {
1129 * Returns whether the specified certificate matches all the criteria
1130 * collected in this instance.
1132 * @param certificate
1133 * the certificate to check.
1134 * @return {@code true} if the certificate matches all the criteria,
1135 * otherwise {@code false}.
1137 public boolean match(Certificate certificate) {
1138 if (! (certificate instanceof X509Certificate)) {
1142 X509Certificate cert = (X509Certificate) certificate;
1143 if ((certificateEquals != null) &&
1144 !certificateEquals.equals(cert)) {
1147 if ((serialNumber != null) &&
1148 !serialNumber.equals(cert.getSerialNumber())) {
1151 if ((issuer != null) &&
1152 !issuer.equals(cert.getIssuerX500Principal())) {
1155 if ((subject != null) &&
1156 !subject.equals(cert.getSubjectX500Principal())) {
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"))) {
1166 if ((authorityKeyIdentifier != null) &&
1167 !Arrays.equals(authorityKeyIdentifier,
1168 getExtensionValue(cert, "2.5.29.35"))) {
1171 if (certificateValid != null) {
1173 cert.checkValidity(certificateValid);
1174 } catch(CertificateExpiredException e) {
1176 } catch(CertificateNotYetValidException e) {
1180 if (privateKeyValid != null) {
1182 byte[] bytes = getExtensionValue(cert, "2.5.29.16");
1183 if (bytes == null) {
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)) {
1193 if ((notBefore != null)
1194 && notBefore.compareTo(privateKeyValid) > 0) {
1197 if ((notAfter != null)
1198 && notAfter.compareTo(privateKeyValid) < 0) {
1201 } catch (IOException e) {
1205 if (subjectPublicKeyAlgID != null) {
1207 byte[] encoding = cert.getPublicKey().getEncoded();
1208 AlgorithmIdentifier ai = ((SubjectPublicKeyInfo)
1209 SubjectPublicKeyInfo.ASN1.decode(encoding))
1210 .getAlgorithmIdentifier();
1211 if (!subjectPublicKeyAlgID.equals(ai.getAlgorithm())) {
1214 } catch (IOException e) {
1215 e.printStackTrace();
1219 if (subjectPublicKey != null) {
1220 if (!Arrays.equals(subjectPublicKey,
1221 cert.getPublicKey().getEncoded())) {
1225 if (keyUsage != null) {
1226 boolean[] ku = cert.getKeyUsage();
1229 int min_length = (ku.length < keyUsage.length) ? ku.length
1231 for (; i < min_length; i++) {
1232 if (keyUsage[i] && !ku[i]) {
1233 // the specified keyUsage allows,
1234 // but certificate does not.
1238 for (; i<keyUsage.length; i++) {
1245 if (extendedKeyUsage != null) {
1247 List keyUsage = cert.getExtendedKeyUsage();
1248 if (keyUsage != null) {
1249 if (!keyUsage.containsAll(extendedKeyUsage)) {
1253 } catch (CertificateParsingException e) {
1257 if (pathLen != -1) {
1258 int p_len = cert.getBasicConstraints();
1259 if ((pathLen < 0) && (p_len >= 0)) {
1260 // need end-entity but got CA
1263 if ((pathLen > 0) && (pathLen > p_len)) {
1264 // allowed _pathLen is small
1268 if (subjectAltNames != null) {
1271 byte[] bytes = getExtensionValue(cert, "2.5.29.17");
1272 if (bytes == null) {
1275 List sans = ((GeneralNames) GeneralNames.ASN1.decode(bytes))
1277 if ((sans == null) || (sans.size() == 0)) {
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)
1285 : new boolean[subjectAltNames[i].size()];
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))
1294 if (!matchAllNames) {
1301 if (!matchAllNames) {
1302 // there was not any match
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]) {
1313 } catch (IOException e) {
1314 e.printStackTrace();
1318 if (nameConstraints != null) {
1319 if (!nameConstraints.isAcceptable(cert)) {
1323 if (policies != null) {
1324 byte[] bytes = getExtensionValue(cert, "2.5.29.32");
1325 if (bytes == null) {
1328 if (policies.size() == 0) {
1329 // if certificate has such extension than it has at least
1330 // one policy in it.
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())) {
1346 } catch (IOException e) {
1347 // the extension is invalid
1351 if (pathToNames != null) {
1352 byte[] bytes = getExtensionValue(cert, "2.5.29.30");
1353 if (bytes != null) {
1354 NameConstraints nameConstraints;
1357 (NameConstraints) NameConstraints.ASN1.decode(bytes);
1358 } catch (IOException e) {
1359 // the extension is invalid;
1362 if (!nameConstraints.isAcceptable(pathToNames)) {
1371 * Clones this {@code X509CertSelector} instance.
1373 * @return the cloned instance.
1375 public Object clone() {
1376 X509CertSelector result;
1379 result = (X509CertSelector) super.clone();
1380 } catch (CloneNotSupportedException e) {
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);
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);
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);
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);
1408 result.extendedKeyUsage = (this.extendedKeyUsage == 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]);
1420 result.policies = (this.policies == null)
1422 : new HashSet(this.policies);
1423 result.pathToNames = (this.pathToNames == null)
1425 : new ArrayList(this.pathToNames);