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