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