From 62ad5b1d348f10e7cf1a5a317a25b5effe1e4779 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Tue, 20 Dec 2016 22:17:44 +0100 Subject: [PATCH] Import some sources from legacy HTTP Client to permit using self signed certificates like before It's not the perfect solution but this works Rename HTTPRequestBuilder to OCHttpClient & make it child of HttpClient --- .../activities/LoginActivity.java | 4 +- ...PRequestBuilder.java => OCHttpClient.java} | 28 ++- .../engine/OCSMSOwnCloudClient.java | 4 +- .../ssl/EasySSLProtocolSocketFactory.java | 224 ++++++++++++++++++ .../contrib/ssl/EasyX509TrustManager.java | 103 ++++++++ 5 files changed, 348 insertions(+), 15 deletions(-) rename src/main/java/fr/unix_experience/owncloud_sms/engine/{HTTPRequestBuilder.java => OCHttpClient.java} (74%) create mode 100644 src/main/java/org/apache/commons/httpclient/contrib/ssl/EasySSLProtocolSocketFactory.java create mode 100644 src/main/java/org/apache/commons/httpclient/contrib/ssl/EasyX509TrustManager.java diff --git a/src/main/java/fr/unix_experience/owncloud_sms/activities/LoginActivity.java b/src/main/java/fr/unix_experience/owncloud_sms/activities/LoginActivity.java index 3799e1d..a5ca51c 100644 --- a/src/main/java/fr/unix_experience/owncloud_sms/activities/LoginActivity.java +++ b/src/main/java/fr/unix_experience/owncloud_sms/activities/LoginActivity.java @@ -48,7 +48,7 @@ import java.io.IOException; import fr.unix_experience.owncloud_sms.R; import fr.unix_experience.owncloud_sms.defines.DefaultPrefs; -import fr.unix_experience.owncloud_sms.engine.HTTPRequestBuilder; +import fr.unix_experience.owncloud_sms.engine.OCHttpClient; /** * A login screen that offers login via email/password. @@ -241,7 +241,7 @@ public class LoginActivity extends AppCompatActivity { @Override protected Boolean doInBackground(Void... params) { _returnCode = 0; - HTTPRequestBuilder http = new HTTPRequestBuilder(_serverURI, _login, _password); + OCHttpClient http = new OCHttpClient(_serverURI, _login, _password); GetMethod testMethod = http.getVersion(); try { _returnCode = http.execute(testMethod); diff --git a/src/main/java/fr/unix_experience/owncloud_sms/engine/HTTPRequestBuilder.java b/src/main/java/fr/unix_experience/owncloud_sms/engine/OCHttpClient.java similarity index 74% rename from src/main/java/fr/unix_experience/owncloud_sms/engine/HTTPRequestBuilder.java rename to src/main/java/fr/unix_experience/owncloud_sms/engine/OCHttpClient.java index 0ba2a74..8d05d49 100644 --- a/src/main/java/fr/unix_experience/owncloud_sms/engine/HTTPRequestBuilder.java +++ b/src/main/java/fr/unix_experience/owncloud_sms/engine/OCHttpClient.java @@ -23,15 +23,19 @@ import android.util.Log; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; +import org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.StringRequestEntity; +import org.apache.commons.httpclient.protocol.Protocol; +import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; import java.io.IOException; -public class HTTPRequestBuilder { +public class OCHttpClient extends HttpClient { - private static final String TAG = HTTPRequestBuilder.class.getCanonicalName(); + private static final String TAG = OCHttpClient.class.getCanonicalName(); private final Uri _serverURI; private final String _username; private final String _password; @@ -48,47 +52,49 @@ public class HTTPRequestBuilder { private static final String OC_V2_GET_MESSAGES_PHONE ="/index.php/apps/ocsms/api/v2/messages/[PHONENUMBER]/[START]/[LIMIT]?format=json"; private static final String OC_V2_GET_MESSAGES_SENDQUEUE = "/index.php/apps/ocsms/api/v2/messages/sendqueue?format=json"; - public HTTPRequestBuilder(Uri serverURI, String accountName, String accountPassword) { + public OCHttpClient(Uri serverURI, String accountName, String accountPassword) { + super(new MultiThreadedHttpConnectionManager()); + Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory)new EasySSLProtocolSocketFactory(), 443); + Protocol.registerProtocol("https", easyhttps); _serverURI = serverURI; _username = accountName; _password = accountPassword; } private GetMethod get(String oc_call) { - Log.i(HTTPRequestBuilder.TAG, "Create GET " + _serverURI + oc_call); + Log.i(OCHttpClient.TAG, "Create GET " + _serverURI + oc_call); return new GetMethod(_serverURI.toString() + oc_call); } GetMethod getAllSmsIds() { - return get(HTTPRequestBuilder.OC_GET_ALL_SMS_IDS); + return get(OCHttpClient.OC_GET_ALL_SMS_IDS); } public GetMethod getVersion() { - return get(HTTPRequestBuilder.OC_GET_VERSION); + return get(OCHttpClient.OC_GET_VERSION); } PostMethod pushSms(StringRequestEntity ent) { - PostMethod post = new PostMethod(_serverURI.toString() + HTTPRequestBuilder.OC_PUSH_ROUTE); + PostMethod post = new PostMethod(_serverURI.toString() + OCHttpClient.OC_PUSH_ROUTE); post.setRequestEntity(ent); return post; } GetMethod getPhoneList() { - return get(HTTPRequestBuilder.OC_V2_GET_PHONELIST); + return get(OCHttpClient.OC_V2_GET_PHONELIST); } GetMethod getMessages(Long start, Integer limit) { - return get(HTTPRequestBuilder.OC_V2_GET_MESSAGES. + return get(OCHttpClient.OC_V2_GET_MESSAGES. replace("[START]", start.toString()).replace("[LIMIT]", limit.toString())); } public int execute(HttpMethod req) throws IOException { - HttpClient http = new HttpClient(); String basicAuth = "Basic " + Base64.encodeToString((_username + ":" + _password).getBytes(), Base64.NO_WRAP); //req.setFollowRedirects(true); // App is SIGKILLED by android when doing this... WTF req.setDoAuthentication(true); req.addRequestHeader("Authorization", basicAuth); - return http.executeMethod(req); + return executeMethod(req); } } diff --git a/src/main/java/fr/unix_experience/owncloud_sms/engine/OCSMSOwnCloudClient.java b/src/main/java/fr/unix_experience/owncloud_sms/engine/OCSMSOwnCloudClient.java index 517462c..2a58aed 100644 --- a/src/main/java/fr/unix_experience/owncloud_sms/engine/OCSMSOwnCloudClient.java +++ b/src/main/java/fr/unix_experience/owncloud_sms/engine/OCSMSOwnCloudClient.java @@ -56,7 +56,7 @@ public class OCSMSOwnCloudClient { } Uri serverURI = Uri.parse(ocURI); - _http = new HTTPRequestBuilder(serverURI, accountManager.getUserData(account, "ocLogin"), + _http = new OCHttpClient(serverURI, accountManager.getUserData(account, "ocLogin"), accountManager.getPassword(account)); _connectivityMonitor = new ConnectivityMonitor(_context); } @@ -369,7 +369,7 @@ public class OCSMSOwnCloudClient { private static final int maximumHttpReqTries = 3; - private final HTTPRequestBuilder _http; + private final OCHttpClient _http; private final Context _context; private final ConnectivityMonitor _connectivityMonitor; diff --git a/src/main/java/org/apache/commons/httpclient/contrib/ssl/EasySSLProtocolSocketFactory.java b/src/main/java/org/apache/commons/httpclient/contrib/ssl/EasySSLProtocolSocketFactory.java new file mode 100644 index 0000000..beb95c3 --- /dev/null +++ b/src/main/java/org/apache/commons/httpclient/contrib/ssl/EasySSLProtocolSocketFactory.java @@ -0,0 +1,224 @@ +/* + * $HeadURL$ + * $Revision$ + * $Date$ + * + * ==================================================================== + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.httpclient.contrib.ssl; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.UnknownHostException; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + +import org.apache.commons.httpclient.ConnectTimeoutException; +import org.apache.commons.httpclient.HttpClientError; +import org.apache.commons.httpclient.params.HttpConnectionParams; +import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory; + +/** + *

+ * EasySSLProtocolSocketFactory can be used to creats SSL {@link Socket}s + * that accept self-signed certificates. + *

+ *

+ * This socket factory SHOULD NOT be used for productive systems + * due to security reasons, unless it is a concious decision and + * you are perfectly aware of security implications of accepting + * self-signed certificates + *

+ * + *

+ * Example of using custom protocol socket factory for a specific host: + *

+ *     Protocol easyhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443);
+ *
+ *     URI uri = new URI("https://localhost/", true);
+ *     // use relative url only
+ *     GetMethod httpget = new GetMethod(uri.getPathQuery());
+ *     HostConfiguration hc = new HostConfiguration();
+ *     hc.setHost(uri.getHost(), uri.getPort(), easyhttps);
+ *     HttpClient client = new HttpClient();
+ *     client.executeMethod(hc, httpget);
+ *     
+ *

+ *

+ * Example of using custom protocol socket factory per default instead of the standard one: + *

+ *     Protocol easyhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443);
+ *     Protocol.registerProtocol("https", easyhttps);
+ *
+ *     HttpClient client = new HttpClient();
+ *     GetMethod httpget = new GetMethod("https://localhost/");
+ *     client.executeMethod(httpget);
+ *     
+ *

+ * + * @author Oleg Kalnichevski + * + *

+ * DISCLAIMER: HttpClient developers DO NOT actively support this component. + * The component is provided as a reference material, which may be inappropriate + * for use without additional customization. + *

+ */ + +public class EasySSLProtocolSocketFactory implements SecureProtocolSocketFactory { + + private SSLContext sslcontext = null; + + /** + * Constructor for EasySSLProtocolSocketFactory. + */ + public EasySSLProtocolSocketFactory() { + super(); + } + + private static SSLContext createEasySSLContext() { + try { + SSLContext context = SSLContext.getInstance("SSL"); + context.init( + null, + new TrustManager[] {new EasyX509TrustManager(null)}, + null); + return context; + } catch (Exception e) { + throw new HttpClientError(e.toString()); + } + } + + private SSLContext getSSLContext() { + if (this.sslcontext == null) { + this.sslcontext = createEasySSLContext(); + } + return this.sslcontext; + } + + /** + * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int) + */ + public Socket createSocket( + String host, + int port, + InetAddress clientHost, + int clientPort) + throws IOException, UnknownHostException { + + return getSSLContext().getSocketFactory().createSocket( + host, + port, + clientHost, + clientPort + ); + } + + /** + * Attempts to get a new socket connection to the given host within the given time limit. + *

+ * To circumvent the limitations of older JREs that do not support connect timeout a + * controller thread is executed. The controller thread attempts to create a new socket + * within the given limit of time. If socket constructor does not return until the + * timeout expires, the controller terminates and throws an {@link ConnectTimeoutException} + *

+ * + * @param host the host name/IP + * @param port the port on the host + * @param params {@link HttpConnectionParams Http connection parameters} + * + * @return Socket a new socket + * + * @throws IOException if an I/O error occurs while creating the socket + * @throws UnknownHostException if the IP address of the host cannot be + * determined + */ + public Socket createSocket( + final String host, + final int port, + final InetAddress localAddress, + final int localPort, + final HttpConnectionParams params + ) throws IOException, UnknownHostException, ConnectTimeoutException { + if (params == null) { + throw new IllegalArgumentException("Parameters may not be null"); + } + int timeout = params.getConnectionTimeout(); + SocketFactory socketfactory = getSSLContext().getSocketFactory(); + if (timeout == 0) { + return socketfactory.createSocket(host, port, localAddress, localPort); + } else { + Socket socket = socketfactory.createSocket(); + SocketAddress localaddr = new InetSocketAddress(localAddress, localPort); + SocketAddress remoteaddr = new InetSocketAddress(host, port); + socket.bind(localaddr); + socket.connect(remoteaddr, timeout); + return socket; + } + } + + /** + * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int) + */ + public Socket createSocket(String host, int port) + throws IOException, UnknownHostException { + return getSSLContext().getSocketFactory().createSocket( + host, + port + ); + } + + /** + * @see SecureProtocolSocketFactory#createSocket(java.net.Socket,java.lang.String,int,boolean) + */ + public Socket createSocket( + Socket socket, + String host, + int port, + boolean autoClose) + throws IOException, UnknownHostException { + return getSSLContext().getSocketFactory().createSocket( + socket, + host, + port, + autoClose + ); + } + + public boolean equals(Object obj) { + return ((obj != null) && obj.getClass().equals(EasySSLProtocolSocketFactory.class)); + } + + public int hashCode() { + return EasySSLProtocolSocketFactory.class.hashCode(); + } + +} diff --git a/src/main/java/org/apache/commons/httpclient/contrib/ssl/EasyX509TrustManager.java b/src/main/java/org/apache/commons/httpclient/contrib/ssl/EasyX509TrustManager.java new file mode 100644 index 0000000..f566f5a --- /dev/null +++ b/src/main/java/org/apache/commons/httpclient/contrib/ssl/EasyX509TrustManager.java @@ -0,0 +1,103 @@ +/* + * ==================================================================== + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.commons.httpclient.contrib.ssl; + +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +/** + *

+ * EasyX509TrustManager unlike default {@link X509TrustManager} accepts + * self-signed certificates. + *

+ *

+ * This trust manager SHOULD NOT be used for productive systems + * due to security reasons, unless it is a concious decision and + * you are perfectly aware of security implications of accepting + * self-signed certificates + *

+ * + * @author Adrian Sutton + * @author Oleg Kalnichevski + * + *

+ * DISCLAIMER: HttpClient developers DO NOT actively support this component. + * The component is provided as a reference material, which may be inappropriate + * for use without additional customization. + *

+ */ + +public class EasyX509TrustManager implements X509TrustManager +{ + private X509TrustManager standardTrustManager = null; + + /** + * Constructor for EasyX509TrustManager. + */ + public EasyX509TrustManager(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException { + super(); + TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + factory.init(keystore); + TrustManager[] trustmanagers = factory.getTrustManagers(); + if (trustmanagers.length == 0) { + throw new NoSuchAlgorithmException("no trust manager found"); + } + this.standardTrustManager = (X509TrustManager)trustmanagers[0]; + } + + /** + * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],String authType) + */ + public void checkClientTrusted(X509Certificate[] certificates,String authType) throws CertificateException { + standardTrustManager.checkClientTrusted(certificates,authType); + } + + /** + * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],String authType) + */ + public void checkServerTrusted(X509Certificate[] certificates,String authType) throws CertificateException { + if ((certificates != null) && (certificates.length == 1)) { + certificates[0].checkValidity(); + } else { + standardTrustManager.checkServerTrusted(certificates,authType); + } + } + + /** + * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers() + */ + public X509Certificate[] getAcceptedIssuers() { + return this.standardTrustManager.getAcceptedIssuers(); + } +}