SSLCertificateSocketFactory.java revision 3dec7d563a2f3e1eb967ce2054a00b6620e3558c
1/* 2 * Copyright (C) 2008 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 17package android.net; 18 19import android.content.Context; 20import android.net.http.DomainNameChecker; 21import android.os.SystemProperties; 22import android.util.Config; 23import android.util.Log; 24 25import com.android.internal.net.SSLSessionCache; 26 27import java.io.IOException; 28import java.net.InetAddress; 29import java.net.Socket; 30import java.security.GeneralSecurityException; 31import java.security.KeyManagementException; 32import java.security.KeyStore; 33import java.security.KeyStoreException; 34import java.security.NoSuchAlgorithmException; 35import java.security.cert.Certificate; 36import java.security.cert.X509Certificate; 37 38import javax.net.SocketFactory; 39import javax.net.ssl.SSLSocket; 40import javax.net.ssl.SSLSocketFactory; 41import javax.net.ssl.TrustManager; 42import javax.net.ssl.TrustManagerFactory; 43import javax.net.ssl.X509TrustManager; 44 45/** 46 * SSLSocketFactory that allows skipping the certificate chain validation 47 * based on system setting (socket.relaxsslcheck=yes, ro.secure=1 - for 48 * testing only). 49 * 50 * It also adds a readTimeout that will be set on each created socket. 51 * The factory will use SSL session persistence if enabled by config. 52 */ 53public class SSLCertificateSocketFactory extends SSLSocketFactory { 54 55 private static final String LOG_TAG = "SSLCertificateSocketFactory"; 56 57 private static X509TrustManager sDefaultTrustManager; 58 59 static { 60 try { 61 TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); 62 tmf.init((KeyStore)null); 63 TrustManager[] tms = tmf.getTrustManagers(); 64 if (tms != null) { 65 for (TrustManager tm : tms) { 66 if (tm instanceof X509TrustManager) { 67 sDefaultTrustManager = (X509TrustManager)tm; 68 break; 69 } 70 } 71 } 72 } catch (NoSuchAlgorithmException e) { 73 Log.e(LOG_TAG, "Unable to get X509 Trust Manager ", e); 74 } catch (KeyStoreException e) { 75 Log.e(LOG_TAG, "Key Store exception while initializing TrustManagerFactory ", e); 76 } 77 } 78 79 private static final TrustManager[] TRUST_MANAGER = new TrustManager[] { 80 new X509TrustManager() { 81 public X509Certificate[] getAcceptedIssuers() { 82 return null; 83 } 84 85 public void checkClientTrusted(X509Certificate[] certs, 86 String authType) { } 87 88 public void checkServerTrusted(X509Certificate[] certs, 89 String authType) { } 90 } 91 }; 92 93 private static SSLSocketFactory factory; 94 95 /** 96 * Initialize a single default factory to be used for all returned 97 * sockets. 98 * 99 * Because of the signature of getDefault(int timeout) it needs to create 100 * a new instance which encapsulates the timeout on each call. We want 101 * to share a single SSLContext and SSLSessionCache. 102 * 103 * Can be called multiple times - but only the first will initialize the factory. 104 * 105 * @param androidContext will be used for SSL session persistence. Null for backward 106 * compatibility, no SSL persistence. 107 * @hide 108 */ 109 public static synchronized void setupDefaultFactory(Context androidContext) { 110 if ( factory != null) { 111 // Can only be initialized once, to avoid having multiple caches. 112 return; 113 } 114 factory = SSLSessionCache.getSocketFactory(androidContext, TRUST_MANAGER); 115 } 116 117 private final int socketReadTimeoutForSslHandshake; 118 119 public SSLCertificateSocketFactory(int socketReadTimeoutForSslHandshake) 120 throws NoSuchAlgorithmException, KeyManagementException { 121 this.socketReadTimeoutForSslHandshake 122 = socketReadTimeoutForSslHandshake; 123 } 124 125 /** 126 * Returns a default instantiation of a new socket factory which 127 * only allows SSL connections with valid certificates. 128 * 129 * @param socketReadTimeoutForSslHandshake the socket read timeout used for performing 130 * ssl handshake. The socket read timeout is set back to 0 after the handshake. 131 * @return a new SocketFactory, or null on error 132 */ 133 public static SocketFactory getDefault(int socketReadTimeoutForSslHandshake) { 134 try { 135 if (factory == null) { 136 // The delegated factory was not initialized explicitely with a context. 137 // Use a default one. 138 setupDefaultFactory(null); 139 } 140 return new SSLCertificateSocketFactory(socketReadTimeoutForSslHandshake); 141 } catch (NoSuchAlgorithmException e) { 142 Log.e(LOG_TAG, 143 "SSLCertifcateSocketFactory.getDefault" + 144 " NoSuchAlgorithmException " , e); 145 return null; 146 } catch (KeyManagementException e) { 147 Log.e(LOG_TAG, 148 "SSLCertifcateSocketFactory.getDefault" + 149 " KeyManagementException " , e); 150 return null; 151 } 152 } 153 154 private boolean hasValidCertificateChain(Certificate[] certs) 155 throws IOException { 156 if (sDefaultTrustManager == null) { 157 if (Config.LOGD) { 158 Log.d(LOG_TAG,"hasValidCertificateChain():" + 159 " null default trust manager!"); 160 } 161 throw new IOException("null default trust manager"); 162 } 163 164 boolean trusted = (certs != null && (certs.length > 0)); 165 166 if (trusted) { 167 try { 168 // the authtype we pass in doesn't actually matter 169 sDefaultTrustManager.checkServerTrusted((X509Certificate[]) certs, "RSA"); 170 } catch (GeneralSecurityException e) { 171 String exceptionMessage = e != null ? e.getMessage() : "none"; 172 if (Config.LOGD) { 173 Log.d(LOG_TAG,"hasValidCertificateChain(): sec. exception: " 174 + exceptionMessage); 175 } 176 trusted = false; 177 } 178 } 179 180 return trusted; 181 } 182 183 private void validateSocket(SSLSocket sslSock, String destHost) 184 throws IOException 185 { 186 if (Config.LOGV) { 187 Log.v(LOG_TAG,"validateSocket() to host "+destHost); 188 } 189 190 String relaxSslCheck = SystemProperties.get("socket.relaxsslcheck"); 191 String secure = SystemProperties.get("ro.secure"); 192 193 // only allow relaxing the ssl check on non-secure builds where the relaxation is 194 // specifically requested. 195 if ("0".equals(secure) && "yes".equals(relaxSslCheck)) { 196 if (Config.LOGD) { 197 Log.d(LOG_TAG,"sys prop socket.relaxsslcheck is set," + 198 " ignoring invalid certs"); 199 } 200 return; 201 } 202 203 Certificate[] certs = null; 204 sslSock.setUseClientMode(true); 205 sslSock.startHandshake(); 206 certs = sslSock.getSession().getPeerCertificates(); 207 208 // check that the root certificate in the chain belongs to 209 // a CA we trust 210 if (certs == null) { 211 Log.e(LOG_TAG, 212 "[SSLCertificateSocketFactory] no trusted root CA"); 213 throw new IOException("no trusted root CA"); 214 } 215 216 if (Config.LOGV) { 217 Log.v(LOG_TAG,"validateSocket # certs = " +certs.length); 218 } 219 220 if (!hasValidCertificateChain(certs)) { 221 if (Config.LOGD) { 222 Log.d(LOG_TAG,"validateSocket(): certificate untrusted!"); 223 } 224 throw new IOException("Certificate untrusted"); 225 } 226 227 X509Certificate lastChainCert = (X509Certificate) certs[0]; 228 229 if (!DomainNameChecker.match(lastChainCert, destHost)) { 230 if (Config.LOGD) { 231 Log.d(LOG_TAG,"validateSocket(): domain name check failed"); 232 } 233 throw new IOException("Domain Name check failed"); 234 } 235 } 236 237 public Socket createSocket(Socket socket, String s, int i, boolean flag) 238 throws IOException 239 { 240 throw new IOException("Cannot validate certification without a hostname"); 241 } 242 243 public Socket createSocket(InetAddress inaddr, int i, InetAddress inaddr2, int j) 244 throws IOException 245 { 246 throw new IOException("Cannot validate certification without a hostname"); 247 } 248 249 public Socket createSocket(InetAddress inaddr, int i) throws IOException { 250 throw new IOException("Cannot validate certification without a hostname"); 251 } 252 253 public Socket createSocket(String s, int i, InetAddress inaddr, int j) throws IOException { 254 SSLSocket sslSock = (SSLSocket) factory.createSocket(s, i, inaddr, j); 255 256 if (socketReadTimeoutForSslHandshake >= 0) { 257 sslSock.setSoTimeout(socketReadTimeoutForSslHandshake); 258 } 259 260 validateSocket(sslSock,s); 261 sslSock.setSoTimeout(0); 262 263 return sslSock; 264 } 265 266 public Socket createSocket(String s, int i) throws IOException { 267 SSLSocket sslSock = (SSLSocket) factory.createSocket(s, i); 268 269 if (socketReadTimeoutForSslHandshake >= 0) { 270 sslSock.setSoTimeout(socketReadTimeoutForSslHandshake); 271 } 272 273 validateSocket(sslSock,s); 274 sslSock.setSoTimeout(0); 275 276 return sslSock; 277 } 278 279 public String[] getDefaultCipherSuites() { 280 return factory.getSupportedCipherSuites(); 281 } 282 283 public String[] getSupportedCipherSuites() { 284 return factory.getSupportedCipherSuites(); 285 } 286} 287 288 289