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 java.io.FileDescriptor; 20 import java.lang.reflect.Field; 21 import java.lang.reflect.Method; 22 import java.net.InetSocketAddress; 23 import java.net.Socket; 24 import java.net.SocketException; 25 import java.net.SocketImpl; 26 import java.nio.channels.SocketChannel; 27 import java.security.NoSuchAlgorithmException; 28 import java.security.PrivateKey; 29 import java.security.Security; 30 import java.security.cert.CertificateException; 31 import java.security.cert.X509Certificate; 32 import java.security.spec.AlgorithmParameterSpec; 33 import java.security.spec.ECParameterSpec; 34 import java.util.Arrays; 35 import java.util.Collections; 36 import java.util.List; 37 import javax.crypto.spec.GCMParameterSpec; 38 import javax.net.ssl.SNIHostName; 39 import javax.net.ssl.SNIServerName; 40 import javax.net.ssl.SSLParameters; 41 import javax.net.ssl.SSLSession; 42 import javax.net.ssl.SSLSocketFactory; 43 import javax.net.ssl.StandardConstants; 44 import javax.net.ssl.X509ExtendedTrustManager; 45 import javax.net.ssl.X509TrustManager; 46 import sun.security.x509.AlgorithmId; 47 48 /** 49 * Platform-specific methods for OpenJDK 50 */ 51 final class Platform { 52 private static final String TAG = "Conscrypt"; 53 54 private static Method m_getCurveName; 55 static { 56 try { 57 m_getCurveName = ECParameterSpec.class.getDeclaredMethod("getCurveName"); 58 m_getCurveName.setAccessible(true); 59 } catch (Exception ignored) { 60 } 61 } 62 63 private Platform() { 64 } 65 66 public static void setup() { 67 } 68 69 public static FileDescriptor getFileDescriptor(Socket s) { 70 try { 71 SocketChannel channel = s.getChannel(); 72 if (channel != null) { 73 Field f_fd = channel.getClass().getDeclaredField("fd"); 74 f_fd.setAccessible(true); 75 return (FileDescriptor) f_fd.get(channel); 76 } 77 } catch (Exception e) { 78 // Try socket class below... 79 } 80 81 try { 82 Field f_impl = Socket.class.getDeclaredField("impl"); 83 f_impl.setAccessible(true); 84 Object socketImpl = f_impl.get(s); 85 Field f_fd = SocketImpl.class.getDeclaredField("fd"); 86 f_fd.setAccessible(true); 87 return (FileDescriptor) f_fd.get(socketImpl); 88 } catch (Exception e) { 89 throw new RuntimeException("Can't get FileDescriptor from socket", e); 90 } 91 } 92 93 public static FileDescriptor getFileDescriptorFromSSLSocket(OpenSSLSocketImpl openSSLSocketImpl) { 94 return getFileDescriptor(openSSLSocketImpl); 95 } 96 97 public static String getCurveName(ECParameterSpec spec) { 98 if (m_getCurveName == null) { 99 return null; 100 } 101 try { 102 return (String) m_getCurveName.invoke(spec); 103 } catch (Exception e) { 104 return null; 105 } 106 } 107 108 public static void setCurveName(ECParameterSpec spec, String curveName) { 109 // This doesn't appear to be needed. 110 } 111 112 /* 113 * Call Os.setsockoptTimeval via reflection. 114 */ 115 public static void setSocketWriteTimeout(Socket s, long timeoutMillis) throws SocketException { 116 // TODO: figure this out on the RI 117 } 118 119 public static void setSSLParameters(SSLParameters params, SSLParametersImpl impl, 120 OpenSSLSocketImpl socket) { 121 impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm()); 122 impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder()); 123 List<SNIServerName> serverNames = params.getServerNames(); 124 if (serverNames != null) { 125 for (SNIServerName serverName : serverNames) { 126 if (serverName.getType() == StandardConstants.SNI_HOST_NAME) { 127 socket.setHostname(((SNIHostName) serverName).getAsciiName()); 128 break; 129 } 130 } 131 } 132 } 133 134 public static void getSSLParameters(SSLParameters params, SSLParametersImpl impl, 135 OpenSSLSocketImpl socket) { 136 params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm()); 137 params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder()); 138 if (impl.getUseSni() && AddressUtils.isValidSniHostname(socket.getHostname())) { 139 params.setServerNames(Collections.<SNIServerName> singletonList( 140 new SNIHostName(socket.getHostname()))); 141 } 142 } 143 144 public static void setSSLParameters( 145 SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) { 146 impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm()); 147 impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder()); 148 List<SNIServerName> serverNames = params.getServerNames(); 149 if (serverNames != null) { 150 for (SNIServerName serverName : serverNames) { 151 if (serverName.getType() == StandardConstants.SNI_HOST_NAME) { 152 engine.setSniHostname(((SNIHostName) serverName).getAsciiName()); 153 break; 154 } 155 } 156 } 157 } 158 159 public static void getSSLParameters( 160 SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) { 161 params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm()); 162 params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder()); 163 if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getSniHostname())) { 164 params.setServerNames(Collections.<SNIServerName>singletonList( 165 new SNIHostName(engine.getSniHostname()))); 166 } 167 } 168 169 /** 170 * Tries to return a Class reference of one of the supplied class names. 171 */ 172 private static Class<?> getClass(String... klasses) { 173 for (String klass : klasses) { 174 try { 175 return Class.forName(klass); 176 } catch (Exception ignored) { 177 } 178 } 179 return null; 180 } 181 182 public static void setEndpointIdentificationAlgorithm(SSLParameters params, 183 String endpointIdentificationAlgorithm) { 184 params.setEndpointIdentificationAlgorithm(endpointIdentificationAlgorithm); 185 } 186 187 public static String getEndpointIdentificationAlgorithm(SSLParameters params) { 188 return params.getEndpointIdentificationAlgorithm(); 189 } 190 191 public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain, 192 String authType, OpenSSLSocketImpl socket) throws CertificateException { 193 if (tm instanceof X509ExtendedTrustManager) { 194 X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm; 195 x509etm.checkClientTrusted(chain, authType, socket); 196 } else { 197 tm.checkClientTrusted(chain, authType); 198 } 199 } 200 201 public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain, 202 String authType, OpenSSLSocketImpl socket) throws CertificateException { 203 if (tm instanceof X509ExtendedTrustManager) { 204 X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm; 205 x509etm.checkServerTrusted(chain, authType, socket); 206 } else { 207 tm.checkServerTrusted(chain, authType); 208 } 209 } 210 211 public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain, 212 String authType, OpenSSLEngineImpl engine) throws CertificateException { 213 if (tm instanceof X509ExtendedTrustManager) { 214 X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm; 215 x509etm.checkClientTrusted(chain, authType, engine); 216 } else { 217 tm.checkClientTrusted(chain, authType); 218 } 219 } 220 221 public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain, 222 String authType, OpenSSLEngineImpl engine) throws CertificateException { 223 if (tm instanceof X509ExtendedTrustManager) { 224 X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm; 225 x509etm.checkServerTrusted(chain, authType, engine); 226 } else { 227 tm.checkServerTrusted(chain, authType); 228 } 229 } 230 231 /** 232 * Wraps an old AndroidOpenSSL key instance. This is not needed on RI. 233 */ 234 public static OpenSSLKey wrapRsaKey(PrivateKey javaKey) { 235 return null; 236 } 237 238 /** 239 * Logs to the system EventLog system. 240 */ 241 public static void logEvent(String message) { 242 } 243 244 /** 245 * Returns true if the supplied hostname is an literal IP address. 246 */ 247 public static boolean isLiteralIpAddress(String hostname) { 248 // TODO: any RI API to make this better? 249 return AddressUtils.isLiteralIpAddress(hostname); 250 } 251 252 /** 253 * For unbundled versions, SNI is always enabled by default. 254 */ 255 public static boolean isSniEnabledByDefault() { 256 return true; 257 } 258 259 /** 260 * Currently we don't wrap anything from the RI. 261 */ 262 public static SSLSocketFactory wrapSocketFactoryIfNeeded(OpenSSLSocketFactoryImpl factory) { 263 return factory; 264 } 265 266 /** 267 * Convert from platform's GCMParameterSpec to our internal version. 268 */ 269 public static GCMParameters fromGCMParameterSpec(AlgorithmParameterSpec params) { 270 if (params instanceof GCMParameterSpec) { 271 GCMParameterSpec gcmParams = (GCMParameterSpec) params; 272 return new GCMParameters(gcmParams.getTLen(), gcmParams.getIV()); 273 } 274 return null; 275 } 276 277 /** 278 * Creates a platform version of {@code GCMParameterSpec}. 279 */ 280 public static AlgorithmParameterSpec toGCMParameterSpec(int tagLenInBits, byte[] iv) { 281 return new GCMParameterSpec(tagLenInBits, iv); 282 } 283 284 /* 285 * CloseGuard functions. 286 */ 287 288 public static Object closeGuardGet() { 289 return null; 290 } 291 292 public static void closeGuardOpen(Object guardObj, String message) { 293 } 294 295 public static void closeGuardClose(Object guardObj) { 296 } 297 298 public static void closeGuardWarnIfOpen(Object guardObj) { 299 } 300 301 /* 302 * BlockGuard functions. 303 */ 304 305 public static void blockGuardOnNetwork() { 306 } 307 308 /** 309 * OID to Algorithm Name mapping. 310 */ 311 public static String oidToAlgorithmName(String oid) { 312 try { 313 return AlgorithmId.get(oid).getName(); 314 } catch (NoSuchAlgorithmException e) { 315 return oid; 316 } 317 } 318 319 /* 320 * Pre-Java-8 backward compatibility. 321 */ 322 323 public static SSLSession wrapSSLSession(AbstractOpenSSLSession sslSession) { 324 return new OpenSSLExtendedSessionImpl(sslSession); 325 } 326 327 public static SSLSession unwrapSSLSession(SSLSession sslSession) { 328 if (sslSession instanceof OpenSSLExtendedSessionImpl) { 329 return ((OpenSSLExtendedSessionImpl) sslSession).getDelegate(); 330 } 331 return sslSession; 332 } 333 334 /* 335 * Pre-Java-7 backward compatibility. 336 */ 337 338 public static String getHostStringFromInetSocketAddress(InetSocketAddress addr) { 339 return addr.getHostString(); 340 } 341 342 /** 343 * Check if SCT verification is required for a given hostname. 344 * 345 * SCT Verification is enabled using {@code Security} properties. 346 * The "conscrypt.ct.enable" property must be true, as well as a per domain property. 347 * The reverse notation of the domain name, prefixed with "conscrypt.ct.enforce." 348 * is used as the property name. 349 * Basic globbing is also supported. 350 * 351 * For example, for the domain foo.bar.com, the following properties will be 352 * looked up, in order of precedence. 353 * - conscrypt.ct.enforce.com.bar.foo 354 * - conscrypt.ct.enforce.com.bar.* 355 * - conscrypt.ct.enforce.com.* 356 * - conscrypt.ct.enforce.* 357 */ 358 public static boolean isCTVerificationRequired(String hostname) { 359 if (hostname == null) { 360 return false; 361 } 362 363 String property = Security.getProperty("conscrypt.ct.enable"); 364 if (property == null || Boolean.valueOf(property.toLowerCase()) == false) { 365 return false; 366 } 367 368 List<String> parts = Arrays.asList(hostname.split("\\.")); 369 Collections.reverse(parts); 370 371 boolean enable = false; 372 String propertyName = "conscrypt.ct.enforce"; 373 // The loop keeps going on even once we've found a match 374 // This allows for finer grained settings on subdomains 375 for (String part: parts) { 376 property = Security.getProperty(propertyName + ".*"); 377 if (property != null) { 378 enable = Boolean.valueOf(property.toLowerCase()); 379 } 380 381 propertyName = propertyName + "." + part; 382 } 383 384 property = Security.getProperty(propertyName); 385 if (property != null) { 386 enable = Boolean.valueOf(property.toLowerCase()); 387 } 388 return enable; 389 } 390 } 391