1 /* 2 * Copyright (C) 2011 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 org.conscrypt; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertTrue; 21 import static org.junit.Assert.fail; 22 23 import java.io.IOException; 24 import java.security.KeyStore; 25 import java.security.Principal; 26 import java.security.cert.Certificate; 27 import java.security.cert.CertificateException; 28 import java.security.cert.X509Certificate; 29 import java.util.Arrays; 30 import java.util.List; 31 import javax.net.ssl.HandshakeCompletedListener; 32 import javax.net.ssl.HttpsURLConnection; 33 import javax.net.ssl.SSLParameters; 34 import javax.net.ssl.SSLPeerUnverifiedException; 35 import javax.net.ssl.SSLSession; 36 import javax.net.ssl.SSLSessionContext; 37 import javax.net.ssl.SSLSocket; 38 import javax.net.ssl.X509TrustManager; 39 import org.conscrypt.java.security.TestKeyStore; 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 import org.junit.runners.JUnit4; 43 44 @RunWith(JUnit4.class) 45 public class TrustManagerImplTest { 46 47 /** 48 * Ensure that our non-standard behavior of learning to trust new 49 * intermediate CAs does not regress. http://b/3404902 50 */ 51 @Test 52 public void testLearnIntermediate() throws Exception { 53 TestUtils.assumeExtendedTrustManagerAvailable(); 54 // chain3 should be server/intermediate/root 55 KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); 56 X509Certificate[] chain3 = (X509Certificate[])pke.getCertificateChain(); 57 X509Certificate root = chain3[2]; 58 X509Certificate intermediate = chain3[1]; 59 X509Certificate server = chain3[0]; 60 X509Certificate[] chain2 = new X509Certificate[] { server, intermediate }; 61 X509Certificate[] chain1 = new X509Certificate[] { server }; 62 63 // Normal behavior 64 assertValid(chain3, trustManager(root)); 65 assertValid(chain2, trustManager(root)); 66 assertInvalid(chain1, trustManager(root)); 67 assertValid(chain3, trustManager(intermediate)); 68 assertValid(chain2, trustManager(intermediate)); 69 assertValid(chain1, trustManager(intermediate)); 70 assertValid(chain3, trustManager(server)); 71 assertValid(chain2, trustManager(server)); 72 assertValid(chain1, trustManager(server)); 73 74 // non-standard behavior 75 X509TrustManager tm = trustManager(root); 76 // fail on short chain with only root trusted 77 assertInvalid(chain1, tm); 78 // succeed on longer chain, learn intermediate 79 assertValid(chain2, tm); 80 // now we can validate the short chain 81 assertValid(chain1, tm); 82 } 83 84 // We should ignore duplicate cruft in the certificate chain 85 // See https://code.google.com/p/android/issues/detail?id=52295 http://b/8313312 86 @Test 87 public void testDuplicateInChain() throws Exception { 88 TestUtils.assumeExtendedTrustManagerAvailable(); 89 // chain3 should be server/intermediate/root 90 KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); 91 X509Certificate[] chain3 = (X509Certificate[])pke.getCertificateChain(); 92 X509Certificate root = chain3[2]; 93 X509Certificate intermediate = chain3[1]; 94 X509Certificate server = chain3[0]; 95 96 X509Certificate[] chain4 = new X509Certificate[] { server, intermediate, 97 server, intermediate 98 }; 99 assertValid(chain4, trustManager(root)); 100 } 101 102 @Test 103 public void testGetFullChain() throws Exception { 104 TestUtils.assumeExtendedTrustManagerAvailable(); 105 // build the trust manager 106 KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); 107 X509Certificate[] chain3 = (X509Certificate[]) pke.getCertificateChain(); 108 X509Certificate root = chain3[2]; 109 X509TrustManager tm = trustManager(root); 110 111 // build the chains we'll use for testing 112 X509Certificate intermediate = chain3[1]; 113 X509Certificate server = chain3[0]; 114 X509Certificate[] chain2 = new X509Certificate[] { server, intermediate }; 115 X509Certificate[] chain1 = new X509Certificate[] { server }; 116 117 assertTrue(tm instanceof TrustManagerImpl); 118 TrustManagerImpl tmi = (TrustManagerImpl) tm; 119 List<X509Certificate> certs = tmi.checkServerTrusted(chain2, "RSA", new FakeSSLSession( 120 "purple.com")); 121 assertEquals(Arrays.asList(chain3), certs); 122 certs = tmi.checkServerTrusted(chain1, "RSA", new FakeSSLSession("purple.com")); 123 assertEquals(Arrays.asList(chain3), certs); 124 } 125 126 @Test 127 public void testHttpsEndpointIdentification() throws Exception { 128 TestUtils.assumeExtendedTrustManagerAvailable(); 129 130 KeyStore.PrivateKeyEntry pke = TestKeyStore.getServerHostname().getPrivateKey("RSA", "RSA"); 131 X509Certificate[] chain = (X509Certificate[]) pke.getCertificateChain(); 132 X509Certificate root = chain[2]; 133 TrustManagerImpl tmi = (TrustManagerImpl) trustManager(root); 134 135 String goodHostname = TestKeyStore.CERT_HOSTNAME; 136 String badHostname = "definitelywrong.nopenopenope"; 137 138 // The default hostname verifier on OpenJDK rejects all hostnames, so use our own 139 javax.net.ssl.HostnameVerifier oldDefault = HttpsURLConnection.getDefaultHostnameVerifier(); 140 try { 141 HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier()); 142 143 SSLParameters params = new SSLParameters(); 144 145 // Without endpoint identification this should pass despite the mismatched hostname 146 params.setEndpointIdentificationAlgorithm(null); 147 148 List<X509Certificate> certs = tmi.getTrustedChainForServer(chain, "RSA", 149 new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); 150 assertEquals(Arrays.asList(chain), certs); 151 152 // Turn on endpoint identification 153 params.setEndpointIdentificationAlgorithm("HTTPS"); 154 155 try { 156 tmi.getTrustedChainForServer(chain, "RSA", 157 new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); 158 } catch (CertificateException expected) { 159 } 160 161 certs = tmi.getTrustedChainForServer(chain, "RSA", 162 new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params)); 163 assertEquals(Arrays.asList(chain), certs); 164 165 // Override the global default hostname verifier with a Conscrypt-specific one that 166 // always passes. Both scenarios should pass. 167 Conscrypt.setDefaultHostnameVerifier(new ConscryptHostnameVerifier() { 168 @Override public boolean verify(String s, SSLSession sslSession) { return true; } 169 }); 170 171 certs = tmi.getTrustedChainForServer(chain, "RSA", 172 new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); 173 assertEquals(Arrays.asList(chain), certs); 174 175 certs = tmi.getTrustedChainForServer(chain, "RSA", 176 new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params)); 177 assertEquals(Arrays.asList(chain), certs); 178 179 // Now set an instance-specific verifier on the trust manager. The bad hostname should 180 // fail again. 181 Conscrypt.setHostnameVerifier(tmi, new TestHostnameVerifier()); 182 183 try { 184 tmi.getTrustedChainForServer(chain, "RSA", 185 new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); 186 } catch (CertificateException expected) { 187 } 188 189 certs = tmi.getTrustedChainForServer(chain, "RSA", 190 new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params)); 191 assertEquals(Arrays.asList(chain), certs); 192 193 // Remove the instance-specific verifier, and both should pass again. 194 Conscrypt.setHostnameVerifier(tmi, null); 195 196 certs = tmi.getTrustedChainForServer(chain, "RSA", 197 new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params)); 198 assertEquals(Arrays.asList(chain), certs); 199 200 certs = tmi.getTrustedChainForServer(chain, "RSA", 201 new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params)); 202 assertEquals(Arrays.asList(chain), certs); 203 } finally { 204 Conscrypt.setDefaultHostnameVerifier(null); 205 HttpsURLConnection.setDefaultHostnameVerifier(oldDefault); 206 } 207 } 208 209 private X509TrustManager trustManager(X509Certificate ca) throws Exception { 210 KeyStore keyStore = TestKeyStore.createKeyStore(); 211 keyStore.setCertificateEntry("alias", ca); 212 213 return new TrustManagerImpl(keyStore); 214 } 215 216 private void assertValid(X509Certificate[] chain, X509TrustManager tm) throws Exception { 217 if (tm instanceof TrustManagerImpl) { 218 TrustManagerImpl tmi = (TrustManagerImpl) tm; 219 tmi.checkServerTrusted(chain, "RSA"); 220 } 221 tm.checkServerTrusted(chain, "RSA"); 222 } 223 224 private void assertInvalid(X509Certificate[] chain, X509TrustManager tm) { 225 try { 226 tm.checkClientTrusted(chain, "RSA"); 227 fail(); 228 } catch (CertificateException expected) { 229 // Expected. 230 } 231 try { 232 tm.checkServerTrusted(chain, "RSA"); 233 fail(); 234 } catch (CertificateException expected) { 235 // Expected. 236 } 237 } 238 239 private static class FakeSSLSession implements SSLSession { 240 private final String hostname; 241 private final X509Certificate[] peerCerts; 242 243 FakeSSLSession(String hostname) { 244 this.hostname = hostname; 245 peerCerts = null; 246 } 247 248 FakeSSLSession(String hostname, X509Certificate[] peerCerts) { 249 this.hostname = hostname; 250 this.peerCerts = peerCerts.clone(); 251 } 252 253 @Override 254 public int getApplicationBufferSize() { 255 throw new UnsupportedOperationException(); 256 } 257 258 @Override 259 public String getCipherSuite() { 260 throw new UnsupportedOperationException(); 261 } 262 263 @Override 264 public long getCreationTime() { 265 throw new UnsupportedOperationException(); 266 } 267 268 @Override 269 public byte[] getId() { 270 throw new UnsupportedOperationException(); 271 } 272 273 @Override 274 public long getLastAccessedTime() { 275 throw new UnsupportedOperationException(); 276 } 277 278 @Override 279 public Certificate[] getLocalCertificates() { 280 throw new UnsupportedOperationException(); 281 } 282 283 @Override 284 public Principal getLocalPrincipal() { 285 throw new UnsupportedOperationException(); 286 } 287 288 @Override 289 public int getPacketBufferSize() { 290 throw new UnsupportedOperationException(); 291 } 292 293 @Override 294 public javax.security.cert.X509Certificate[] getPeerCertificateChain() 295 throws SSLPeerUnverifiedException { 296 throw new UnsupportedOperationException(); 297 } 298 299 @Override 300 public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { 301 if (peerCerts == null) { 302 throw new SSLPeerUnverifiedException("Null peerCerts"); 303 } else { 304 return peerCerts.clone(); 305 } 306 } 307 308 @Override 309 public String getPeerHost() { 310 return hostname; 311 } 312 313 @Override 314 public int getPeerPort() { 315 throw new UnsupportedOperationException(); 316 } 317 318 @Override 319 public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { 320 throw new UnsupportedOperationException(); 321 } 322 323 @Override 324 public String getProtocol() { 325 throw new UnsupportedOperationException(); 326 } 327 328 @Override 329 public SSLSessionContext getSessionContext() { 330 throw new UnsupportedOperationException(); 331 } 332 333 @Override 334 public Object getValue(String name) { 335 throw new UnsupportedOperationException(); 336 } 337 338 @Override 339 public String[] getValueNames() { 340 throw new UnsupportedOperationException(); 341 } 342 343 @Override 344 public void invalidate() { 345 throw new UnsupportedOperationException(); 346 } 347 348 @Override 349 public boolean isValid() { 350 throw new UnsupportedOperationException(); 351 } 352 353 @Override 354 public void putValue(String name, Object value) { 355 throw new UnsupportedOperationException(); 356 } 357 358 @Override 359 public void removeValue(String name) { 360 throw new UnsupportedOperationException(); 361 } 362 } 363 364 private static class FakeSSLSocket extends SSLSocket { 365 366 private final SSLSession session; 367 private final SSLParameters parameters; 368 369 public FakeSSLSocket(SSLSession session, SSLParameters parameters) { 370 this.session = session; 371 this.parameters = parameters; 372 } 373 374 @Override 375 public SSLParameters getSSLParameters() { 376 return parameters; 377 } 378 379 @Override 380 public String[] getSupportedCipherSuites() { 381 throw new UnsupportedOperationException(); 382 } 383 384 @Override 385 public String[] getEnabledCipherSuites() { 386 throw new UnsupportedOperationException(); 387 } 388 389 @Override 390 public void setEnabledCipherSuites(String[] strings) { 391 throw new UnsupportedOperationException(); 392 } 393 394 @Override 395 public String[] getSupportedProtocols() { 396 throw new UnsupportedOperationException(); 397 } 398 399 @Override 400 public String[] getEnabledProtocols() { 401 throw new UnsupportedOperationException(); 402 } 403 404 @Override 405 public void setEnabledProtocols(String[] strings) { 406 throw new UnsupportedOperationException(); 407 } 408 409 @Override 410 public SSLSession getSession() { 411 return session; 412 } 413 414 @Override 415 public SSLSession getHandshakeSession() { 416 return session; 417 } 418 419 @Override 420 public void addHandshakeCompletedListener( 421 HandshakeCompletedListener handshakeCompletedListener) { 422 throw new UnsupportedOperationException(); 423 } 424 425 @Override 426 public void removeHandshakeCompletedListener( 427 HandshakeCompletedListener handshakeCompletedListener) { 428 throw new UnsupportedOperationException(); 429 } 430 431 @Override 432 public void startHandshake() throws IOException { 433 throw new UnsupportedOperationException(); 434 } 435 436 @Override 437 public void setUseClientMode(boolean b) { 438 throw new UnsupportedOperationException(); 439 } 440 441 @Override 442 public boolean getUseClientMode() { 443 throw new UnsupportedOperationException(); 444 } 445 446 @Override 447 public void setNeedClientAuth(boolean b) { 448 throw new UnsupportedOperationException(); 449 } 450 451 @Override 452 public boolean getNeedClientAuth() { 453 throw new UnsupportedOperationException(); 454 } 455 456 @Override 457 public void setWantClientAuth(boolean b) { 458 throw new UnsupportedOperationException(); 459 } 460 461 @Override 462 public boolean getWantClientAuth() { 463 throw new UnsupportedOperationException(); 464 } 465 466 @Override 467 public void setEnableSessionCreation(boolean b) { 468 throw new UnsupportedOperationException(); 469 } 470 471 @Override 472 public boolean getEnableSessionCreation() { 473 throw new UnsupportedOperationException(); 474 } 475 } 476 477 private static class TestHostnameVerifier 478 extends org.conscrypt.javax.net.ssl.TestHostnameVerifier 479 implements ConscryptHostnameVerifier {} 480 } 481