178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo/* 278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * Copyright (C) 2011 The Android Open Source Project 378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * 478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * Licensed under the Apache License, Version 2.0 (the "License"); 578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * you may not use this file except in compliance with the License. 678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * You may obtain a copy of the License at 778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * 878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * http://www.apache.org/licenses/LICENSE-2.0 978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * 1078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * Unless required by applicable law or agreed to in writing, software 1178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * distributed under the License is distributed on an "AS IS" BASIS, 1278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * See the License for the specific language governing permissions and 1478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * limitations under the License. 1578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo */ 1678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 1778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 1878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalopackage com.android.emailcommon.utility; 1978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 20f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komaloimport android.content.Context; 214b629cf25804fd74712b0f7acce1c2e59135b9bfTony Mantlerimport android.text.TextUtils; 22f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo 2378959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport com.android.emailcommon.Logging; 24f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.emailcommon.provider.HostAuth; 2578959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport com.android.emailcommon.utility.SSLUtils.KeyChainKeyManager; 26f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komaloimport com.android.emailcommon.utility.SSLUtils.TrackingKeyManager; 27560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedyimport com.android.mail.utils.LogUtils; 2878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 2978959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport org.apache.http.conn.scheme.PlainSocketFactory; 3078959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport org.apache.http.conn.scheme.Scheme; 3178959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport org.apache.http.conn.scheme.SchemeRegistry; 3278959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; 3378959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport org.apache.http.params.HttpParams; 3478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 35cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komaloimport java.security.cert.CertificateException; 36cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komalo 3778959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport javax.net.ssl.KeyManager; 3878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 3978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo/** 4078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * A thread-safe client connection manager that manages the use of client certificates from the 4178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * {@link android.security.KeyChain} for SSL connections. 4278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo */ 4378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalopublic class EmailClientConnectionManager extends ThreadSafeClientConnManager { 4478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 45f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank private static final int STANDARD_PORT = 80; 46f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank private static final int STANDARD_SSL_PORT = 443; 4778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo private static final boolean LOG_ENABLED = false; 4878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 4978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo /** 50f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo * A {@link KeyManager} to track client certificate requests from servers. 51f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo */ 52f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo private final TrackingKeyManager mTrackingKeyManager; 53f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo 54f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo /** 5578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * Not publicly instantiable except via {@link #newInstance(HttpParams)} 5678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo */ 57f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo private EmailClientConnectionManager( 58f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo HttpParams params, SchemeRegistry registry, TrackingKeyManager keyManager) { 5978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo super(params, registry); 60f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo mTrackingKeyManager = keyManager; 6178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 6278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 637d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank public static EmailClientConnectionManager newInstance(Context context, HttpParams params, 647d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank HostAuth hostAuth) { 65f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo TrackingKeyManager keyManager = new TrackingKeyManager(); 667d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank boolean ssl = hostAuth.shouldUseSsl(); 677d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank int port = hostAuth.mPort; 68f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo 6978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo // Create a registry for our three schemes; http and https will use built-in factories 7078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo SchemeRegistry registry = new SchemeRegistry(); 71f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 72f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank ssl ? STANDARD_PORT : port)); 73f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank // Register https with the secure factory 74f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo registry.register(new Scheme("https", 757d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank SSLUtils.getHttpSocketFactory(context, hostAuth, keyManager, false), 767d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank ssl ? port : STANDARD_SSL_PORT)); 7778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo // Register the httpts scheme with our insecure factory 7878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo registry.register(new Scheme("httpts", 797d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank SSLUtils.getHttpSocketFactory(context, hostAuth, keyManager, true), 80f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank ssl ? port : STANDARD_SSL_PORT)); 8178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 82f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo return new EmailClientConnectionManager(params, registry, keyManager); 8378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 8478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 8578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo /** 8678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * Ensures that a client SSL certificate is known to be used for the specified connection 8778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * manager. 8878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * A {@link SchemeRegistry} is used to denote which client certificates to use for a given 8978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * connection, so clients of this connection manager should use 9078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * {@link #makeSchemeForClientCert(String, boolean)}. 9178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo */ 92f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank public synchronized void registerClientCert(Context context, HostAuth hostAuth) 93cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komalo throws CertificateException { 944b629cf25804fd74712b0f7acce1c2e59135b9bfTony Mantler if (TextUtils.isEmpty(hostAuth.mClientCertAlias)) { 954b629cf25804fd74712b0f7acce1c2e59135b9bfTony Mantler return; 964b629cf25804fd74712b0f7acce1c2e59135b9bfTony Mantler } 9778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo SchemeRegistry registry = getSchemeRegistry(); 98f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank String schemeName = makeSchemeForClientCert(hostAuth.mClientCertAlias, 99f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank hostAuth.shouldTrustAllServerCerts()); 10078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo Scheme existing = registry.get(schemeName); 10178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo if (existing == null) { 10278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo if (LOG_ENABLED) { 103560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.i(Logging.LOG_TAG, "Registering socket factory for certificate alias [" 104f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank + hostAuth.mClientCertAlias + "]"); 10578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 106f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank KeyManager keyManager = 107f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank KeyChainKeyManager.fromAlias(context, hostAuth.mClientCertAlias); 1080e969171bb32050e845412fb51bc856bf8a2c954Marc Blank boolean insecure = hostAuth.shouldTrustAllServerCerts(); 1090e969171bb32050e845412fb51bc856bf8a2c954Marc Blank SSLSocketFactory ssf = 1100e969171bb32050e845412fb51bc856bf8a2c954Marc Blank SSLUtils.getHttpSocketFactory(context, hostAuth, keyManager, insecure); 1110e969171bb32050e845412fb51bc856bf8a2c954Marc Blank registry.register(new Scheme(schemeName, ssf, hostAuth.mPort)); 11278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 11378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 11478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 11578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo /** 11678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * Unregisters a custom connection type that uses a client certificate on the connection 11778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * manager. 11878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * @see #registerClientCert(Context, String, boolean) 11978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo */ 12078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo public synchronized void unregisterClientCert( 12178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo String clientCertAlias, boolean trustAllServerCerts) { 12278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo SchemeRegistry registry = getSchemeRegistry(); 12378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo String schemeName = makeSchemeForClientCert(clientCertAlias, trustAllServerCerts); 12478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo Scheme existing = registry.get(schemeName); 12578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo if (existing != null) { 12678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo registry.unregister(schemeName); 12778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 12878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 12978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 13078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo /** 13178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * Builds a custom scheme name to be used in a connection manager according to the connection 13278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * parameters. 13378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo */ 13478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo public static String makeScheme( 13578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo boolean useSsl, boolean trustAllServerCerts, String clientCertAlias) { 136f1789afa535b113047ef20623d79fdfb257537aaTony Mantler if (!TextUtils.isEmpty(clientCertAlias)) { 13778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo return makeSchemeForClientCert(clientCertAlias, trustAllServerCerts); 13878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } else { 13978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo return useSsl ? (trustAllServerCerts ? "httpts" : "https") : "http"; 14078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 14178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 14278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 14378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo /** 14478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * Builds a unique scheme name for an SSL connection that uses a client user certificate. 14578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo */ 14678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo private static String makeSchemeForClientCert( 14778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo String clientCertAlias, boolean trustAllServerCerts) { 14878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo String safeAlias = SSLUtils.escapeForSchemeName(clientCertAlias); 14978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo return (trustAllServerCerts ? "httpts" : "https") + "+clientCert+" + safeAlias; 15078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 151f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo 152f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo /** 153f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo * @param since A timestamp in millis from epoch from which to check 154f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo * @return whether or not this connection manager has detected any unsatisfied requests for 155f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo * a client SSL certificate by any servers 156f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo */ 157f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo public synchronized boolean hasDetectedUnsatisfiedCertReq(long since) { 158f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo return mTrackingKeyManager.getLastCertReqTime() >= since; 159f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo } 16078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo} 161