Home | History | Annotate | Download | only in tls
      1 /*
      2  * Copyright (C) 2016 Square, Inc.
      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.squareup.okhttp.internal.tls;
     17 
     18 import com.squareup.okhttp.internal.HeldCertificate;
     19 import java.security.GeneralSecurityException;
     20 import java.security.cert.Certificate;
     21 import java.security.cert.X509Certificate;
     22 import java.util.ArrayList;
     23 import java.util.List;
     24 import javax.net.ssl.SSLPeerUnverifiedException;
     25 import org.junit.Test;
     26 
     27 import static org.junit.Assert.assertEquals;
     28 import static org.junit.Assert.fail;
     29 
     30 public final class CertificateChainCleanerTest {
     31   @Test public void normalizeSingleSelfSignedCertificate() throws Exception {
     32     HeldCertificate root = new HeldCertificate.Builder()
     33         .serialNumber("1")
     34         .build();
     35     CertificateChainCleaner council = new CertificateChainCleaner(
     36         new RealTrustRootIndex(root.certificate));
     37     assertEquals(list(root), council.clean(list(root)));
     38   }
     39 
     40   @Test public void normalizeUnknownSelfSignedCertificate() throws Exception {
     41     HeldCertificate root = new HeldCertificate.Builder()
     42         .serialNumber("1")
     43         .build();
     44     CertificateChainCleaner council = new CertificateChainCleaner(new RealTrustRootIndex());
     45 
     46     try {
     47       council.clean(list(root));
     48       fail();
     49     } catch (SSLPeerUnverifiedException expected) {
     50     }
     51   }
     52 
     53   @Test public void orderedChainOfCertificatesWithRoot() throws Exception {
     54     HeldCertificate root = new HeldCertificate.Builder()
     55         .serialNumber("1")
     56         .build();
     57     HeldCertificate certA = new HeldCertificate.Builder()
     58         .serialNumber("2")
     59         .issuedBy(root)
     60         .build();
     61     HeldCertificate certB = new HeldCertificate.Builder()
     62         .serialNumber("3")
     63         .issuedBy(certA)
     64         .build();
     65 
     66     CertificateChainCleaner council = new CertificateChainCleaner(
     67         new RealTrustRootIndex(root.certificate));
     68     assertEquals(list(certB, certA, root), council.clean(list(certB, certA, root)));
     69   }
     70 
     71   @Test public void orderedChainOfCertificatesWithoutRoot() throws Exception {
     72     HeldCertificate root = new HeldCertificate.Builder()
     73         .serialNumber("1")
     74         .build();
     75     HeldCertificate certA = new HeldCertificate.Builder()
     76         .serialNumber("2")
     77         .issuedBy(root)
     78         .build();
     79     HeldCertificate certB = new HeldCertificate.Builder()
     80         .serialNumber("3")
     81         .issuedBy(certA)
     82         .build();
     83 
     84     CertificateChainCleaner council = new CertificateChainCleaner(
     85         new RealTrustRootIndex(root.certificate));
     86     assertEquals(list(certB, certA, root), council.clean(list(certB, certA))); // Root is added!
     87   }
     88 
     89   @Test public void unorderedChainOfCertificatesWithRoot() throws Exception {
     90     HeldCertificate root = new HeldCertificate.Builder()
     91         .serialNumber("1")
     92         .build();
     93     HeldCertificate certA = new HeldCertificate.Builder()
     94         .serialNumber("2")
     95         .issuedBy(root)
     96         .build();
     97     HeldCertificate certB = new HeldCertificate.Builder()
     98         .serialNumber("3")
     99         .issuedBy(certA)
    100         .build();
    101     HeldCertificate certC = new HeldCertificate.Builder()
    102         .serialNumber("4")
    103         .issuedBy(certB)
    104         .build();
    105 
    106     CertificateChainCleaner council = new CertificateChainCleaner(
    107         new RealTrustRootIndex(root.certificate));
    108     assertEquals(list(certC, certB, certA, root), council.clean(list(certC, certA, root, certB)));
    109   }
    110 
    111   @Test public void unorderedChainOfCertificatesWithoutRoot() throws Exception {
    112     HeldCertificate root = new HeldCertificate.Builder()
    113         .serialNumber("1")
    114         .build();
    115     HeldCertificate certA = new HeldCertificate.Builder()
    116         .serialNumber("2")
    117         .issuedBy(root)
    118         .build();
    119     HeldCertificate certB = new HeldCertificate.Builder()
    120         .serialNumber("3")
    121         .issuedBy(certA)
    122         .build();
    123     HeldCertificate certC = new HeldCertificate.Builder()
    124         .serialNumber("4")
    125         .issuedBy(certB)
    126         .build();
    127 
    128     CertificateChainCleaner council = new CertificateChainCleaner(
    129         new RealTrustRootIndex(root.certificate));
    130     assertEquals(list(certC, certB, certA, root), council.clean(list(certC, certA, certB)));
    131   }
    132 
    133   @Test public void unrelatedCertificatesAreOmitted() throws Exception {
    134     HeldCertificate root = new HeldCertificate.Builder()
    135         .serialNumber("1")
    136         .build();
    137     HeldCertificate certA = new HeldCertificate.Builder()
    138         .serialNumber("2")
    139         .issuedBy(root)
    140         .build();
    141     HeldCertificate certB = new HeldCertificate.Builder()
    142         .serialNumber("3")
    143         .issuedBy(certA)
    144         .build();
    145     HeldCertificate certUnnecessary = new HeldCertificate.Builder()
    146         .serialNumber("4")
    147         .build();
    148 
    149     CertificateChainCleaner council = new CertificateChainCleaner(
    150         new RealTrustRootIndex(root.certificate));
    151     assertEquals(list(certB, certA, root),
    152         council.clean(list(certB, certUnnecessary, certA, root)));
    153   }
    154 
    155   @Test public void chainGoesAllTheWayToSelfSignedRoot() throws Exception {
    156     HeldCertificate selfSigned = new HeldCertificate.Builder()
    157         .serialNumber("1")
    158         .build();
    159     HeldCertificate trusted = new HeldCertificate.Builder()
    160         .serialNumber("2")
    161         .issuedBy(selfSigned)
    162         .build();
    163     HeldCertificate certA = new HeldCertificate.Builder()
    164         .serialNumber("3")
    165         .issuedBy(trusted)
    166         .build();
    167     HeldCertificate certB = new HeldCertificate.Builder()
    168         .serialNumber("4")
    169         .issuedBy(certA)
    170         .build();
    171 
    172     CertificateChainCleaner council = new CertificateChainCleaner(
    173         new RealTrustRootIndex(selfSigned.certificate, trusted.certificate));
    174     assertEquals(list(certB, certA, trusted, selfSigned),
    175         council.clean(list(certB, certA)));
    176     assertEquals(list(certB, certA, trusted, selfSigned),
    177         council.clean(list(certB, certA, trusted)));
    178     assertEquals(list(certB, certA, trusted, selfSigned),
    179         council.clean(list(certB, certA, trusted, selfSigned)));
    180   }
    181 
    182   @Test public void trustedRootNotSelfSigned() throws Exception {
    183     HeldCertificate unknownSigner = new HeldCertificate.Builder()
    184         .serialNumber("1")
    185         .build();
    186     HeldCertificate trusted = new HeldCertificate.Builder()
    187         .issuedBy(unknownSigner)
    188         .serialNumber("2")
    189         .build();
    190     HeldCertificate intermediateCa = new HeldCertificate.Builder()
    191         .issuedBy(trusted)
    192         .serialNumber("3")
    193         .build();
    194     HeldCertificate certificate = new HeldCertificate.Builder()
    195         .issuedBy(intermediateCa)
    196         .serialNumber("4")
    197         .build();
    198 
    199     CertificateChainCleaner council = new CertificateChainCleaner(
    200         new RealTrustRootIndex(trusted.certificate));
    201     assertEquals(list(certificate, intermediateCa, trusted),
    202         council.clean(list(certificate, intermediateCa)));
    203     assertEquals(list(certificate, intermediateCa, trusted),
    204         council.clean(list(certificate, intermediateCa, trusted)));
    205   }
    206 
    207   @Test public void chainMaxLength() throws Exception {
    208     List<HeldCertificate> heldCertificates = chainOfLength(10);
    209     List<Certificate> certificates = new ArrayList<>();
    210     for (HeldCertificate heldCertificate : heldCertificates) {
    211       certificates.add(heldCertificate.certificate);
    212     }
    213 
    214     X509Certificate root = heldCertificates.get(heldCertificates.size() - 1).certificate;
    215     CertificateChainCleaner council = new CertificateChainCleaner(new RealTrustRootIndex(root));
    216     assertEquals(certificates, council.clean(certificates));
    217     assertEquals(certificates, council.clean(certificates.subList(0, 9)));
    218   }
    219 
    220   @Test public void chainTooLong() throws Exception {
    221     List<HeldCertificate> heldCertificates = chainOfLength(11);
    222     List<Certificate> certificates = new ArrayList<>();
    223     for (HeldCertificate heldCertificate : heldCertificates) {
    224       certificates.add(heldCertificate.certificate);
    225     }
    226 
    227     X509Certificate root = heldCertificates.get(heldCertificates.size() - 1).certificate;
    228     CertificateChainCleaner council = new CertificateChainCleaner(new RealTrustRootIndex(root));
    229     try {
    230       council.clean(certificates);
    231       fail();
    232     } catch (SSLPeerUnverifiedException expected) {
    233     }
    234   }
    235 
    236   /** Returns a chain starting at the leaf certificate and progressing to the root. */
    237   private List<HeldCertificate> chainOfLength(int length) throws GeneralSecurityException {
    238     List<HeldCertificate> result = new ArrayList<>();
    239     for (int i = 1; i <= length; i++) {
    240       result.add(0, new HeldCertificate.Builder()
    241           .issuedBy(!result.isEmpty() ? result.get(0) : null)
    242           .serialNumber(Integer.toString(i))
    243           .build());
    244     }
    245     return result;
    246   }
    247 
    248   private List<Certificate> list(HeldCertificate... heldCertificates) {
    249     List<Certificate> result = new ArrayList<>();
    250     for (HeldCertificate heldCertificate : heldCertificates) {
    251       result.add(heldCertificate.certificate);
    252     }
    253     return result;
    254   }
    255 }
    256