aboutsummaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
authorChristian Schneppe <christian@pix-art.de>2016-12-18 21:56:56 +0100
committerChristian Schneppe <christian@pix-art.de>2016-12-18 21:56:56 +0100
commit6815a788a3dc37e9294b944a26fa56e74e4f88d3 (patch)
tree1fee26e435b8175bd53ac3ccac225f46a3b03897 /libs
parentc25a6520a0eed6635262a4124d1dbdc10a6972e4 (diff)
add support for RFC7711 to MTM
Diffstat (limited to 'libs')
-rw-r--r--libs/MemorizingTrustManager/build.gradle12
-rw-r--r--libs/MemorizingTrustManager/example/AndroidManifest.xml32
-rw-r--r--libs/MemorizingTrustManager/example/ant.properties14
-rw-r--r--libs/MemorizingTrustManager/example/build.gradle23
-rw-r--r--libs/MemorizingTrustManager/example/build.xml91
-rw-r--r--libs/MemorizingTrustManager/example/proguard-project.txt20
-rw-r--r--libs/MemorizingTrustManager/example/project.properties11
-rw-r--r--libs/MemorizingTrustManager/example/res/layout/mtmexample.xml36
-rw-r--r--libs/MemorizingTrustManager/example/res/values/strings.xml4
-rw-r--r--libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/JULHandler.java179
-rw-r--r--libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/MTMExample.java151
-rw-r--r--libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingTrustManager.java276
12 files changed, 230 insertions, 619 deletions
diff --git a/libs/MemorizingTrustManager/build.gradle b/libs/MemorizingTrustManager/build.gradle
index 12072b81f..46d464a33 100644
--- a/libs/MemorizingTrustManager/build.gradle
+++ b/libs/MemorizingTrustManager/build.gradle
@@ -3,18 +3,18 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.2.2'
+ classpath 'com.android.tools.build:gradle:2.0.0'
}
}
-apply plugin: 'android-library'
+apply plugin: 'com.android.library'
android {
- compileSdkVersion 19
- buildToolsVersion "19.1"
+ compileSdkVersion 24
+ buildToolsVersion "23.0.3"
defaultConfig {
- minSdkVersion 7
- targetSdkVersion 19
+ minSdkVersion 14
+ targetSdkVersion 24
}
sourceSets {
diff --git a/libs/MemorizingTrustManager/example/AndroidManifest.xml b/libs/MemorizingTrustManager/example/AndroidManifest.xml
deleted file mode 100644
index ebc664d65..000000000
--- a/libs/MemorizingTrustManager/example/AndroidManifest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="de.duenndns.mtmexample"
- android:versionCode="1"
- android:versionName="1.0">
-
- <uses-sdk
- android:minSdkVersion="3"
- android:targetSdkVersion="19" />
-
- <uses-permission android:name="android.permission.INTERNET" />
-
- <application
- android:label="@string/app_name"
- android:icon="@android:drawable/ic_lock_lock">
- <activity
- android:name=".MTMExample"
- android:configChanges="keyboardHidden|orientation|screenSize|screenLayout"
- android:label="@string/app_name">
-
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
-
- <!-- ADD THE FOLLOWING TO YOUR MANIFEST: -->
- <activity
- android:name="de.duenndns.ssl.MemorizingActivity"
- android:theme="@android:style/Theme.Translucent.NoTitleBar" />
- </application>
-</manifest>
diff --git a/libs/MemorizingTrustManager/example/ant.properties b/libs/MemorizingTrustManager/example/ant.properties
deleted file mode 100644
index 2f48b3655..000000000
--- a/libs/MemorizingTrustManager/example/ant.properties
+++ /dev/null
@@ -1,14 +0,0 @@
-# This file is used to override default values used by the Ant build system.
-#
-# This file must be checked in Version Control Systems, as it is
-# integral to the build system of your project.
-# This file is only used by the Ant script.
-# You can use this to override default values such as
-# 'source.dir' for the location of your java source folder and
-# 'out.dir' for the location of your output folder.
-# You can also use it define how the release builds are signed by declaring
-# the following properties:
-# 'key.store' for the location of your keystore and
-# 'key.alias' for the name of the key to use.
-# The password will be asked during the build when you use the 'release' target.
-application.package=de.duenndns.mtmexample
diff --git a/libs/MemorizingTrustManager/example/build.gradle b/libs/MemorizingTrustManager/example/build.gradle
deleted file mode 100644
index a07fbe87b..000000000
--- a/libs/MemorizingTrustManager/example/build.gradle
+++ /dev/null
@@ -1,23 +0,0 @@
-apply plugin: 'android'
-
-dependencies {
- compile rootProject
-}
-
-android {
- compileSdkVersion 19
- buildToolsVersion "19.1"
- defaultConfig {
- minSdkVersion 7
- targetSdkVersion 19
- }
-
- sourceSets {
- main {
- manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = ['src']
- res.srcDirs = ['res']
- }
- }
-
-}
diff --git a/libs/MemorizingTrustManager/example/build.xml b/libs/MemorizingTrustManager/example/build.xml
deleted file mode 100644
index 0c61a2917..000000000
--- a/libs/MemorizingTrustManager/example/build.xml
+++ /dev/null
@@ -1,91 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project name="MTMExample" default="help">
-
- <!-- The local.properties file is created and updated by the 'android' tool.
- It contains the path to the SDK. It should *NOT* be checked into
- Version Control Systems. -->
- <property file="local.properties" />
-
- <!-- The ant.properties file can be created by you. It is only edited by the
- 'android' tool to add properties to it.
- This is the place to change some Ant specific build properties.
- Here are some properties you may want to change/update:
-
- source.dir
- The name of the source directory. Default is 'src'.
- out.dir
- The name of the output directory. Default is 'bin'.
-
- For other overridable properties, look at the beginning of the rules
- files in the SDK, at tools/ant/build.xml
-
- Properties related to the SDK location or the project target should
- be updated using the 'android' tool with the 'update' action.
-
- This file is an integral part of the build system for your
- application and should be checked into Version Control Systems.
-
- -->
- <property file="ant.properties" />
-
- <!-- if sdk.dir was not set from one of the property file, then
- get it from the ANDROID_HOME env var.
- This must be done before we load project.properties since
- the proguard config can use sdk.dir -->
- <property environment="env" />
- <condition property="sdk.dir" value="${env.ANDROID_HOME}">
- <isset property="env.ANDROID_HOME" />
- </condition>
-
- <!-- The project.properties file is created and updated by the 'android'
- tool, as well as ADT.
-
- This contains project specific properties such as project target, and library
- dependencies. Lower level build properties are stored in ant.properties
- (or in .classpath for Eclipse projects).
-
- This file is an integral part of the build system for your
- application and should be checked into Version Control Systems. -->
- <loadproperties srcFile="project.properties" />
-
- <!-- quick check on sdk.dir -->
- <fail
- message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
- unless="sdk.dir" />
-
- <!--
- Import per project custom build rules if present at the root of the project.
- This is the place to put custom intermediary targets such as:
- -pre-build
- -pre-compile
- -post-compile (This is typically used for code obfuscation.
- Compiled code location: ${out.classes.absolute.dir}
- If this is not done in place, override ${out.dex.input.absolute.dir})
- -post-package
- -post-build
- -pre-clean
- -->
- <import file="custom_rules.xml" optional="true" />
-
- <!-- Import the actual build file.
-
- To customize existing targets, there are two options:
- - Customize only one target:
- - copy/paste the target into this file, *before* the
- <import> task.
- - customize it to your needs.
- - Customize the whole content of build.xml
- - copy/paste the content of the rules files (minus the top node)
- into this file, replacing the <import> task.
- - customize to your needs.
-
- ***********************
- ****** IMPORTANT ******
- ***********************
- In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
- in order to avoid having your file be overridden by tools such as "android update project"
- -->
- <!-- version-tag: 1 -->
- <import file="${sdk.dir}/tools/ant/build.xml" />
-
-</project>
diff --git a/libs/MemorizingTrustManager/example/proguard-project.txt b/libs/MemorizingTrustManager/example/proguard-project.txt
deleted file mode 100644
index f2fe1559a..000000000
--- a/libs/MemorizingTrustManager/example/proguard-project.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-# To enable ProGuard in your project, edit project.properties
-# to define the proguard.config property as described in that file.
-#
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in ${sdk.dir}/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the ProGuard
-# include property in project.properties.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
diff --git a/libs/MemorizingTrustManager/example/project.properties b/libs/MemorizingTrustManager/example/project.properties
deleted file mode 100644
index be830d977..000000000
--- a/libs/MemorizingTrustManager/example/project.properties
+++ /dev/null
@@ -1,11 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system use,
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-android.library.reference.1=../
-# Project target.
-target=android-19
diff --git a/libs/MemorizingTrustManager/example/res/layout/mtmexample.xml b/libs/MemorizingTrustManager/example/res/layout/mtmexample.xml
deleted file mode 100644
index 4a08b6899..000000000
--- a/libs/MemorizingTrustManager/example/res/layout/mtmexample.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <EditText
- android:id="@+id/url"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:hint="HTTPS address"
- android:text="https://op-co.de/mtm/"
- android:singleLine="true" />
-
- <Button
- android:id="@+id/connect"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="Connect" />
-
- <TextView
- android:id="@+id/content"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="Please enter a HTTPS URL and press 'Connect'!"
- android:textSize="11pt" />
-
- <Button
- android:id="@+id/manage"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="Clean up Certificates"
- android:onClick="onManage" />
-</LinearLayout>
-
diff --git a/libs/MemorizingTrustManager/example/res/values/strings.xml b/libs/MemorizingTrustManager/example/res/values/strings.xml
deleted file mode 100644
index e4f505bc0..000000000
--- a/libs/MemorizingTrustManager/example/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <string name="app_name">MemorizingTrustManager Example</string>
-</resources>
diff --git a/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/JULHandler.java b/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/JULHandler.java
deleted file mode 100644
index 2cd70d0c5..000000000
--- a/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/JULHandler.java
+++ /dev/null
@@ -1,179 +0,0 @@
-package de.duenndns.mtmexample;
-
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.io.StringBufferInputStream;
-import java.io.StringWriter;
-import java.util.logging.Formatter;
-import java.util.logging.Handler;
-import java.util.logging.Level;
-import java.util.logging.LogManager;
-import java.util.logging.LogRecord;
-import java.util.logging.Logger;
-
-/**
- * A <code>java.util.logging</code> (JUL) Handler for Android.
- * <p>
- * If you want fine-grained control over MTM's logging, you can copy this
- * class to your code base and call the static {@link #initialize()} method.
- * </p>
- * <p>
- * This JUL Handler passes log messages sent to JUL to the Android log, while
- * keeping the format and stack traces of optionally supplied Exceptions. It
- * further allows to install a {@link DebugLogSettings} class via
- * {@link #setDebugLogSettings(DebugLogSettings)} that determines whether JUL log messages of
- * level {@link java.util.logging.Level#FINE} or lower are logged. This gives
- * the application developer more control over the logged messages, while
- * allowing a library developer to place debug log messages without risking to
- * spam the Android log.
- * </p>
- * <p>
- * If there are no {@code DebugLogSettings} configured, then all messages sent
- * to JUL will be logged.
- * </p>
- *
- * @author Florian Schmaus
- */
-@SuppressWarnings("deprecation")
-public class JULHandler extends Handler {
-
- /**
- * Implement this interface to toggle debug logging.
- */
- public interface DebugLogSettings {
- public boolean isDebugLogEnabled();
- }
-
- private static final String CLASS_NAME = JULHandler.class.getName();
-
- /**
- * The global LogManager configuration.
- * <p>
- * This configures:
- * <ul>
- * <li> JULHandler as the default handler for all log messages
- * <li> A default log level FINEST (300). Meaning that log messages of a level 300 or higher a
- * logged
- * </ul>
- * </p>
- */
- private static final InputStream LOG_MANAGER_CONFIG = new StringBufferInputStream(
-// @formatter:off
- "handlers = " + CLASS_NAME + '\n' +
- ".level = FINEST"
- );
-// @formatter:on
-
- // Constants for Android vs. JUL debug level comparisons
- private static final int FINE_INT = Level.FINE.intValue();
- private static final int INFO_INT = Level.INFO.intValue();
- private static final int WARN_INT = Level.WARNING.intValue();
- private static final int SEVE_INT = Level.SEVERE.intValue();
-
- private static final Logger LOGGER = Logger.getLogger(CLASS_NAME);
-
- /**
- * A formatter that creates output similar to Android's Log.x.
- */
- private static final Formatter FORMATTER = new Formatter() {
- @Override
- public String format(LogRecord logRecord) {
- Throwable thrown = logRecord.getThrown();
- if (thrown != null) {
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw, false);
- pw.write(logRecord.getMessage() + ' ');
- thrown.printStackTrace(pw);
- pw.flush();
- return sw.toString();
- } else {
- return logRecord.getMessage();
- }
- }
- };
-
- private static DebugLogSettings sDebugLogSettings;
- private static boolean initialized = false;
-
- public static void initialize() {
- try {
- LogManager.getLogManager().readConfiguration(LOG_MANAGER_CONFIG);
- initialized = true;
- } catch (IOException e) {
- Log.e("JULHandler", "Can not initialize configuration", e);
- }
- if (initialized) LOGGER.info("Initialzied java.util.logging logger");
- }
-
- public static void setDebugLogSettings(DebugLogSettings debugLogSettings) {
- if (!isInitialized()) initialize();
- sDebugLogSettings = debugLogSettings;
- }
-
- public static boolean isInitialized() {
- return initialized;
- }
-
- public JULHandler() {
- setFormatter(FORMATTER);
- }
-
- @Override
- public void close() {
- }
-
- @Override
- public void flush() {
- }
-
- @Override
- public boolean isLoggable(LogRecord record) {
- final boolean debugLog = sDebugLogSettings == null ? true : sDebugLogSettings
- .isDebugLogEnabled();
-
- if (record.getLevel().intValue() <= FINE_INT) {
- return debugLog;
- }
- return true;
- }
-
- /**
- * JUL method that forwards log records to Android's LogCat.
- */
- @Override
- public void publish(LogRecord record) {
- if (!isLoggable(record)) return;
-
- final int priority = getAndroidPriority(record.getLevel());
- final String tag = substringAfterLastDot(record.getSourceClassName());
- final String msg = getFormatter().format(record);
-
- Log.println(priority, tag, msg);
- }
-
- /**
- * Helper to convert JUL verbosity levels to Android's Log.
- */
- private static int getAndroidPriority(Level level) {
- int value = level.intValue();
- if (value >= SEVE_INT) {
- return Log.ERROR;
- } else if (value >= WARN_INT) {
- return Log.WARN;
- } else if (value >= INFO_INT) {
- return Log.INFO;
- } else {
- return Log.DEBUG;
- }
- }
-
- /**
- * Helper to extract short class names.
- */
- private static String substringAfterLastDot(String s) {
- return s.substring(s.lastIndexOf('.') + 1).trim();
- }
-}
diff --git a/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/MTMExample.java b/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/MTMExample.java
deleted file mode 100644
index 31e37bd00..000000000
--- a/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/MTMExample.java
+++ /dev/null
@@ -1,151 +0,0 @@
-package de.duenndns.mtmexample;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.os.Handler;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.Window;
-import android.widget.ArrayAdapter;
-import android.widget.EditText;
-import android.widget.TextView;
-
-import java.net.URL;
-import java.security.KeyStoreException;
-import java.util.ArrayList;
-import java.util.Collections;
-
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.X509TrustManager;
-
-import de.duenndns.ssl.MemorizingTrustManager;
-
-/**
- * Example to demonstrate the use of MemorizingTrustManager on HTTPS
- * sockets.
- */
-public class MTMExample extends Activity implements OnClickListener {
- MemorizingTrustManager mtm;
-
- TextView content;
- HostnameVerifier defaultverifier;
- EditText urlinput;
- String text;
- Handler hdlr;
-
- /**
- * Creates the Activity and registers a MemorizingTrustManager.
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- JULHandler.initialize();
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- setContentView(R.layout.mtmexample);
-
-
- // set up gui elements
- findViewById(R.id.connect).setOnClickListener(this);
- content = (TextView) findViewById(R.id.content);
- urlinput = (EditText) findViewById(R.id.url);
-
- // register handler for background thread
- hdlr = new Handler();
-
- // Here, the MemorizingTrustManager is activated for HTTPS
- try {
- // set location of the keystore
- MemorizingTrustManager.setKeyStoreFile("private", "sslkeys.bks");
-
- // register MemorizingTrustManager for HTTPS
- SSLContext sc = SSLContext.getInstance("TLS");
- mtm = new MemorizingTrustManager(this);
- sc.init(null, new X509TrustManager[]{mtm},
- new java.security.SecureRandom());
- HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
- HttpsURLConnection.setDefaultHostnameVerifier(
- mtm.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier()));
-
- // disable redirects to reduce possible confusion
- HttpsURLConnection.setFollowRedirects(false);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Updates the screen content from a background thread.
- */
- void setText(final String s, final boolean progress) {
- text = s;
- hdlr.post(new Runnable() {
- public void run() {
- content.setText(s);
- setProgressBarIndeterminateVisibility(progress);
- }
- });
- }
-
- /**
- * Spawns a new thread connecting to the specified URL.
- * The result of the request is displayed on the screen.
- *
- * @param urlString a HTTPS URL to connect to.
- */
- void connect(final String urlString) {
- new Thread() {
- public void run() {
- try {
- URL u = new URL(urlString);
- HttpsURLConnection c = (HttpsURLConnection) u.openConnection();
- c.connect();
- setText("" + c.getResponseCode() + " "
- + c.getResponseMessage(), false);
- c.disconnect();
- } catch (Exception e) {
- setText(e.toString(), false);
- e.printStackTrace();
- }
- }
- }.start();
- }
-
- /**
- * Reacts on the connect Button press.
- */
- @Override
- public void onClick(View view) {
- String url = urlinput.getText().toString();
- setText("Loading " + url, true);
- setProgressBarIndeterminateVisibility(true);
- connect(url);
- }
-
- /**
- * React on the "Manage Certificates" button press.
- */
- public void onManage(View view) {
- final ArrayList<String> aliases = Collections.list(mtm.getCertificates());
- ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.select_dialog_item, aliases);
- new AlertDialog.Builder(this).setTitle("Tap Certificate to Delete")
- .setNegativeButton(android.R.string.cancel, null)
- .setAdapter(adapter, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- try {
- String alias = aliases.get(which);
- mtm.deleteCertificate(alias);
- setText("Deleted " + alias, false);
- } catch (KeyStoreException e) {
- e.printStackTrace();
- setText("Error: " + e.getLocalizedMessage(), false);
- }
- }
- })
- .create().show();
- }
-}
diff --git a/libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingTrustManager.java b/libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingTrustManager.java
index d2fc75db8..e46b6d5fb 100644
--- a/libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingTrustManager.java
+++ b/libs/MemorizingTrustManager/src/de/duenndns/ssl/MemorizingTrustManager.java
@@ -28,35 +28,52 @@ package de.duenndns.ssl;
import android.app.Activity;
import android.app.Application;
-import android.app.Notification;
import android.app.NotificationManager;
-import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Handler;
+import android.preference.PreferenceManager;
+import android.util.Base64;
+import android.util.Log;
import android.util.SparseArray;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
+import java.util.regex.Pattern;
import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
@@ -72,7 +89,15 @@ import javax.net.ssl.X509TrustManager;
* <b>WARNING:</b> This only works if a dedicated thread is used for
* opening sockets!
*/
-public class MemorizingTrustManager implements X509TrustManager {
+public class MemorizingTrustManager {
+
+
+ private static final Pattern PATTERN_IPV4 = Pattern.compile("\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
+ private static final Pattern PATTERN_IPV6_HEX4DECCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
+ private static final Pattern PATTERN_IPV6_6HEX4DEC = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
+ private static final Pattern PATTERN_IPV6_HEXCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\z");
+ private static final Pattern PATTERN_IPV6 = Pattern.compile("\\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\z");
+
final static String DECISION_INTENT = "de.duenndns.ssl.DECISION";
final static String DECISION_INTENT_ID = DECISION_INTENT + ".decisionId";
final static String DECISION_INTENT_CERT = DECISION_INTENT + ".cert";
@@ -98,6 +123,7 @@ public class MemorizingTrustManager implements X509TrustManager {
private KeyStore appKeyStore;
private X509TrustManager defaultTrustManager;
private X509TrustManager appTrustManager;
+ private String poshCacheDir;
/**
* Creates an instance of the MemorizingTrustManager class that falls back to a custom TrustManager.
@@ -156,31 +182,13 @@ public class MemorizingTrustManager implements X509TrustManager {
File dir = app.getDir(KEYSTORE_DIR, Context.MODE_PRIVATE);
keyStoreFile = new File(dir + File.separator + KEYSTORE_FILE);
+ poshCacheDir = app.getFilesDir().getAbsolutePath() + "/posh_cache/";
+
appKeyStore = loadAppKeyStore();
}
/**
- * Returns a X509TrustManager list containing a new instance of
- * TrustManagerFactory.
- * <p>
- * This function is meant for convenience only. You can use it
- * as follows to integrate TrustManagerFactory for HTTPS sockets:
- * <p>
- * <pre>
- * SSLContext sc = SSLContext.getInstance("TLS");
- * sc.init(null, MemorizingTrustManager.getInstanceList(this),
- * new java.security.SecureRandom());
- * HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
- * </pre>
- *
- * @param c Activity or Service to show the Dialog / Notification
- */
- public static X509TrustManager[] getInstanceList(Context c) {
- return new X509TrustManager[]{new MemorizingTrustManager(c)};
- }
-
- /**
* Binds an Activity to the MTM for displaying the query dialog.
* <p>
* This is useful if your connection is run from a service that is
@@ -395,7 +403,7 @@ public class MemorizingTrustManager implements X509TrustManager {
return false;
}
- public void checkCertTrusted(X509Certificate[] chain, String authType, boolean isServer, boolean interactive)
+ public void checkCertTrusted(X509Certificate[] chain, String authType, String domain, boolean isServer, boolean interactive)
throws CertificateException {
LOGGER.log(Level.FINE, "checkCertTrusted(" + chain + ", " + authType + ", " + isServer + ")");
try {
@@ -424,6 +432,15 @@ public class MemorizingTrustManager implements X509TrustManager {
else
defaultTrustManager.checkClientTrusted(chain, authType);
} catch (CertificateException e) {
+ boolean trustSystemCAs = !PreferenceManager.getDefaultSharedPreferences(master).getBoolean("dont_trust_system_cas", false);
+ if (domain != null && isServer && trustSystemCAs && !isIp(domain)) {
+ String hash = getBase64Hash(chain[0], "SHA-256");
+ List<String> fingerprints = getPoshFingerprints(domain);
+ if (hash != null && fingerprints.contains(hash)) {
+ Log.d("mtm", "trusted cert fingerprint of " + domain + " via posh");
+ return;
+ }
+ }
e.printStackTrace();
if (interactive) {
interactCert(chain, authType, e);
@@ -434,17 +451,147 @@ public class MemorizingTrustManager implements X509TrustManager {
}
}
- public void checkClientTrusted(X509Certificate[] chain, String authType)
- throws CertificateException {
- checkCertTrusted(chain, authType, false, true);
+ private List<String> getPoshFingerprints(String domain) {
+ List<String> cached = getPoshFingerprintsFromCache(domain);
+ if (cached == null) {
+ return getPoshFingerprintsFromServer(domain);
+ } else {
+ return cached;
+ }
}
- public void checkServerTrusted(X509Certificate[] chain, String authType)
- throws CertificateException {
- checkCertTrusted(chain, authType, true, true);
+ private List<String> getPoshFingerprintsFromServer(String domain) {
+ return getPoshFingerprintsFromServer(domain, "https://" + domain + "/.well-known/posh/xmpp-client.json", -1, true);
+ }
+
+ private List<String> getPoshFingerprintsFromServer(String domain, String url, int maxTtl, boolean followUrl) {
+ Log.d("mtm", "downloading json for " + domain + " from " + url);
+ try {
+ List<String> results = new ArrayList<>();
+ HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
+ connection.setConnectTimeout(5000);
+ connection.setReadTimeout(5000);
+ BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ String inputLine;
+ StringBuilder builder = new StringBuilder();
+ while ((inputLine = in.readLine()) != null) {
+ builder.append(inputLine);
+ }
+ JSONObject jsonObject = new JSONObject(builder.toString());
+ in.close();
+ int expires = jsonObject.getInt("expires");
+ if (expires <= 0) {
+ return new ArrayList<>();
+ }
+ if (maxTtl >= 0) {
+ expires = Math.min(maxTtl, expires);
+ }
+ String redirect;
+ try {
+ redirect = jsonObject.getString("url");
+ } catch (JSONException e) {
+ redirect = null;
+ }
+ if (followUrl && redirect != null && redirect.toLowerCase().startsWith("https")) {
+ return getPoshFingerprintsFromServer(domain, redirect, expires, false);
+ }
+ JSONArray fingerprints = jsonObject.getJSONArray("fingerprints");
+ for (int i = 0; i < fingerprints.length(); i++) {
+ JSONObject fingerprint = fingerprints.getJSONObject(i);
+ String sha256 = fingerprint.getString("sha-256");
+ if (sha256 != null) {
+ results.add(sha256);
+ }
+ }
+ writeFingerprintsToCache(domain, results, 1000L * expires + System.currentTimeMillis());
+ return results;
+ } catch (Exception e) {
+ Log.d("mtm", "error fetching posh " + e.getMessage());
+ return new ArrayList<>();
+ }
+ }
+
+ private File getPoshCacheFile(String domain) {
+ return new File(poshCacheDir + domain + ".json");
+ }
+
+ private void writeFingerprintsToCache(String domain, List<String> results, long expires) {
+ File file = getPoshCacheFile(domain);
+ file.getParentFile().mkdirs();
+ try {
+ file.createNewFile();
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("expires", expires);
+ jsonObject.put("fingerprints", new JSONArray(results));
+ FileOutputStream outputStream = new FileOutputStream(file);
+ outputStream.write(jsonObject.toString().getBytes());
+ outputStream.flush();
+ outputStream.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
}
- public X509Certificate[] getAcceptedIssuers() {
+ private List<String> getPoshFingerprintsFromCache(String domain) {
+ File file = getPoshCacheFile(domain);
+ try {
+ InputStream is = new FileInputStream(file);
+ BufferedReader buf = new BufferedReader(new InputStreamReader(is));
+
+ String line = buf.readLine();
+ StringBuilder sb = new StringBuilder();
+
+ while (line != null) {
+ sb.append(line).append("\n");
+ line = buf.readLine();
+ }
+ JSONObject jsonObject = new JSONObject(sb.toString());
+ is.close();
+ long expires = jsonObject.getLong("expires");
+ long expiresIn = expires - System.currentTimeMillis();
+ if (expiresIn < 0) {
+ file.delete();
+ return null;
+ } else {
+ Log.d("mtm", "posh fingerprints expire in " + (expiresIn / 1000) + "s");
+ }
+ List<String> result = new ArrayList<>();
+ JSONArray jsonArray = jsonObject.getJSONArray("fingerprints");
+ for (int i = 0; i < jsonArray.length(); ++i) {
+ result.add(jsonArray.getString(i));
+ }
+ return result;
+ } catch (FileNotFoundException e) {
+ return null;
+ } catch (IOException e) {
+ return null;
+ } catch (JSONException e) {
+ file.delete();
+ return null;
+ }
+ }
+
+ private static boolean isIp(final String server) {
+ return server != null && (
+ PATTERN_IPV4.matcher(server).matches()
+ || PATTERN_IPV6.matcher(server).matches()
+ || PATTERN_IPV6_6HEX4DEC.matcher(server).matches()
+ || PATTERN_IPV6_HEX4DECCOMPRESSED.matcher(server).matches()
+ || PATTERN_IPV6_HEXCOMPRESSED.matcher(server).matches());
+ }
+
+ private static String getBase64Hash(X509Certificate certificate, String digest) throws CertificateEncodingException {
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance(digest);
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ }
+ md.update(certificate.getEncoded());
+ return Base64.encodeToString(md.digest(), Base64.NO_WRAP);
+ }
+
+ private X509Certificate[] getAcceptedIssuers() {
LOGGER.log(Level.FINE, "getAcceptedIssuers()");
return defaultTrustManager.getAcceptedIssuers();
}
@@ -556,21 +703,6 @@ public class MemorizingTrustManager implements X509TrustManager {
return si.toString();
}
- // We can use Notification.Builder once MTM's minSDK is >= 11
- @SuppressWarnings("deprecation")
- void startActivityNotification(Intent intent, int decisionId, String certName) {
- Notification n = new Notification(android.R.drawable.ic_lock_lock,
- master.getString(R.string.mtm_notification),
- System.currentTimeMillis());
- PendingIntent call = PendingIntent.getActivity(master, 0, intent, 0);
- n.setLatestEventInfo(master.getApplicationContext(),
- master.getString(R.string.mtm_notification),
- certName, call);
- n.flags |= Notification.FLAG_AUTO_CANCEL;
-
- notificationManager.notify(NOTIFICATION_ID + decisionId, n);
- }
-
/**
* Returns the top-most entry of the activity stack.
*
@@ -600,7 +732,6 @@ public class MemorizingTrustManager implements X509TrustManager {
getUI().startActivity(ni);
} catch (Exception e) {
LOGGER.log(Level.FINE, "startActivity(MemorizingActivity)", e);
- startActivityNotification(ni, myId, message);
}
}
});
@@ -711,22 +842,39 @@ public class MemorizingTrustManager implements X509TrustManager {
}
+ public X509TrustManager getNonInteractive(String domain) {
+ return new NonInteractiveMemorizingTrustManager(domain);
+ }
+
+ public X509TrustManager getInteractive(String domain) {
+ return new InteractiveMemorizingTrustManager(domain);
+ }
+
public X509TrustManager getNonInteractive() {
- return new NonInteractiveMemorizingTrustManager();
+ return new NonInteractiveMemorizingTrustManager(null);
+ }
+
+ public X509TrustManager getInteractive() {
+ return new InteractiveMemorizingTrustManager(null);
}
private class NonInteractiveMemorizingTrustManager implements X509TrustManager {
+ private final String domain;
+
+ public NonInteractiveMemorizingTrustManager(String domain) {
+ this.domain = domain;
+ }
+
@Override
- public void checkClientTrusted(X509Certificate[] chain, String authType)
- throws CertificateException {
- MemorizingTrustManager.this.checkCertTrusted(chain, authType, false, false);
+ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ MemorizingTrustManager.this.checkCertTrusted(chain, authType, domain, false, false);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
- MemorizingTrustManager.this.checkCertTrusted(chain, authType, true, false);
+ MemorizingTrustManager.this.checkCertTrusted(chain, authType, domain, true, false);
}
@Override
@@ -735,4 +883,28 @@ public class MemorizingTrustManager implements X509TrustManager {
}
}
+
+ private class InteractiveMemorizingTrustManager implements X509TrustManager {
+ private final String domain;
+
+ public InteractiveMemorizingTrustManager(String domain) {
+ this.domain = domain;
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ MemorizingTrustManager.this.checkCertTrusted(chain, authType, domain, false, true);
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ MemorizingTrustManager.this.checkCertTrusted(chain, authType, domain, true, true);
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return MemorizingTrustManager.this.getAcceptedIssuers();
+ }
+ }
}