1c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki/*
2c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki * Copyright (C) 2010 The Android Open Source Project
3c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki *
4c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki * Licensed under the Apache License, Version 2.0 (the "License");
5c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki * you may not use this file except in compliance with the License.
6c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki * You may obtain a copy of the License at
7c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki *
8c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki *      http://www.apache.org/licenses/LICENSE-2.0
9c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki *
10c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki * Unless required by applicable law or agreed to in writing, software
11c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki * distributed under the License is distributed on an "AS IS" BASIS,
12c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki * See the License for the specific language governing permissions and
14c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki * limitations under the License.
15c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki */
16c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki
173a5c1fb274a9ce72d708d88509bf2607cb018dddMarc Blankpackage com.android.emailcommon.utility;
18c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki
1978959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport android.content.Context;
20c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onukiimport android.net.SSLCertificateSocketFactory;
2178959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport android.security.KeyChain;
2278959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport android.security.KeyChainException;
2378959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport android.util.Log;
24c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki
25f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komaloimport com.google.common.annotations.VisibleForTesting;
26f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo
27f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komaloimport java.net.InetAddress;
2878959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport java.net.Socket;
2978959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport java.security.Principal;
3078959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport java.security.PrivateKey;
31cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komaloimport java.security.cert.CertificateException;
3278959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport java.security.cert.X509Certificate;
3378959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport java.util.Arrays;
3478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
3578959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport javax.net.ssl.KeyManager;
3678959916e771114ff8c48fc181e34a7dff0aa672Ben Komaloimport javax.net.ssl.X509ExtendedKeyManager;
37c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki
38c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onukipublic class SSLUtils {
3978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo    private static SSLCertificateSocketFactory sInsecureFactory;
4078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo    private static SSLCertificateSocketFactory sSecureFactory;
4178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
4278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo    private static final boolean LOG_ENABLED = false;
4378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo    private static final String TAG = "Email.Ssl";
44c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki
45c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki    /**
4678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo     * Returns a {@link javax.net.ssl.SSLSocketFactory}.
4778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo     * Optionally bypass all SSL certificate checks.
48c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki     *
49c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki     * @param insecure if true, bypass all SSL certificate checks
50c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki     */
51877b9070fa4d7a6b51ae1f75640a6c23cc86c963Ben Komalo    public synchronized static SSLCertificateSocketFactory getSSLSocketFactory(
5278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            boolean insecure) {
53c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki        if (insecure) {
54c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki            if (sInsecureFactory == null) {
5578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo                sInsecureFactory = (SSLCertificateSocketFactory)
5678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo                        SSLCertificateSocketFactory.getInsecure(0, null);
57c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki            }
58c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki            return sInsecureFactory;
59c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki        } else {
60c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki            if (sSecureFactory == null) {
6178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo                sSecureFactory = (SSLCertificateSocketFactory)
6278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo                        SSLCertificateSocketFactory.getDefault(0, null);
63c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki            }
64c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki            return sSecureFactory;
65c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki        }
66c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki    }
67724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo
68724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo    /**
6978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo     * Returns a {@link org.apache.http.conn.ssl.SSLSocketFactory SSLSocketFactory} for use with the
7078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo     * Apache HTTP stack.
7178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo     */
72f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo    public static SSLSocketFactory getHttpSocketFactory(boolean insecure, KeyManager keyManager) {
7378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        SSLCertificateSocketFactory underlying = getSSLSocketFactory(insecure);
74f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo        if (keyManager != null) {
75f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo            underlying.setKeyManagers(new KeyManager[] { keyManager });
76f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo        }
774d3f3f3ab95c03d4c1ab308801b92ba1d9df2276Ben Komalo        SSLSocketFactory wrapped = new SSLSocketFactory(underlying);
784d3f3f3ab95c03d4c1ab308801b92ba1d9df2276Ben Komalo        if (insecure) {
794d3f3f3ab95c03d4c1ab308801b92ba1d9df2276Ben Komalo            wrapped.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
804d3f3f3ab95c03d4c1ab308801b92ba1d9df2276Ben Komalo        }
814d3f3f3ab95c03d4c1ab308801b92ba1d9df2276Ben Komalo        return wrapped;
8278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo    }
8378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
8478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo    /**
85724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo     * Escapes the contents a string to be used as a safe scheme name in the URI according to
86724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo     * http://tools.ietf.org/html/rfc3986#section-3.1
87724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo     *
88724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo     * This does not ensure that the first character is a letter (which is required by the RFC).
89724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo     */
90745b33b8ff55e9a9c4871f07f9d97db893f784b2Makoto Onuki    @VisibleForTesting
91724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo    public static String escapeForSchemeName(String s) {
92724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo        // According to the RFC, scheme names are case-insensitive.
93724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo        s = s.toLowerCase();
94724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo
95724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo        StringBuilder sb = new StringBuilder();
96724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo        for (int i = 0; i < s.length(); i++) {
97724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo            char c = s.charAt(i);
98724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo            if (Character.isLetter(c) || Character.isDigit(c)
99724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo                    || ('-' == c) || ('.' == c)) {
100724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo                // Safe - use as is.
101724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo                sb.append(c);
102724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo            } else if ('+' == c) {
103724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo                // + is used as our escape character, so double it up.
104724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo                sb.append("++");
105724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo            } else {
106724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo                // Unsafe - escape.
107724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo                sb.append('+').append((int) c);
108724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo            }
109724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo        }
110724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo        return sb.toString();
111724c3a81cd3649b48ab47c6e49cb42f73f20c815Ben Komalo    }
11278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
11378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo    private static abstract class StubKeyManager extends X509ExtendedKeyManager {
11478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        @Override public abstract String chooseClientAlias(
11578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo                String[] keyTypes, Principal[] issuers, Socket socket);
11678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
11778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        @Override public abstract X509Certificate[] getCertificateChain(String alias);
11878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
11978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        @Override public abstract PrivateKey getPrivateKey(String alias);
12078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
12178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
12278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        // The following methods are unused.
12378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
12478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        @Override
12578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        public final String chooseServerAlias(
12678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo                String keyType, Principal[] issuers, Socket socket) {
12778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            // not a client SSLSocket callback
12878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            throw new UnsupportedOperationException();
12978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        }
13078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
13178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        @Override
13278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        public final String[] getClientAliases(String keyType, Principal[] issuers) {
13378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            // not a client SSLSocket callback
13478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            throw new UnsupportedOperationException();
13578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        }
13678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
13778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        @Override
13878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        public final String[] getServerAliases(String keyType, Principal[] issuers) {
13978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            // not a client SSLSocket callback
14078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            throw new UnsupportedOperationException();
14178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        }
14278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo    }
14378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
14478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo    /**
145f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo     * A dummy {@link KeyManager} which keeps track of the last time a server has requested
146f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo     * a client certificate.
147f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo     */
148f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo    public static class TrackingKeyManager extends StubKeyManager {
149f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo        private volatile long mLastTimeCertRequested = 0L;
150f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo
151f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo        @Override
152f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo        public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
153f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo            if (LOG_ENABLED) {
154f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo                InetAddress address = socket.getInetAddress();
155f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo                Log.i(TAG, "TrackingKeyManager: requesting a client cert alias for "
156f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo                        + address.getCanonicalHostName());
157f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo            }
158f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo            mLastTimeCertRequested = System.currentTimeMillis();
159f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo            return null;
160f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo        }
161f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo
162f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo        @Override
163f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo        public X509Certificate[] getCertificateChain(String alias) {
164f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo            if (LOG_ENABLED) {
165f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo                Log.i(TAG, "TrackingKeyManager: returning a null cert chain");
166f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo            }
167f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo            return null;
168f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo        }
169f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo
170f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo        @Override
171f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo        public PrivateKey getPrivateKey(String alias) {
172f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo            if (LOG_ENABLED) {
173f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo                Log.i(TAG, "TrackingKeyManager: returning a null private key");
174f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo            }
175f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo            return null;
176f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo        }
177f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo
178f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo        /**
179f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo         * @return the last time that this {@link KeyManager} detected a request by a server
180f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo         *     for a client certificate (in millis since epoch).
181f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo         */
182f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo        public long getLastCertReqTime() {
183f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo            return mLastTimeCertRequested;
184f4f10a3fdf3fdf94db4780017c4392823942b1d7Ben Komalo        }
185f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo    }
186f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo
187f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo    /**
18878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo     * A {@link KeyManager} that reads uses credentials stored in the system {@link KeyChain}.
18978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo     */
19078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo    public static class KeyChainKeyManager extends StubKeyManager {
19178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        private final String mClientAlias;
19278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        private final X509Certificate[] mCertificateChain;
19378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        private final PrivateKey mPrivateKey;
19478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
19578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        /**
19678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo         * Builds an instance of a KeyChainKeyManager using the given certificate alias.
19778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo         * If for any reason retrieval of the credentials from the system {@link KeyChain} fails,
19878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo         * a {@code null} value will be returned.
19978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo         */
200cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komalo        public static KeyChainKeyManager fromAlias(Context context, String alias)
201cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komalo                throws CertificateException {
20278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            X509Certificate[] certificateChain;
20378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            try {
20478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo                certificateChain = KeyChain.getCertificateChain(context, alias);
20578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            } catch (KeyChainException e) {
206f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo                logError(alias, "certificate chain", e);
207cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komalo                throw new CertificateException(e);
20878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            } catch (InterruptedException e) {
209f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo                logError(alias, "certificate chain", e);
210cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komalo                throw new CertificateException(e);
21178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            }
21278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
21378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            PrivateKey privateKey;
21478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            try {
21578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo                privateKey = KeyChain.getPrivateKey(context, alias);
21678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            } catch (KeyChainException e) {
217f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo                logError(alias, "private key", e);
218cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komalo                throw new CertificateException(e);
21978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            } catch (InterruptedException e) {
220f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo                logError(alias, "private key", e);
221cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komalo                throw new CertificateException(e);
222cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komalo            }
223cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komalo
224cb24e515b7983133133ca38bd3e3e6354daaab76Ben Komalo            if (certificateChain == null || privateKey == null) {
225877b9070fa4d7a6b51ae1f75640a6c23cc86c963Ben Komalo                throw new CertificateException("Can't access certificate from keystore");
22678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            }
22778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
22878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            return new KeyChainKeyManager(alias, certificateChain, privateKey);
22978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        }
23078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
231f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo        private static void logError(String alias, String type, Exception ex) {
232877b9070fa4d7a6b51ae1f75640a6c23cc86c963Ben Komalo            // Avoid logging PII when explicit logging is not on.
233877b9070fa4d7a6b51ae1f75640a6c23cc86c963Ben Komalo            if (LOG_ENABLED) {
234877b9070fa4d7a6b51ae1f75640a6c23cc86c963Ben Komalo                Log.e(TAG, "Unable to retrieve " + type + " for [" + alias + "] due to " + ex);
235877b9070fa4d7a6b51ae1f75640a6c23cc86c963Ben Komalo            } else {
236877b9070fa4d7a6b51ae1f75640a6c23cc86c963Ben Komalo                Log.e(TAG, "Unable to retrieve " + type + " due to " + ex);
237877b9070fa4d7a6b51ae1f75640a6c23cc86c963Ben Komalo            }
238f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo        }
239f4dbbf10996e6bca926a5825bbc69e1e172c20c0Ben Komalo
24078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        private KeyChainKeyManager(
24178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo                String clientAlias, X509Certificate[] certificateChain, PrivateKey privateKey) {
24278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            mClientAlias = clientAlias;
24378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            mCertificateChain = certificateChain;
24478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            mPrivateKey = privateKey;
24578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        }
24678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
24778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
24878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        @Override
24978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
25078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            if (LOG_ENABLED) {
25178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo                Log.i(TAG, "Requesting a client cert alias for " + Arrays.toString(keyTypes));
25278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            }
25378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            return mClientAlias;
25478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        }
25578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
25678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        @Override
25778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        public X509Certificate[] getCertificateChain(String alias) {
25878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            if (LOG_ENABLED) {
25978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo                Log.i(TAG, "Requesting a client certificate chain for alias [" + alias + "]");
26078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            }
26178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            return mCertificateChain;
26278959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        }
26378959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo
26478959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        @Override
26578959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        public PrivateKey getPrivateKey(String alias) {
26678959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            if (LOG_ENABLED) {
26778959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo                Log.i(TAG, "Requesting a client private key for alias [" + alias + "]");
26878959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            }
26978959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo            return mPrivateKey;
27078959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo        }
27178959916e771114ff8c48fc181e34a7dff0aa672Ben Komalo    }
272c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki}
273