SSLCertificateSocketFactory.java revision 076357b8567458d4b6dfdcf839ef751634cd2bfb
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.util.Log; 20import android.util.Config; 21import android.net.http.DomainNameChecker; 22import android.os.SystemProperties; 23 24import javax.net.SocketFactory; 25import javax.net.ssl.SSLContext; 26import javax.net.ssl.SSLSocket; 27import javax.net.ssl.SSLSocketFactory; 28import javax.net.ssl.TrustManager; 29import javax.net.ssl.TrustManagerFactory; 30import javax.net.ssl.X509TrustManager; 31 32import java.io.IOException; 33import java.net.InetAddress; 34import java.net.Socket; 35import java.security.NoSuchAlgorithmException; 36import java.security.KeyManagementException; 37import java.security.KeyStore; 38import java.security.KeyStoreException; 39import java.security.GeneralSecurityException; 40import java.security.cert.Certificate; 41import java.security.cert.X509Certificate; 42 43public class SSLCertificateSocketFactory extends SSLSocketFactory { 44 45 private static final boolean DBG = true; 46 private static final String LOG_TAG = "SSLCertificateSocketFactory"; 47 48 private static X509TrustManager sDefaultTrustManager; 49 50 private final int socketReadTimeoutForSslHandshake; 51 52 static { 53 try { 54 TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); 55 tmf.init((KeyStore)null); 56 TrustManager[] tms = tmf.getTrustManagers(); 57 if (tms != null) { 58 for (TrustManager tm : tms) { 59 if (tm instanceof X509TrustManager) { 60 sDefaultTrustManager = (X509TrustManager)tm; 61 break; 62 } 63 } 64 } 65 } catch (NoSuchAlgorithmException e) { 66 Log.e(LOG_TAG, "Unable to get X509 Trust Manager ", e); 67 } catch (KeyStoreException e) { 68 Log.e(LOG_TAG, "Key Store exception while initializing TrustManagerFactory ", e); 69 } 70 } 71 72 private static final TrustManager[] TRUST_MANAGER = new TrustManager[] { 73 new X509TrustManager() { 74 public X509Certificate[] getAcceptedIssuers() { 75 return null; 76 } 77 78 public void checkClientTrusted(X509Certificate[] certs, 79 String authType) { } 80 81 public void checkServerTrusted(X509Certificate[] certs, 82 String authType) { } 83 } 84 }; 85 86 private SSLSocketFactory factory; 87 88 public SSLCertificateSocketFactory(int socketReadTimeoutForSslHandshake) 89 throws NoSuchAlgorithmException, KeyManagementException { 90 SSLContext context = SSLContext.getInstance("TLS"); 91 context.init(null, TRUST_MANAGER, new java.security.SecureRandom()); 92 factory = (SSLSocketFactory) context.getSocketFactory(); 93 this.socketReadTimeoutForSslHandshake = socketReadTimeoutForSslHandshake; 94 } 95 96 /** 97 * Returns a default instantiation of a new socket factory which 98 * only allows SSL connections with valid certificates. 99 * 100 * @param socketReadTimeoutForSslHandshake the socket read timeout used for performing 101 * ssl handshake. The socket read timeout is set back to 0 after the handshake. 102 * @return a new SocketFactory, or null on error 103 */ 104 public static SocketFactory getDefault(int socketReadTimeoutForSslHandshake) { 105 try { 106 return new SSLCertificateSocketFactory(socketReadTimeoutForSslHandshake); 107 } catch (NoSuchAlgorithmException e) { 108 Log.e(LOG_TAG, 109 "SSLCertifcateSocketFactory.getDefault" + 110 " NoSuchAlgorithmException " , e); 111 return null; 112 } catch (KeyManagementException e) { 113 Log.e(LOG_TAG, 114 "SSLCertifcateSocketFactory.getDefault" + 115 " KeyManagementException " , e); 116 return null; 117 } 118 } 119 120 private boolean hasValidCertificateChain(Certificate[] certs) 121 throws IOException { 122 if (sDefaultTrustManager == null) { 123 if (Config.LOGD) { 124 Log.d(LOG_TAG,"hasValidCertificateChain():" + 125 " null default trust manager!"); 126 } 127 throw new IOException("null default trust manager"); 128 } 129 130 boolean trusted = (certs != null && (certs.length > 0)); 131 132 if (trusted) { 133 try { 134 // the authtype we pass in doesn't actually matter 135 sDefaultTrustManager.checkServerTrusted((X509Certificate[]) certs, "RSA"); 136 } catch (GeneralSecurityException e) { 137 String exceptionMessage = e != null ? e.getMessage() : "none"; 138 if (Config.LOGD) { 139 Log.d(LOG_TAG,"hasValidCertificateChain(): sec. exception: " 140 + exceptionMessage); 141 } 142 trusted = false; 143 } 144 } 145 146 return trusted; 147 } 148 149 private void validateSocket(SSLSocket sslSock, String destHost) 150 throws IOException 151 { 152 if (Config.LOGV) { 153 Log.v(LOG_TAG,"validateSocket() to host "+destHost); 154 } 155 156 String relaxSslCheck = SystemProperties.get("socket.relaxsslcheck"); 157 String secure = SystemProperties.get("ro.secure"); 158 159 // only allow relaxing the ssl check on non-secure builds where the relaxation is 160 // specifically requested. 161 if ("0".equals(secure) && "yes".equals(relaxSslCheck)) { 162 if (Config.LOGD) { 163 Log.d(LOG_TAG,"sys prop socket.relaxsslcheck is set," + 164 " ignoring invalid certs"); 165 } 166 return; 167 } 168 169 Certificate[] certs = null; 170 sslSock.setUseClientMode(true); 171 sslSock.startHandshake(); 172 certs = sslSock.getSession().getPeerCertificates(); 173 174 // check that the root certificate in the chain belongs to 175 // a CA we trust 176 if (certs == null) { 177 Log.e(LOG_TAG, 178 "[SSLCertificateSocketFactory] no trusted root CA"); 179 throw new IOException("no trusted root CA"); 180 } 181 182 if (Config.LOGV) { 183 Log.v(LOG_TAG,"validateSocket # certs = " +certs.length); 184 } 185 186 if (!hasValidCertificateChain(certs)) { 187 if (Config.LOGD) { 188 Log.d(LOG_TAG,"validateSocket(): certificate untrusted!"); 189 } 190 throw new IOException("Certificate untrusted"); 191 } 192 193 X509Certificate lastChainCert = (X509Certificate) certs[0]; 194 195 if (!DomainNameChecker.match(lastChainCert, destHost)) { 196 if (Config.LOGD) { 197 Log.d(LOG_TAG,"validateSocket(): domain name check failed"); 198 } 199 throw new IOException("Domain Name check failed"); 200 } 201 } 202 203 public Socket createSocket(Socket socket, String s, int i, boolean flag) 204 throws IOException 205 { 206 throw new IOException("Cannot validate certification without a hostname"); 207 } 208 209 public Socket createSocket(InetAddress inaddr, int i, InetAddress inaddr2, int j) 210 throws IOException 211 { 212 throw new IOException("Cannot validate certification without a hostname"); 213 } 214 215 public Socket createSocket(InetAddress inaddr, int i) throws IOException { 216 throw new IOException("Cannot validate certification without a hostname"); 217 } 218 219 public Socket createSocket(String s, int i, InetAddress inaddr, int j) throws IOException { 220 SSLSocket sslSock = (SSLSocket) factory.createSocket(s, i, inaddr, j); 221 222 if (socketReadTimeoutForSslHandshake >= 0) { 223 sslSock.setSoTimeout(socketReadTimeoutForSslHandshake); 224 } 225 226 validateSocket(sslSock,s); 227 sslSock.setSoTimeout(0); 228 229 return sslSock; 230 } 231 232 public Socket createSocket(String s, int i) throws IOException { 233 SSLSocket sslSock = (SSLSocket) factory.createSocket(s, i); 234 235 if (socketReadTimeoutForSslHandshake >= 0) { 236 sslSock.setSoTimeout(socketReadTimeoutForSslHandshake); 237 } 238 239 validateSocket(sslSock,s); 240 sslSock.setSoTimeout(0); 241 242 return sslSock; 243 } 244 245 public String[] getDefaultCipherSuites() { 246 return factory.getSupportedCipherSuites(); 247 } 248 249 public String[] getSupportedCipherSuites() { 250 return factory.getSupportedCipherSuites(); 251 } 252} 253 254 255