1 /* 2 * Copyright (C) 2012 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 17 package libcore.java.security.cert; 18 19 import static java.nio.charset.StandardCharsets.UTF_8; 20 21 import dalvik.system.VMRuntime; 22 import sun.security.jca.Providers; 23 import sun.security.provider.X509Factory; 24 import sun.security.x509.X509CRLImpl; 25 import tests.support.resource.Support_Resources; 26 27 import java.io.BufferedReader; 28 import java.io.ByteArrayOutputStream; 29 import java.io.DataInputStream; 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.io.InputStreamReader; 33 import java.io.PrintStream; 34 import java.security.InvalidKeyException; 35 import java.security.Provider; 36 import java.security.Security; 37 import java.security.cert.CRL; 38 import java.security.cert.CRLReason; 39 import java.security.cert.CertificateFactory; 40 import java.security.cert.X509CRL; 41 import java.security.cert.X509CRLEntry; 42 import java.security.cert.X509Certificate; 43 import java.text.SimpleDateFormat; 44 import java.util.Arrays; 45 import java.util.Calendar; 46 import java.util.Date; 47 import java.util.HashMap; 48 import java.util.Locale; 49 import java.util.Map; 50 import java.util.Set; 51 52 import junit.framework.TestCase; 53 import libcore.java.security.StandardNames; 54 55 public class X509CRLTest extends TestCase { 56 57 @Override 58 public void setUp() throws Exception { 59 super.setUp(); 60 mX509Providers = Security.getProviders("CertificateFactory.X509"); 61 62 // Allow access to deprecated BC algorithms in this test, so we can ensure they 63 // continue to work 64 Providers.setMaximumAllowableApiLevelForBcDeprecation( 65 VMRuntime.getRuntime().getTargetSdkVersion()); 66 } 67 68 @Override 69 public void tearDown() throws Exception { 70 Providers.setMaximumAllowableApiLevelForBcDeprecation( 71 Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION); 72 super.tearDown(); 73 } 74 75 private Provider[] mX509Providers; 76 77 private static final String CERT_RSA = "x509/cert-rsa.der"; 78 79 private static final String CERT_DSA = "x509/cert-dsa.der"; 80 81 private static final String CERT_CRL_CA = "x509/cert-crl-ca.der"; 82 83 private static final String CRL_RSA = "x509/crl-rsa.der"; 84 85 private static final String CRL_RSA_DSA = "x509/crl-rsa-dsa.der"; 86 87 private static final String CRL_RSA_DSA_SIGOPT = "x509/crl-rsa-dsa-sigopt.der"; 88 89 private static final String CRL_UNSUPPORTED = "x509/crl-unsupported.der"; 90 91 private static final String CRL_RSA_DATES = "x509/crl-rsa-dates.txt"; 92 93 private static final String CRL_RSA_DSA_DATES = "x509/crl-rsa-dsa-dates.txt"; 94 95 private static final String CRL_RSA_SIG = "x509/crl-rsa-sig.der"; 96 97 private static final String CRL_RSA_TBS = "x509/crl-rsa-tbs.der"; 98 99 private static final String CRL_EMPTY = "x509/crl-empty.der"; 100 101 private final X509Certificate getCertificate(CertificateFactory f, String name) 102 throws Exception { 103 final InputStream is = Support_Resources.getStream(name); 104 assertNotNull("File does not exist: " + name, is); 105 try { 106 X509Certificate c = (X509Certificate) f.generateCertificate(is); 107 assertNotNull(c); 108 return c; 109 } finally { 110 try { 111 is.close(); 112 } catch (IOException ignored) { 113 } 114 } 115 } 116 117 private final X509CRL getCRL(CertificateFactory f, String name) throws Exception { 118 final InputStream is = Support_Resources.getStream(name); 119 assertNotNull("File does not exist: " + name, is); 120 try { 121 X509CRL crl = (X509CRL) f.generateCRL(is); 122 assertNotNull(crl); 123 return crl; 124 } finally { 125 try { 126 is.close(); 127 } catch (IOException ignored) { 128 } 129 } 130 } 131 132 private byte[] getResourceAsBytes(String name) throws Exception { 133 final InputStream ris = Support_Resources.getStream(name); 134 try { 135 DataInputStream dis = new DataInputStream(ris); 136 byte[] buf = new byte[ris.available()]; 137 dis.readFully(buf); 138 return buf; 139 } finally { 140 try { 141 ris.close(); 142 } catch (IOException ignored) { 143 } 144 } 145 } 146 147 private Map<String, Date> getCrlDates(String name) throws Exception { 148 Map<String, Date> dates = new HashMap<String, Date>(); 149 final SimpleDateFormat sdf = new SimpleDateFormat("MMM dd HH:mm:ss yyyy zzz", Locale.US); 150 151 final InputStream ris = Support_Resources.getStream(name); 152 try { 153 154 final BufferedReader buf = new BufferedReader(new InputStreamReader(ris, UTF_8)); 155 156 String line; 157 while ((line = buf.readLine()) != null) { 158 int index = line.indexOf('='); 159 String key = line.substring(0, index); 160 final Date value = sdf.parse(line.substring(index + 1)); 161 dates.put(key, value); 162 } 163 164 return dates; 165 } finally { 166 try { 167 ris.close(); 168 } catch (IOException ignored) { 169 } 170 } 171 } 172 173 public void test_X509CRLImpl_verify() throws Exception { 174 CertificateFactory f = CertificateFactory.getInstance("X.509"); 175 X509CRL crlRsa = getCRL(f, CRL_RSA); 176 X509CRLImpl interned = X509Factory.intern(crlRsa); 177 X509Certificate caCert = getCertificate(f, CERT_CRL_CA); 178 interned.verify(caCert.getPublicKey(), f.getProvider()); 179 } 180 181 public void test_Provider() throws Exception { 182 final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream(); 183 PrintStream out = new PrintStream(errBuffer); 184 185 for (Provider p : mX509Providers) { 186 try { 187 CertificateFactory f = CertificateFactory.getInstance("X.509", p); 188 isRevoked(f); 189 getType(f); 190 getEncoded(f); 191 getVersion(f); 192 hasUnsupportedCriticalExtension(f); 193 getSignature(f); 194 getTBSCertList(f); 195 getRevokedCertificates(f); 196 getThisUpdateNextUpdate(f); 197 getSigAlgName(f); 198 getSigAlgOID(f); 199 getSigAlgParams(f); 200 verify(f); 201 test_toString(f); 202 test_equals(f); 203 } catch (Throwable e) { 204 out.append("Error encountered checking " + p.getName() + "\n"); 205 e.printStackTrace(out); 206 } 207 } 208 209 out.flush(); 210 if (errBuffer.size() > 0) { 211 throw new Exception("Errors encountered:\n\n" + errBuffer.toString() + "\n\n"); 212 } 213 } 214 215 private void verify(CertificateFactory f) throws Exception { 216 X509CRL crlRsa = getCRL(f, CRL_RSA); 217 218 X509Certificate caCert = getCertificate(f, CERT_CRL_CA); 219 220 // Test the "verify" method that doesn't specify the provider. 221 crlRsa.verify(caCert.getPublicKey()); 222 223 // Test the "verify" method that does specify the provider. 224 try { 225 crlRsa.verify(caCert.getPublicKey(), f.getProvider()); 226 } catch (UnsupportedOperationException unsupportedOperationException) { 227 // TODO(31294527): X590CRL objects from AndroidOpenSSL do not have this method. 228 // The "default" implementation from OpenJDK results in an infinite loop, so in libcore 229 // we throw an UnsupportedOperationException instead. 230 if (!f.getProvider().getName().equals("AndroidOpenSSL")) { 231 throw unsupportedOperationException; 232 } 233 } 234 235 X509Certificate dsaCert = getCertificate(f, CERT_DSA); 236 237 // Test the "verify" method that does specify the provider. 238 try { 239 crlRsa.verify(dsaCert.getPublicKey()); 240 fail("should not verify using incorrect key type"); 241 } catch (InvalidKeyException expected) { 242 } 243 244 // Test the "verify" method that does specify the provider. 245 try { 246 crlRsa.verify(dsaCert.getPublicKey(), f.getProvider()); 247 fail("should not verify using incorrect key type"); 248 } catch (InvalidKeyException expected) { 249 250 } catch (UnsupportedOperationException unsupportedOperationException) { 251 // TODO(31294527): X590CRL objects from AndroidOpenSSL do not have this method. 252 // The "default" implementation from OpenJDK results in an infinite loop, so in libcore 253 // we throw an UnsupportedOperationException instead. 254 if (!f.getProvider().getName().equals("AndroidOpenSSL")) { 255 throw unsupportedOperationException; 256 } 257 } 258 } 259 260 private void getType(CertificateFactory f) throws Exception { 261 CRL crlRsa = getCRL(f, CRL_RSA); 262 263 assertEquals("X.509", crlRsa.getType()); 264 } 265 266 private void isRevoked(CertificateFactory f) throws Exception { 267 X509Certificate rsaCert = getCertificate(f, CERT_RSA); 268 X509Certificate dsaCert = getCertificate(f, CERT_DSA); 269 X509CRL crlRsa = getCRL(f, CRL_RSA); 270 X509CRL crlRsaDsa = getCRL(f, CRL_RSA_DSA); 271 272 assertTrue(crlRsa.isRevoked(rsaCert)); 273 assertFalse(crlRsa.isRevoked(dsaCert)); 274 275 assertTrue(crlRsaDsa.isRevoked(rsaCert)); 276 assertTrue(crlRsaDsa.isRevoked(dsaCert)); 277 278 try { 279 assertFalse(crlRsa.isRevoked(null)); 280 if ("BC".equals(f.getProvider().getName())) { 281 fail("BouncyCastle throws on null input"); 282 } 283 } catch (NullPointerException e) { 284 if (!"BC".equals(f.getProvider().getName())) { 285 fail("Should not throw on null input"); 286 } 287 } 288 } 289 290 private void getThisUpdateNextUpdate(CertificateFactory f) throws Exception { 291 { 292 X509CRL crl = getCRL(f, CRL_RSA); 293 Map<String, Date> dates = getCrlDates(CRL_RSA_DATES); 294 295 Date lastUpdate = dates.get("lastUpdate"); 296 Date nextUpdate = dates.get("nextUpdate"); 297 298 assertNotNull(lastUpdate); 299 assertNotNull(nextUpdate); 300 301 assertDateEquals(lastUpdate, crl.getThisUpdate()); 302 assertDateEquals(nextUpdate, crl.getNextUpdate()); 303 } 304 305 { 306 X509CRL crl = getCRL(f, CRL_RSA_DSA); 307 Map<String, Date> dates = getCrlDates(CRL_RSA_DSA_DATES); 308 309 Date lastUpdate = dates.get("lastUpdate"); 310 Date nextUpdate = dates.get("nextUpdate"); 311 312 assertNotNull(lastUpdate); 313 assertNotNull(nextUpdate); 314 315 assertDateEquals(lastUpdate, crl.getThisUpdate()); 316 assertDateEquals(nextUpdate, crl.getNextUpdate()); 317 } 318 } 319 320 private void getSigAlgName(CertificateFactory f) throws Exception { 321 X509CRL crlRsa = getCRL(f, CRL_RSA); 322 assertEquals("SHA1WITHRSA", getCRL(f, CRL_RSA).getSigAlgName().toUpperCase(Locale.ROOT)); 323 } 324 325 private void getSigAlgOID(CertificateFactory f) throws Exception { 326 X509CRL crlRsa = getCRL(f, CRL_RSA); 327 328 assertEquals("1.2.840.113549.1.1.5", crlRsa.getSigAlgOID()); 329 } 330 331 private void getVersion(CertificateFactory f) throws Exception { 332 X509CRL crlRsa = getCRL(f, CRL_RSA); 333 334 assertEquals(1, crlRsa.getVersion()); 335 } 336 337 private void hasUnsupportedCriticalExtension(CertificateFactory f) throws Exception { 338 X509CRL crlRsa = getCRL(f, CRL_RSA); 339 assertFalse(crlRsa.hasUnsupportedCriticalExtension()); 340 341 X509CRL unsupportedCrl = getCRL(f, CRL_UNSUPPORTED); 342 assertTrue(unsupportedCrl.hasUnsupportedCriticalExtension()); 343 } 344 345 private void getSignature(CertificateFactory f) throws Exception { 346 X509CRL crlRsa = getCRL(f, CRL_RSA); 347 byte[] expected = getResourceAsBytes(CRL_RSA_SIG); 348 349 assertEquals(Arrays.toString(expected), Arrays.toString(crlRsa.getSignature())); 350 } 351 352 private void getTBSCertList(CertificateFactory f) throws Exception { 353 X509CRL crlRsa = getCRL(f, CRL_RSA); 354 byte[] expected = getResourceAsBytes(CRL_RSA_TBS); 355 356 assertEquals(Arrays.toString(expected), Arrays.toString(crlRsa.getTBSCertList())); 357 } 358 359 private void getEncoded(CertificateFactory f) throws Exception { 360 X509CRL crlRsa = getCRL(f, CRL_RSA); 361 362 byte[] crlRsaBytes = getResourceAsBytes(CRL_RSA); 363 364 assertEquals(Arrays.toString(crlRsaBytes), Arrays.toString(crlRsa.getEncoded())); 365 } 366 367 private static void assertDateEquals(Date date1, Date date2) throws Exception { 368 SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss"); 369 370 String result1 = formatter.format(date1); 371 String result2 = formatter.format(date2); 372 373 assertEquals(result1, result2); 374 } 375 376 /* 377 * This is needed because the certificate revocation in our CRL can be a 378 * couple seconds ahead of the lastUpdate time in the CRL. 379 */ 380 private static void assertDateSlightlyBefore(Date expected, Date actual) throws Exception { 381 Calendar c = Calendar.getInstance(); 382 383 // Make sure it's within 2 seconds of expected. 384 c.setTime(expected); 385 c.add(Calendar.SECOND, -2); 386 assertTrue(actual.after(c.getTime())); 387 388 // Before or equal... 389 assertTrue(actual.before(expected) || actual.equals(expected)); 390 } 391 392 private void assertRsaCrlEntry(CertificateFactory f, X509CRLEntry rsaEntry) throws Exception { 393 assertNotNull(rsaEntry); 394 395 X509Certificate rsaCert = getCertificate(f, CERT_RSA); 396 Map<String, Date> dates = getCrlDates(CRL_RSA_DSA_DATES); 397 Date expectedDate = dates.get("lastUpdate"); 398 399 assertEquals(rsaCert.getSerialNumber(), rsaEntry.getSerialNumber()); 400 assertDateSlightlyBefore(expectedDate, rsaEntry.getRevocationDate()); 401 assertNull(rsaEntry.getRevocationReason()); 402 assertNull(rsaEntry.getCertificateIssuer()); 403 assertFalse(rsaEntry.hasExtensions()); 404 assertNull(rsaEntry.getCriticalExtensionOIDs()); 405 assertNull(rsaEntry.getNonCriticalExtensionOIDs()); 406 407 assertNotNull(rsaEntry.toString()); 408 } 409 410 private void assertDsaCrlEntry(CertificateFactory f, X509CRLEntry dsaEntry) throws Exception { 411 assertNotNull(dsaEntry); 412 413 X509Certificate dsaCert = getCertificate(f, CERT_DSA); 414 Map<String, Date> dates = getCrlDates(CRL_RSA_DSA_DATES); 415 Date expectedDate = dates.get("lastUpdate"); 416 417 assertEquals(dsaCert.getSerialNumber(), dsaEntry.getSerialNumber()); 418 assertDateSlightlyBefore(expectedDate, dsaEntry.getRevocationDate()); 419 assertEquals(CRLReason.CESSATION_OF_OPERATION, dsaEntry.getRevocationReason()); 420 assertNull(dsaEntry.getCertificateIssuer()); 421 assertTrue(dsaEntry.hasExtensions()); 422 assertNotNull(dsaEntry.getCriticalExtensionOIDs()); 423 assertEquals(0, dsaEntry.getCriticalExtensionOIDs().size()); 424 assertNotNull(dsaEntry.getNonCriticalExtensionOIDs()); 425 assertEquals(1, dsaEntry.getNonCriticalExtensionOIDs().size()); 426 assertTrue(Arrays.toString(dsaEntry.getNonCriticalExtensionOIDs().toArray()), 427 dsaEntry.getNonCriticalExtensionOIDs().contains("2.5.29.21")); 428 System.out.println(Arrays.toString(dsaEntry.getExtensionValue("2.5.29.21"))); 429 430 assertNotNull(dsaEntry.toString()); 431 } 432 433 private void getRevokedCertificates(CertificateFactory f) throws Exception { 434 X509CRL crlEmpty = getCRL(f, CRL_EMPTY); 435 assertNull(crlEmpty.getRevokedCertificates()); 436 437 X509CRL crlRsa = getCRL(f, CRL_RSA); 438 X509Certificate rsaCert = getCertificate(f, CERT_RSA); 439 X509Certificate dsaCert = getCertificate(f, CERT_DSA); 440 441 Set<? extends X509CRLEntry> entries = crlRsa.getRevokedCertificates(); 442 assertEquals(1, entries.size()); 443 for (X509CRLEntry e : entries) { 444 assertRsaCrlEntry(f, e); 445 assertRsaCrlEntry(f, crlRsa.getRevokedCertificate(e.getSerialNumber())); 446 } 447 448 X509CRL crlRsaDsa = getCRL(f, CRL_RSA_DSA); 449 Set<? extends X509CRLEntry> entries2 = crlRsaDsa.getRevokedCertificates(); 450 assertEquals(2, entries2.size()); 451 assertRsaCrlEntry(f, crlRsaDsa.getRevokedCertificate(rsaCert)); 452 assertRsaCrlEntry(f, crlRsaDsa.getRevokedCertificate(rsaCert.getSerialNumber())); 453 assertDsaCrlEntry(f, crlRsaDsa.getRevokedCertificate(dsaCert)); 454 assertDsaCrlEntry(f, crlRsaDsa.getRevokedCertificate(dsaCert.getSerialNumber())); 455 } 456 457 private void getSigAlgParams(CertificateFactory f) throws Exception { 458 X509CRL crl1 = getCRL(f, CRL_RSA); 459 final byte[] sigAlgParams = crl1.getSigAlgParams(); 460 if (StandardNames.IS_RI) { 461 assertNull(f.getProvider().getName(), sigAlgParams); 462 } else { 463 assertNotNull(f.getProvider().getName(), sigAlgParams); 464 /* ASN.1 NULL */ 465 final byte[] expected = new byte[] { 466 0x05, 0x00, 467 }; 468 assertEquals(f.getProvider().getName(), Arrays.toString(expected), 469 Arrays.toString(sigAlgParams)); 470 } 471 472 { 473 X509CRL crlSigOpt = getCRL(f, CRL_RSA_DSA_SIGOPT); 474 475 /* SEQUENCE, INTEGER 1 */ 476 final byte[] expected = new byte[] { 477 /* SEQUENCE, constructed, len=5 */ 478 (byte) 0x30, (byte) 0x05, 479 /* Type=2, constructed, context-specific, len=3 */ 480 (byte) 0xA2, (byte) 0x03, 481 /* INTEGER, len=1, value=1 */ 482 (byte) 0x02, (byte) 0x01, (byte) 0x01, 483 }; 484 485 final byte[] params = crlSigOpt.getSigAlgParams(); 486 assertNotNull(f.getProvider().getName(), params); 487 assertEquals(Arrays.toString(expected), Arrays.toString(params)); 488 } 489 } 490 491 private void test_toString(CertificateFactory f) throws Exception { 492 X509CRL crl1 = getCRL(f, CRL_RSA); 493 X509CRL crl2 = getCRL(f, CRL_RSA); 494 495 X509CRL crlRsaDsa = getCRL(f, CRL_RSA_DSA); 496 497 assertNotNull(crl1); 498 499 assertNotNull(crlRsaDsa); 500 501 assertEquals(crl1.toString(), crl2.toString()); 502 503 assertFalse(crl1.toString().equals(crlRsaDsa.toString())); 504 } 505 506 private void test_equals(CertificateFactory f) throws Exception { 507 X509CRL crl1 = getCRL(f, CRL_RSA); 508 X509CRL crl2 = getCRL(f, CRL_RSA); 509 X509Certificate rsaCert = getCertificate(f, CERT_RSA); 510 511 X509CRL crlRsaDsa = getCRL(f, CRL_RSA_DSA); 512 513 assertEquals(crl1, crl2); 514 assertFalse(crl1.equals(crlRsaDsa)); 515 516 X509CRLEntry entry1 = crl1.getRevokedCertificate(rsaCert); 517 assertNotNull(entry1); 518 X509CRLEntry entry2 = crl2.getRevokedCertificate(rsaCert); 519 assertNotNull(entry2); 520 521 assertEquals(entry1, entry2); 522 } 523 } 524