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