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