diff options
author | Sam Whited <sam@samwhited.com> | 2014-10-28 12:11:51 -0400 |
---|---|---|
committer | Sam Whited <sam@samwhited.com> | 2014-10-28 12:11:51 -0400 |
commit | 41d2e72be7b36a7ee9a89e3f4a4ddec8b2e9c73e (patch) | |
tree | aec475fe700b3f7ef371d8310dd26943e440d107 /libs/MemorizingTrustManager/example/src/de/duenndns | |
parent | 89c294af11a22886b3e6be6ed0d6cfd98b3ea672 (diff) | |
parent | 9e42bff01440c1351946a432126d5a1b87fb7c78 (diff) |
Subtree merged in MemorizingTrustManager
Diffstat (limited to 'libs/MemorizingTrustManager/example/src/de/duenndns')
3 files changed, 312 insertions, 0 deletions
diff --git a/libs/MemorizingTrustManager b/libs/MemorizingTrustManager deleted file mode 160000 -Subproject fad835037adc1bd313bb56b694426fca4eb6734 diff --git a/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/JULHandler.java b/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/JULHandler.java new file mode 100644 index 000000000..40f71f580 --- /dev/null +++ b/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/JULHandler.java @@ -0,0 +1,169 @@ +package de.duenndns.mtmexample; + +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; + +import android.util.Log; + +/** + * 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 new file mode 100644 index 000000000..0d16ae82f --- /dev/null +++ b/libs/MemorizingTrustManager/example/src/de/duenndns/mtmexample/MTMExample.java @@ -0,0 +1,143 @@ +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.SSLContext; +import javax.net.ssl.HttpsURLConnection; +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(); + } +} |