Home | History | Annotate | Download | only in cert
      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