X509CertSelector.java revision 693eacca9fa67ad79d1b35dbaad61c5ac1ac457c
1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.security.cert;
19
20import java.io.IOException;
21import java.math.BigInteger;
22import java.security.PublicKey;
23import java.util.ArrayList;
24import java.util.Arrays;
25import java.util.Collection;
26import java.util.Collections;
27import java.util.Date;
28import java.util.HashSet;
29import java.util.Iterator;
30import java.util.List;
31import java.util.Set;
32import javax.security.auth.x500.X500Principal;
33import libcore.base.EmptyArray;
34import org.apache.harmony.security.asn1.ASN1OctetString;
35import org.apache.harmony.security.x509.AlgorithmIdentifier;
36import org.apache.harmony.security.x509.CertificatePolicies;
37import org.apache.harmony.security.x509.GeneralName;
38import org.apache.harmony.security.x509.GeneralNames;
39import org.apache.harmony.security.x509.NameConstraints;
40import org.apache.harmony.security.x509.PolicyInformation;
41import org.apache.harmony.security.x509.PrivateKeyUsagePeriod;
42import org.apache.harmony.security.x509.SubjectPublicKeyInfo;
43
44
45
46/**
47 * A certificate selector ({@code CertSelector} for selecting {@code
48 * X509Certificate}s that match the specified criteria.
49 */
50public class X509CertSelector implements CertSelector {
51
52    // match criteria
53    private X509Certificate certificateEquals;
54    private BigInteger serialNumber;
55    private X500Principal issuer;
56    private X500Principal subject;
57    private byte[] subjectKeyIdentifier;
58    private byte[] authorityKeyIdentifier;
59    private Date certificateValid;
60    private String subjectPublicKeyAlgID;
61    private Date privateKeyValid;
62    private byte[] subjectPublicKey;
63    private boolean[] keyUsage;
64    private Set extendedKeyUsage;
65    private boolean matchAllNames = true;
66    private int pathLen = -1;
67    private List[] subjectAltNames;
68    private NameConstraints nameConstraints;
69    private Set policies;
70    private ArrayList pathToNames;
71
72    // needed to avoid needless encoding/decoding work
73    private PublicKey subjectPublicKeyImpl;
74    private String issuerName;
75    private byte[] issuerBytes;
76
77    /**
78     * Creates a new {@code X509CertSelector}.
79     */
80    public X509CertSelector() {}
81
82    /**
83     * Sets the certificate that a matching certificate must be equal to.
84     *
85     * @param certificate
86     *            the certificate to match, or null to not check this criteria.
87     */
88    public void setCertificate(X509Certificate certificate) {
89        certificateEquals = certificate;
90    }
91
92    /**
93     * Returns the certificate that a matching certificate must be equal to.
94     *
95     * @return the certificate to match, or null if this criteria is not
96     *         checked.
97     */
98    public X509Certificate getCertificate() {
99        return certificateEquals;
100    }
101
102    /**
103     * Sets the serial number that a certificate must match.
104     *
105     * @param serialNumber
106     *            the serial number to match, or {@code null} to not check the
107     *            serial number.
108     */
109    public void setSerialNumber(BigInteger serialNumber) {
110        this.serialNumber = serialNumber;
111    }
112
113    /**
114     * Returns the serial number that a certificate must match.
115     *
116     * @return the serial number to match, or {@code null} if the serial number
117     *         is not to be checked.
118     */
119    public BigInteger getSerialNumber() {
120        return serialNumber;
121    }
122
123    /**
124     * Sets the issuer that a certificate must match.
125     *
126     * @param issuer
127     *            the issuer to match, or {@code null} if the issuer is not to
128     *            be checked.
129     */
130    public void setIssuer(X500Principal issuer) {
131        this.issuer = issuer;
132        this.issuerName = null;
133        this.issuerBytes = null;
134    }
135
136    /**
137     * Returns the issuer that a certificate must match.
138     *
139     * @return the issuer that a certificate must match, or {@code null} if the
140     *         issuer is not to be checked.
141     */
142    public X500Principal getIssuer() {
143        return issuer;
144    }
145
146    /**
147     * <b>Do not use</b>, use {@link #getIssuer()} or
148     * {@link #getIssuerAsBytes()} instead. Sets the issuer that a certificate
149     * must match.
150     *
151     * @param issuerName
152     *            the issuer in a RFC 2253 format string, or {@code null} to not
153     *            check the issuer.
154     * @throws IOException
155     *             if parsing the issuer fails.
156     */
157    public void setIssuer(String issuerName) throws IOException {
158        if (issuerName == null) {
159            this.issuer = null;
160            this.issuerName = null;
161            this.issuerBytes = null;
162            return;
163        }
164        try {
165            this.issuer = new X500Principal(issuerName);
166            this.issuerName = issuerName;
167            this.issuerBytes = null;
168        } catch (IllegalArgumentException e) {
169            throw new IOException(e.getMessage());
170        }
171    }
172
173    /**
174     * <b>Do not use</b>, use {@link #getIssuer()} or
175     * {@link #getIssuerAsBytes()} instead. Returns the issuer that a
176     * certificate must match in a RFC 2253 format string.
177     *
178     * @return the issuer in a RFC 2253 format string, or {@code null} if the
179     *         issuer is not to be checked.
180     */
181    public String getIssuerAsString() {
182        if (issuer == null) {
183            return null;
184        }
185        if (issuerName == null) {
186            issuerName = issuer.getName();
187        }
188        return issuerName;
189    }
190
191    /**
192     * Sets the issuer that a certificate must match.
193     *
194     * @param issuerDN
195     *            the distinguished issuer name in ASN.1 DER encoded format, or
196     *            {@code null} to not check the issuer.
197     * @throws IOException
198     *             if decoding the issuer fail.
199     */
200    public void setIssuer(byte[] issuerDN) throws IOException {
201        if (issuerDN == null) {
202            issuer = null;
203            return;
204        }
205        try {
206            issuer = new X500Principal(issuerDN);
207            this.issuerName = null;
208            this.issuerBytes = new byte[issuerDN.length];
209            System.arraycopy(issuerDN, 0, this.issuerBytes, 0, issuerDN.length);
210        } catch (IllegalArgumentException e) {
211            throw new IOException(e.getMessage());
212        }
213    }
214
215    /**
216     * Returns the issuer that a certificate must match.
217     *
218     * @return the distinguished issuer name in ASN.1 DER encoded format, or
219     *         {@code null} if the issuer is not to be checked.
220     * @throws IOException
221     *             if encoding the issuer fails.
222     */
223    public byte[] getIssuerAsBytes() throws IOException {
224        if (issuer == null) {
225            return null;
226        }
227        if (issuerBytes == null) {
228            issuerBytes = issuer.getEncoded();
229        }
230        byte[] result = new byte[issuerBytes.length];
231        System.arraycopy(issuerBytes, 0, result, 0, issuerBytes.length);
232        return result;
233    }
234
235    /**
236     * Set the subject that a certificate must match.
237     *
238     * @param subject
239     *            the subject distinguished name or {@code null} to not check
240     *            the subject.
241     */
242    public void setSubject(X500Principal subject) {
243        this.subject = subject;
244    }
245
246    /**
247     * Returns the subject that a certificate must match.
248     *
249     * @return the subject distinguished name, or null if the subject is not to
250     *         be checked.
251     */
252    public X500Principal getSubject() {
253        return subject;
254    }
255
256    /**
257     * <b>Do not use</b>, use {@link #setSubject(byte[])} or
258     * {@link #setSubject(X500Principal)} instead. Returns the subject that a
259     * certificate must match.
260     *
261     * @param subjectDN
262     *            the subject distinguished name in RFC 2253 format or {@code
263     *            null} to not check the subject.
264     * @throws IOException
265     *             if decoding the subject fails.
266     */
267    public void setSubject(String subjectDN) throws IOException {
268        if (subjectDN == null) {
269            subject = null;
270            return;
271        }
272        try {
273            subject = new X500Principal(subjectDN);
274        } catch (IllegalArgumentException e) {
275            throw new IOException(e.getMessage());
276        }
277    }
278
279    /**
280     * <b>Do not use</b>, use {@link #getSubject()} or
281     * {@link #getSubjectAsBytes()} instead. Returns the subject that a
282     * certificate must match.
283     *
284     * @return the subject distinguished name in RFC 2253 format, or {@code
285     *         null} if the subject is not to be checked.
286     */
287    public String getSubjectAsString() {
288        if (subject == null) {
289            return null;
290        }
291        return subject.getName();
292    }
293
294    /**
295     * Sets the subject that a certificate must match.
296     *
297     * @param subjectDN
298     *            the subject distinguished name in ASN.1 DER format, or {@code
299     *            null} to not check the subject.
300     * @throws IOException
301     *             if decoding the subject fails.
302     */
303    public void setSubject(byte[] subjectDN) throws IOException {
304        if (subjectDN == null) {
305            subject = null;
306            return;
307        }
308        try {
309            subject = new X500Principal(subjectDN);
310        } catch (IllegalArgumentException e) {
311            throw new IOException(e.getMessage());
312        }
313    }
314
315    /**
316     * Returns the subject that a certificate must match.
317     *
318     * @return the subject distinguished name in ASN.1 DER format, or {@code
319     *         null} if the subject is not to be checked.
320     * @throws IOException
321     *             if encoding the subject fails.
322     */
323    public byte[] getSubjectAsBytes() throws IOException {
324        if (subject == null) {
325            return null;
326        }
327        return subject.getEncoded();
328    }
329
330    /**
331     * Sets the criterion for the {@literal SubjectKeyIdentifier} extension.
332     * <p>
333     * The {@code subjectKeyIdentifier} should be a single DER encoded value.
334     *
335     * @param subjectKeyIdentifier
336     *            the subject key identifier or {@code null} to disable this
337     *            check.
338     */
339    public void setSubjectKeyIdentifier(byte[] subjectKeyIdentifier) {
340        if (subjectKeyIdentifier == null) {
341            this.subjectKeyIdentifier = null;
342            return;
343        }
344        this.subjectKeyIdentifier = new byte[subjectKeyIdentifier.length];
345        System.arraycopy(subjectKeyIdentifier, 0, this.subjectKeyIdentifier, 0,
346                         subjectKeyIdentifier.length);
347    }
348
349    /**
350     * Returns the criterion for the {@literal SubjectKeyIdentifier} extension.
351     *
352     * @return the subject key identifier or {@code null} if it is not to be
353     *         checked.
354     */
355    public byte[] getSubjectKeyIdentifier() {
356        if (subjectKeyIdentifier == null) {
357            return null;
358        }
359        byte[] res = new byte[subjectKeyIdentifier.length];
360        System.arraycopy(subjectKeyIdentifier, 0, res, 0, res.length);
361        return res;
362    }
363
364    /**
365     * Sets the criterion for the {@literal AuthorityKeyIdentifier} extension.
366     *
367     * @param authorityKeyIdentifier
368     *            the authority key identifier, or {@code null} to disable this
369     *            check.
370     */
371    public void setAuthorityKeyIdentifier(byte[] authorityKeyIdentifier) {
372        if (authorityKeyIdentifier == null) {
373            this.authorityKeyIdentifier = null;
374            return;
375        }
376        this.authorityKeyIdentifier = new byte[authorityKeyIdentifier.length];
377        System.arraycopy(authorityKeyIdentifier, 0,
378                         this.authorityKeyIdentifier, 0,
379                         authorityKeyIdentifier.length);
380    }
381
382    /**
383     * Returns the criterion for the {@literal AuthorityKeyIdentifier}
384     * extension.
385     *
386     * @return the authority key identifier, or {@code null} if it is not to be
387     *         checked.
388     */
389    public byte[] getAuthorityKeyIdentifier() {
390        if (authorityKeyIdentifier == null) {
391            return null;
392        }
393        byte[] res = new byte[authorityKeyIdentifier.length];
394        System.arraycopy(authorityKeyIdentifier, 0, res, 0, res.length);
395        return res;
396    }
397
398    /**
399     * Sets the criterion for the validity date of the certificate.
400     * <p>
401     * The certificate must be valid at the specified date.
402     * @param certificateValid
403     *            the validity date or {@code null} to not check the date.
404     */
405    public void setCertificateValid(Date certificateValid) {
406        this.certificateValid = (certificateValid == null)
407                                ? null
408                                : (Date) certificateValid.clone();
409    }
410
411    /**
412     * Returns the criterion for the validity date of the certificate.
413     *
414     * @return the validity date or {@code null} if the date is not to be
415     *         checked.
416     */
417    public Date getCertificateValid() {
418        return (certificateValid == null)
419                                ? null
420                                : (Date) certificateValid.clone();
421    }
422
423    /**
424     * Sets the criterion for the validity date of the private key.
425     * <p>
426     * The private key must be valid at the specified date.
427     *
428     * @param privateKeyValid
429     *            the validity date or {@code null} to not check the date.
430     */
431    public void setPrivateKeyValid(Date privateKeyValid) {
432        if (privateKeyValid == null) {
433            this.privateKeyValid = null;
434            return;
435        }
436        this.privateKeyValid = (Date) privateKeyValid.clone();
437    }
438
439    /**
440     * Returns the criterion for the validity date of the private key.
441     * <p>
442     * The private key must be valid at the specified date.
443     *
444     * @return the validity date or {@code null} if the date is not to be
445     *         checked.
446     */
447    public Date getPrivateKeyValid() {
448        if (privateKeyValid != null) {
449            return (Date) privateKeyValid.clone();
450        }
451        return null;
452    }
453
454    private void checkOID(String oid) throws IOException {
455        int beg = 0;
456        int end = oid.indexOf('.', beg);
457        try {
458            int comp = Integer.parseInt(oid.substring(beg, end));
459            beg = end + 1;
460            if ((comp < 0) || (comp > 2)) {
461                throw new IOException("Bad OID: " + oid);
462            }
463            end = oid.indexOf('.', beg);
464            comp = Integer.parseInt(oid.substring(beg, end));
465            if ((comp < 0) || (comp > 39)) {
466                throw new IOException("Bad OID: " + oid);
467            }
468        } catch (IndexOutOfBoundsException e) {
469            throw new IOException("Bad OID: " + oid);
470        } catch (NumberFormatException e) {
471            throw new IOException("Bad OID: " + oid);
472        }
473    }
474
475    /**
476     * Sets the criterion for the subject public key signature algorithm.
477     * <p>
478     * The certificate must contain a subject public key with the algorithm
479     * specified.
480     *
481     * @param oid
482     *            the OID (object identifier) of the signature algorithm or
483     *            {@code null} to not check the OID.
484     * @throws IOException
485     *             if the specified object identifier is invalid.
486     */
487    public void setSubjectPublicKeyAlgID(String oid) throws IOException {
488        if (oid == null) {
489            subjectPublicKeyAlgID = null;
490            return;
491        }
492        checkOID(oid);
493        subjectPublicKeyAlgID = oid;
494    }
495
496    /**
497     * Returns the criterion for the subject public key signature algorithm.
498     *
499     * @return the OID (object identifier) or the signature algorithm or {@code
500     *         null} if it's not to be checked.
501     */
502    public String getSubjectPublicKeyAlgID() {
503        return subjectPublicKeyAlgID;
504    }
505
506    /**
507     * Sets the criterion for the subject public key.
508     *
509     * @param key
510     *            the subject public key or {@code null} to not check the key.
511     */
512    public void setSubjectPublicKey(PublicKey key) {
513        subjectPublicKey = (key == null) ? null : key.getEncoded();
514        subjectPublicKeyImpl = key;
515    }
516
517    /**
518     * Sets the criterion for the subject public key.
519     *
520     * @param key
521     *            the subject public key in ASN.1 DER encoded format or {@code null} to
522     *            not check the key.
523     * @throws IOException
524     *             if decoding the the public key fails.
525     */
526    public void setSubjectPublicKey(byte[] key) throws IOException {
527        if (key == null) {
528            subjectPublicKey = null;
529            subjectPublicKeyImpl = null;
530            return;
531        }
532        subjectPublicKey = new byte[key.length];
533        System.arraycopy(key, 0, subjectPublicKey, 0, key.length);
534        subjectPublicKeyImpl =
535            ((SubjectPublicKeyInfo) SubjectPublicKeyInfo.ASN1.decode(key))
536            .getPublicKey();
537    }
538
539    /**
540     * Returns the criterion for the subject public key.
541     *
542     * @return the subject public key or {@code null} if the key is not to be
543     *         checked.
544     */
545    public PublicKey getSubjectPublicKey() {
546        return subjectPublicKeyImpl;
547    }
548
549    /**
550     * Sets the criterion for the {@literal KeyUsage} extension.
551     *
552     * @param keyUsage
553     *            the boolean array in the format as returned by
554     *            {@link X509Certificate#getKeyUsage()}, or {@code null} to not
555     *            check the key usage.
556     */
557    public void setKeyUsage(boolean[] keyUsage) {
558        if (keyUsage == null) {
559            this.keyUsage = null;
560            return;
561        }
562        this.keyUsage = new boolean[keyUsage.length];
563        System.arraycopy(keyUsage, 0, this.keyUsage, 0, keyUsage.length);
564    }
565
566    /**
567     * Returns the criterion for the {@literal KeyUsage} extension.
568     *
569     * @return the boolean array in the format as returned by
570     *         {@link X509Certificate#getKeyUsage()}, or {@code null} if the key
571     *         usage is not to be checked.
572     */
573    public boolean[] getKeyUsage() {
574        if (keyUsage == null) {
575            return null;
576        }
577        boolean[] result = new boolean[keyUsage.length];
578        System.arraycopy(keyUsage, 0, result, 0, keyUsage.length);
579        return result;
580    }
581
582    /**
583     * Sets the criterion for the {@literal ExtendedKeyUsage} extension.
584     *
585     * @param keyUsage
586     *            the set of key usage OIDs, or {@code null} to not check it.
587     * @throws IOException
588     *             if one of the OIDs is invalid.
589     */
590    public void setExtendedKeyUsage(Set<String> keyUsage)
591                             throws IOException {
592        extendedKeyUsage = null;
593        if ((keyUsage == null) || (keyUsage.size() == 0)) {
594            return;
595        }
596        HashSet key_u = new HashSet();
597        Iterator it = keyUsage.iterator();
598        while (it.hasNext()) {
599            String usage = (String) it.next();
600            checkOID(usage);
601            key_u.add(usage);
602        }
603        extendedKeyUsage = Collections.unmodifiableSet(key_u);
604    }
605
606    /**
607     * Returns the criterion for the {@literal ExtendedKeyUsage} extension.
608     *
609     * @return the set of key usage OIDs, or {@code null} if it's not to be
610     *         checked.
611     */
612    public Set<String> getExtendedKeyUsage() {
613        return extendedKeyUsage;
614    }
615
616    /**
617     * Sets the flag for the matching behavior for subject alternative names.
618     * <p>
619     * The flag indicates whether a certificate must contain all or at least one
620     * of the subject alternative names specified by {@link
621     * #setSubjectAlternativeNames} or {@link #addSubjectAlternativeName}.
622     *
623     * @param matchAllNames
624     *            {@code true} if a certificate must contain all of the
625     *            specified subject alternative names, otherwise {@code false}.
626     */
627    public void setMatchAllSubjectAltNames(boolean matchAllNames) {
628        this.matchAllNames = matchAllNames;
629    }
630
631    /**
632     * Returns the flag for the matching behavior for subject alternative names.
633     * <p>
634     * The flag indicates whether a certificate must contain all or at least one
635     * of the subject alternative names specified by {@link
636     * #setSubjectAlternativeNames} or {@link #addSubjectAlternativeName}.
637     *
638     * @return {@code true} if a certificate must contain all of the specified
639     *         subject alternative names, otherwise {@code false}.
640     */
641    public boolean getMatchAllSubjectAltNames() {
642        return matchAllNames;
643    }
644
645    /**
646     * Sets the criterion for subject alternative names.
647     * <p>
648     * the certificate must contain all or at least one of the specified subject
649     * alternative names. The behavior is specified by
650     * {@link #getMatchAllSubjectAltNames}.
651     * <p>
652     * The specified parameter {@code names} is a collection with an entry for
653     * each name to be included in the criterion. The name is specified as a
654     * {@code List}, the first entry must be an {@code Integer} specifying the
655     * name type (0-8), the second entry must be a {@code String} or a byte
656     * array specifying the name (in string or ASN.1 DER encoded form)
657     *
658     * @param names
659     *            the names collection or {@code null} to not perform this check.
660     * @throws IOException
661     *             if the decoding of a name fails.
662     */
663    public void setSubjectAlternativeNames(Collection<List<?>> names)
664                                    throws IOException {
665        subjectAltNames = null;
666        if ((names == null) || (names.size() == 0)) {
667            return;
668        }
669        Iterator it = names.iterator();
670        while (it.hasNext()) {
671            List name = (List) it.next();
672            int tag = ((Integer) name.get(0)).intValue();
673            Object value = name.get(1);
674            if (value instanceof String) {
675                addSubjectAlternativeName(tag, (String) value);
676            } else if (value instanceof byte[]) {
677                addSubjectAlternativeName(tag, (byte[]) value);
678            } else {
679                throw new IOException("name neither a String nor a byte[]");
680            }
681        }
682    }
683
684    /**
685     * Adds a subject alternative name to the respective criterion.
686     *
687     * @param tag
688     *            the type of the name
689     * @param name
690     *            the name in string format.
691     * @throws IOException
692     *             if parsing the name fails.
693     */
694    public void addSubjectAlternativeName(int tag, String name)
695                                                       throws IOException {
696        GeneralName alt_name = new GeneralName(tag, name);
697        // create only if there was not any errors
698        if (subjectAltNames == null) {
699            subjectAltNames = new ArrayList[9];
700        }
701        if (subjectAltNames[tag] == null) {
702            subjectAltNames[tag] = new ArrayList();
703        }
704        subjectAltNames[tag].add(alt_name);
705    }
706
707    /**
708     * Adds a subject alternative name to the respective criterion.
709     *
710     * @param tag
711     *            the type of the name.
712     * @param name
713     *            the name in ASN.1 DER encoded form.
714     * @throws IOException
715     *             if the decoding of the name fails.
716     */
717    public void addSubjectAlternativeName(int tag, byte[] name)
718                                            throws IOException {
719        GeneralName alt_name = new GeneralName(tag, name);
720        // create only if there was not any errors
721        if (subjectAltNames == null) {
722            subjectAltNames = new ArrayList[9];
723        }
724        if (subjectAltNames[tag] == null) {
725            subjectAltNames[tag] = new ArrayList();
726        }
727        subjectAltNames[tag].add(alt_name);
728    }
729
730    /**
731     * Returns the criterion for subject alternative names.
732     * <p>
733     * the certificate must contain all or at least one of the specified subject
734     * alternative names. The behavior is specified by
735     * {@link #getMatchAllSubjectAltNames}.
736     * <p>
737     * The subject alternative names is a collection with an entry for each name
738     * included in the criterion. The name is specified as a {@code List}, the
739     * first entry is an {@code Integer} specifying the name type (0-8), the
740     * second entry is byte array specifying the name in ASN.1 DER encoded form)
741     *
742     * @return the names collection or {@code null} if none specified.
743     */
744    public Collection<List<?>> getSubjectAlternativeNames() {
745        if (subjectAltNames == null) {
746            return null;
747        }
748        ArrayList result = new ArrayList();
749        for (int tag=0; tag<9; tag++) {
750            if (subjectAltNames[tag] != null) {
751                for (int name=0; name<subjectAltNames[tag].size(); name++) {
752                    Object neim = subjectAltNames[tag].get(name);
753                    if (neim instanceof byte[]) {
754                        byte[] arr_neim = (byte[]) neim;
755                        neim = new byte[arr_neim.length];
756                        System.arraycopy(arr_neim, 0, neim, 0, arr_neim.length);
757                    }
758                    List list = new ArrayList(2);
759                    list.add(Integer.valueOf(tag)); // android-changed
760                    list.add(neim);
761                    result.add(list);
762                }
763            }
764        }
765        return result;
766    }
767
768    /**
769     * Sets the criterion for the name constraints.
770     * <p>
771     * The certificate must constraint subject and subject alternative names
772     * that match the specified name constraints.
773     * <p>
774     * The name constraints in ASN.1:
775     *
776     * <pre>
777     * NameConstraints ::= SEQUENCE {
778     *        permittedSubtrees       [0]     GeneralSubtrees OPTIONAL,
779     *        excludedSubtrees        [1]     GeneralSubtrees OPTIONAL }
780     *
781     * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
782     *
783     * GeneralSubtree ::= SEQUENCE {
784     *        base                    GeneralName,
785     *        minimum         [0]     BaseDistance DEFAULT 0,
786     *        maximum         [1]     BaseDistance OPTIONAL }
787     *
788     * BaseDistance ::= INTEGER (0..MAX)
789     *
790     * GeneralName ::= CHOICE {
791     *        otherName                       [0]     OtherName,
792     *        rfc822Name                      [1]     IA5String,
793     *        dNSName                         [2]     IA5String,
794     *        x400Address                     [3]     ORAddress,
795     *        directoryName                   [4]     Name,
796     *        ediPartyName                    [5]     EDIPartyName,
797     *        uniformResourceIdentifier       [6]     IA5String,
798     *        iPAddress                       [7]     OCTET STRING,
799     *        registeredID                    [8]     OBJECT IDENTIFIER}
800     *
801     * </pre>
802     *
803     * @param bytes
804     *            the name constraints in ASN.1 DER encoded format, or null to
805     *            not check any constraints.
806     * @throws IOException
807     *             if decoding the name constraints fail.
808     */
809    public void setNameConstraints(byte[] bytes) throws IOException {
810        this.nameConstraints = (bytes == null)
811            ? null
812            : (NameConstraints) NameConstraints.ASN1.decode(bytes);
813    }
814
815    /**
816     * Returns the criterion for the name constraints.
817     *
818     * @return the name constraints or {@code null} if none specified.
819     * @see #setNameConstraints
820     */
821    public byte[] getNameConstraints() {
822        return (nameConstraints == null)
823            ? null
824            : nameConstraints.getEncoded();
825    }
826
827    /**
828     * Sets the criterion for the basic constraints extension.
829     * <p>
830     * A value greater than or equal to zero indicates that a certificate must
831     * include a basic constraints extension with a path length of a least that
832     * value. A value of {@code -2} indicates that only end-entity certificates
833     * are accepted. A value of {@code -1} indicates that no check is done.
834     *
835     * @param pathLen
836     *            the value specifying the criterion.
837     * @throws IllegalArgumentException
838     *             if {@code pathLen} is less than {@code -2}.
839     */
840    public void setBasicConstraints(int pathLen) {
841        if (pathLen < -2) {
842            throw new IllegalArgumentException("pathLen < -2");
843        }
844        this.pathLen = pathLen;
845    }
846
847    /**
848     * Returns the criterion for the basic constraints extension.
849     * <p>
850     * A value greater than or equal to zero indicates that a certificate must
851     * include a basic constraints extension with a path length of a least that
852     * value. A value of {@code -2} indicates that only end-entity certificates
853     * are accepted. A value of {@code -1} indicates that no check is done.
854     *
855     * @return the value of the criterion.
856     */
857    public int getBasicConstraints() {
858        return pathLen;
859    }
860
861    /**
862     * Sets the criterion for the policy constraint.
863     * <p>
864     * The certificate must have at least one of the specified certificate
865     * policy extensions. For an empty set the certificate must have at least
866     * some policies in its policy extension.
867     *
868     * @param policies
869     *            the certificate policy OIDs, an empty set, or {@code null} to
870     *            not perform this check.
871     * @throws IOException
872     *             if parsing the specified OIDs fails.
873     */
874    public void setPolicy(Set<String> policies) throws IOException {
875        if (policies == null) {
876            this.policies = null;
877            return;
878        }
879        HashSet pols = new HashSet(policies.size());
880        Iterator it = policies.iterator();
881        while (it.hasNext()) {
882            String certPolicyId = (String) it.next();
883            checkOID(certPolicyId);
884            pols.add(certPolicyId);
885        }
886        this.policies = Collections.unmodifiableSet(pols);
887    }
888
889    /**
890     * Returns the criterion for the policy constraint.
891     * <p>
892     * The certificate must have at least one of the certificate policy
893     * extensions. For an empty set the certificate must have at least some
894     * policies in its policy extension.
895     *
896     * @return the certificate policy OIDs, an empty set, or {@code null} if not
897     *         to be checked.
898     */
899    public Set<String> getPolicy() {
900        return policies;
901    }
902
903    /**
904     * Sets the criterion for the pathToNames constraint.
905     * <p>
906     * This allows to specify the complete set of names, a certificate's name
907     * constraints must permit.
908     * <p>
909     * The specified parameter {@code names} is a collection with an entry for
910     * each name to be included in the criterion. The name is specified as a
911     * {@code List}, the first entry must be an {@code Integer} specifying the
912     * name type (0-8), the second entry must be a {@code String} or a byte
913     * array specifying the name (in string or ASN.1 DER encoded form)
914     *
915     * @param names
916     *            the names collection or {@code null} to not perform this
917     *            check.
918     * @throws IOException
919     *             if decoding fails.
920     */
921    public void setPathToNames(Collection<List<?>> names)
922                                                        throws IOException {
923        pathToNames = null;
924        if ((names == null) || (names.size() == 0)) {
925            return;
926        }
927        Iterator it = names.iterator();
928        while (it.hasNext()) {
929            List name = (List) it.next();
930            int tag = ((Integer) name.get(0)).intValue();
931            Object value = name.get(1);
932            if (value instanceof String) {
933                addPathToName(tag, (String) value);
934            } else if (value instanceof byte[]) {
935                addPathToName(tag, (byte[]) value);
936            } else {
937                throw new IOException("name neither a String nor a byte[]");
938            }
939        }
940    }
941
942    /**
943     * Adds a {@literal "pathToName"} to the respective criterion.
944     *
945     * @param type
946     *            the type of the name.
947     * @param name
948     *            the name in string format.
949     * @throws IOException
950     *             if parsing fails.
951     * @see #setPathToNames
952     */
953    public void addPathToName(int type, String name) throws IOException {
954        GeneralName path_name = new GeneralName(type, name);
955        // create only if there was not any errors
956        if (pathToNames == null) {
957            pathToNames = new ArrayList();
958        }
959        pathToNames.add(path_name);
960    }
961
962    /**
963     * Adds a {@literal "pathToName"} to the respective criterion.
964     *
965     * @param type
966     *            the type of the name
967     * @param name
968     *            the name in ASN.1 DER encoded form.
969     * @throws IOException
970     *             if decoding fails.
971     * @see #setPathToNames
972     */
973    public void addPathToName(int type, byte[] name) throws IOException {
974        GeneralName path_name= new GeneralName(type, name);
975        // create only if there was not any errors
976        if (pathToNames == null) {
977            pathToNames = new ArrayList();
978        }
979        pathToNames.add(path_name);
980    }
981
982    /**
983     * Returns the criterion for the pathToNames constraint.
984     * <p>
985     * The constraint is a collection with an entry for each name to be included
986     * in the criterion. The name is specified as a {@code List}, the first
987     * entry is an {@code Integer} specifying the name type (0-8), the second
988     * entry is a byte array specifying the name in ASN.1 DER encoded form.
989     *
990     * @return the pathToNames constraint or {@code null} if none specified.
991     */
992    public Collection<List<?>> getPathToNames() {
993        if (pathToNames == null) {
994            return null;
995        }
996        ArrayList result = new ArrayList();
997        Iterator it = pathToNames.iterator();
998        while (it.hasNext()) {
999            GeneralName name = (GeneralName) it.next();
1000            result.add(name.getAsList());
1001        }
1002        return result;
1003    }
1004
1005    /**
1006     * Returns a string representation of this {@code X509CertSelector}
1007     * instance.
1008     *
1009     * @return a string representation of this {@code X509CertSelector}
1010     *         instance.
1011     */
1012    public String toString() {
1013        // For convenient reading of the string representation
1014        // all of the fields named according to the rfc 3280
1015        // (http://www.ietf.org/rfc/rfc3280.txt).
1016
1017        StringBuilder result = new StringBuilder();
1018        result.append("X509CertSelector: \n[");
1019        if (this.certificateEquals != null) {
1020            result.append("\n  certificateEquals: " + certificateEquals);
1021        }
1022        if (this.serialNumber != null) {
1023            //FIXME: needs DRL's BigInteger.toString implementation
1024            //result.append("\n  serialNumber: " + serialNumber);
1025        }
1026        if (this.issuer != null) {
1027            result.append("\n  issuer: " + issuer);
1028        }
1029        if (this.subject != null) {
1030            result.append("\n  subject: " + subject);
1031        }
1032        if (this.subjectKeyIdentifier != null) {
1033            result.append("\n  subjectKeyIdentifier: "
1034                    + getBytesAsString(subjectKeyIdentifier));
1035        }
1036        if (this.authorityKeyIdentifier != null) {
1037            result.append("\n  authorityKeyIdentifier: "
1038                    + getBytesAsString(authorityKeyIdentifier));
1039        }
1040        if (this.certificateValid != null) {
1041            result.append("\n  certificateValid: " + certificateValid);
1042        }
1043        if (this.subjectPublicKeyAlgID != null) {
1044            result.append("\n  subjectPublicKeyAlgID: "
1045                    + subjectPublicKeyAlgID);
1046        }
1047        if (this.privateKeyValid != null) {
1048            result.append("\n  privateKeyValid: " + privateKeyValid);
1049        }
1050        if (this.subjectPublicKey != null) {
1051            result.append("\n  subjectPublicKey: "
1052                    + getBytesAsString(subjectPublicKey));
1053        }
1054        if (this.keyUsage != null) {
1055            result.append("\n  keyUsage: \n  [");
1056            String[] kuNames = new String[] {
1057                "digitalSignature", "nonRepudiation", "keyEncipherment",
1058                "dataEncipherment", "keyAgreement", "keyCertSign", "cRLSign",
1059                "encipherOnly", "decipherOnly"
1060            };
1061            for (int i=0; i<9; i++) {
1062                if (keyUsage[i]) {
1063                    result.append("\n    " + kuNames[i]);
1064                }
1065            }
1066            result.append("\n  ]");
1067        }
1068        if (this.extendedKeyUsage != null) {
1069            result.append("\n  extendedKeyUsage: "
1070                    + extendedKeyUsage.toString());
1071        }
1072        result.append("\n  matchAllNames: " + matchAllNames);
1073        result.append("\n  pathLen: " + pathLen);
1074        if (this.subjectAltNames != null) {
1075            result.append("\n  subjectAltNames:  \n  [");
1076            for (int i=0; i<9; i++) {
1077                List names = this.subjectAltNames[i];
1078                if (names != null) {
1079                    int size = names.size();
1080                    for (int j=0; j<size; j++) {
1081                        result.append("\n    "
1082                            + ((GeneralName)names.get(j)).toString());
1083                    }
1084                }
1085            }
1086            result.append("\n  ]");
1087        }
1088        if (this.nameConstraints != null) {
1089        }
1090        if (this.policies != null) {
1091            result.append("\n  policies: " + policies.toString());
1092        }
1093        if (this.pathToNames != null) {
1094            result.append("\n  pathToNames:  \n  [");
1095            int size = pathToNames.size();
1096            for (int i = 0; i < size; i++) {
1097                result.append("\n    "
1098                    + ((GeneralName)pathToNames.get(i)).toString());
1099            }
1100        }
1101        result.append("\n]");
1102        return result.toString();
1103    }
1104
1105    private String getBytesAsString(byte[] data) {
1106        String result = "";
1107        for (int i=0; i<data.length; i++) {
1108            String tail = Integer.toHexString(0x00ff & data[i]);
1109            if (tail.length() == 1) {
1110                tail = "0" + tail;
1111            }
1112            result += tail + " ";
1113        }
1114        return result;
1115    }
1116
1117    private byte[] getExtensionValue(X509Certificate cert, String oid) {
1118        try {
1119            byte[] bytes = cert.getExtensionValue(oid);
1120            if (bytes == null) {
1121                return null;
1122            }
1123            return (byte[]) ASN1OctetString.getInstance().decode(bytes);
1124        } catch (IOException e) {
1125            return null;
1126        }
1127    }
1128
1129    /**
1130     * Returns whether the specified certificate matches all the criteria
1131     * collected in this instance.
1132     *
1133     * @param certificate
1134     *            the certificate to check.
1135     * @return {@code true} if the certificate matches all the criteria,
1136     *         otherwise {@code false}.
1137     */
1138    public boolean match(Certificate certificate) {
1139        if (! (certificate instanceof X509Certificate)) {
1140            return false;
1141        }
1142
1143        X509Certificate cert = (X509Certificate) certificate;
1144        if ((certificateEquals != null) &&
1145            !certificateEquals.equals(cert)) {
1146            return false;
1147        }
1148        if ((serialNumber != null) &&
1149            !serialNumber.equals(cert.getSerialNumber())) {
1150            return false;
1151        }
1152        if ((issuer != null) &&
1153            !issuer.equals(cert.getIssuerX500Principal())) {
1154            return false;
1155        }
1156        if ((subject != null) &&
1157            !subject.equals(cert.getSubjectX500Principal())) {
1158            return false;
1159        }
1160        if ((subjectKeyIdentifier != null) &&
1161            !Arrays.equals(subjectKeyIdentifier,
1162            // Here and later all of the extension OIDs
1163            // are taken from rfc 3280 (http://www.ietf.org/rfc/rfc3280.txt)
1164                           getExtensionValue(cert, "2.5.29.14"))) {
1165            return false;
1166        }
1167        if ((authorityKeyIdentifier != null) &&
1168            !Arrays.equals(authorityKeyIdentifier,
1169                           getExtensionValue(cert, "2.5.29.35"))) {
1170            return false;
1171        }
1172        if (certificateValid != null) {
1173            try {
1174                cert.checkValidity(certificateValid);
1175            } catch(CertificateExpiredException e) {
1176                return false;
1177            } catch(CertificateNotYetValidException e) {
1178                return false;
1179            }
1180        }
1181        if (privateKeyValid != null) {
1182            try {
1183                byte[] bytes = getExtensionValue(cert, "2.5.29.16");
1184                if (bytes == null) {
1185                    return false;
1186                }
1187                PrivateKeyUsagePeriod pkup = (PrivateKeyUsagePeriod)
1188                                    PrivateKeyUsagePeriod.ASN1.decode(bytes);
1189                Date notBefore = pkup.getNotBefore();
1190                Date notAfter = pkup.getNotAfter();
1191                if ((notBefore == null) && (notAfter == null)) {
1192                    return false;
1193                }
1194                if ((notBefore != null)
1195                    && notBefore.compareTo(privateKeyValid) > 0) {
1196                    return false;
1197                }
1198                if ((notAfter != null)
1199                    && notAfter.compareTo(privateKeyValid) < 0) {
1200                    return false;
1201                }
1202            } catch (IOException e) {
1203                return false;
1204            }
1205        }
1206        if (subjectPublicKeyAlgID  != null) {
1207            try {
1208                byte[] encoding = cert.getPublicKey().getEncoded();
1209                AlgorithmIdentifier ai = ((SubjectPublicKeyInfo)
1210                        SubjectPublicKeyInfo.ASN1.decode(encoding))
1211                        .getAlgorithmIdentifier();
1212                if (!subjectPublicKeyAlgID.equals(ai.getAlgorithm())) {
1213                    return false;
1214                }
1215            } catch (IOException e) {
1216                e.printStackTrace();
1217                return false;
1218            }
1219        }
1220        if (subjectPublicKey != null) {
1221            if (!Arrays.equals(subjectPublicKey,
1222                               cert.getPublicKey().getEncoded())) {
1223                return false;
1224            }
1225        }
1226        if (keyUsage != null) {
1227            boolean[] ku = cert.getKeyUsage();
1228            if (ku != null) {
1229                int i = 0;
1230                int min_length = (ku.length < keyUsage.length) ? ku.length
1231                        : keyUsage.length;
1232                for (; i < min_length; i++) {
1233                    if (keyUsage[i] && !ku[i]) {
1234                        // the specified keyUsage allows,
1235                        // but certificate does not.
1236                        return false;
1237                    }
1238                }
1239                for (; i<keyUsage.length; i++) {
1240                    if (keyUsage[i]) {
1241                        return false;
1242                    }
1243                }
1244            }
1245        }
1246        if (extendedKeyUsage != null) {
1247            try {
1248                List keyUsage = cert.getExtendedKeyUsage();
1249                if (keyUsage != null) {
1250                    if (!keyUsage.containsAll(extendedKeyUsage)) {
1251                        return false;
1252                    }
1253                }
1254            } catch (CertificateParsingException e) {
1255                return false;
1256            }
1257        }
1258        if (pathLen != -1) {
1259            int p_len = cert.getBasicConstraints();
1260            if ((pathLen < 0) && (p_len >= 0)) {
1261                // need end-entity but got CA
1262                return false;
1263            }
1264            if ((pathLen > 0) && (pathLen > p_len)) {
1265                // allowed _pathLen is small
1266                return false;
1267            }
1268        }
1269        if (subjectAltNames != null) {
1270            PASSED:
1271            try {
1272                byte[] bytes = getExtensionValue(cert, "2.5.29.17");
1273                if (bytes == null) {
1274                    return false;
1275                }
1276                List sans = ((GeneralNames) GeneralNames.ASN1.decode(bytes))
1277                            .getNames();
1278                if ((sans == null) || (sans.size() == 0)) {
1279                    return false;
1280                }
1281                boolean[][] map = new boolean[9][];
1282                // initialize the check map
1283                for (int i=0; i<9; i++) {
1284                    map[i] = (subjectAltNames[i] == null)
1285                            ? EmptyArray.BOOLEAN : new boolean[subjectAltNames[i].size()];
1286                }
1287                Iterator it = sans.iterator();
1288                while (it.hasNext()) {
1289                    GeneralName name = (GeneralName) it.next();
1290                    int tag = name.getTag();
1291                    for (int i=0; i<map[tag].length; i++) {
1292                        if (((GeneralName) subjectAltNames[tag].get(i))
1293                                                            .equals(name)) {
1294                            if (!matchAllNames) {
1295                                break PASSED;
1296                            }
1297                            map[tag][i] = true;
1298                        }
1299                    }
1300                }
1301                if (!matchAllNames) {
1302                    // there was not any match
1303                    return false;
1304                }
1305                // else check the map
1306                for (int tag=0; tag<9; tag++) {
1307                    for (int name=0; name<map[tag].length; name++) {
1308                        if (!map[tag][name]) {
1309                            return false;
1310                        }
1311                    }
1312                }
1313            } catch (IOException e) {
1314                e.printStackTrace();
1315                return false;
1316            }
1317        }
1318        if (nameConstraints != null) {
1319            if (!nameConstraints.isAcceptable(cert)) {
1320                return false;
1321            }
1322        }
1323        if (policies != null) {
1324            byte[] bytes = getExtensionValue(cert, "2.5.29.32");
1325            if (bytes == null) {
1326                return false;
1327            }
1328            if (policies.size() == 0) {
1329                // if certificate has such extension than it has at least
1330                // one policy in it.
1331                return true;
1332            }
1333            PASSED:
1334            try {
1335                List policyInformations = ((CertificatePolicies)
1336                        CertificatePolicies.ASN1.decode(bytes))
1337                        .getPolicyInformations();
1338                Iterator it = policyInformations.iterator();
1339                while (it.hasNext()) {
1340                    if (policies.contains(((PolicyInformation) it.next())
1341                                          .getPolicyIdentifier())) {
1342                        break PASSED;
1343                    }
1344                }
1345                return false;
1346            } catch (IOException e) {
1347                // the extension is invalid
1348                return false;
1349            }
1350        }
1351        if (pathToNames != null) {
1352            byte[] bytes = getExtensionValue(cert, "2.5.29.30");
1353            if (bytes != null) {
1354                NameConstraints nameConstraints;
1355                try {
1356                    nameConstraints =
1357                        (NameConstraints) NameConstraints.ASN1.decode(bytes);
1358                } catch (IOException e) {
1359                    // the extension is invalid;
1360                    return false;
1361                }
1362                if (!nameConstraints.isAcceptable(pathToNames)) {
1363                    return false;
1364                }
1365            }
1366        }
1367        return true;
1368    }
1369
1370    /**
1371     * Clones this {@code X509CertSelector} instance.
1372     *
1373     * @return the cloned instance.
1374     */
1375    public Object clone() {
1376        X509CertSelector result;
1377
1378        try {
1379            result = (X509CertSelector) super.clone();
1380        } catch (CloneNotSupportedException e) {
1381            return null;
1382        }
1383
1384        if (this.subjectKeyIdentifier != null) {
1385            result.subjectKeyIdentifier =
1386                new byte[this.subjectKeyIdentifier.length];
1387            System.arraycopy(this.subjectKeyIdentifier, 0,
1388                    result.subjectKeyIdentifier, 0,
1389                    this.subjectKeyIdentifier.length);
1390        }
1391        if (this.authorityKeyIdentifier != null) {
1392            result.authorityKeyIdentifier =
1393                new byte[this.authorityKeyIdentifier.length];
1394            System.arraycopy(this.authorityKeyIdentifier, 0,
1395                    result.authorityKeyIdentifier, 0,
1396                    this.authorityKeyIdentifier.length);
1397        }
1398        if (this.subjectPublicKey != null) {
1399            result.subjectPublicKey = new byte[this.subjectPublicKey.length];
1400            System.arraycopy(this.subjectPublicKey, 0, result.subjectPublicKey,
1401                    0, this.subjectPublicKey.length);
1402        }
1403        if (this.keyUsage != null) {
1404            result.keyUsage = new boolean[this.keyUsage.length];
1405            System.arraycopy(this.keyUsage, 0, result.keyUsage, 0,
1406                    this.keyUsage.length);
1407        }
1408        result.extendedKeyUsage = (this.extendedKeyUsage == null)
1409            ? null
1410            : new HashSet(this.extendedKeyUsage);
1411        if (this.subjectAltNames != null) {
1412            result.subjectAltNames = new ArrayList[9];
1413            for (int i=0; i<9; i++) {
1414                if (this.subjectAltNames[i] != null) {
1415                    result.subjectAltNames[i] =
1416                        new ArrayList(this.subjectAltNames[i]);
1417                }
1418            }
1419        }
1420        result.policies = (this.policies == null)
1421            ? null
1422            : new HashSet(this.policies);
1423        result.pathToNames = (this.pathToNames == null)
1424            ? null
1425            : new ArrayList(this.pathToNames);
1426        return result;
1427    }
1428}
1429