Home | History | Annotate | Download | only in net
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.internal.net;
     17 
     18 import com.android.internal.net.DomainNameValidator;
     19 import com.android.frameworks.coretests.R;
     20 import com.android.frameworks.coretests.R.raw;
     21 
     22 import android.test.AndroidTestCase;
     23 
     24 import java.io.InputStream;
     25 import java.math.BigInteger;
     26 import java.security.InvalidKeyException;
     27 import java.security.NoSuchAlgorithmException;
     28 import java.security.NoSuchProviderException;
     29 import java.security.Principal;
     30 import java.security.PublicKey;
     31 import java.security.SignatureException;
     32 import java.security.cert.CertificateEncodingException;
     33 import java.security.cert.CertificateException;
     34 import java.security.cert.CertificateExpiredException;
     35 import java.security.cert.CertificateFactory;
     36 import java.security.cert.CertificateNotYetValidException;
     37 import java.security.cert.CertificateParsingException;
     38 import java.security.cert.X509Certificate;
     39 import java.util.ArrayList;
     40 import java.util.Collection;
     41 import java.util.Date;
     42 import java.util.LinkedList;
     43 import java.util.List;
     44 import java.util.Set;
     45 
     46 import javax.security.auth.x500.X500Principal;
     47 
     48 public class DomainNameValidatorTest extends AndroidTestCase {
     49     private static final int ALT_UNKNOWN = 0;
     50     private static final int ALT_DNS_NAME = 2;
     51     private static final int ALT_IPA_NAME = 7;
     52 
     53     /**
     54      * Tests {@link DomainNameValidator#match}, using a simple {@link X509Certificate}
     55      * implementation.
     56      */
     57     public void testMatch() {
     58         checkMatch("11", new StubX509Certificate("cn=imap.g.com"), "imap.g.com", true);
     59         checkMatch("12", new StubX509Certificate("cn=imap2.g.com"), "imap.g.com", false);
     60         checkMatch("13", new StubX509Certificate("cn=sub.imap.g.com"), "imap.g.com", false);
     61 
     62         // If a subjectAltName extension of type dNSName is present, that MUST
     63         // be used as the identity
     64         checkMatch("21", new StubX509Certificate("")
     65                 .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")
     66                 , "imap.g.com", false);
     67         checkMatch("22", new StubX509Certificate("cn=imap.g.com") // This cn should be ignored
     68                 .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")
     69                 , "imap.g.com", false);
     70         checkMatch("23", new StubX509Certificate("")
     71                 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
     72                 , "imap.g.com", true);
     73 
     74         // With wildcards
     75         checkMatch("24", new StubX509Certificate("")
     76                 .addSubjectAlternativeName(ALT_DNS_NAME, "*.g.com")
     77                 , "imap.g.com", true);
     78 
     79         // host name is ip address
     80         checkMatch("31", new StubX509Certificate("")
     81                 .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")
     82                 , "1.2.3.4", true);
     83         checkMatch("32", new StubX509Certificate("")
     84                 .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")
     85                 , "1.2.3.5", false);
     86         checkMatch("32", new StubX509Certificate("")
     87                 .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")
     88                 .addSubjectAlternativeName(ALT_IPA_NAME, "192.168.100.1")
     89                 , "192.168.100.1", true);
     90 
     91         // Has unknown subject alternative names
     92         checkMatch("41", new StubX509Certificate("")
     93                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
     94                 .addSubjectAlternativeName(ALT_UNKNOWN,  "random string 2")
     95                 .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
     96                 .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com")
     97                 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
     98                 .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
     99                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")
    100                 , "imap.g.com", true);
    101 
    102         checkMatch("42", new StubX509Certificate("")
    103                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
    104                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
    105                 .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
    106                 .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com")
    107                 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
    108                 .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
    109                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")
    110                 , "2.33.44.55", true);
    111 
    112         checkMatch("43", new StubX509Certificate("")
    113                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
    114                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
    115                 .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
    116                 .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com")
    117                 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
    118                 .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
    119                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")
    120                 , "g.com", false);
    121 
    122         checkMatch("44", new StubX509Certificate("")
    123                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
    124                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
    125                 .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
    126                 .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com")
    127                 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
    128                 .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
    129                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")
    130                 , "2.33.44.1", false);
    131     }
    132 
    133     private void checkMatch(String message, X509Certificate certificate, String thisDomain,
    134             boolean expected) {
    135         Boolean actual = DomainNameValidator.match(certificate, thisDomain);
    136         assertEquals(message, (Object) expected, (Object) actual);
    137     }
    138 
    139     /**
    140      * Tests {@link DomainNameValidator#matchDns}
    141      */
    142     public void testMatchDns() {
    143         checkMatchDns("11", "a.b.c.d", "a.b.c.d", true);
    144         checkMatchDns("12", "a.b.c.d", "*.b.c.d", true);
    145         checkMatchDns("13", "b.c.d", "*.b.c.d", true);
    146         checkMatchDns("14", "b.c.d", "b*.c.d", true);
    147 
    148         checkMatchDns("15", "a.b.c.d", "*.*.c.d", false);
    149         checkMatchDns("16", "a.b.c.d", "*.c.d", false);
    150 
    151         checkMatchDns("21", "imap.google.com", "imap.google.com", true);
    152         checkMatchDns("22", "imap2.google.com", "imap.google.com", false);
    153         checkMatchDns("23", "imap.google.com", "*.google.com", true);
    154         checkMatchDns("24", "imap2.google.com", "*.google.com", true);
    155         checkMatchDns("25", "imap.google.com", "*.googl.com", false);
    156         checkMatchDns("26", "imap2.google2.com", "*.google3.com", false);
    157         checkMatchDns("27", "imap.google.com", "ima*.google.com", true);
    158         checkMatchDns("28", "imap.google.com", "imap*.google.com", true);
    159         checkMatchDns("29", "imap.google.com", "*.imap.google.com", true);
    160 
    161         checkMatchDns("41", "imap.google.com", "a*.google.com", false);
    162         checkMatchDns("42", "imap.google.com", "ix*.google.com", false);
    163 
    164         checkMatchDns("51", "imap.google.com", "iMap.Google.Com", true);
    165     }
    166 
    167     private void checkMatchDns(String message, String thisDomain, String thatDomain,
    168             boolean expected) {
    169         boolean actual = DomainNameValidator.matchDns(thisDomain, thatDomain);
    170         assertEquals(message, expected, actual);
    171     }
    172 
    173     /**
    174      * Test {@link DomainNameValidator#match} with actual certificates.
    175      */
    176     public void testWithActualCert() throws Exception {
    177         // subject_only
    178         //
    179         // subject: C=JP, CN=www.example.com
    180         // subject alt names: n/a
    181         checkWithActualCert("11", R.raw.subject_only, "www.example.com", true);
    182         checkWithActualCert("12", R.raw.subject_only, "www2.example.com", false);
    183 
    184         // subject_alt_only
    185         //
    186         // subject: C=JP (no CN)
    187         // subject alt names: DNS:www.example.com
    188         checkWithActualCert("21", R.raw.subject_alt_only, "www.example.com", true);
    189         checkWithActualCert("22", R.raw.subject_alt_only, "www2.example.com", false);
    190 
    191         // subject_with_alt_names
    192         //
    193         // subject: C=JP, CN=www.example.com
    194         // subject alt names: DNS:www2.example.com, DNS:www3.example.com
    195         // * Subject should be ignored, because it has subject alt names.
    196         checkWithActualCert("31", R.raw.subject_with_alt_names, "www.example.com", false);
    197         checkWithActualCert("32", R.raw.subject_with_alt_names, "www2.example.com", true);
    198         checkWithActualCert("33", R.raw.subject_with_alt_names, "www3.example.com", true);
    199         checkWithActualCert("34", R.raw.subject_with_alt_names, "www4.example.com", false);
    200 
    201         // subject_with_wild_alt_name
    202         //
    203         // subject: C=JP, CN=www.example.com
    204         // subject alt names: DNS:*.example2.com
    205         // * Subject should be ignored, because it has subject alt names.
    206         checkWithActualCert("41", R.raw.subject_with_wild_alt_name, "www.example.com", false);
    207         checkWithActualCert("42", R.raw.subject_with_wild_alt_name, "www2.example.com", false);
    208         checkWithActualCert("43", R.raw.subject_with_wild_alt_name, "www.example2.com", true);
    209         checkWithActualCert("44", R.raw.subject_with_wild_alt_name, "abc.example2.com", true);
    210         checkWithActualCert("45", R.raw.subject_with_wild_alt_name, "www.example3.com", false);
    211 
    212         // wild_alt_name_only
    213         //
    214         // subject: C=JP
    215         // subject alt names: DNS:*.example.com
    216         checkWithActualCert("51", R.raw.wild_alt_name_only, "www.example.com", true);
    217         checkWithActualCert("52", R.raw.wild_alt_name_only, "www2.example.com", true);
    218         checkWithActualCert("53", R.raw.wild_alt_name_only, "www.example2.com", false);
    219 
    220         // wild_alt_name_only
    221         //
    222         // subject: C=JP
    223         // subject alt names: IP Address:192.168.10.1
    224         checkWithActualCert("61", R.raw.alt_ip_only, "192.168.10.1", true);
    225         checkWithActualCert("61", R.raw.alt_ip_only, "192.168.10.2", false);
    226     }
    227 
    228     private void checkWithActualCert(String message, int resId, String domain,
    229             boolean expected) throws Exception {
    230         CertificateFactory factory = CertificateFactory.getInstance("X509");
    231         InputStream certStream = getContext().getResources().openRawResource(resId);
    232         X509Certificate certificate = (X509Certificate) factory.generateCertificate(certStream);
    233 
    234         checkMatch(message, certificate, domain, expected);
    235     }
    236 
    237     /**
    238      * Minimal {@link X509Certificate} implementation for {@link DomainNameValidator}.
    239      */
    240     private static class StubX509Certificate extends X509Certificate {
    241         private final X500Principal subjectX500Principal;
    242         private Collection<List<?>> subjectAlternativeNames;
    243 
    244         public StubX509Certificate(String subjectDn) {
    245             subjectX500Principal = new X500Principal(subjectDn);
    246             subjectAlternativeNames = null;
    247         }
    248 
    249         public StubX509Certificate addSubjectAlternativeName(int type, String name) {
    250             if (subjectAlternativeNames == null) {
    251                 subjectAlternativeNames = new ArrayList<List<?>>();
    252             }
    253             LinkedList<Object> entry = new LinkedList<Object>();
    254             entry.add(type);
    255             entry.add(name);
    256             subjectAlternativeNames.add(entry);
    257             return this;
    258         }
    259 
    260         @Override
    261         public Collection<List<?>> getSubjectAlternativeNames() throws CertificateParsingException {
    262             return subjectAlternativeNames;
    263         }
    264 
    265         @Override
    266         public X500Principal getSubjectX500Principal() {
    267             return subjectX500Principal;
    268         }
    269 
    270         @Override
    271         public void checkValidity() throws CertificateExpiredException,
    272                 CertificateNotYetValidException {
    273             throw new RuntimeException("Method not implemented");
    274         }
    275 
    276         @Override
    277         public void checkValidity(Date date) throws CertificateExpiredException,
    278                 CertificateNotYetValidException {
    279             throw new RuntimeException("Method not implemented");
    280         }
    281 
    282         @Override
    283         public int getBasicConstraints() {
    284             throw new RuntimeException("Method not implemented");
    285         }
    286 
    287         @Override
    288         public Principal getIssuerDN() {
    289             throw new RuntimeException("Method not implemented");
    290         }
    291 
    292         @Override
    293         public boolean[] getIssuerUniqueID() {
    294             throw new RuntimeException("Method not implemented");
    295         }
    296 
    297         @Override
    298         public boolean[] getKeyUsage() {
    299             throw new RuntimeException("Method not implemented");
    300         }
    301 
    302         @Override
    303         public Date getNotAfter() {
    304             throw new RuntimeException("Method not implemented");
    305         }
    306 
    307         @Override
    308         public Date getNotBefore() {
    309             throw new RuntimeException("Method not implemented");
    310         }
    311 
    312         @Override
    313         public BigInteger getSerialNumber() {
    314             throw new RuntimeException("Method not implemented");
    315         }
    316 
    317         @Override
    318         public String getSigAlgName() {
    319             throw new RuntimeException("Method not implemented");
    320         }
    321 
    322         @Override
    323         public String getSigAlgOID() {
    324             throw new RuntimeException("Method not implemented");
    325         }
    326 
    327         @Override
    328         public byte[] getSigAlgParams() {
    329             throw new RuntimeException("Method not implemented");
    330         }
    331 
    332         @Override
    333         public byte[] getSignature() {
    334             throw new RuntimeException("Method not implemented");
    335         }
    336 
    337         @Override
    338         public Principal getSubjectDN() {
    339             throw new RuntimeException("Method not implemented");
    340         }
    341 
    342         @Override
    343         public boolean[] getSubjectUniqueID() {
    344             throw new RuntimeException("Method not implemented");
    345         }
    346 
    347         @Override
    348         public byte[] getTBSCertificate() throws CertificateEncodingException {
    349             throw new RuntimeException("Method not implemented");
    350         }
    351 
    352         @Override
    353         public int getVersion() {
    354             throw new RuntimeException("Method not implemented");
    355         }
    356 
    357         @Override
    358         public byte[] getEncoded() throws CertificateEncodingException {
    359             throw new RuntimeException("Method not implemented");
    360         }
    361 
    362         @Override
    363         public PublicKey getPublicKey() {
    364             throw new RuntimeException("Method not implemented");
    365         }
    366 
    367         @Override
    368         public String toString() {
    369             throw new RuntimeException("Method not implemented");
    370         }
    371 
    372         @Override
    373         public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException,
    374                 InvalidKeyException, NoSuchProviderException, SignatureException {
    375             throw new RuntimeException("Method not implemented");
    376         }
    377 
    378         @Override
    379         public void verify(PublicKey key, String sigProvider) throws CertificateException,
    380                 NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException,
    381                 SignatureException {
    382             throw new RuntimeException("Method not implemented");
    383         }
    384 
    385         public Set<String> getCriticalExtensionOIDs() {
    386             throw new RuntimeException("Method not implemented");
    387         }
    388 
    389         public byte[] getExtensionValue(String oid) {
    390             throw new RuntimeException("Method not implemented");
    391         }
    392 
    393         public Set<String> getNonCriticalExtensionOIDs() {
    394             throw new RuntimeException("Method not implemented");
    395         }
    396 
    397         public boolean hasUnsupportedCriticalExtension() {
    398             throw new RuntimeException("Method not implemented");
    399         }
    400     }
    401 }
    402