112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo/* 212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java $ 312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * $Revision: 659194 $ 412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * $Date: 2008-05-22 11:33:47 -0700 (Thu, 22 May 2008) $ 512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * 612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * ==================================================================== 712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * Licensed to the Apache Software Foundation (ASF) under one 812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * or more contributor license agreements. See the NOTICE file 912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * distributed with this work for additional information 1012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * regarding copyright ownership. The ASF licenses this file 1112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * to you under the Apache License, Version 2.0 (the 1212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * "License"); you may not use this file except in compliance 1312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * with the License. You may obtain a copy of the License at 1412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * 1512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * http://www.apache.org/licenses/LICENSE-2.0 1612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * 1712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * Unless required by applicable law or agreed to in writing, 1812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * software distributed under the License is distributed on an 1912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 2012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * KIND, either express or implied. See the License for the 2112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * specific language governing permissions and limitations 2212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * under the License. 2312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * ==================================================================== 2412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * 2512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * This software consists of voluntary contributions made by many 2612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * individuals on behalf of the Apache Software Foundation. For more 2712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * information on the Apache Software Foundation, please see 2812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <http://www.apache.org/>. 2912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * 3012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * This class was copied from org.apache.http.conn.ssl, because it didn't have a suitable 3112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * constructor. 3212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo */ 3312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 3412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalopackage com.android.emailcommon.utility; 3512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 3612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport org.apache.http.conn.scheme.HostNameResolver; 3712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport org.apache.http.conn.scheme.LayeredSocketFactory; 3812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport org.apache.http.conn.ssl.AllowAllHostnameVerifier; 3912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport org.apache.http.conn.ssl.BrowserCompatHostnameVerifier; 4012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport org.apache.http.conn.ssl.StrictHostnameVerifier; 4112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport org.apache.http.conn.ssl.X509HostnameVerifier; 4212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport org.apache.http.params.HttpConnectionParams; 4312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport org.apache.http.params.HttpParams; 4412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 4512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport javax.net.ssl.HttpsURLConnection; 4612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport javax.net.ssl.KeyManager; 4712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport javax.net.ssl.KeyManagerFactory; 4812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport javax.net.ssl.SSLContext; 4912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport javax.net.ssl.SSLSocket; 5012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport javax.net.ssl.TrustManager; 5112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport javax.net.ssl.TrustManagerFactory; 5212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport java.io.IOException; 5312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport java.net.InetAddress; 5412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport java.net.InetSocketAddress; 5512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport java.net.Socket; 5612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport java.net.UnknownHostException; 5712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport java.security.KeyManagementException; 5812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport java.security.KeyStore; 5912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport java.security.KeyStoreException; 6012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport java.security.NoSuchAlgorithmException; 6112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport java.security.SecureRandom; 6212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komaloimport java.security.UnrecoverableKeyException; 6312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 6412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo/** 6512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * Layered socket factory for TLS/SSL connections, based on JSSE. 6612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo *. 6712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <p> 6812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * SSLSocketFactory can be used to validate the identity of the HTTPS 6912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * server against a list of trusted certificates and to authenticate to 7012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * the HTTPS server using a private key. 7112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </p> 7212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * 7312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <p> 7412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * SSLSocketFactory will enable server authentication when supplied with 7512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * a {@link KeyStore truststore} file containg one or several trusted 7612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * certificates. The client secure socket will reject the connection during 7712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * the SSL session handshake if the target HTTPS server attempts to 7812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * authenticate itself with a non-trusted certificate. 7912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </p> 8012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * 8112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <p> 8212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * Use JDK keytool utility to import a trusted certificate and generate a truststore file: 8312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <pre> 8412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * keytool -import -alias "my server cert" -file server.crt -keystore my.truststore 8512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </pre> 8612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </p> 8712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * 8812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <p> 8912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * SSLSocketFactory will enable client authentication when supplied with 9012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * a {@link KeyStore keystore} file containg a private key/public certificate 9112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * pair. The client secure socket will use the private key to authenticate 9212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * itself to the target HTTPS server during the SSL session handshake if 9312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * requested to do so by the server. 9412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * The target HTTPS server will in its turn verify the certificate presented 9512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * by the client in order to establish client's authenticity 9612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </p> 9712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * 9812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <p> 9912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * Use the following sequence of actions to generate a keystore file 10012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </p> 10112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <ul> 10212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <li> 10312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <p> 10412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * Use JDK keytool utility to generate a new key 10512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre> 10612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * For simplicity use the same password for the key as that of the keystore 10712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </p> 10812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </li> 10912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <li> 11012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <p> 11112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * Issue a certificate signing request (CSR) 11212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre> 11312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </p> 11412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </li> 11512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <li> 11612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <p> 11712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * Send the certificate request to the trusted Certificate Authority for signature. 11812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * One may choose to act as her own CA and sign the certificate request using a PKI 11912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * tool, such as OpenSSL. 12012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </p> 12112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </li> 12212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <li> 12312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <p> 12412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * Import the trusted CA root certificate 12512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre> 12612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </p> 12712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </li> 12812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <li> 12912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <p> 13012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * Import the PKCS#7 file containg the complete certificate chain 13112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre> 13212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </p> 13312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </li> 13412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <li> 13512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <p> 13612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * Verify the content the resultant keystore file 13712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <pre>keytool -list -v -keystore my.keystore</pre> 13812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </p> 13912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </li> 14012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * </ul> 14112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a> 14212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * @author Julius Davies 14312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo */ 14412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 14512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalopublic class SSLSocketFactory implements LayeredSocketFactory { 14612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 14712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public static final String TLS = "TLS"; 14812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public static final String SSL = "SSL"; 14912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public static final String SSLV2 = "SSLv2"; 15012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 15112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER 15212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo = new AllowAllHostnameVerifier(); 15312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 15412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER 15512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo = new BrowserCompatHostnameVerifier(); 15612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 15712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER 15812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo = new StrictHostnameVerifier(); 15912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo /** 16012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * The factory using the default JVM settings for secure connections. 16112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo */ 16212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo private static final SSLSocketFactory DEFAULT_FACTORY = new SSLSocketFactory(); 16312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 16412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo /** 16512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * Gets an singleton instance of the SSLProtocolSocketFactory. 16612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * @return a SSLProtocolSocketFactory 16712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo */ 16812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public static SSLSocketFactory getSocketFactory() { 16912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo return DEFAULT_FACTORY; 17012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 17112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 17212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo private final SSLContext sslcontext; 17312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo private final javax.net.ssl.SSLSocketFactory socketfactory; 17412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo private final HostNameResolver nameResolver; 17512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo private X509HostnameVerifier hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER; 17612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 17712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public SSLSocketFactory( 17812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo String algorithm, 17912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final KeyStore keystore, 18012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final String keystorePassword, 18112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final KeyStore truststore, 18212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final SecureRandom random, 18312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final HostNameResolver nameResolver) 18412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException 18512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo { 18612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo super(); 18712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo if (algorithm == null) { 18812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo algorithm = TLS; 18912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 19012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo KeyManager[] keymanagers = null; 19112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo if (keystore != null) { 19212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo keymanagers = createKeyManagers(keystore, keystorePassword); 19312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 19412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo TrustManager[] trustmanagers = null; 19512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo if (truststore != null) { 19612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo trustmanagers = createTrustManagers(truststore); 19712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 19812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo sslcontext = SSLContext.getInstance(algorithm); 19912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo sslcontext.init(keymanagers, trustmanagers, random); 20012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo socketfactory = sslcontext.getSocketFactory(); 20112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo this.nameResolver = nameResolver; 20212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 20312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 20412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public SSLSocketFactory( 20512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final KeyStore keystore, 20612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final String keystorePassword, 20712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final KeyStore truststore) 20812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException 20912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo { 21012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo this(TLS, keystore, keystorePassword, truststore, null, null); 21112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 21212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 21312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public SSLSocketFactory(final KeyStore keystore, final String keystorePassword) 21412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException 21512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo { 21612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo this(TLS, keystore, keystorePassword, null, null, null); 21712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 21812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 21912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public SSLSocketFactory(final KeyStore truststore) 22012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException 22112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo { 22212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo this(TLS, null, null, truststore, null, null); 22312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 22412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 22512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo /** 22612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * Constructs an HttpClient SSLSocketFactory backed by the given JSSE 22712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * SSLSocketFactory. 22812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo */ 22912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public SSLSocketFactory(javax.net.ssl.SSLSocketFactory socketfactory) { 23012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo super(); 23112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo sslcontext = null; 23212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo this.socketfactory = socketfactory; 23312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo nameResolver = null; 23412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 23512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 23612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo /** 23712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * Creates the default SSL socket factory. 23812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * This constructor is used exclusively to instantiate the factory for 23912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * {@link #getSocketFactory getSocketFactory}. 24012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo */ 24112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo private SSLSocketFactory() { 24212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo super(); 24312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo sslcontext = null; 24412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory(); 24512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo nameResolver = null; 24612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 24712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 24812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo private static KeyManager[] createKeyManagers(final KeyStore keystore, final String password) 24912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { 25012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo if (keystore == null) { 25112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throw new IllegalArgumentException("Keystore may not be null"); 25212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 25312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo KeyManagerFactory kmfactory = KeyManagerFactory.getInstance( 25412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo KeyManagerFactory.getDefaultAlgorithm()); 25512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo kmfactory.init(keystore, password != null ? password.toCharArray(): null); 25612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo return kmfactory.getKeyManagers(); 25712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 25812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 25912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo private static TrustManager[] createTrustManagers(final KeyStore keystore) 26012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throws KeyStoreException, NoSuchAlgorithmException { 26112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo if (keystore == null) { 26212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throw new IllegalArgumentException("Keystore may not be null"); 26312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 26412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo TrustManagerFactory tmfactory = TrustManagerFactory.getInstance( 26512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo TrustManagerFactory.getDefaultAlgorithm()); 26612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo tmfactory.init(keystore); 26712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo return tmfactory.getTrustManagers(); 26812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 26912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 27012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 27112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo // non-javadoc, see interface org.apache.http.conn.SocketFactory 27212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public Socket createSocket() 27312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throws IOException { 27412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 27512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo // the cast makes sure that the factory is working as expected 27612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo return socketfactory.createSocket(); 27712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 27812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 27912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 28012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo // non-javadoc, see interface org.apache.http.conn.SocketFactory 28112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public Socket connectSocket( 28212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final Socket sock, 28312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final String host, 28412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final int port, 28512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final InetAddress localAddress, 28612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo int localPort, 28712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final HttpParams params 28812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo ) throws IOException { 28912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 29012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo if (host == null) { 29112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throw new IllegalArgumentException("Target host may not be null."); 29212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 29312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo if (params == null) { 29412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throw new IllegalArgumentException("Parameters may not be null."); 29512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 29612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 29712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo SSLSocket sslsock = (SSLSocket) 29812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo ((sock != null) ? sock : createSocket()); 29912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 30012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo if ((localAddress != null) || (localPort > 0)) { 30112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 30212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo // we need to bind explicitly 30312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo if (localPort < 0) 30412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo localPort = 0; // indicates "any" 30512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 30612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo InetSocketAddress isa = 30712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo new InetSocketAddress(localAddress, localPort); 30812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo sslsock.bind(isa); 30912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 31012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 31112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo int connTimeout = HttpConnectionParams.getConnectionTimeout(params); 31212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo int soTimeout = HttpConnectionParams.getSoTimeout(params); 31312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 31412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo InetSocketAddress remoteAddress; 31512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo if (nameResolver != null) { 31612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo remoteAddress = new InetSocketAddress(nameResolver.resolve(host), port); 31712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } else { 31812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo remoteAddress = new InetSocketAddress(host, port); 31912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 32012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 32112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo sslsock.connect(remoteAddress, connTimeout); 32212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 32312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo sslsock.setSoTimeout(soTimeout); 32412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo try { 32512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo hostnameVerifier.verify(host, sslsock); 32612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo // verifyHostName() didn't blowup - good! 32712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } catch (IOException iox) { 32812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo // close the socket before re-throwing the exception 32912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo try { sslsock.close(); } catch (Exception x) { /*ignore*/ } 33012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throw iox; 33112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 33212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 33312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo return sslsock; 33412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 33512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 33612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 33712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo /** 33812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * Checks whether a socket connection is secure. 33912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * This factory creates TLS/SSL socket connections 34012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * which, by default, are considered secure. 34112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * <br/> 34212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * Derived classes may override this method to perform 34312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * runtime checks, for example based on the cypher suite. 34412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * 34512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * @param sock the connected socket 34612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * 34712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * @return <code>true</code> 34812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * 34912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo * @throws IllegalArgumentException if the argument is invalid 35012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo */ 35112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public boolean isSecure(Socket sock) 35212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throws IllegalArgumentException { 35312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 35412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo if (sock == null) { 35512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throw new IllegalArgumentException("Socket may not be null."); 35612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 35712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo // This instanceof check is in line with createSocket() above. 35812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo if (!(sock instanceof SSLSocket)) { 35912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throw new IllegalArgumentException 36012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo ("Socket not created by this factory."); 36112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 36212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo // This check is performed last since it calls the argument object. 36312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo if (sock.isClosed()) { 36412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throw new IllegalArgumentException("Socket is closed."); 36512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 36612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 36712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo return true; 36812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 36912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } // isSecure 37012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 37112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 37212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo // non-javadoc, see interface LayeredSocketFactory 37312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public Socket createSocket( 37412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final Socket socket, 37512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final String host, 37612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final int port, 37712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo final boolean autoClose 37812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo ) throws IOException, UnknownHostException { 37912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo SSLSocket sslSocket = (SSLSocket) socketfactory.createSocket( 38012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo socket, 38112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo host, 38212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo port, 38312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo autoClose 38412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo ); 38512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo hostnameVerifier.verify(host, sslSocket); 38612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo // verifyHostName() didn't blowup - good! 38712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo return sslSocket; 38812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 38912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 39012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public void setHostnameVerifier(X509HostnameVerifier hostnameVerifier) { 39112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo if ( hostnameVerifier == null ) { 39212a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo throw new IllegalArgumentException("Hostname verifier may not be null"); 39312a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 39412a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo this.hostnameVerifier = hostnameVerifier; 39512a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 39612a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 39712a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo public X509HostnameVerifier getHostnameVerifier() { 39812a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo return hostnameVerifier; 39912a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo } 40012a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo 40112a3e3e4b91ebab1ac4538d9f7a336b9e05dce0eBen Komalo} 402