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