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.List;
30import java.util.Set;
31import javax.security.auth.x500.X500Principal;
32import libcore.util.EmptyArray;
33import org.apache.harmony.security.asn1.ASN1OctetString;
34import org.apache.harmony.security.utils.Array;
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<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