This commit is contained in:
Dmitry Donskoy 2018-10-10 19:31:28 +03:00 committed by Aleksandr Zatsepin
parent d0b89deef4
commit d66fcd9d04
4 changed files with 531 additions and 1 deletions

View file

@ -1,8 +1,10 @@
package com.mapswithme.maps;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;

View file

@ -0,0 +1,161 @@
package com.mapswithme.util;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import org.apache.http.conn.ssl.SSLSocketFactory;
/**
* Allows you to trust certificates from additional KeyStores in addition to
* the default KeyStore
*/
public class AdditionalKeyStoresSSLSocketFactory extends javax.net.ssl.SSLSocketFactory
{
protected SSLContext sslContext = SSLContext.getInstance("TLS");
public AdditionalKeyStoresSSLSocketFactory(KeyStore keyStore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, "".toCharArray());
sslContext.init(kmf.getKeyManagers(), new TrustManager[]{new ClientKeyStoresTrustManager(keyStore)}, new SecureRandom());
}
@Override
public String[] getDefaultCipherSuites()
{
return new String[0];
}
@Override
public String[] getSupportedCipherSuites()
{
return new String[0];
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}
@Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException
{
return null;
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException
{
return null;
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException
{
return null;
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException
{
return null;
}
/**
* Based on http://download.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#X509TrustManager
*/
public static class ClientKeyStoresTrustManager implements X509TrustManager {
protected ArrayList<X509TrustManager> x509TrustManagers = new ArrayList<X509TrustManager>();
protected ClientKeyStoresTrustManager(KeyStore... additionalkeyStores) {
final ArrayList<TrustManagerFactory> factories = new ArrayList<TrustManagerFactory>();
try {
// The default Trustmanager with default keystore
final TrustManagerFactory original = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
original.init((KeyStore) null);
factories.add(original);
for ( KeyStore keyStore : additionalkeyStores ) {
final TrustManagerFactory additionalCerts = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
additionalCerts.init(keyStore);
factories.add(additionalCerts);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
/*
* Iterate over the returned trustmanagers, and hold on
* to any that are X509TrustManagers
*/
for (TrustManagerFactory tmf : factories)
for ( TrustManager tm : tmf.getTrustManagers() )
if (tm instanceof X509TrustManager)
x509TrustManagers.add( (X509TrustManager) tm );
if ( x509TrustManagers.size() == 0 )
throw new RuntimeException("Couldn't find any X509TrustManagers");
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
for ( X509TrustManager tm : x509TrustManagers ) {
try {
tm.checkClientTrusted(chain, authType);
return;
} catch ( CertificateException e ) {
}
}
throw new CertificateException();
}
/*
* Loop over the trustmanagers until we find one that accepts our server
*/
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
for ( X509TrustManager tm : x509TrustManagers ) {
try {
tm.checkServerTrusted(chain, authType);
return;
} catch ( CertificateException e ) {
}
}
throw new CertificateException();
}
public X509Certificate[] getAcceptedIssuers() {
final ArrayList<X509Certificate> list = new ArrayList<X509Certificate>();
for ( X509TrustManager tm : x509TrustManagers )
list.addAll(Arrays.asList(tm.getAcceptedIssuers()));
return list.toArray(new X509Certificate[list.size()]);
}
}
}

View file

@ -24,15 +24,21 @@
package com.mapswithme.util;
import android.content.res.Resources;
import android.os.Build;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.mapswithme.maps.MwmApplication;
import com.mapswithme.maps.R;
import com.mapswithme.util.log.Logger;
import com.mapswithme.util.log.LoggerFactory;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
@ -44,6 +50,12 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -71,7 +83,7 @@ public final class HttpClient
try
{
connection = (HttpURLConnection) new URL(p.url).openConnection();
connection = (HttpURLConnection) new URL("https://user-request.mapsme.devmail.ru/binding_request").openConnection();
setupTLSForPreLollipop(connection);
// NullPointerException, MalformedUrlException, IOException
@ -195,6 +207,9 @@ public final class HttpClient
ostream.close(); // IOException
if (ostream instanceof ByteArrayOutputStream)
p.data = ((ByteArrayOutputStream) ostream).toByteArray();
} catch (IOException e)
{
throw new IOException(e);
}
finally
{
@ -217,6 +232,11 @@ public final class HttpClient
SSLSocketFactory factory = sslConnection.getSSLSocketFactory();
sslConnection.setSSLSocketFactory(new PreLollipopSSLSocketFactory(factory));
}
if ((connection instanceof HttpsURLConnection)) {
SSLSocketFactory socketFactory = TLSSocketFactory.create();
((HttpsURLConnection)connection).setSSLSocketFactory(socketFactory);
}
}
@NonNull

View file

@ -0,0 +1,347 @@
package com.mapswithme.util;
import android.content.Context;
import android.net.SSLCertificateSocketFactory;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RawRes;
import com.mapswithme.maps.MwmApplication;
import com.mapswithme.maps.R;
import com.mopub.common.Preconditions;
import com.mopub.common.VisibleForTesting;
import com.mopub.common.logging.MoPubLog;
import com.mopub.common.util.Reflection;
import com.mopub.network.InetAddressUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
/**
* An {@link javax.net.ssl.SSLSocketFactory} that supports TLS settings for the MoPub ad servers.
*/
public class TLSSocketFactory extends SSLSocketFactory {
@Nullable private SSLSocketFactory mCertificateSocketFactory;
protected SSLContext sslContext = SSLContext.getInstance("TLS");
public TLSSocketFactory(KeyStore keyStore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
{
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, "".toCharArray());
sslContext.init(kmf.getKeyManagers(), new TrustManager[]{ new AdditionalKeyStoresSSLSocketFactory.ClientKeyStoresTrustManager(keyStore)}, new SecureRandom());
}
@NonNull
public static TLSSocketFactory getDefault(KeyStore keyStore) throws UnrecoverableKeyException,
NoSuchAlgorithmException, KeyStoreException, KeyManagementException
{
TLSSocketFactory factory = new TLSSocketFactory(keyStore);
factory.mCertificateSocketFactory = SSLCertificateSocketFactory.getDefault((int) TimeUnit.MINUTES.toMillis(5), null);
return factory;
}
// Forward all methods. Enable TLS 1.1 and 1.2 before returning.
// SocketFactory overrides
@Override
public Socket createSocket() throws IOException {
if (mCertificateSocketFactory == null) {
throw new SocketException("SSLSocketFactory was null. Unable to create socket.");
}
final Socket socket = mCertificateSocketFactory.createSocket();
enableTlsIfAvailable(socket);
return socket;
}
@Override
public Socket createSocket(final String host, final int i) throws IOException, UnknownHostException {
if (mCertificateSocketFactory == null) {
throw new SocketException("SSLSocketFactory was null. Unable to create socket.");
}
final Socket socket = mCertificateSocketFactory.createSocket(host, i);
enableTlsIfAvailable(socket);
return socket;
}
@Override
public Socket createSocket(final String host, final int port, final InetAddress localhost, final int localPort) throws IOException, UnknownHostException {
if (mCertificateSocketFactory == null) {
throw new SocketException("SSLSocketFactory was null. Unable to create socket.");
}
final Socket socket = mCertificateSocketFactory.createSocket(host, port, localhost, localPort);
enableTlsIfAvailable(socket);
return socket;
}
@Override
public Socket createSocket(final InetAddress address, final int port) throws IOException {
if (mCertificateSocketFactory == null) {
throw new SocketException("SSLSocketFactory was null. Unable to create socket.");
}
final Socket socket = mCertificateSocketFactory.createSocket(address, port);
enableTlsIfAvailable(socket);
return socket;
}
@Override
public Socket createSocket(final InetAddress address, final int port, final InetAddress localhost, final int localPort) throws IOException {
if (mCertificateSocketFactory == null) {
throw new SocketException("SSLSocketFactory was null. Unable to create socket.");
}
final Socket socket = mCertificateSocketFactory.createSocket(address, port, localhost, localPort);
enableTlsIfAvailable(socket);
return socket;
}
// SSLSocketFactory overrides
@Override
public String[] getDefaultCipherSuites() {
if (mCertificateSocketFactory == null) {
return new String[]{};
}
return mCertificateSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
if (mCertificateSocketFactory == null) {
return new String[]{};
}
return mCertificateSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(final Socket socketParam, final String host, final int port, final boolean autoClose) throws IOException {
if (mCertificateSocketFactory == null) {
throw new SocketException("SSLSocketFactory was null. Unable to create socket.");
}
// There is a bug in Android before version 6.0 where SNI does not work, so we try to do
// it manually here.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Don't use the original socket and create a new one. This closes the original socket
// if the autoClose flag is set.
if (autoClose && socketParam != null) {
socketParam.close();
}
final Socket socket = mCertificateSocketFactory.createSocket(
InetAddressUtils.getInetAddressByName(host), port);
enableTlsIfAvailable(socket);
doManualServerNameIdentification(socket, host);
return socket;
}
final Socket socket = mCertificateSocketFactory.createSocket(socketParam, host, port,
autoClose);
enableTlsIfAvailable(socket);
return socket;
}
/**
* Some versions of Android fail to do server name identification (SNI) even though they are
* able to. This method forces SNI to happen, if possible. SNI is only used in https
* connections, and this method will no-op for http connections. This method throws an
* SSLHandshakeException if SNI fails. This method may also throw other socket-related
* IOExceptions.
*
* @param socket The socket to do SNI on
* @param host The host to verify the server name
* @throws IOException
*/
private void doManualServerNameIdentification(@NonNull final Socket socket,
@Nullable final String host) throws IOException {
Preconditions.checkNotNull(socket);
if (mCertificateSocketFactory == null) {
throw new SocketException("SSLSocketFactory was null. Unable to create socket.");
}
if (socket instanceof SSLSocket) {
final SSLSocket sslSocket = (SSLSocket) socket;
setHostnameOnSocket((SSLCertificateSocketFactory) mCertificateSocketFactory, sslSocket,
host);
verifyServerName(sslSocket, host);
}
}
/**
* Calling setHostname on a socket turns on the server name identification feature.
* Unfortunately, this was introduced in Android version 17, so we do what we can.
*/
static void setHostnameOnSocket(@NonNull final SSLCertificateSocketFactory certificateSocketFactory,
@NonNull final SSLSocket sslSocket, @Nullable final String host) {
Preconditions.checkNotNull(certificateSocketFactory);
Preconditions.checkNotNull(sslSocket);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
certificateSocketFactory.setHostname(sslSocket, host);
} else {
try {
new Reflection.MethodBuilder(sslSocket, "setHostname")
.addParam(String.class, host)
.execute();
} catch (Exception e) {
}
}
}
static void verifyServerName(@NonNull final SSLSocket sslSocket,
@Nullable final String host) throws IOException {
Preconditions.checkNotNull(sslSocket);
sslSocket.startHandshake();
final HostnameVerifier hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
if (!hostnameVerifier.verify(host, sslSocket.getSession())) {
throw new SSLHandshakeException("Server Name Identification failed.");
}
}
private void enableTlsIfAvailable(@Nullable Socket socket) {
if (socket instanceof SSLSocket) {
SSLSocket sslSocket = (SSLSocket) socket;
String[] supportedProtocols = sslSocket.getSupportedProtocols();
// Make sure all supported protocols are enabled. Android does not enable TLSv1.1 or
// TLSv1.2 by default.
sslSocket.setEnabledProtocols(supportedProtocols);
}
}
public static SSLSocketFactory create()
{
InputStream stream = MwmApplication.get().getResources().openRawResource(R.raw.my_cret);
String type = KeyStore.getDefaultType();
try
{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(stream, "123".toCharArray());
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
return sslContext.getSocketFactory();
}
catch (KeyStoreException e)
{
e.printStackTrace();
}
catch (CertificateException e)
{
e.printStackTrace();
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
catch (KeyManagementException e)
{
e.printStackTrace();
}
throw new RuntimeException("e");
}
public static SSLSocketFactory createFactory()
{
InputStream caInput = null;
try {
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(MwmApplication.get().getResources().openRawResource(R.raw.cert), "".toCharArray());
return new AdditionalKeyStoresSSLSocketFactory(keystore);
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
if (caInput != null) {
try {
caInput.close();
} catch (IOException ignored) {
}
}
}
}
public static SSLSocketFactory create(Context context, @RawRes int caRawFile) {
InputStream caInput = null;
try {
// Generate the CA Certificate from the raw resource file
caInput = context.getResources().openRawResource(caRawFile);
Certificate ca = CertificateFactory.getInstance("X.509", "BC").generateCertificate(caInput);
// Load the key store using the CA
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Initialize the TrustManager with this CA
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
/*/* InputStream stream = MwmApplication.get().getResources().openRawResource(R.raw.cert);
String type = KeyStore.getDefaultType();
try
{
KeyStore keyStore = KeyStore.getInstance(type);
keyStore.load(stream, null);
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
trustManagerFactory.init(keyStore);
}*/
// Create an SSL context that uses the created trust manager
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
return sslContext.getSocketFactory();
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
if (caInput != null) {
try {
caInput.close();
} catch (IOException ignored) {
}
}
}
}
}