PageRenderTime 56ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/core/java/android/net/SSLCertificateSocketFactory.java

https://github.com/doixanh/android_frameworks_base
Java | 352 lines | 177 code | 32 blank | 143 comment | 18 complexity | 541edaa460195547ed89f0cbb53e7da9 MD5 | raw file
  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. package android.net;
  17. import com.android.internal.net.DomainNameValidator;
  18. import android.os.SystemProperties;
  19. import android.util.Config;
  20. import android.util.Log;
  21. import java.io.IOException;
  22. import java.net.InetAddress;
  23. import java.net.Socket;
  24. import java.security.GeneralSecurityException;
  25. import java.security.KeyManagementException;
  26. import java.security.KeyStore;
  27. import java.security.KeyStoreException;
  28. import java.security.NoSuchAlgorithmException;
  29. import java.security.cert.Certificate;
  30. import java.security.cert.X509Certificate;
  31. import javax.net.SocketFactory;
  32. import javax.net.ssl.HostnameVerifier;
  33. import javax.net.ssl.HttpsURLConnection;
  34. import javax.net.ssl.SSLException;
  35. import javax.net.ssl.SSLPeerUnverifiedException;
  36. import javax.net.ssl.SSLSession;
  37. import javax.net.ssl.SSLSocket;
  38. import javax.net.ssl.SSLSocketFactory;
  39. import javax.net.ssl.TrustManager;
  40. import javax.net.ssl.TrustManagerFactory;
  41. import javax.net.ssl.X509TrustManager;
  42. import org.apache.harmony.xnet.provider.jsse.OpenSSLContextImpl;
  43. import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl;
  44. import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache;
  45. /**
  46. * SSLSocketFactory implementation with several extra features:
  47. *
  48. * <ul>
  49. * <li>Timeout specification for SSL handshake operations
  50. * <li>Hostname verification in most cases (see WARNINGs below)
  51. * <li>Optional SSL session caching with {@link SSLSessionCache}
  52. * <li>Optionally bypass all SSL certificate checks
  53. * </ul>
  54. *
  55. * The handshake timeout does not apply to actual TCP socket connection.
  56. * If you want a connection timeout as well, use {@link #createSocket()}
  57. * and {@link Socket#connect(SocketAddress, int)}, after which you
  58. * must verify the identity of the server you are connected to.
  59. *
  60. * <p class="caution"><b>Most {@link SSLSocketFactory} implementations do not
  61. * verify the server's identity, allowing man-in-the-middle attacks.</b>
  62. * This implementation does check the server's certificate hostname, but only
  63. * for createSocket variants that specify a hostname. When using methods that
  64. * use {@link InetAddress} or which return an unconnected socket, you MUST
  65. * verify the server's identity yourself to ensure a secure connection.</p>
  66. *
  67. * <p>One way to verify the server's identity is to use
  68. * {@link HttpsURLConnection#getDefaultHostnameVerifier()} to get a
  69. * {@link HostnameVerifier} to verify the certificate hostname.
  70. *
  71. * <p>On development devices, "setprop socket.relaxsslcheck yes" bypasses all
  72. * SSL certificate and hostname checks for testing purposes. This setting
  73. * requires root access.
  74. */
  75. public class SSLCertificateSocketFactory extends SSLSocketFactory {
  76. private static final String TAG = "SSLCertificateSocketFactory";
  77. private static final TrustManager[] INSECURE_TRUST_MANAGER = new TrustManager[] {
  78. new X509TrustManager() {
  79. public X509Certificate[] getAcceptedIssuers() { return null; }
  80. public void checkClientTrusted(X509Certificate[] certs, String authType) { }
  81. public void checkServerTrusted(X509Certificate[] certs, String authType) { }
  82. }
  83. };
  84. private static final HostnameVerifier HOSTNAME_VERIFIER =
  85. HttpsURLConnection.getDefaultHostnameVerifier();
  86. private SSLSocketFactory mInsecureFactory = null;
  87. private SSLSocketFactory mSecureFactory = null;
  88. private final int mHandshakeTimeoutMillis;
  89. private final SSLClientSessionCache mSessionCache;
  90. private final boolean mSecure;
  91. /** @deprecated Use {@link #getDefault(int)} instead. */
  92. @Deprecated
  93. public SSLCertificateSocketFactory(int handshakeTimeoutMillis) {
  94. this(handshakeTimeoutMillis, null, true);
  95. }
  96. private SSLCertificateSocketFactory(
  97. int handshakeTimeoutMillis, SSLSessionCache cache, boolean secure) {
  98. mHandshakeTimeoutMillis = handshakeTimeoutMillis;
  99. mSessionCache = cache == null ? null : cache.mSessionCache;
  100. mSecure = secure;
  101. }
  102. /**
  103. * Returns a new socket factory instance with an optional handshake timeout.
  104. *
  105. * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
  106. * for none. The socket timeout is reset to 0 after the handshake.
  107. * @return a new SSLSocketFactory with the specified parameters
  108. */
  109. public static SocketFactory getDefault(int handshakeTimeoutMillis) {
  110. return new SSLCertificateSocketFactory(handshakeTimeoutMillis, null, true);
  111. }
  112. /**
  113. * Returns a new socket factory instance with an optional handshake timeout
  114. * and SSL session cache.
  115. *
  116. * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
  117. * for none. The socket timeout is reset to 0 after the handshake.
  118. * @param cache The {@link SSLClientSessionCache} to use, or null for no cache.
  119. * @return a new SSLSocketFactory with the specified parameters
  120. */
  121. public static SSLSocketFactory getDefault(int handshakeTimeoutMillis, SSLSessionCache cache) {
  122. return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true);
  123. }
  124. /**
  125. * Returns a new instance of a socket factory with all SSL security checks
  126. * disabled, using an optional handshake timeout and SSL session cache.
  127. *
  128. * <p class="caution"><b>Warning:</b> Sockets created using this factory
  129. * are vulnerable to man-in-the-middle attacks!</p>
  130. *
  131. * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
  132. * for none. The socket timeout is reset to 0 after the handshake.
  133. * @param cache The {@link SSLClientSessionCache} to use, or null for no cache.
  134. * @return an insecure SSLSocketFactory with the specified parameters
  135. */
  136. public static SSLSocketFactory getInsecure(int handshakeTimeoutMillis, SSLSessionCache cache) {
  137. return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, false);
  138. }
  139. /**
  140. * Returns a socket factory (also named SSLSocketFactory, but in a different
  141. * namespace) for use with the Apache HTTP stack.
  142. *
  143. * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
  144. * for none. The socket timeout is reset to 0 after the handshake.
  145. * @param cache The {@link SSLClientSessionCache} to use, or null for no cache.
  146. * @return a new SocketFactory with the specified parameters
  147. */
  148. public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(
  149. int handshakeTimeoutMillis,
  150. SSLSessionCache cache) {
  151. return new org.apache.http.conn.ssl.SSLSocketFactory(
  152. new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true));
  153. }
  154. /**
  155. * Verify the hostname of the certificate used by the other end of a
  156. * connected socket. You MUST call this if you did not supply a hostname
  157. * to {@link #createSocket()}. It is harmless to call this method
  158. * redundantly if the hostname has already been verified.
  159. *
  160. * <p>Wildcard certificates are allowed to verify any matching hostname,
  161. * so "foo.bar.example.com" is verified if the peer has a certificate
  162. * for "*.example.com".
  163. *
  164. * @param socket An SSL socket which has been connected to a server
  165. * @param hostname The expected hostname of the remote server
  166. * @throws IOException if something goes wrong handshaking with the server
  167. * @throws SSLPeerUnverifiedException if the server cannot prove its identity
  168. *
  169. * @hide
  170. */
  171. public static void verifyHostname(Socket socket, String hostname) throws IOException {
  172. if (!(socket instanceof SSLSocket)) {
  173. throw new IllegalArgumentException("Attempt to verify non-SSL socket");
  174. }
  175. if (!isSslCheckRelaxed()) {
  176. // The code at the start of OpenSSLSocketImpl.startHandshake()
  177. // ensures that the call is idempotent, so we can safely call it.
  178. SSLSocket ssl = (SSLSocket) socket;
  179. ssl.startHandshake();
  180. SSLSession session = ssl.getSession();
  181. if (session == null) {
  182. throw new SSLException("Cannot verify SSL socket without session");
  183. }
  184. if (!HOSTNAME_VERIFIER.verify(hostname, session)) {
  185. throw new SSLPeerUnverifiedException("Cannot verify hostname: " + hostname);
  186. }
  187. }
  188. }
  189. private SSLSocketFactory makeSocketFactory(TrustManager[] trustManagers) {
  190. try {
  191. OpenSSLContextImpl sslContext = new OpenSSLContextImpl();
  192. sslContext.engineInit(null, trustManagers, null);
  193. sslContext.engineGetClientSessionContext().setPersistentCache(mSessionCache);
  194. return sslContext.engineGetSocketFactory();
  195. } catch (KeyManagementException e) {
  196. Log.wtf(TAG, e);
  197. return (SSLSocketFactory) SSLSocketFactory.getDefault(); // Fallback
  198. }
  199. }
  200. private static boolean isSslCheckRelaxed() {
  201. return "1".equals(SystemProperties.get("ro.debuggable")) &&
  202. "yes".equals(SystemProperties.get("socket.relaxsslcheck"));
  203. }
  204. private synchronized SSLSocketFactory getDelegate() {
  205. // Relax the SSL check if instructed (for this factory, or systemwide)
  206. if (!mSecure || isSslCheckRelaxed()) {
  207. if (mInsecureFactory == null) {
  208. if (mSecure) {
  209. Log.w(TAG, "*** BYPASSING SSL SECURITY CHECKS (socket.relaxsslcheck=yes) ***");
  210. } else {
  211. Log.w(TAG, "Bypassing SSL security checks at caller's request");
  212. }
  213. mInsecureFactory = makeSocketFactory(INSECURE_TRUST_MANAGER);
  214. }
  215. return mInsecureFactory;
  216. } else {
  217. if (mSecureFactory == null) {
  218. mSecureFactory = makeSocketFactory(null);
  219. }
  220. return mSecureFactory;
  221. }
  222. }
  223. /**
  224. * {@inheritDoc}
  225. *
  226. * <p>This method verifies the peer's certificate hostname after connecting
  227. * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
  228. */
  229. @Override
  230. public Socket createSocket(Socket k, String host, int port, boolean close) throws IOException {
  231. OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(k, host, port, close);
  232. s.setHandshakeTimeout(mHandshakeTimeoutMillis);
  233. if (mSecure) {
  234. verifyHostname(s, host);
  235. }
  236. return s;
  237. }
  238. /**
  239. * Creates a new socket which is not connected to any remote host.
  240. * You must use {@link Socket#connect} to connect the socket.
  241. *
  242. * <p class="caution"><b>Warning:</b> Hostname verification is not performed
  243. * with this method. You MUST verify the server's identity after connecting
  244. * the socket to avoid man-in-the-middle attacks.</p>
  245. */
  246. @Override
  247. public Socket createSocket() throws IOException {
  248. OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket();
  249. s.setHandshakeTimeout(mHandshakeTimeoutMillis);
  250. return s;
  251. }
  252. /**
  253. * {@inheritDoc}
  254. *
  255. * <p class="caution"><b>Warning:</b> Hostname verification is not performed
  256. * with this method. You MUST verify the server's identity after connecting
  257. * the socket to avoid man-in-the-middle attacks.</p>
  258. */
  259. @Override
  260. public Socket createSocket(InetAddress addr, int port, InetAddress localAddr, int localPort)
  261. throws IOException {
  262. OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
  263. addr, port, localAddr, localPort);
  264. s.setHandshakeTimeout(mHandshakeTimeoutMillis);
  265. return s;
  266. }
  267. /**
  268. * {@inheritDoc}
  269. *
  270. * <p class="caution"><b>Warning:</b> Hostname verification is not performed
  271. * with this method. You MUST verify the server's identity after connecting
  272. * the socket to avoid man-in-the-middle attacks.</p>
  273. */
  274. @Override
  275. public Socket createSocket(InetAddress addr, int port) throws IOException {
  276. OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(addr, port);
  277. s.setHandshakeTimeout(mHandshakeTimeoutMillis);
  278. return s;
  279. }
  280. /**
  281. * {@inheritDoc}
  282. *
  283. * <p>This method verifies the peer's certificate hostname after connecting
  284. * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
  285. */
  286. @Override
  287. public Socket createSocket(String host, int port, InetAddress localAddr, int localPort)
  288. throws IOException {
  289. OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
  290. host, port, localAddr, localPort);
  291. s.setHandshakeTimeout(mHandshakeTimeoutMillis);
  292. if (mSecure) {
  293. verifyHostname(s, host);
  294. }
  295. return s;
  296. }
  297. /**
  298. * {@inheritDoc}
  299. *
  300. * <p>This method verifies the peer's certificate hostname after connecting
  301. * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
  302. */
  303. @Override
  304. public Socket createSocket(String host, int port) throws IOException {
  305. OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(host, port);
  306. s.setHandshakeTimeout(mHandshakeTimeoutMillis);
  307. if (mSecure) {
  308. verifyHostname(s, host);
  309. }
  310. return s;
  311. }
  312. @Override
  313. public String[] getDefaultCipherSuites() {
  314. return getDelegate().getSupportedCipherSuites();
  315. }
  316. @Override
  317. public String[] getSupportedCipherSuites() {
  318. return getDelegate().getSupportedCipherSuites();
  319. }
  320. }