blob: f0d581e855ae5242f2512493bb564265590af054 [file] [log] [blame]
Makoto Onuki8f028a92010-01-08 13:34:57 -08001/*
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 */
Brett Chabot0dc59e72010-04-01 18:21:38 -070016package com.android.internal.net;
Makoto Onuki8f028a92010-01-08 13:34:57 -080017
Dianne Hackborn2269d1572010-02-24 19:54:22 -080018import com.android.internal.net.DomainNameValidator;
Brett Chabot0dc59e72010-04-01 18:21:38 -070019import com.android.frameworks.coretests.R;
20import com.android.frameworks.coretests.R.raw;
Makoto Onuki691c0702010-01-12 14:00:07 -080021
22import android.test.AndroidTestCase;
23
24import java.io.InputStream;
Makoto Onuki8f028a92010-01-08 13:34:57 -080025import java.math.BigInteger;
26import java.security.InvalidKeyException;
27import java.security.NoSuchAlgorithmException;
28import java.security.NoSuchProviderException;
29import java.security.Principal;
30import java.security.PublicKey;
31import java.security.SignatureException;
32import java.security.cert.CertificateEncodingException;
33import java.security.cert.CertificateException;
34import java.security.cert.CertificateExpiredException;
Makoto Onuki691c0702010-01-12 14:00:07 -080035import java.security.cert.CertificateFactory;
Makoto Onuki8f028a92010-01-08 13:34:57 -080036import java.security.cert.CertificateNotYetValidException;
37import java.security.cert.CertificateParsingException;
38import java.security.cert.X509Certificate;
39import java.util.ArrayList;
40import java.util.Collection;
41import java.util.Date;
42import java.util.LinkedList;
43import java.util.List;
44import java.util.Set;
45
46import javax.security.auth.x500.X500Principal;
47
Makoto Onuki691c0702010-01-12 14:00:07 -080048public class DomainNameValidatorTest extends AndroidTestCase {
Makoto Onuki8f028a92010-01-08 13:34:57 -080049 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 /**
Makoto Onuki691c0702010-01-12 14:00:07 -080054 * Tests {@link DomainNameValidator#match}, using a simple {@link X509Certificate}
55 * implementation.
Makoto Onuki8f028a92010-01-08 13:34:57 -080056 */
57 public void testMatch() {
Makoto Onuki8f028a92010-01-08 13:34:57 -080058 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
Makoto Onuki8f028a92010-01-08 13:34:57 -080079 // 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 /**
Makoto Onuki691c0702010-01-12 14:00:07 -0800174 * 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 /**
Makoto Onuki8f028a92010-01-08 13:34:57 -0800238 * 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}