1bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom/*
2bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom * Copyright (C) 2010 The Android Open Source Project
3bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom *
4bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom * Licensed under the Apache License, Version 2.0 (the "License");
5bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom * you may not use this file except in compliance with the License.
6bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom * You may obtain a copy of the License at
7bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom *
8bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom *      http://www.apache.org/licenses/LICENSE-2.0
9bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom *
10bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom * Unless required by applicable law or agreed to in writing, software
11bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom * distributed under the License is distributed on an "AS IS" BASIS,
12bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom * See the License for the specific language governing permissions and
14bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom * limitations under the License.
15bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom */
16bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom
174557728efb66c455a52b7669a8eefef7a9e54854Jesse Wilsonpackage libcore.javax.net.ssl;
18bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom
191ce90cc78f833da6ff674fb2028f2560938313ecKenny Rootimport java.io.ByteArrayInputStream;
201ce90cc78f833da6ff674fb2028f2560938313ecKenny Rootimport java.io.ByteArrayOutputStream;
213258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstromimport java.io.IOException;
221ce90cc78f833da6ff674fb2028f2560938313ecKenny Rootimport java.io.ObjectInputStream;
231ce90cc78f833da6ff674fb2028f2560938313ecKenny Rootimport java.io.ObjectOutput;
241ce90cc78f833da6ff674fb2028f2560938313ecKenny Rootimport java.io.ObjectOutputStream;
251ce90cc78f833da6ff674fb2028f2560938313ecKenny Rootimport java.io.OutputStream;
26bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstromimport java.net.InetAddress;
271ce90cc78f833da6ff674fb2028f2560938313ecKenny Rootimport java.net.InetSocketAddress;
283258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstromimport java.net.Socket;
293258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstromimport java.net.UnknownHostException;
30bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstromimport java.security.KeyStore;
31204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstromimport java.security.Principal;
32bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstromimport java.security.SecureRandom;
33e688a4123f165ed2905878e312b074b8c825d119Brian Carlstromimport java.security.cert.Certificate;
34059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstromimport java.security.cert.CertificateException;
35bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstromimport java.security.cert.X509Certificate;
36204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstromimport java.util.Collections;
374557728efb66c455a52b7669a8eefef7a9e54854Jesse Wilsonimport javax.net.ssl.KeyManager;
384557728efb66c455a52b7669a8eefef7a9e54854Jesse Wilsonimport javax.net.ssl.SSLContext;
394557728efb66c455a52b7669a8eefef7a9e54854Jesse Wilsonimport javax.net.ssl.SSLServerSocket;
403258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstromimport javax.net.ssl.SSLSocket;
413258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstromimport javax.net.ssl.SSLSocketFactory;
424557728efb66c455a52b7669a8eefef7a9e54854Jesse Wilsonimport javax.net.ssl.TrustManager;
4301b7734160977458d44d1fb179984fd91672f08dKenny Rootimport javax.net.ssl.X509ExtendedTrustManager;
444557728efb66c455a52b7669a8eefef7a9e54854Jesse Wilsonimport javax.net.ssl.X509TrustManager;
450c131a2ca38465b7d1df4eaee63ac73ce4d5986dBrian Carlstromimport junit.framework.Assert;
463258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstromimport libcore.java.security.StandardNames;
473258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstromimport libcore.java.security.TestKeyStore;
48bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom
49bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom/**
50bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom * TestSSLContext is a convenience class for other tests that
51bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom * want a canned SSLContext and related state for testing so they
52bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom * don't have to duplicate the logic.
53bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom */
540c131a2ca38465b7d1df4eaee63ac73ce4d5986dBrian Carlstrompublic final class TestSSLContext extends Assert {
55bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom
56f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes    /*
570af0a7959d838c48e6b4e8dc9ac188ff6bbb6a87Brian Carlstrom     * The RI and Android have very different default SSLSession cache behaviors.
580af0a7959d838c48e6b4e8dc9ac188ff6bbb6a87Brian Carlstrom     * The RI keeps an unlimited number of SSLSesions around for 1 day.
59f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     * Android keeps 10 SSLSessions forever.
600af0a7959d838c48e6b4e8dc9ac188ff6bbb6a87Brian Carlstrom     */
619a106a63508697a6f5f02c20b7cc6b7c6152695fBrian Carlstrom    private static final boolean IS_RI = StandardNames.IS_RI;
620af0a7959d838c48e6b4e8dc9ac188ff6bbb6a87Brian Carlstrom    public static final int EXPECTED_DEFAULT_CLIENT_SSL_SESSION_CACHE_SIZE = (IS_RI) ? 0 : 10;
630af0a7959d838c48e6b4e8dc9ac188ff6bbb6a87Brian Carlstrom    public static final int EXPECTED_DEFAULT_SERVER_SSL_SESSION_CACHE_SIZE = (IS_RI) ? 0 : 100;
644e1404f2017dc7db05b69ecad241f78c5bb1a4eeAlex Klyubin    public static final int EXPECTED_DEFAULT_SSL_SESSION_CACHE_TIMEOUT =
654e1404f2017dc7db05b69ecad241f78c5bb1a4eeAlex Klyubin            (IS_RI) ? 24 * 3600 : 8 * 3600;
66bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom
67bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom    /**
68bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     * The Android SSLSocket and SSLServerSocket implementations are
69bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     * based on a version of OpenSSL which includes support for RFC
70bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     * 4507 session tickets. When using session tickets, the server
71bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     * does not need to keep a cache mapping session IDs to SSL
72bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     * sessions for reuse. Instead, the client presents the server
73bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     * with a session ticket it received from the server earlier,
74bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     * which is an SSL session encrypted by the server's secret
75bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     * key. Since in this case the server does not need to keep a
76bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     * cache, some tests may find different results depending on
77bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     * whether or not the session tickets are in use. These tests can
78bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     * use this function to determine if loopback SSL connections are
79bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     * expected to use session tickets and conditionalize their
80bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     * results appropriately.
81bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     */
82bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom    public static boolean sslServerSocketSupportsSessionTickets () {
830c131a2ca38465b7d1df4eaee63ac73ce4d5986dBrian Carlstrom        // Disabled session tickets for better compatability b/2682876
840c131a2ca38465b7d1df4eaee63ac73ce4d5986dBrian Carlstrom        // return !IS_RI;
850c131a2ca38465b7d1df4eaee63ac73ce4d5986dBrian Carlstrom        return false;
86bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom    }
87bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom
88059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom    public final KeyStore clientKeyStore;
89e3a187163504f00c98bd75cbd8bcbdde123ae2cdBrian Carlstrom    public final char[] clientStorePassword;
90059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom    public final KeyStore serverKeyStore;
91e3a187163504f00c98bd75cbd8bcbdde123ae2cdBrian Carlstrom    public final char[] serverStorePassword;
92edeec21a9c9e97cad91dffd47d4f2f7185dffe07Alex Klyubin    public final KeyManager[] clientKeyManagers;
93edeec21a9c9e97cad91dffd47d4f2f7185dffe07Alex Klyubin    public final KeyManager[] serverKeyManagers;
9401b7734160977458d44d1fb179984fd91672f08dKenny Root    public final X509ExtendedTrustManager clientTrustManager;
9501b7734160977458d44d1fb179984fd91672f08dKenny Root    public final X509ExtendedTrustManager serverTrustManager;
96059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom    public final SSLContext clientContext;
97059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom    public final SSLContext serverContext;
98bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom    public final SSLServerSocket serverSocket;
99bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom    public final InetAddress host;
100bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom    public final int port;
101bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom
1021ce90cc78f833da6ff674fb2028f2560938313ecKenny Root    /**
1031ce90cc78f833da6ff674fb2028f2560938313ecKenny Root     * Used for replacing the hostname in an InetSocketAddress object during
1041ce90cc78f833da6ff674fb2028f2560938313ecKenny Root     * serialization.
1051ce90cc78f833da6ff674fb2028f2560938313ecKenny Root     */
1061ce90cc78f833da6ff674fb2028f2560938313ecKenny Root    private static class HostnameRewritingObjectOutputStream extends ObjectOutputStream {
1071ce90cc78f833da6ff674fb2028f2560938313ecKenny Root        private final String hostname;
1081ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1091ce90cc78f833da6ff674fb2028f2560938313ecKenny Root        public HostnameRewritingObjectOutputStream(OutputStream out, String hostname)
1101ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                throws IOException {
1111ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            super(out);
1121ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            this.hostname = hostname;
1131ce90cc78f833da6ff674fb2028f2560938313ecKenny Root        }
1141ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1151ce90cc78f833da6ff674fb2028f2560938313ecKenny Root        @Override
1161ce90cc78f833da6ff674fb2028f2560938313ecKenny Root        public PutField putFields() throws IOException {
1171ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            return new PutFieldProxy(super.putFields(), hostname);
1181ce90cc78f833da6ff674fb2028f2560938313ecKenny Root        }
1191ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1201ce90cc78f833da6ff674fb2028f2560938313ecKenny Root        private static class PutFieldProxy extends ObjectOutputStream.PutField {
1211ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            private final PutField delegate;
1221ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            private final String hostname;
1231ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1241ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            public PutFieldProxy(ObjectOutputStream.PutField delegate, String hostname) {
1251ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                this.delegate = delegate;
1261ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                this.hostname = hostname;
1271ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            }
1281ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1291ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            @Override
1301ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            public void put(String name, boolean val) {
1311ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                delegate.put(name, val);
1321ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            }
1331ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1341ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            @Override
1351ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            public void put(String name, byte val) {
1361ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                delegate.put(name, val);
1371ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            }
1381ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1391ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            @Override
1401ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            public void put(String name, char val) {
1411ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                delegate.put(name, val);
1421ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            }
1431ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1441ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            @Override
1451ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            public void put(String name, short val) {
1461ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                delegate.put(name, val);
1471ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            }
1481ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1491ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            @Override
1501ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            public void put(String name, int val) {
1511ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                delegate.put(name, val);
1521ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            }
1531ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1541ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            @Override
1551ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            public void put(String name, long val) {
1561ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                delegate.put(name, val);
1571ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            }
1581ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1591ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            @Override
1601ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            public void put(String name, float val) {
1611ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                delegate.put(name, val);
1621ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            }
1631ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1641ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            @Override
1651ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            public void put(String name, double val) {
1661ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                delegate.put(name, val);
1671ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            }
1681ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1691ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            @Override
1701ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            public void put(String name, Object val) {
1711ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                if ("hostname".equals(name)) {
1721ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                    delegate.put(name, hostname);
1731ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                } else {
1741ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                    delegate.put(name, val);
1751ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                }
1761ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            }
1771ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1781ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            @SuppressWarnings("deprecation")
1791ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            @Override
1801ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            public void write(ObjectOutput out) throws IOException {
1811ce90cc78f833da6ff674fb2028f2560938313ecKenny Root                delegate.write(out);
1821ce90cc78f833da6ff674fb2028f2560938313ecKenny Root            }
1831ce90cc78f833da6ff674fb2028f2560938313ecKenny Root        }
1841ce90cc78f833da6ff674fb2028f2560938313ecKenny Root    }
1851ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1861ce90cc78f833da6ff674fb2028f2560938313ecKenny Root    /**
1871ce90cc78f833da6ff674fb2028f2560938313ecKenny Root     * Creates an InetSocketAddress where the hostname points to an arbitrary
1881ce90cc78f833da6ff674fb2028f2560938313ecKenny Root     * hostname, but the address points to the loopback address. Useful for
1891ce90cc78f833da6ff674fb2028f2560938313ecKenny Root     * testing SNI where both "localhost" and IP addresses are not allowed.
1901ce90cc78f833da6ff674fb2028f2560938313ecKenny Root     */
1911ce90cc78f833da6ff674fb2028f2560938313ecKenny Root    public InetSocketAddress getLoopbackAsHostname(String hostname, int port) throws IOException, ClassNotFoundException {
1921ce90cc78f833da6ff674fb2028f2560938313ecKenny Root        InetSocketAddress addr = new InetSocketAddress(InetAddress.getLoopbackAddress(), port);
1931ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1941ce90cc78f833da6ff674fb2028f2560938313ecKenny Root        ByteArrayOutputStream baos = new ByteArrayOutputStream();
1951ce90cc78f833da6ff674fb2028f2560938313ecKenny Root        HostnameRewritingObjectOutputStream oos = new HostnameRewritingObjectOutputStream(baos, hostname);
1961ce90cc78f833da6ff674fb2028f2560938313ecKenny Root        oos.writeObject(addr);
1971ce90cc78f833da6ff674fb2028f2560938313ecKenny Root        oos.close();
1981ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
1991ce90cc78f833da6ff674fb2028f2560938313ecKenny Root        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
2001ce90cc78f833da6ff674fb2028f2560938313ecKenny Root        return (InetSocketAddress) ois.readObject();
2011ce90cc78f833da6ff674fb2028f2560938313ecKenny Root    }
2021ce90cc78f833da6ff674fb2028f2560938313ecKenny Root
203059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom    private TestSSLContext(KeyStore clientKeyStore,
204e3a187163504f00c98bd75cbd8bcbdde123ae2cdBrian Carlstrom                           char[] clientStorePassword,
205059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom                           KeyStore serverKeyStore,
206e3a187163504f00c98bd75cbd8bcbdde123ae2cdBrian Carlstrom                           char[] serverStorePassword,
207edeec21a9c9e97cad91dffd47d4f2f7185dffe07Alex Klyubin                           KeyManager[] clientKeyManagers,
208edeec21a9c9e97cad91dffd47d4f2f7185dffe07Alex Klyubin                           KeyManager[] serverKeyManagers,
20901b7734160977458d44d1fb179984fd91672f08dKenny Root                           X509ExtendedTrustManager clientTrustManager,
21001b7734160977458d44d1fb179984fd91672f08dKenny Root                           X509ExtendedTrustManager serverTrustManager,
211059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom                           SSLContext clientContext,
212059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom                           SSLContext serverContext,
213bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom                           SSLServerSocket serverSocket,
214bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom                           InetAddress host,
215bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom                           int port) {
216059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom        this.clientKeyStore = clientKeyStore;
217e3a187163504f00c98bd75cbd8bcbdde123ae2cdBrian Carlstrom        this.clientStorePassword = clientStorePassword;
218059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom        this.serverKeyStore = serverKeyStore;
219e3a187163504f00c98bd75cbd8bcbdde123ae2cdBrian Carlstrom        this.serverStorePassword = serverStorePassword;
220edeec21a9c9e97cad91dffd47d4f2f7185dffe07Alex Klyubin        this.clientKeyManagers = clientKeyManagers;
221edeec21a9c9e97cad91dffd47d4f2f7185dffe07Alex Klyubin        this.serverKeyManagers = serverKeyManagers;
222059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom        this.clientTrustManager = clientTrustManager;
223059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom        this.serverTrustManager = serverTrustManager;
224059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom        this.clientContext = clientContext;
225059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom        this.serverContext = serverContext;
226bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom        this.serverSocket = serverSocket;
227bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom        this.host = host;
228bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom        this.port = port;
229bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom    }
230bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom
231f7aab022dcbfcd8f27b409ab92b4bca4a84d0b8aBrian Carlstrom    public void close() {
232f7aab022dcbfcd8f27b409ab92b4bca4a84d0b8aBrian Carlstrom        try {
233f7aab022dcbfcd8f27b409ab92b4bca4a84d0b8aBrian Carlstrom            serverSocket.close();
234f7aab022dcbfcd8f27b409ab92b4bca4a84d0b8aBrian Carlstrom        } catch (Exception e) {
235f7aab022dcbfcd8f27b409ab92b4bca4a84d0b8aBrian Carlstrom            throw new RuntimeException(e);
236f7aab022dcbfcd8f27b409ab92b4bca4a84d0b8aBrian Carlstrom        }
237f7aab022dcbfcd8f27b409ab92b4bca4a84d0b8aBrian Carlstrom    }
238f7aab022dcbfcd8f27b409ab92b4bca4a84d0b8aBrian Carlstrom
239e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom    /**
240e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom     * Usual TestSSLContext creation method, creates underlying
241e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom     * SSLContext with certificate and key as well as SSLServerSocket
242e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom     * listening provided host and port.
243e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom     */
244bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom    public static TestSSLContext create() {
245059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom        return create(TestKeyStore.getClient(),
246059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom                      TestKeyStore.getServer());
247bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom    }
248bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom
249e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom    /**
250059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom     * TestSSLContext creation method that allows separate creation of server key store
251e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom     */
252059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom    public static TestSSLContext create(TestKeyStore client, TestKeyStore server) {
253c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin        return createWithAdditionalKeyManagers(client, server, null, null);
254c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin    }
255c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin
256c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin    /**
257c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin     * TestSSLContext creation method that allows separate creation of server key store and
258c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin     * the use of additional {@code KeyManager} instances
259c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin     */
260c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin    public static TestSSLContext createWithAdditionalKeyManagers(
261c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin            TestKeyStore client, TestKeyStore server,
262c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin            KeyManager[] additionalClientKeyManagers, KeyManager[] additionalServerKeyManagers) {
2633ad1704dc8e4653f4ceaeb5d8315ddb28318a1bbKenny Root        String protocol = "TLSv1.2";
264c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin        KeyManager[] clientKeyManagers = concat(client.keyManagers, additionalClientKeyManagers);
265c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin        KeyManager[] serverKeyManagers = concat(server.keyManagers, additionalServerKeyManagers);
2662cca77af136c57106bd9a1652e54a0ee99154d89Alex Klyubin        SSLContext clientContext =
267c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin                createSSLContext(protocol, clientKeyManagers, client.trustManagers);
2682cca77af136c57106bd9a1652e54a0ee99154d89Alex Klyubin        SSLContext serverContext =
269c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin                createSSLContext(protocol, serverKeyManagers, server.trustManagers);
270e3a187163504f00c98bd75cbd8bcbdde123ae2cdBrian Carlstrom        return create(client.keyStore, client.storePassword,
2716882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom                      server.keyStore, server.storePassword,
272c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin                      clientKeyManagers,
273c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin                      serverKeyManagers,
2746882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom                      client.trustManagers[0],
2756882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom                      server.trustManagers[0],
2766882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom                      clientContext,
2776882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom                      serverContext);
278bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom    }
279bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom
280bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom    /**
281059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom     * TestSSLContext creation method that allows separate creation of client and server key store
282e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom     */
283e3a187163504f00c98bd75cbd8bcbdde123ae2cdBrian Carlstrom    public static TestSSLContext create(KeyStore clientKeyStore, char[] clientStorePassword,
2846882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom                                        KeyStore serverKeyStore, char[] serverStorePassword,
285edeec21a9c9e97cad91dffd47d4f2f7185dffe07Alex Klyubin                                        KeyManager[] clientKeyManagers,
286edeec21a9c9e97cad91dffd47d4f2f7185dffe07Alex Klyubin                                        KeyManager[] serverKeyManagers,
2876882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom                                        TrustManager clientTrustManagers,
2886882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom                                        TrustManager serverTrustManagers,
2896882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom                                        SSLContext clientContext,
2906882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom                                        SSLContext serverContext) {
291e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom        try {
292059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom            SSLServerSocket serverSocket = (SSLServerSocket)
293059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom                serverContext.getServerSocketFactory().createServerSocket(0);
294547450702efd233213f953ba2213bb38803c34c3Jesse Wilson            InetAddress host = InetAddress.getLocalHost();
295547450702efd233213f953ba2213bb38803c34c3Jesse Wilson            int port = serverSocket.getLocalPort();
296e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom
297e3a187163504f00c98bd75cbd8bcbdde123ae2cdBrian Carlstrom            return new TestSSLContext(clientKeyStore, clientStorePassword,
298e3a187163504f00c98bd75cbd8bcbdde123ae2cdBrian Carlstrom                                      serverKeyStore, serverStorePassword,
299edeec21a9c9e97cad91dffd47d4f2f7185dffe07Alex Klyubin                                      clientKeyManagers,
300edeec21a9c9e97cad91dffd47d4f2f7185dffe07Alex Klyubin                                      serverKeyManagers,
30101b7734160977458d44d1fb179984fd91672f08dKenny Root                                      (X509ExtendedTrustManager) clientTrustManagers,
30201b7734160977458d44d1fb179984fd91672f08dKenny Root                                      (X509ExtendedTrustManager) serverTrustManagers,
303059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom                                      clientContext, serverContext,
304059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom                                      serverSocket, host, port);
305e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom        } catch (RuntimeException e) {
306e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom            throw e;
307e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom        } catch (Exception e) {
308e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom            throw new RuntimeException(e);
309e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom        }
310e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom    }
311e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom
312e688a4123f165ed2905878e312b074b8c825d119Brian Carlstrom    /**
313bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     * Create a SSLContext with a KeyManager using the private key and
314bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     * certificate chain from the given KeyStore and a TrustManager
315bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     * using the certificates authorities from the same KeyStore.
316bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom     */
3176882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom    public static final SSLContext createSSLContext(final String protocol,
3186882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom                                                    final KeyManager[] keyManagers,
319059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom                                                    final TrustManager[] trustManagers)
3206882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom    {
3216882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom        try {
3222cca77af136c57106bd9a1652e54a0ee99154d89Alex Klyubin            SSLContext context = SSLContext.getInstance(protocol);
3236882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom            context.init(keyManagers, trustManagers, new SecureRandom());
3246882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom            return context;
3256882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom        } catch (Exception e) {
3266882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom            throw new RuntimeException(e);
3276882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5Brian Carlstrom        }
328bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom    }
329204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom
330204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom    public static void assertCertificateInKeyStore(Principal principal,
331204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom                                                   KeyStore keyStore) throws Exception {
332204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom        String subjectName = principal.getName();
333204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom        boolean found = false;
334204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom        for (String alias: Collections.list(keyStore.aliases())) {
335204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom            if (!keyStore.isCertificateEntry(alias)) {
336204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom                continue;
337204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom            }
338204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom            X509Certificate keyStoreCertificate = (X509Certificate) keyStore.getCertificate(alias);
339204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom            if (subjectName.equals(keyStoreCertificate.getSubjectDN().getName())) {
340204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom                found = true;
341204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom                break;
342204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom            }
343204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom        }
3440c131a2ca38465b7d1df4eaee63ac73ce4d5986dBrian Carlstrom        assertTrue(found);
345204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom    }
346204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom
347204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom    public static void assertCertificateInKeyStore(Certificate certificate,
348204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom                                                   KeyStore keyStore) throws Exception {
349204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom        boolean found = false;
350204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom        for (String alias: Collections.list(keyStore.aliases())) {
351204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom            if (!keyStore.isCertificateEntry(alias)) {
352204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom                continue;
353204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom            }
354204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom            Certificate keyStoreCertificate = keyStore.getCertificate(alias);
355204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom            if (certificate.equals(keyStoreCertificate)) {
356204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom                found = true;
357204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom                break;
358204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom            }
359204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom        }
3600c131a2ca38465b7d1df4eaee63ac73ce4d5986dBrian Carlstrom        assertTrue(found);
361204cab3c22b4d75c866c95e2d2eec42e14cbd924Brian Carlstrom    }
362059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom
363059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom    public static void assertServerCertificateChain(X509TrustManager trustManager,
364059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom                                                    Certificate[] serverChain)
365059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom            throws CertificateException {
366059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom        X509Certificate[] chain = (X509Certificate[]) serverChain;
367059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom        trustManager.checkServerTrusted(chain, chain[0].getPublicKey().getAlgorithm());
368059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom    }
369059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom
370059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom    public static void assertClientCertificateChain(X509TrustManager trustManager,
371059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom                                                    Certificate[] clientChain)
372059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom            throws CertificateException {
373059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom        X509Certificate[] chain = (X509Certificate[]) clientChain;
374059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom        trustManager.checkClientTrusted(chain, chain[0].getPublicKey().getAlgorithm());
375059dbc04218144f985b20a228bbe98139d400d0cBrian Carlstrom    }
3763258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom
3773258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom    /**
3783258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom     * Returns an SSLSocketFactory that calls setWantClientAuth and
3793258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom     * setNeedClientAuth as specified on all returned sockets.
3803258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom     */
3813258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom    public static SSLSocketFactory clientAuth(final SSLSocketFactory sf,
3823258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                                              final boolean want,
3833258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                                              final boolean need) {
3843258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom        return new SSLSocketFactory() {
3853258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom            private SSLSocket set(Socket socket) {
3863258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                SSLSocket s = (SSLSocket) socket;
3873258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                s.setWantClientAuth(want);
3883258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                s.setNeedClientAuth(need);
3893258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                return s;
3903258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom            }
3913258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom            public Socket createSocket(String host, int port)
3923258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                    throws IOException, UnknownHostException {
3933258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                return set(sf.createSocket(host, port));
3943258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom            }
3953258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom            public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
3963258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                    throws IOException, UnknownHostException {
3973258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                return set(sf.createSocket(host, port, localHost, localPort));
3983258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom            }
3993258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom            public Socket createSocket(InetAddress host, int port) throws IOException {
4003258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                return set(sf.createSocket(host, port));
4013258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom            }
4023258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom            public Socket createSocket(InetAddress address, int port,
4033258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                                       InetAddress localAddress, int localPort) throws IOException {
4043258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                return set(sf.createSocket(address, port));
4053258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom            }
4063258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom
4073258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom            public String[] getDefaultCipherSuites() {
4083258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                return sf.getDefaultCipherSuites();
4093258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom            }
4103258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom            public String[] getSupportedCipherSuites() {
4113258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                return sf.getSupportedCipherSuites();
4123258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom            }
4133258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom
4143258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom            public Socket createSocket(Socket s, String host, int port, boolean autoClose)
4153258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                    throws IOException {
4163258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom                return set(sf.createSocket(s, host, port, autoClose));
4173258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom            }
4183258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom        };
4193258b52429c7768ea91bda93c5a15257cdd390e5Brian Carlstrom    }
420c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin
421c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin    private static KeyManager[] concat(KeyManager[] a, KeyManager[] b) {
422c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin        if ((a == null) || (a.length == 0)) {
423c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin            return b;
424c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin        }
425c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin        if ((b == null) || (b.length == 0)) {
426c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin            return a;
427c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin        }
428c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin        KeyManager[] result = new KeyManager[a.length + b.length];
429c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin        System.arraycopy(a, 0, result, 0, a.length);
430c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin        System.arraycopy(b, 0, result, a.length, b.length);
431c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin        return result;
432c9461f39290f815f560f2ec50e9ccde5ff4eb8f7Alex Klyubin    }
433bcfb325d5b1f9529b439cc0805a1c140521510f7Brian Carlstrom}
434