1 /* 2 * Copyright 2014 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 android.annotation.SuppressLint; 20 import android.annotation.TargetApi; 21 import android.os.Build; 22 import android.util.Log; 23 import dalvik.system.BlockGuard; 24 import dalvik.system.CloseGuard; 25 import java.io.FileDescriptor; 26 import java.io.IOException; 27 import java.lang.reflect.Constructor; 28 import java.lang.reflect.Field; 29 import java.lang.reflect.InvocationTargetException; 30 import java.lang.reflect.Method; 31 import java.net.InetAddress; 32 import java.net.InetSocketAddress; 33 import java.net.Socket; 34 import java.net.SocketException; 35 import java.net.SocketImpl; 36 import java.security.AlgorithmParameters; 37 import java.security.KeyStore; 38 import java.security.KeyStoreException; 39 import java.security.NoSuchAlgorithmException; 40 import java.security.PrivateKey; 41 import java.security.Security; 42 import java.security.cert.CertificateException; 43 import java.security.cert.X509Certificate; 44 import java.security.spec.AlgorithmParameterSpec; 45 import java.security.spec.ECParameterSpec; 46 import java.security.spec.InvalidParameterSpecException; 47 import java.util.Arrays; 48 import java.util.Collections; 49 import java.util.List; 50 import javax.net.ssl.SNIHostName; 51 import javax.net.ssl.SNIServerName; 52 import javax.net.ssl.SSLEngine; 53 import javax.net.ssl.SSLParameters; 54 import javax.net.ssl.SSLSession; 55 import javax.net.ssl.SSLSocketFactory; 56 import javax.net.ssl.StandardConstants; 57 import javax.net.ssl.X509TrustManager; 58 import org.conscrypt.ct.CTLogStore; 59 import org.conscrypt.ct.CTPolicy; 60 61 /** 62 * Platform-specific methods for unbundled Android. 63 */ 64 final class Platform { 65 private static final String TAG = "Conscrypt"; 66 67 private static Method m_getCurveName; 68 static { 69 try { 70 m_getCurveName = ECParameterSpec.class.getDeclaredMethod("getCurveName"); 71 m_getCurveName.setAccessible(true); 72 } catch (Exception ignored) { 73 } 74 } 75 76 private Platform() {} 77 78 public static void setup() {} 79 80 /** 81 * Default name used in the {@link java.security.Security JCE system} by {@code OpenSSLProvider} 82 * if the default constructor is used. 83 */ 84 public static String getDefaultProviderName() { 85 return "Conscrypt"; 86 } 87 88 static boolean provideTrustManagerByDefault() { 89 return false; 90 } 91 92 public static FileDescriptor getFileDescriptor(Socket s) { 93 try { 94 Field f_impl = Socket.class.getDeclaredField("impl"); 95 f_impl.setAccessible(true); 96 Object socketImpl = f_impl.get(s); 97 Field f_fd = SocketImpl.class.getDeclaredField("fd"); 98 f_fd.setAccessible(true); 99 return (FileDescriptor) f_fd.get(socketImpl); 100 } catch (Exception e) { 101 throw new RuntimeException("Can't get FileDescriptor from socket", e); 102 } 103 } 104 105 public static FileDescriptor getFileDescriptorFromSSLSocket(AbstractConscryptSocket socket) { 106 return getFileDescriptor(socket); 107 } 108 109 public static String getCurveName(ECParameterSpec spec) { 110 if (m_getCurveName == null) { 111 return null; 112 } 113 try { 114 return (String) m_getCurveName.invoke(spec); 115 } catch (Exception e) { 116 return null; 117 } 118 } 119 120 public static void setCurveName(ECParameterSpec spec, String curveName) { 121 try { 122 Method setCurveName = spec.getClass().getDeclaredMethod("setCurveName", String.class); 123 setCurveName.invoke(spec, curveName); 124 } catch (Exception ignored) { 125 } 126 } 127 128 /** 129 * Call Os.setsockoptTimeval via reflection. 130 */ 131 public static void setSocketWriteTimeout(Socket s, long timeoutMillis) throws SocketException { 132 try { 133 FileDescriptor fd = getFileDescriptor(s); 134 if (fd == null || !fd.valid()) { 135 // Mirror the behavior of platform sockets when calling methods with bad fds 136 throw new SocketException("Socket closed"); 137 } 138 Class<?> c_structTimeval = 139 getClass("android.system.StructTimeval", "libcore.io.StructTimeval"); 140 if (c_structTimeval == null) { 141 Log.w(TAG, "StructTimeval == null; not setting socket write timeout"); 142 return; 143 } 144 145 Method m_fromMillis = c_structTimeval.getDeclaredMethod("fromMillis", long.class); 146 if (m_fromMillis == null) { 147 Log.w(TAG, "fromMillis == null; not setting socket write timeout"); 148 return; 149 } 150 151 Object timeval = m_fromMillis.invoke(null, timeoutMillis); 152 153 Class<?> c_Libcore = Class.forName("libcore.io.Libcore"); 154 if (c_Libcore == null) { 155 Log.w(TAG, "Libcore == null; not setting socket write timeout"); 156 return; 157 } 158 159 Field f_os = c_Libcore.getField("os"); 160 if (f_os == null) { 161 Log.w(TAG, "os == null; not setting socket write timeout"); 162 return; 163 } 164 165 Object instance_os = f_os.get(null); 166 if (instance_os == null) { 167 Log.w(TAG, "instance_os == null; not setting socket write timeout"); 168 return; 169 } 170 171 Class<?> c_osConstants = 172 getClass("android.system.OsConstants", "libcore.io.OsConstants"); 173 if (c_osConstants == null) { 174 Log.w(TAG, "OsConstants == null; not setting socket write timeout"); 175 return; 176 } 177 178 Field f_SOL_SOCKET = c_osConstants.getField("SOL_SOCKET"); 179 if (f_SOL_SOCKET == null) { 180 Log.w(TAG, "SOL_SOCKET == null; not setting socket write timeout"); 181 return; 182 } 183 184 Field f_SO_SNDTIMEO = c_osConstants.getField("SO_SNDTIMEO"); 185 if (f_SO_SNDTIMEO == null) { 186 Log.w(TAG, "SO_SNDTIMEO == null; not setting socket write timeout"); 187 return; 188 } 189 190 Method m_setsockoptTimeval = instance_os.getClass().getMethod("setsockoptTimeval", 191 FileDescriptor.class, int.class, int.class, c_structTimeval); 192 if (m_setsockoptTimeval == null) { 193 Log.w(TAG, "setsockoptTimeval == null; not setting socket write timeout"); 194 return; 195 } 196 197 m_setsockoptTimeval.invoke(instance_os, fd, f_SOL_SOCKET.get(null), 198 f_SO_SNDTIMEO.get(null), timeval); 199 } catch (Exception e) { 200 // We don't want to spam the logcat since this isn't a fatal error, but we want to know 201 // why this might be happening. 202 logStackTraceSnippet("Could not set socket write timeout: " + e, e); 203 Throwable cause = e.getCause(); 204 while (cause != null) { 205 logStackTraceSnippet("Caused by: " + cause, cause); 206 cause = cause.getCause(); 207 } 208 } 209 } 210 211 /** 212 * Logs an abbreviated stacktrace (summary and a couple of StackTraceElements). 213 */ 214 private static void logStackTraceSnippet(String summary, Throwable throwable) { 215 Log.w(TAG, summary); 216 StackTraceElement[] elements = throwable.getStackTrace(); 217 for (int i = 0; i < 2 && i < elements.length; i++) { 218 Log.w(TAG, "\tat " + elements[i].toString()); 219 } 220 } 221 222 private static void setSSLParametersOnImpl(SSLParameters params, SSLParametersImpl impl) 223 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 224 Method m_getEndpointIdentificationAlgorithm = 225 params.getClass().getMethod("getEndpointIdentificationAlgorithm"); 226 impl.setEndpointIdentificationAlgorithm( 227 (String) m_getEndpointIdentificationAlgorithm.invoke(params)); 228 229 Method m_getUseCipherSuitesOrder = params.getClass().getMethod("getUseCipherSuitesOrder"); 230 impl.setUseCipherSuitesOrder((boolean) m_getUseCipherSuitesOrder.invoke(params)); 231 } 232 233 public static void setSSLParameters( 234 SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) { 235 try { 236 setSSLParametersOnImpl(params, impl); 237 238 if (Build.VERSION.SDK_INT >= 24) { 239 String sniHostname = getSniHostnameFromParams(params); 240 if (sniHostname != null) { 241 socket.setHostname(sniHostname); 242 } 243 } 244 } catch (NoSuchMethodException ignored) { 245 } catch (IllegalAccessException ignored) { 246 } catch (InvocationTargetException e) { 247 throw new RuntimeException(e.getCause()); 248 } 249 } 250 251 public static void setSSLParameters( 252 SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) { 253 try { 254 setSSLParametersOnImpl(params, impl); 255 256 if (Build.VERSION.SDK_INT >= 24) { 257 String sniHostname = getSniHostnameFromParams(params); 258 if (sniHostname != null) { 259 engine.setHostname(sniHostname); 260 } 261 } 262 } catch (NoSuchMethodException ignored) { 263 } catch (IllegalAccessException ignored) { 264 } catch (InvocationTargetException e) { 265 throw new RuntimeException(e.getCause()); 266 } 267 } 268 269 @TargetApi(24) 270 private static String getSniHostnameFromParams(SSLParameters params) 271 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 272 Method m_getServerNames = params.getClass().getMethod("getServerNames"); 273 @SuppressWarnings("unchecked") 274 List<SNIServerName> serverNames = (List<SNIServerName>) m_getServerNames.invoke(params); 275 if (serverNames != null) { 276 for (SNIServerName serverName : serverNames) { 277 if (serverName.getType() == StandardConstants.SNI_HOST_NAME) { 278 return ((SNIHostName) serverName).getAsciiName(); 279 } 280 } 281 } 282 283 return null; 284 } 285 286 private static void getSSLParametersFromImpl(SSLParameters params, SSLParametersImpl impl) 287 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 288 Method m_setEndpointIdentificationAlgorithm = 289 params.getClass().getMethod("setEndpointIdentificationAlgorithm", String.class); 290 m_setEndpointIdentificationAlgorithm.invoke( 291 params, impl.getEndpointIdentificationAlgorithm()); 292 293 Method m_setUseCipherSuitesOrder = 294 params.getClass().getMethod("setUseCipherSuitesOrder", boolean.class); 295 m_setUseCipherSuitesOrder.invoke(params, impl.getUseCipherSuitesOrder()); 296 } 297 298 public static void getSSLParameters( 299 SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) { 300 try { 301 getSSLParametersFromImpl(params, impl); 302 303 if (Build.VERSION.SDK_INT >= 24) { 304 setParametersSniHostname(params, impl, socket); 305 } 306 } catch (NoSuchMethodException ignored) { 307 } catch (IllegalAccessException ignored) { 308 } catch (InvocationTargetException e) { 309 throw new RuntimeException(e.getCause()); 310 } 311 } 312 313 @TargetApi(24) 314 private static void setParametersSniHostname( 315 SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) 316 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 317 if (impl.getUseSni() && AddressUtils.isValidSniHostname(socket.getHostname())) { 318 Method m_setServerNames = params.getClass().getMethod("setServerNames", List.class); 319 m_setServerNames.invoke(params, 320 Collections.<SNIServerName>singletonList( 321 new SNIHostName(socket.getHostname()))); 322 } 323 } 324 325 public static void getSSLParameters( 326 SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) { 327 try { 328 getSSLParametersFromImpl(params, impl); 329 330 if (Build.VERSION.SDK_INT >= 24) { 331 setParametersSniHostname(params, impl, engine); 332 } 333 } catch (NoSuchMethodException ignored) { 334 } catch (IllegalAccessException ignored) { 335 } catch (InvocationTargetException e) { 336 throw new RuntimeException(e.getCause()); 337 } 338 } 339 340 @TargetApi(24) 341 private static void setParametersSniHostname( 342 SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) 343 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 344 if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getHostname())) { 345 Method m_setServerNames = params.getClass().getMethod("setServerNames", List.class); 346 m_setServerNames.invoke(params, 347 Collections.<SNIServerName>singletonList( 348 new SNIHostName(engine.getHostname()))); 349 } 350 } 351 352 /** 353 * Tries to return a Class reference of one of the supplied class names. 354 */ 355 private static Class<?> getClass(String... klasses) { 356 for (String klass : klasses) { 357 try { 358 return Class.forName(klass); 359 } catch (Exception ignored) { 360 } 361 } 362 return null; 363 } 364 365 public static void setEndpointIdentificationAlgorithm( 366 SSLParameters params, String endpointIdentificationAlgorithm) { 367 // TODO: implement this for unbundled 368 } 369 370 public static String getEndpointIdentificationAlgorithm(SSLParameters params) { 371 // TODO: implement this for unbundled 372 return null; 373 } 374 375 /** 376 * Helper function to unify calls to the different names used for each function taking a 377 * Socket, SSLEngine, or String (legacy Android). 378 */ 379 private static boolean checkTrusted(String methodName, X509TrustManager tm, 380 X509Certificate[] chain, String authType, Class<?> argumentClass, 381 Object argumentInstance) throws CertificateException { 382 // Use duck-typing to try and call the hostname-aware method if available. 383 try { 384 Method method = tm.getClass().getMethod( 385 methodName, X509Certificate[].class, String.class, argumentClass); 386 method.invoke(tm, chain, authType, argumentInstance); 387 return true; 388 } catch (NoSuchMethodException ignored) { 389 } catch (IllegalAccessException ignored) { 390 } catch (InvocationTargetException e) { 391 if (e.getCause() instanceof CertificateException) { 392 throw(CertificateException) e.getCause(); 393 } 394 throw new RuntimeException(e.getCause()); 395 } 396 return false; 397 } 398 399 @SuppressLint("NewApi") // AbstractConscryptSocket defines getHandshakeSession() 400 public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain, 401 String authType, AbstractConscryptSocket socket) throws CertificateException { 402 if (!checkTrusted("checkClientTrusted", tm, chain, authType, Socket.class, socket) 403 && !checkTrusted("checkClientTrusted", tm, chain, authType, String.class, 404 socket.getHandshakeSession().getPeerHost())) { 405 tm.checkClientTrusted(chain, authType); 406 } 407 } 408 409 @SuppressLint("NewApi") // AbstractConscryptSocket defines getHandshakeSession() 410 public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain, 411 String authType, AbstractConscryptSocket socket) throws CertificateException { 412 if (!checkTrusted("checkServerTrusted", tm, chain, authType, Socket.class, socket) 413 && !checkTrusted("checkServerTrusted", tm, chain, authType, String.class, 414 socket.getHandshakeSession().getPeerHost())) { 415 tm.checkServerTrusted(chain, authType); 416 } 417 } 418 419 @SuppressLint("NewApi") // AbstractConscryptSocket defines getHandshakeSession() 420 public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain, 421 String authType, ConscryptEngine engine) throws CertificateException { 422 if (!checkTrusted("checkClientTrusted", tm, chain, authType, SSLEngine.class, engine) 423 && !checkTrusted("checkClientTrusted", tm, chain, authType, String.class, 424 engine.getHandshakeSession().getPeerHost())) { 425 tm.checkClientTrusted(chain, authType); 426 } 427 } 428 429 @SuppressLint("NewApi") // AbstractConscryptSocket defines getHandshakeSession() 430 public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain, 431 String authType, ConscryptEngine engine) throws CertificateException { 432 if (!checkTrusted("checkServerTrusted", tm, chain, authType, SSLEngine.class, engine) 433 && !checkTrusted("checkServerTrusted", tm, chain, authType, String.class, 434 engine.getHandshakeSession().getPeerHost())) { 435 tm.checkServerTrusted(chain, authType); 436 } 437 } 438 439 /** 440 * Wraps an old AndroidOpenSSL key instance. This is not needed on platform 441 * builds since we didn't backport, so return null. This code is from 442 * Chromium's net/android/java/src/org/chromium/net/DefaultAndroidKeyStore.java 443 */ 444 @SuppressWarnings("LiteralClassName") 445 public static OpenSSLKey wrapRsaKey(PrivateKey javaKey) { 446 // This fixup only applies to pre-JB-MR1 447 if (Build.VERSION.SDK_INT >= 17) { 448 return null; 449 } 450 451 // First, check that this is a proper instance of OpenSSLRSAPrivateKey 452 // or one of its sub-classes. 453 Class<?> superClass; 454 try { 455 superClass = 456 Class.forName("org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey"); 457 } catch (Exception e) { 458 // This may happen if the target device has a completely different 459 // implementation of the java.security APIs, compared to vanilla 460 // Android. Highly unlikely, but still possible. 461 Log.e(TAG, "Cannot find system OpenSSLRSAPrivateKey class: " + e); 462 return null; 463 } 464 if (!superClass.isInstance(javaKey)) { 465 // This may happen if the PrivateKey was not created by the 466 // Conscrypt provider, which should be the default. That could happen if an 467 // OEM decided to implement a different default provider. Also highly unlikely. 468 Log.e(TAG, 469 "Private key is not an OpenSSLRSAPrivateKey instance, its class name is:" 470 + javaKey.getClass().getCanonicalName()); 471 return null; 472 } 473 474 try { 475 // Use reflection to invoke the 'getOpenSSLKey()' method on 476 // the private key. This returns another Java object that wraps 477 // a native EVP_PKEY. Note that the method is final, so calling 478 // the superclass implementation is ok. 479 Method getKey = superClass.getDeclaredMethod("getOpenSSLKey"); 480 getKey.setAccessible(true); 481 Object opensslKey = null; 482 try { 483 opensslKey = getKey.invoke(javaKey); 484 } finally { 485 getKey.setAccessible(false); 486 } 487 if (opensslKey == null) { 488 // Bail when detecting OEM "enhancement". 489 Log.e(TAG, "Could not getOpenSSLKey on instance: " + javaKey.toString()); 490 return null; 491 } 492 493 // Use reflection to invoke the 'getPkeyContext' method on the 494 // result of the getOpenSSLKey(). This is an 32-bit integer 495 // which is the address of an EVP_PKEY object. Note that this 496 // method these days returns a 64-bit long, but since this code 497 // path is used for older Android versions, it may still return 498 // a 32-bit int here. To be on the safe side, we cast the return 499 // value via Number rather than directly to Integer or Long. 500 Method getPkeyContext; 501 try { 502 getPkeyContext = opensslKey.getClass().getDeclaredMethod("getPkeyContext"); 503 } catch (Exception e) { 504 // Bail here too, something really not working as expected. 505 Log.e(TAG, "No getPkeyContext() method on OpenSSLKey member:" + e); 506 return null; 507 } 508 getPkeyContext.setAccessible(true); 509 long evp_pkey = 0; 510 try { 511 evp_pkey = ((Number) getPkeyContext.invoke(opensslKey)).longValue(); 512 } finally { 513 getPkeyContext.setAccessible(false); 514 } 515 if (evp_pkey == 0) { 516 // The PrivateKey is probably rotten for some reason. 517 Log.e(TAG, "getPkeyContext() returned null"); 518 return null; 519 } 520 return new OpenSSLKey(evp_pkey); 521 } catch (Exception e) { 522 Log.e(TAG, "Error during conversion of privatekey instance: " + javaKey.toString(), e); 523 return null; 524 } 525 } 526 527 /** 528 * Logs to the system EventLog system. 529 */ 530 @SuppressWarnings("LiteralClassName") 531 public static void logEvent(String message) { 532 try { 533 Class<?> processClass = Class.forName("android.os.Process"); 534 Object processInstance = processClass.getDeclaredConstructor().newInstance(); 535 Method myUidMethod = processClass.getMethod("myUid", (Class[]) null); 536 int uid = (Integer) myUidMethod.invoke(processInstance); 537 538 Class<?> eventLogClass = Class.forName("android.util.EventLog"); 539 Object eventLogInstance = eventLogClass.getDeclaredConstructor().newInstance(); 540 Method writeEventMethod = 541 eventLogClass.getMethod("writeEvent", Integer.TYPE, Object[].class); 542 writeEventMethod.invoke(eventLogInstance, 0x534e4554 /* SNET */, 543 new Object[] {"conscrypt", uid, message}); 544 } catch (Exception e) { 545 // Fail silently 546 } 547 } 548 549 static SSLEngine wrapEngine(ConscryptEngine engine) { 550 // For now, don't wrap on Android. 551 return engine; 552 } 553 554 static SSLEngine unwrapEngine(SSLEngine engine) { 555 // For now, don't wrap on Android. 556 return engine; 557 } 558 559 static ConscryptEngineSocket createEngineSocket(SSLParametersImpl sslParameters) 560 throws IOException { 561 if (Build.VERSION.SDK_INT >= 24) { 562 return new Java8EngineSocket(sslParameters); 563 } 564 return new ConscryptEngineSocket(sslParameters); 565 } 566 567 static ConscryptEngineSocket createEngineSocket(String hostname, int port, 568 SSLParametersImpl sslParameters) throws IOException { 569 if (Build.VERSION.SDK_INT >= 24) { 570 return new Java8EngineSocket(hostname, port, sslParameters); 571 } 572 return new ConscryptEngineSocket(hostname, port, sslParameters); 573 } 574 575 static ConscryptEngineSocket createEngineSocket(InetAddress address, int port, 576 SSLParametersImpl sslParameters) throws IOException { 577 if (Build.VERSION.SDK_INT >= 24) { 578 return new Java8EngineSocket(address, port, sslParameters); 579 } 580 return new ConscryptEngineSocket(address, port, sslParameters); 581 } 582 583 static ConscryptEngineSocket createEngineSocket(String hostname, int port, 584 InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters) 585 throws IOException { 586 if (Build.VERSION.SDK_INT >= 24) { 587 return new Java8EngineSocket(hostname, port, clientAddress, clientPort, sslParameters); 588 } 589 return new ConscryptEngineSocket(hostname, port, clientAddress, clientPort, sslParameters); 590 } 591 592 static ConscryptEngineSocket createEngineSocket(InetAddress address, int port, 593 InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters) 594 throws IOException { 595 if (Build.VERSION.SDK_INT >= 24) { 596 return new Java8EngineSocket(address, port, clientAddress, clientPort, sslParameters); 597 } 598 return new ConscryptEngineSocket(address, port, clientAddress, clientPort, sslParameters); 599 } 600 601 static ConscryptEngineSocket createEngineSocket(Socket socket, String hostname, int port, 602 boolean autoClose, SSLParametersImpl sslParameters) throws IOException { 603 if (Build.VERSION.SDK_INT >= 24) { 604 return new Java8EngineSocket(socket, hostname, port, autoClose, sslParameters); 605 } 606 return new ConscryptEngineSocket(socket, hostname, port, autoClose, sslParameters); 607 } 608 609 static ConscryptFileDescriptorSocket createFileDescriptorSocket(SSLParametersImpl sslParameters) 610 throws IOException { 611 if (Build.VERSION.SDK_INT >= 24) { 612 return new Java8FileDescriptorSocket(sslParameters); 613 } 614 return new ConscryptFileDescriptorSocket(sslParameters); 615 } 616 617 static ConscryptFileDescriptorSocket createFileDescriptorSocket(String hostname, int port, 618 SSLParametersImpl sslParameters) throws IOException { 619 if (Build.VERSION.SDK_INT >= 24) { 620 return new Java8FileDescriptorSocket(hostname, port, sslParameters); 621 } 622 return new ConscryptFileDescriptorSocket(hostname, port, sslParameters); 623 } 624 625 static ConscryptFileDescriptorSocket createFileDescriptorSocket(InetAddress address, int port, 626 SSLParametersImpl sslParameters) throws IOException { 627 if (Build.VERSION.SDK_INT >= 24) { 628 return new Java8FileDescriptorSocket(address, port, sslParameters); 629 } 630 return new ConscryptFileDescriptorSocket(address, port, sslParameters); 631 } 632 633 static ConscryptFileDescriptorSocket createFileDescriptorSocket(String hostname, int port, 634 InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters) 635 throws IOException { 636 if (Build.VERSION.SDK_INT >= 24) { 637 return new Java8FileDescriptorSocket( 638 hostname, port, clientAddress, clientPort, sslParameters); 639 } 640 return new ConscryptFileDescriptorSocket( 641 hostname, port, clientAddress, clientPort, sslParameters); 642 } 643 644 static ConscryptFileDescriptorSocket createFileDescriptorSocket(InetAddress address, int port, 645 InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters) 646 throws IOException { 647 if (Build.VERSION.SDK_INT >= 24) { 648 return new Java8FileDescriptorSocket( 649 address, port, clientAddress, clientPort, sslParameters); 650 } 651 return new ConscryptFileDescriptorSocket( 652 address, port, clientAddress, clientPort, sslParameters); 653 } 654 655 static ConscryptFileDescriptorSocket createFileDescriptorSocket(Socket socket, String hostname, 656 int port, boolean autoClose, SSLParametersImpl sslParameters) throws IOException { 657 if (Build.VERSION.SDK_INT >= 24) { 658 return new Java8FileDescriptorSocket(socket, hostname, port, autoClose, sslParameters); 659 } 660 return new ConscryptFileDescriptorSocket(socket, hostname, port, autoClose, sslParameters); 661 } 662 663 /** 664 * Wrap the SocketFactory with the platform wrapper if needed for compatability. 665 */ 666 public static SSLSocketFactory wrapSocketFactoryIfNeeded(OpenSSLSocketFactoryImpl factory) { 667 if (Build.VERSION.SDK_INT < 19) { 668 return new PreKitKatPlatformOpenSSLSocketAdapterFactory(factory); 669 } else if (Build.VERSION.SDK_INT < 22) { 670 return new KitKatPlatformOpenSSLSocketAdapterFactory(factory); 671 } 672 return factory; 673 } 674 675 /** 676 * Convert from platform's GCMParameterSpec to our internal version. 677 */ 678 @SuppressWarnings("LiteralClassName") 679 public static GCMParameters fromGCMParameterSpec(AlgorithmParameterSpec params) { 680 Class<?> gcmSpecClass; 681 try { 682 gcmSpecClass = Class.forName("javax.crypto.spec.GCMParameterSpec"); 683 } catch (ClassNotFoundException e) { 684 gcmSpecClass = null; 685 } 686 687 if (gcmSpecClass != null && gcmSpecClass.isAssignableFrom(params.getClass())) { 688 try { 689 int tLen; 690 byte[] iv; 691 692 Method getTLenMethod = gcmSpecClass.getMethod("getTLen"); 693 Method getIVMethod = gcmSpecClass.getMethod("getIV"); 694 tLen = (int) getTLenMethod.invoke(params); 695 iv = (byte[]) getIVMethod.invoke(params); 696 697 return new GCMParameters(tLen, iv); 698 } catch (NoSuchMethodException e) { 699 throw new RuntimeException("GCMParameterSpec lacks expected methods", e); 700 } catch (IllegalAccessException e) { 701 throw new RuntimeException("GCMParameterSpec lacks expected methods", e); 702 } catch (InvocationTargetException e) { 703 throw new RuntimeException( 704 "Could not fetch GCM parameters", e.getTargetException()); 705 } 706 } 707 return null; 708 } 709 710 /** 711 * Convert from an opaque AlgorithmParameters to the platform's GCMParameterSpec. 712 */ 713 @SuppressWarnings("LiteralClassName") 714 static AlgorithmParameterSpec fromGCMParameters(AlgorithmParameters params) { 715 Class<?> gcmSpecClass; 716 try { 717 gcmSpecClass = Class.forName("javax.crypto.spec.GCMParameterSpec"); 718 } catch (ClassNotFoundException e) { 719 gcmSpecClass = null; 720 } 721 722 if (gcmSpecClass != null) { 723 try { 724 return params.getParameterSpec((Class) gcmSpecClass); 725 } catch (InvalidParameterSpecException e) { 726 return null; 727 } 728 } 729 return null; 730 } 731 732 /** 733 * Creates a platform version of {@code GCMParameterSpec}. 734 */ 735 @SuppressWarnings("LiteralClassName") 736 public static AlgorithmParameterSpec toGCMParameterSpec(int tagLenInBits, byte[] iv) { 737 Class<?> gcmSpecClass; 738 try { 739 gcmSpecClass = Class.forName("javax.crypto.spec.GCMParameterSpec"); 740 } catch (ClassNotFoundException e) { 741 gcmSpecClass = null; 742 } 743 744 if (gcmSpecClass != null) { 745 try { 746 Constructor<?> constructor = gcmSpecClass.getConstructor(int.class, byte[].class); 747 return (AlgorithmParameterSpec) constructor.newInstance(tagLenInBits, iv); 748 } catch (NoSuchMethodException | InstantiationException | IllegalAccessException 749 | IllegalArgumentException e) { 750 e.printStackTrace(); 751 } catch (InvocationTargetException e) { 752 e.getCause().printStackTrace(); 753 } 754 } 755 return null; 756 } 757 758 /* 759 * CloseGuard functions. 760 */ 761 762 public static CloseGuard closeGuardGet() { 763 if (Build.VERSION.SDK_INT < 14) { 764 return null; 765 } 766 767 return CloseGuard.get(); 768 } 769 770 public static void closeGuardOpen(Object guardObj, String message) { 771 if (Build.VERSION.SDK_INT < 14) { 772 return; 773 } 774 775 CloseGuard guard = (CloseGuard) guardObj; 776 guard.open(message); 777 } 778 779 public static void closeGuardClose(Object guardObj) { 780 if (Build.VERSION.SDK_INT < 14) { 781 return; 782 } 783 784 CloseGuard guard = (CloseGuard) guardObj; 785 guard.close(); 786 } 787 788 public static void closeGuardWarnIfOpen(Object guardObj) { 789 if (Build.VERSION.SDK_INT < 14) { 790 return; 791 } 792 793 CloseGuard guard = (CloseGuard) guardObj; 794 guard.warnIfOpen(); 795 } 796 797 /* 798 * BlockGuard functions. 799 */ 800 801 public static void blockGuardOnNetwork() { 802 BlockGuard.getThreadPolicy().onNetwork(); 803 } 804 805 /** 806 * OID to Algorithm Name mapping. 807 */ 808 @SuppressWarnings("LiteralClassName") 809 public static String oidToAlgorithmName(String oid) { 810 // Old Harmony style 811 try { 812 Class<?> algNameMapperClass = 813 Class.forName("org.apache.harmony.security.utils.AlgNameMapper"); 814 Method map2AlgNameMethod = 815 algNameMapperClass.getDeclaredMethod("map2AlgName", String.class); 816 map2AlgNameMethod.setAccessible(true); 817 return (String) map2AlgNameMethod.invoke(null, oid); 818 } catch (InvocationTargetException e) { 819 Throwable cause = e.getCause(); 820 if (cause instanceof RuntimeException) { 821 throw(RuntimeException) cause; 822 } else if (cause instanceof Error) { 823 throw(Error) cause; 824 } 825 throw new RuntimeException(e); 826 } catch (Exception ignored) { 827 } 828 829 // Newer OpenJDK style 830 try { 831 Class<?> algorithmIdClass = Class.forName("sun.security.x509.AlgorithmId"); 832 Method getMethod = algorithmIdClass.getDeclaredMethod("get", String.class); 833 getMethod.setAccessible(true); 834 Method getNameMethod = algorithmIdClass.getDeclaredMethod("getName"); 835 getNameMethod.setAccessible(true); 836 837 Object algIdObj = getMethod.invoke(null, oid); 838 return (String) getNameMethod.invoke(algIdObj); 839 } catch (InvocationTargetException e) { 840 Throwable cause = e.getCause(); 841 if (cause instanceof RuntimeException) { 842 throw(RuntimeException) cause; 843 } else if (cause instanceof Error) { 844 throw(Error) cause; 845 } 846 throw new RuntimeException(e); 847 } catch (Exception ignored) { 848 } 849 850 return oid; 851 } 852 853 /** 854 * Provides extended capabilities for the session if supported by the platform. 855 */ 856 public static SSLSession wrapSSLSession(ExternalSession sslSession) { 857 if (Build.VERSION.SDK_INT >= 24) { 858 return new Java8ExtendedSSLSession(sslSession); 859 } 860 861 return sslSession; 862 } 863 864 public static String getOriginalHostNameFromInetAddress(InetAddress addr) { 865 if (Build.VERSION.SDK_INT > 27) { 866 try { 867 Method getHolder = InetAddress.class.getDeclaredMethod("holder"); 868 getHolder.setAccessible(true); 869 870 Method getOriginalHostName = Class.forName("java.net.InetAddress$InetAddressHolder") 871 .getDeclaredMethod("getOriginalHostName"); 872 getOriginalHostName.setAccessible(true); 873 874 String originalHostName = 875 (String) getOriginalHostName.invoke(getHolder.invoke(addr)); 876 if (originalHostName == null) { 877 return addr.getHostAddress(); 878 } 879 return originalHostName; 880 } catch (InvocationTargetException e) { 881 throw new RuntimeException("Failed to get originalHostName", e); 882 } catch (ClassNotFoundException ignore) { 883 // passthrough and return addr.getHostAddress() 884 } catch (IllegalAccessException ignore) { 885 } catch (NoSuchMethodException ignore) { 886 } 887 } 888 return addr.getHostAddress(); 889 } 890 891 /* 892 * Pre-Java-7 backward compatibility. 893 */ 894 895 public static String getHostStringFromInetSocketAddress(InetSocketAddress addr) { 896 if (Build.VERSION.SDK_INT > 23) { 897 try { 898 Method m_getHostString = InetSocketAddress.class.getDeclaredMethod("getHostString"); 899 return (String) m_getHostString.invoke(addr); 900 } catch (InvocationTargetException e) { 901 throw new RuntimeException(e); 902 } catch (Exception ignored) { 903 } 904 } 905 return null; 906 } 907 908 // X509ExtendedTrustManager was added in API 24 909 static boolean supportsX509ExtendedTrustManager() { 910 return Build.VERSION.SDK_INT > 23; 911 } 912 913 /** 914 * Check if SCT verification is required for a given hostname. 915 * 916 * SCT Verification is enabled using {@code Security} properties. 917 * The "conscrypt.ct.enable" property must be true, as well as a per domain property. 918 * The reverse notation of the domain name, prefixed with "conscrypt.ct.enforce." 919 * is used as the property name. 920 * Basic globbing is also supported. 921 * 922 * For example, for the domain foo.bar.com, the following properties will be 923 * looked up, in order of precedence. 924 * - conscrypt.ct.enforce.com.bar.foo 925 * - conscrypt.ct.enforce.com.bar.* 926 * - conscrypt.ct.enforce.com.* 927 * - conscrypt.ct.enforce.* 928 */ 929 public static boolean isCTVerificationRequired(String hostname) { 930 if (hostname == null) { 931 return false; 932 } 933 // TODO: Use the platform version on platforms that support it 934 935 String property = Security.getProperty("conscrypt.ct.enable"); 936 if (property == null || !Boolean.valueOf(property)) { 937 return false; 938 } 939 940 List<String> parts = Arrays.asList(hostname.split("\\.")); 941 Collections.reverse(parts); 942 943 boolean enable = false; 944 String propertyName = "conscrypt.ct.enforce"; 945 // The loop keeps going on even once we've found a match 946 // This allows for finer grained settings on subdomains 947 for (String part : parts) { 948 property = Security.getProperty(propertyName + ".*"); 949 if (property != null) { 950 enable = Boolean.valueOf(property); 951 } 952 953 propertyName = propertyName + "." + part; 954 } 955 956 property = Security.getProperty(propertyName); 957 if (property != null) { 958 enable = Boolean.valueOf(property); 959 } 960 return enable; 961 } 962 963 static boolean supportsConscryptCertStore() { 964 return false; 965 } 966 967 static KeyStore getDefaultCertKeyStore() throws KeyStoreException { 968 KeyStore keyStore = KeyStore.getInstance("AndroidCAStore"); 969 try { 970 keyStore.load(null, null); 971 } catch (IOException e) { 972 throw new KeyStoreException(e); 973 } catch (CertificateException e) { 974 throw new KeyStoreException(e); 975 } catch (NoSuchAlgorithmException e) { 976 throw new KeyStoreException(e); 977 } 978 return keyStore; 979 } 980 981 static ConscryptCertStore newDefaultCertStore() { 982 return null; 983 } 984 985 static CertBlacklist newDefaultBlacklist() { 986 return null; 987 } 988 989 static CTLogStore newDefaultLogStore() { 990 return null; 991 } 992 993 static CTPolicy newDefaultPolicy(CTLogStore logStore) { 994 return null; 995 } 996 } 997