EmailClientConnectionManager.java revision f4f10a3fdf3fdf94db4780017c4392823942b1d7
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; 21f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komaloimport android.net.SSLCertificateSocketFactory; 22f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komaloimport android.util.Log; 23f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo 2478959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport com.android.emailcommon.Logging; 2578959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport com.android.emailcommon.utility.SSLUtils.KeyChainKeyManager; 26f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komaloimport com.android.emailcommon.utility.SSLUtils.TrackingKeyManager; 2778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 2878959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport org.apache.http.conn.scheme.PlainSocketFactory; 2978959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport org.apache.http.conn.scheme.Scheme; 3078959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport org.apache.http.conn.scheme.SchemeRegistry; 3178959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; 3278959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport org.apache.http.params.HttpParams; 3378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 34cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komaloimport java.security.cert.CertificateException; 35cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komalo 3678959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport javax.net.ssl.KeyManager; 3778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 3878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo/** 3978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * A thread-safe client connection manager that manages the use of client certificates from the 4078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * {@link android.security.KeyChain} for SSL connections. 4178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo */ 4278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalopublic class EmailClientConnectionManager extends ThreadSafeClientConnManager { 4378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 4478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo private static final boolean LOG_ENABLED = false; 4578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 4678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo /** 47f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo * A {@link KeyManager} to track client certificate requests from servers. 48f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo */ 49f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo private final TrackingKeyManager mTrackingKeyManager; 50f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo 51f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo /** 5278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * Not publicly instantiable except via {@link #newInstance(HttpParams)} 5378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo */ 54f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo private EmailClientConnectionManager( 55f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo HttpParams params, SchemeRegistry registry, TrackingKeyManager keyManager) { 5678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo super(params, registry); 57f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo mTrackingKeyManager = keyManager; 5878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 5978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 6078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo public static EmailClientConnectionManager newInstance(HttpParams params) { 61f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo TrackingKeyManager keyManager = new TrackingKeyManager(); 62f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo 6378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo // Create a registry for our three schemes; http and https will use built-in factories 6478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo SchemeRegistry registry = new SchemeRegistry(); 6578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo registry.register(new Scheme("http", 6678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo PlainSocketFactory.getSocketFactory(), 80)); 67f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo registry.register(new Scheme("https", 68f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo SSLUtils.getHttpSocketFactory(false, keyManager), 443)); 6978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 7078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo // Register the httpts scheme with our insecure factory 7178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo registry.register(new Scheme("httpts", 72f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo SSLUtils.getHttpSocketFactory(true /*insecure*/, keyManager), 443)); 7378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 74f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo return new EmailClientConnectionManager(params, registry, keyManager); 7578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 7678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 7778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo /** 7878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * Ensures that a client SSL certificate is known to be used for the specified connection 7978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * manager. 8078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * A {@link SchemeRegistry} is used to denote which client certificates to use for a given 8178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * connection, so clients of this connection manager should use 8278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * {@link #makeSchemeForClientCert(String, boolean)}. 8378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo */ 8478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo public synchronized void registerClientCert( 85cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komalo Context context, String clientCertAlias, boolean trustAllServerCerts) 86cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komalo throws CertificateException { 8778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo SchemeRegistry registry = getSchemeRegistry(); 8878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo String schemeName = makeSchemeForClientCert(clientCertAlias, trustAllServerCerts); 8978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo Scheme existing = registry.get(schemeName); 9078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo if (existing == null) { 9178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo if (LOG_ENABLED) { 9278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo Log.i(Logging.LOG_TAG, "Registering socket factory for certificate alias [" 9378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo + clientCertAlias + "]"); 9478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 9578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo KeyManager keyManager = KeyChainKeyManager.fromAlias(context, clientCertAlias); 9678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo SSLCertificateSocketFactory underlying = SSLUtils.getSSLSocketFactory( 9778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo trustAllServerCerts); 9878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo underlying.setKeyManagers(new KeyManager[] { keyManager }); 9978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo registry.register(new Scheme(schemeName, new SSLSocketFactory(underlying), 443)); 10078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 10178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 10278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 10378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo /** 10478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * Unregisters a custom connection type that uses a client certificate on the connection 10578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * manager. 10678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * @see #registerClientCert(Context, String, boolean) 10778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo */ 10878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo public synchronized void unregisterClientCert( 10978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo String clientCertAlias, boolean trustAllServerCerts) { 11078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo SchemeRegistry registry = getSchemeRegistry(); 11178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo String schemeName = makeSchemeForClientCert(clientCertAlias, trustAllServerCerts); 11278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo Scheme existing = registry.get(schemeName); 11378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo if (existing != null) { 11478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo registry.unregister(schemeName); 11578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 11678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 11778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 11878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo /** 11978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * Builds a custom scheme name to be used in a connection manager according to the connection 12078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * parameters. 12178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo */ 12278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo public static String makeScheme( 12378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo boolean useSsl, boolean trustAllServerCerts, String clientCertAlias) { 12478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo if (clientCertAlias != null) { 12578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo return makeSchemeForClientCert(clientCertAlias, trustAllServerCerts); 12678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } else { 12778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo return useSsl ? (trustAllServerCerts ? "httpts" : "https") : "http"; 12878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 12978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 13078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo 13178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo /** 13278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo * Builds a unique scheme name for an SSL connection that uses a client user certificate. 13378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo */ 13478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo private static String makeSchemeForClientCert( 13578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo String clientCertAlias, boolean trustAllServerCerts) { 13678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo String safeAlias = SSLUtils.escapeForSchemeName(clientCertAlias); 13778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo return (trustAllServerCerts ? "httpts" : "https") + "+clientCert+" + safeAlias; 13878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo } 139f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo 140f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo /** 141f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo * @param since A timestamp in millis from epoch from which to check 142f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo * @return whether or not this connection manager has detected any unsatisfied requests for 143f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo * a client SSL certificate by any servers 144f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo */ 145f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo public synchronized boolean hasDetectedUnsatisfiedCertReq(long since) { 146f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo return mTrackingKeyManager.getLastCertReqTime() >= since; 147f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo } 14878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo} 149