Eric Kok
10 years ago
12 changed files with 316 additions and 194 deletions
@ -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 @@ |
|||||||
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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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