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