Eric Kok
10 years ago
12 changed files with 316 additions and 194 deletions
@ -1,86 +0,0 @@
@@ -1,86 +0,0 @@
|
||||
package org.transdroid.daemon.util; |
||||
|
||||
import java.io.IOException; |
||||
import java.net.InetAddress; |
||||
import java.net.InetSocketAddress; |
||||
import java.net.Socket; |
||||
import java.net.UnknownHostException; |
||||
|
||||
import javax.net.ssl.SSLContext; |
||||
import javax.net.ssl.SSLSocket; |
||||
import javax.net.ssl.TrustManager; |
||||
|
||||
import org.apache.http.conn.ConnectTimeoutException; |
||||
import org.apache.http.conn.scheme.LayeredSocketFactory; |
||||
import org.apache.http.conn.scheme.SocketFactory; |
||||
import org.apache.http.params.HttpConnectionParams; |
||||
import org.apache.http.params.HttpParams; |
||||
|
||||
public class FakeSocketFactory implements SocketFactory, LayeredSocketFactory { |
||||
private String certKey = null; |
||||
private SSLContext sslcontext = null; |
||||
|
||||
public FakeSocketFactory(String certKey){ |
||||
this.certKey = certKey; |
||||
} |
||||
public FakeSocketFactory() { } |
||||
|
||||
private static SSLContext createEasySSLContext(String certKey) throws IOException { |
||||
try { |
||||
SSLContext context = SSLContext.getInstance("TLS"); |
||||
context.init(null, new TrustManager[] { new FakeTrustManager(certKey) }, null); |
||||
return context; |
||||
} catch (Exception e) { |
||||
throw new IOException(e.getMessage()); |
||||
} |
||||
} |
||||
|
||||
private SSLContext getSSLContext() throws IOException { |
||||
if (this.sslcontext == null) { |
||||
this.sslcontext = createEasySSLContext(this.certKey); |
||||
} |
||||
return this.sslcontext; |
||||
} |
||||
|
||||
@Override |
||||
public Socket connectSocket(Socket sock, String host, int port, |
||||
InetAddress localAddress, int localPort, HttpParams params) throws IOException, |
||||
UnknownHostException, ConnectTimeoutException { |
||||
int connTimeout = HttpConnectionParams.getConnectionTimeout(params); |
||||
int soTimeout = HttpConnectionParams.getSoTimeout(params); |
||||
|
||||
InetSocketAddress remoteAddress = new InetSocketAddress(host, port); |
||||
SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket()); |
||||
|
||||
if ((localAddress != null) || (localPort > 0)) { |
||||
// we need to bind explicitly
|
||||
if (localPort < 0) { |
||||
localPort = 0; // indicates "any"
|
||||
} |
||||
InetSocketAddress isa = new InetSocketAddress(localAddress, |
||||
localPort); |
||||
sslsock.bind(isa); |
||||
} |
||||
|
||||
sslsock.connect(remoteAddress, connTimeout); |
||||
sslsock.setSoTimeout(soTimeout); |
||||
return sslsock; |
||||
} |
||||
|
||||
@Override |
||||
public Socket createSocket() throws IOException { |
||||
return getSSLContext().getSocketFactory().createSocket(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isSecure(Socket arg0) throws IllegalArgumentException { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) |
||||
throws IOException, UnknownHostException { |
||||
return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose); |
||||
} |
||||
|
||||
} |
@ -1,89 +0,0 @@
@@ -1,89 +0,0 @@
|
||||
package org.transdroid.daemon.util; |
||||
|
||||
import java.security.MessageDigest; |
||||
import java.security.NoSuchAlgorithmException; |
||||
import java.security.cert.CertificateEncodingException; |
||||
import java.security.cert.CertificateException; |
||||
import java.security.cert.X509Certificate; |
||||
|
||||
import javax.net.ssl.X509TrustManager; |
||||
|
||||
public class FakeTrustManager implements X509TrustManager { |
||||
private String certKey = null; |
||||
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[] {}; |
||||
private static final String LOG_NAME = "TrustManager"; |
||||
|
||||
public FakeTrustManager(String certKey){ |
||||
super(); |
||||
this.certKey = certKey; |
||||
} |
||||
FakeTrustManager(){ |
||||
super(); |
||||
} |
||||
@Override |
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { |
||||
} |
||||
|
||||
@Override |
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { |
||||
if( this.certKey == null ){ |
||||
// This is the Accept All certificates case.
|
||||
return; |
||||
} |
||||
|
||||
// Otherwise, we have a certKey defined. We should now examine the one we got from the server.
|
||||
// They match? All is good. They don't, throw an exception.
|
||||
String our_key = this.certKey.replaceAll("[^a-fA-F0-9]+", ""); |
||||
try { |
||||
//Assume self-signed root is okay?
|
||||
X509Certificate ss_cert = chain[0]; |
||||
String thumbprint = FakeTrustManager.getThumbPrint(ss_cert); |
||||
DLog.d(LOG_NAME, thumbprint); |
||||
if( our_key.equalsIgnoreCase(thumbprint) ){ |
||||
return; |
||||
} |
||||
else { |
||||
throw new CertificateException("Certificate key [" + thumbprint + "] doesn't match expected value."); |
||||
} |
||||
} catch (NoSuchAlgorithmException e) { |
||||
throw new CertificateException("Unable to check self-signed cert, unknown algorithm. " + e.toString()); |
||||
} |
||||
|
||||
} |
||||
|
||||
public boolean isClientTrusted(X509Certificate[] chain) { |
||||
return true; |
||||
} |
||||
|
||||
public boolean isServerTrusted(X509Certificate[] chain) { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public X509Certificate[] getAcceptedIssuers() { |
||||
return _AcceptedIssuers; |
||||
} |
||||
|
||||
// Thank you: http://stackoverflow.com/questions/1270703/how-to-retrieve-compute-an-x509-certificates-thumbprint-in-java
|
||||
private static String getThumbPrint(X509Certificate cert) throws NoSuchAlgorithmException, CertificateEncodingException { |
||||
MessageDigest md = MessageDigest.getInstance("SHA-1"); |
||||
byte[] der = cert.getEncoded(); |
||||
md.update(der); |
||||
byte[] digest = md.digest(); |
||||
return hexify(digest); |
||||
} |
||||
|
||||
private static String hexify (byte bytes[]) { |
||||
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', |
||||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; |
||||
|
||||
StringBuffer buf = new StringBuffer(bytes.length * 2); |
||||
|
||||
for (int i = 0; i < bytes.length; ++i) { |
||||
buf.append(hexDigits[(bytes[i] & 0xf0) >> 4]); |
||||
buf.append(hexDigits[bytes[i] & 0x0f]); |
||||
} |
||||
|
||||
return buf.toString(); |
||||
} |
||||
} |
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
/* |
||||
* Copyright 2010-2013 Eric Kok et al. |
||||
* |
||||
* Transdroid is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* Transdroid is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with Transdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
package org.transdroid.daemon.util; |
||||
|
||||
import java.security.cert.CertificateException; |
||||
import java.security.cert.X509Certificate; |
||||
|
||||
import javax.net.ssl.X509TrustManager; |
||||
|
||||
public class IgnoreSSLTrustManager implements X509TrustManager { |
||||
|
||||
@Override |
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { |
||||
// Perform no check whatsoever on the validity of the SSL certificate
|
||||
} |
||||
|
||||
@Override |
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { |
||||
// Perform no check whatsoever on the validity of the SSL certificate
|
||||
} |
||||
|
||||
@Override |
||||
public X509Certificate[] getAcceptedIssuers() { |
||||
return null; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,98 @@
@@ -0,0 +1,98 @@
|
||||
/* |
||||
* Copyright 2010-2013 Eric Kok et al. |
||||
* |
||||
* Transdroid is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* Transdroid is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with Transdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
package org.transdroid.daemon.util; |
||||
|
||||
import java.security.MessageDigest; |
||||
import java.security.NoSuchAlgorithmException; |
||||
import java.security.cert.CertificateEncodingException; |
||||
import java.security.cert.CertificateException; |
||||
import java.security.cert.X509Certificate; |
||||
|
||||
import javax.net.ssl.X509TrustManager; |
||||
|
||||
public class SelfSignedTrustManager implements X509TrustManager { |
||||
|
||||
private static final X509Certificate[] acceptedIssuers = new X509Certificate[]{}; |
||||
private static final String LOG_NAME = "TrustManager"; |
||||
|
||||
private String certKey = null; |
||||
|
||||
public SelfSignedTrustManager(String certKey) { |
||||
super(); |
||||
this.certKey = certKey; |
||||
} |
||||
|
||||
// Thank you: http://stackoverflow.com/questions/1270703/how-to-retrieve-compute-an-x509-certificates-thumbprint-in-java
|
||||
private static String getThumbPrint(X509Certificate cert) |
||||
throws NoSuchAlgorithmException, CertificateEncodingException { |
||||
MessageDigest md = MessageDigest.getInstance("SHA-1"); |
||||
byte[] der = cert.getEncoded(); |
||||
md.update(der); |
||||
byte[] digest = md.digest(); |
||||
return hexify(digest); |
||||
} |
||||
|
||||
private static String hexify(byte bytes[]) { |
||||
|
||||
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; |
||||
StringBuffer buf = new StringBuffer(bytes.length * 2); |
||||
for (int i = 0; i < bytes.length; ++i) { |
||||
buf.append(hexDigits[(bytes[i] & 0xf0) >> 4]); |
||||
buf.append(hexDigits[bytes[i] & 0x0f]); |
||||
} |
||||
return buf.toString(); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { |
||||
} |
||||
|
||||
@Override |
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { |
||||
if (this.certKey == null) { |
||||
throw new CertificateException("Requires a non-null certificate key in SHA-1 format to match."); |
||||
} |
||||
|
||||
// Qe have a certKey defined. We should now examine the one we got from the server.
|
||||
// They match? All is good. They don't, throw an exception.
|
||||
String ourKey = this.certKey.replaceAll("[^a-fA-F0-9]+", ""); |
||||
try { |
||||
// Assume self-signed root is okay?
|
||||
X509Certificate sslCert = chain[0]; |
||||
String thumbprint = SelfSignedTrustManager.getThumbPrint(sslCert); |
||||
DLog.d(LOG_NAME, thumbprint); |
||||
if (ourKey.equalsIgnoreCase(thumbprint)) { |
||||
return; |
||||
} else { |
||||
CertificateException certificateException = |
||||
new CertificateException("Certificate key [" + thumbprint + "] doesn't match expected value."); |
||||
DLog.e(SelfSignedTrustManager.class.getSimpleName(), certificateException.toString()); |
||||
throw certificateException; |
||||
} |
||||
} catch (NoSuchAlgorithmException e) { |
||||
throw new CertificateException("Unable to check self-signed cert, unknown algorithm. " + e.toString()); |
||||
} |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public X509Certificate[] getAcceptedIssuers() { |
||||
return acceptedIssuers; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,148 @@
@@ -0,0 +1,148 @@
|
||||
/* |
||||
* Copyright 2010-2013 Eric Kok et al. |
||||
* |
||||
* Transdroid is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* Transdroid is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with Transdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
package org.transdroid.daemon.util; |
||||
|
||||
import android.net.SSLCertificateSocketFactory; |
||||
import android.os.Build; |
||||
|
||||
import org.apache.http.conn.scheme.LayeredSocketFactory; |
||||
import org.apache.http.conn.ssl.StrictHostnameVerifier; |
||||
import org.apache.http.params.HttpParams; |
||||
|
||||
import java.io.IOException; |
||||
import java.net.InetAddress; |
||||
import java.net.Socket; |
||||
|
||||
import javax.net.ssl.HostnameVerifier; |
||||
import javax.net.ssl.SSLPeerUnverifiedException; |
||||
import javax.net.ssl.SSLSession; |
||||
import javax.net.ssl.SSLSocket; |
||||
import javax.net.ssl.TrustManager; |
||||
|
||||
/** |
||||
* Implements an HttpClient socket factory with extensive support for SSL. Many thanks to |
||||
* http://blog.dev001.net/post/67082904181/android-using-sni-and-tlsv1-2-with-apache-httpclient for the base
|
||||
* implementation. |
||||
* <p/> |
||||
* Firstly, all SSL protocols that a particular Android version support will be enabled (according to |
||||
* http://developer.android.com/reference/javax/net/ssl/SSLSocket.html). This currently includes SSL v3 and TLSv1.0,
|
||||
* v1.1 and v1.2. |
||||
* <p/> |
||||
* Second, SNI is supported for host name verification. For Android 4.2+, which supports it natively, the default |
||||
* (strict) hostname verifier is used. For Android 4.1 and earlier it is possibly supported through reflexion on the |
||||
* same methods. |
||||
* <p/> |
||||
* Third, self-signed certificates are supported through the checking of the received certificate key with a given SHA-1 |
||||
* encoded hex of the self-signed certificate key. When a key is given but not a correct match, the thumbprint of the |
||||
* server certificate is given, such that the correct SHA-1 hash to use can be foudn in the log. |
||||
* <p/> |
||||
* Finally, the ignoring of all SSL certificates (and hostname) is possible (which is obviously very insecure!). |
||||
*/ |
||||
public class TlsSniSocketFactory implements LayeredSocketFactory { |
||||
|
||||
private final static HostnameVerifier hostnameVerifier = new StrictHostnameVerifier(); |
||||
|
||||
private final boolean acceptAllCertificates; |
||||
private final String selfSignedCertificateKey; |
||||
|
||||
public TlsSniSocketFactory() { |
||||
this.acceptAllCertificates = false; |
||||
this.selfSignedCertificateKey = null; |
||||
} |
||||
|
||||
public TlsSniSocketFactory(String certKey) { |
||||
this.acceptAllCertificates = false; |
||||
this.selfSignedCertificateKey = certKey; |
||||
} |
||||
|
||||
public TlsSniSocketFactory(boolean acceptAllCertificates) { |
||||
this.acceptAllCertificates = acceptAllCertificates; |
||||
this.selfSignedCertificateKey = null; |
||||
} |
||||
|
||||
// Plain TCP/IP (layer below TLS)
|
||||
|
||||
@Override |
||||
public Socket connectSocket(Socket s, String host, int port, InetAddress localAddress, int localPort, |
||||
HttpParams params) throws IOException { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public Socket createSocket() throws IOException { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isSecure(Socket s) throws IllegalArgumentException { |
||||
if (s instanceof SSLSocket) { |
||||
return s.isConnected(); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
// TLS layer
|
||||
|
||||
@Override |
||||
public Socket createSocket(Socket plainSocket, String host, int port, boolean autoClose) throws IOException { |
||||
if (autoClose) { |
||||
// we don't need the plainSocket
|
||||
plainSocket.close(); |
||||
} |
||||
|
||||
SSLCertificateSocketFactory sslSocketFactory = |
||||
(SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(0); |
||||
|
||||
// For self-signed certificates use a custom trust manager
|
||||
if (acceptAllCertificates) { |
||||
sslSocketFactory.setTrustManagers(new TrustManager[]{new IgnoreSSLTrustManager()}); |
||||
} else if (selfSignedCertificateKey != null) { |
||||
sslSocketFactory.setTrustManagers(new TrustManager[]{new SelfSignedTrustManager(selfSignedCertificateKey)}); |
||||
} |
||||
|
||||
// create and connect SSL socket, but don't do hostname/certificate verification yet
|
||||
SSLSocket ssl = (SSLSocket) sslSocketFactory.createSocket(InetAddress.getByName(host), port); |
||||
|
||||
// enable TLSv1.1/1.2 if available
|
||||
ssl.setEnabledProtocols(ssl.getSupportedProtocols()); |
||||
|
||||
// set up SNI before the handshake
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { |
||||
sslSocketFactory.setHostname(ssl, host); |
||||
} else { |
||||
try { |
||||
java.lang.reflect.Method setHostnameMethod = ssl.getClass().getMethod("setHostname", String.class); |
||||
setHostnameMethod.invoke(ssl, host); |
||||
} catch (Exception e) { |
||||
DLog.d(TlsSniSocketFactory.class.getSimpleName(), "SNI not usable: " + e); |
||||
} |
||||
} |
||||
|
||||
// verify hostname and certificate
|
||||
SSLSession session = ssl.getSession(); |
||||
if (!(acceptAllCertificates || selfSignedCertificateKey != null) && !hostnameVerifier.verify(host, session)) { |
||||
throw new SSLPeerUnverifiedException("Cannot verify hostname: " + host); |
||||
} |
||||
|
||||
DLog.d(TlsSniSocketFactory.class.getSimpleName(), |
||||
"Established " + session.getProtocol() + " connection with " + session.getPeerHost() + |
||||
" using " + session.getCipherSuite()); |
||||
|
||||
return ssl; |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue