From 7a1204e044c69aa9d5e1de6511f2bb8c9371aefe Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Sun, 31 Jan 2016 00:32:02 +0100
Subject: [PATCH] Added SSL Handling

---
 .../java/de/kuschku/libquassel/Client.java    |  17 +-
 .../de/kuschku/libquassel/CoreConnection.java |  46 +++--
 .../kuschku/libquassel/ProtocolHandler.java   |   6 +-
 .../events/CertificateAcceptedEvent.java      |  11 ++
 .../events/UnknownCertificateEvent.java       |  20 ++
 .../serializers/HeartbeatReplySerializer.java |   2 -
 .../libquassel/localtypes/QueryBuffer.java    |   6 +-
 .../backlogmanagers/BacklogFilter.java        |  16 +-
 .../backlogmanagers/BacklogManager.java       |   3 -
 .../backlogmanagers/SimpleBacklogManager.java |   4 +-
 .../serializers/MessageSerializer.java        |   2 +-
 .../libquassel/protocols/DatastreamPeer.java  |  14 +-
 .../libquassel/protocols/LegacyPeer.java      |   5 +-
 .../libquassel/ssl/CertificateManager.java    |  24 +++
 .../libquassel/ssl/QuasselTrustManager.java   |  70 +++++++
 .../ssl/UnknownCertificateException.java      |  16 ++
 .../serializers/AliasManagerSerializer.java   |   8 +-
 .../IgnoreListManagerSerializer.java          |   7 +-
 .../serializers/NetworkConfigSerializer.java  |   8 +-
 .../syncables/types/BufferSyncer.java         |   1 -
 .../syncables/types/BufferViewConfig.java     |   3 -
 .../syncables/types/BufferViewManager.java    |   1 -
 .../libquassel/syncables/types/Identity.java  |   2 -
 .../syncables/types/IgnoreListManager.java    |  45 +++--
 .../syncables/types/IrcChannel.java           |  29 ++-
 .../libquassel/syncables/types/IrcUser.java   | 123 ++++++------
 .../libquassel/syncables/types/Network.java   |  61 +++---
 .../syncables/types/NetworkConfig.java        |   1 -
 .../syncables/types/SyncableObject.java       |   5 +-
 .../service/ClientBackgroundThread.java       |  18 +-
 .../service/QuasselService.java               |   2 +-
 .../quasseldroid_ng/ui/chat/ChatActivity.java | 179 +++++++++---------
 .../ui/chat/chatview/ChatMessageRenderer.java |   8 -
 .../ui/chat/chatview/MessageAdapter.java      |  10 +-
 .../ui/chat/drawer/BufferItem.java            |  12 +-
 .../chat/drawer/BufferViewConfigWrapper.java  |   9 -
 .../ui/chat/drawer/NetworkItem.java           |   3 +-
 .../ui/editor/AdvancedEditor.java             |   5 +
 .../ui/editor/FormattingHelper.java           |   4 +-
 .../quasseldroid_ng/ui/theme/AppTheme.java    |  12 +-
 .../quasseldroid_ng/ui/theme/ThemeUtil.java   |   1 -
 .../java/de/kuschku/util/ServerAddress.java   |   4 +
 .../CertificateDatabaseHandler.java           | 152 +++++++++++++++
 .../util/certificates/CertificateUtils.java   |  56 ++++++
 .../SQLiteCertificateManager.java             |  67 +++++++
 .../de/kuschku/util/irc/IrcFormatHelper.java  |   1 -
 .../util/niohelpers/WrappedChannel.java       |  33 +++-
 .../util/observables/ContentComparable.java   |   1 +
 .../ChildParentObservableSortedList.java      |   1 -
 .../lists/ObservableSortedList.java           |  10 +-
 .../kuschku/util/ui/DateTimeFormatHelper.java |  32 ++--
 .../java/de/kuschku/util/ui/MessageUtil.java  |  12 +-
 52 files changed, 836 insertions(+), 352 deletions(-)
 create mode 100644 app/src/main/java/de/kuschku/libquassel/events/CertificateAcceptedEvent.java
 create mode 100644 app/src/main/java/de/kuschku/libquassel/events/UnknownCertificateEvent.java
 create mode 100644 app/src/main/java/de/kuschku/libquassel/ssl/CertificateManager.java
 create mode 100644 app/src/main/java/de/kuschku/libquassel/ssl/QuasselTrustManager.java
 create mode 100644 app/src/main/java/de/kuschku/libquassel/ssl/UnknownCertificateException.java
 create mode 100644 app/src/main/java/de/kuschku/util/certificates/CertificateDatabaseHandler.java
 create mode 100644 app/src/main/java/de/kuschku/util/certificates/CertificateUtils.java
 create mode 100644 app/src/main/java/de/kuschku/util/certificates/SQLiteCertificateManager.java

diff --git a/app/src/main/java/de/kuschku/libquassel/Client.java b/app/src/main/java/de/kuschku/libquassel/Client.java
index 62fe02b78..d942bf33a 100644
--- a/app/src/main/java/de/kuschku/libquassel/Client.java
+++ b/app/src/main/java/de/kuschku/libquassel/Client.java
@@ -10,17 +10,17 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import de.kuschku.libquassel.events.LagChangedEvent;
-import de.kuschku.libquassel.localtypes.NotificationManager;
-import de.kuschku.libquassel.localtypes.backlogmanagers.BacklogManager;
-import de.kuschku.libquassel.localtypes.backlogmanagers.SimpleBacklogManager;
 import de.kuschku.libquassel.events.ConnectionChangeEvent;
+import de.kuschku.libquassel.events.LagChangedEvent;
 import de.kuschku.libquassel.events.StatusMessageEvent;
 import de.kuschku.libquassel.functions.types.HandshakeFunction;
 import de.kuschku.libquassel.functions.types.InitRequestFunction;
 import de.kuschku.libquassel.functions.types.RpcCallFunction;
 import de.kuschku.libquassel.localtypes.Buffer;
 import de.kuschku.libquassel.localtypes.Buffers;
+import de.kuschku.libquassel.localtypes.NotificationManager;
+import de.kuschku.libquassel.localtypes.backlogmanagers.BacklogManager;
+import de.kuschku.libquassel.localtypes.backlogmanagers.SimpleBacklogManager;
 import de.kuschku.libquassel.message.Message;
 import de.kuschku.libquassel.objects.types.ClientInitAck;
 import de.kuschku.libquassel.objects.types.ClientLogin;
@@ -28,7 +28,6 @@ import de.kuschku.libquassel.objects.types.SessionState;
 import de.kuschku.libquassel.primitives.types.BufferInfo;
 import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.libquassel.syncables.types.BufferSyncer;
-import de.kuschku.libquassel.syncables.types.BufferViewConfig;
 import de.kuschku.libquassel.syncables.types.BufferViewManager;
 import de.kuschku.libquassel.syncables.types.Identity;
 import de.kuschku.libquassel.syncables.types.IgnoreListManager;
@@ -273,15 +272,15 @@ public class Client {
         return notificationManager;
     }
 
+    public long getLag() {
+        return lag;
+    }
+
     public void setLag(long l) {
         lag = l;
         busProvider.sendEvent(new LagChangedEvent(lag));
     }
 
-    public long getLag() {
-        return lag;
-    }
-
     public IgnoreListManager getIgnoreListManager() {
         return ignoreListManager;
     }
diff --git a/app/src/main/java/de/kuschku/libquassel/CoreConnection.java b/app/src/main/java/de/kuschku/libquassel/CoreConnection.java
index 71baec598..2ae9a6547 100644
--- a/app/src/main/java/de/kuschku/libquassel/CoreConnection.java
+++ b/app/src/main/java/de/kuschku/libquassel/CoreConnection.java
@@ -21,6 +21,7 @@ import java.util.concurrent.Executors;
 import de.kuschku.libquassel.events.ConnectionChangeEvent;
 import de.kuschku.libquassel.events.GeneralErrorEvent;
 import de.kuschku.libquassel.events.HandshakeFailedEvent;
+import de.kuschku.libquassel.events.UnknownCertificateEvent;
 import de.kuschku.libquassel.functions.types.HandshakeFunction;
 import de.kuschku.libquassel.functions.types.Heartbeat;
 import de.kuschku.libquassel.objects.types.ClientInit;
@@ -30,6 +31,8 @@ import de.kuschku.libquassel.primitives.types.Protocol;
 import de.kuschku.libquassel.protocols.DatastreamPeer;
 import de.kuschku.libquassel.protocols.LegacyPeer;
 import de.kuschku.libquassel.protocols.RemotePeer;
+import de.kuschku.libquassel.ssl.CertificateManager;
+import de.kuschku.libquassel.ssl.UnknownCertificateException;
 import de.kuschku.util.ServerAddress;
 import de.kuschku.util.niohelpers.WrappedChannel;
 
@@ -66,11 +69,14 @@ public class CoreConnection {
     private ConnectionChangeEvent.Status status = ConnectionChangeEvent.Status.DISCONNECTED;
     @Nullable
     private Client client;
+    @NonNull
+    private CertificateManager certificateManager;
 
-    public CoreConnection(@NonNull final ServerAddress address, @NonNull final ClientData clientData, @NonNull final BusProvider busProvider) {
+    public CoreConnection(@NonNull final ServerAddress address, @NonNull final ClientData clientData, @NonNull final BusProvider busProvider, @NonNull CertificateManager certificateManager) {
         this.address = address;
         this.clientData = clientData;
         this.busProvider = busProvider;
+        this.certificateManager = certificateManager;
     }
 
     @NonNull
@@ -107,10 +113,8 @@ public class CoreConnection {
 
     /**
      * Closes the connection and interrupts all threads this connection has spawned.
-     *
-     * @throws IOException
      */
-    public void close() throws IOException {
+    public void close() {
         assertNotNull(client);
 
         client.setConnectionStatus(ConnectionChangeEvent.Status.DISCONNECTED);
@@ -121,8 +125,12 @@ public class CoreConnection {
         if (outputExecutor != null) outputExecutor.shutdownNow();
 
         // Which we do exactly here
-        if (channel != null) channel.close();
-        if (socket != null) socket.close();
+        try {
+            if (channel != null) channel.close();
+            if (socket != null) socket.close();
+        } catch (Exception e) {
+            // We won’t report these issues, as these don’t matter to us anyway anymore
+        }
     }
 
     @Nullable
@@ -177,11 +185,7 @@ public class CoreConnection {
     }
 
     public void onEventAsync(HandshakeFailedEvent event) {
-        try {
-            this.close();
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
+        this.close();
     }
 
     public void onEventAsync(@NonNull ConnectionChangeEvent event) {
@@ -189,7 +193,23 @@ public class CoreConnection {
     }
 
     public void setCompression(boolean supportsCompression) {
-        if (supportsCompression) channel = WrappedChannel.withCompression(getChannel());
+        if (supportsCompression)
+            channel = WrappedChannel.withCompression(getChannel());
+    }
+
+    private void setSSL(boolean supportsSSL) {
+        if (supportsSSL) {
+            try {
+                channel = WrappedChannel.withSSL(getChannel(), certificateManager, address);
+            } catch (Exception e) {
+                if (e.getCause() instanceof UnknownCertificateException) {
+                    busProvider.sendEvent(new UnknownCertificateEvent((UnknownCertificateException) e.getCause()));
+                } else {
+                    busProvider.sendEvent(new GeneralErrorEvent(e));
+                }
+                close();
+            }
+        }
     }
 
     public void setClient(@NonNull Client client) {
@@ -220,6 +240,8 @@ public class CoreConnection {
 
                         final Protocol protocol = ProtocolSerializer.get().deserialize(buffer);
 
+                        // Wrap socket in SSL context if ssl is enabled
+                        setSSL(protocol.protocolFlags.supportsSSL);
                         // Wrap socket in deflater if compression is enabled
                         setCompression(protocol.protocolFlags.supportsCompression);
 
diff --git a/app/src/main/java/de/kuschku/libquassel/ProtocolHandler.java b/app/src/main/java/de/kuschku/libquassel/ProtocolHandler.java
index 82bfaae07..d94d35666 100644
--- a/app/src/main/java/de/kuschku/libquassel/ProtocolHandler.java
+++ b/app/src/main/java/de/kuschku/libquassel/ProtocolHandler.java
@@ -5,14 +5,11 @@ import android.util.Log;
 
 import org.joda.time.DateTime;
 
-import java.util.List;
-
 import de.kuschku.libquassel.events.ConnectionChangeEvent;
 import de.kuschku.libquassel.events.GeneralErrorEvent;
 import de.kuschku.libquassel.events.HandshakeFailedEvent;
 import de.kuschku.libquassel.events.LoginFailedEvent;
 import de.kuschku.libquassel.events.LoginSuccessfulEvent;
-import de.kuschku.libquassel.exceptions.UnknownTypeException;
 import de.kuschku.libquassel.functions.types.Heartbeat;
 import de.kuschku.libquassel.functions.types.HeartbeatReply;
 import de.kuschku.libquassel.functions.types.InitDataFunction;
@@ -26,7 +23,6 @@ import de.kuschku.libquassel.objects.types.ClientLoginReject;
 import de.kuschku.libquassel.objects.types.SessionInit;
 import de.kuschku.libquassel.primitives.types.BufferInfo;
 import de.kuschku.libquassel.syncables.SyncableRegistry;
-import de.kuschku.libquassel.syncables.types.BufferViewConfig;
 import de.kuschku.libquassel.syncables.types.Identity;
 import de.kuschku.libquassel.syncables.types.SyncableObject;
 import de.kuschku.util.AndroidAssert;
@@ -89,7 +85,7 @@ public class ProtocolHandler implements IProtocolHandler {
     public void onEventMainThread(@NonNull SyncFunction packedFunc) {
         try {
             final Object syncable = client.getObjectByIdentifier(packedFunc.className, packedFunc.objectName);
-            AndroidAssert.assertNotNull("Object not found: " + packedFunc.className+":"+packedFunc.objectName, syncable);
+            AndroidAssert.assertNotNull("Object not found: " + packedFunc.className + ":" + packedFunc.objectName, syncable);
             ReflectionUtils.invokeMethod(syncable, packedFunc.methodName, packedFunc.params);
         } catch (Exception e) {
             busProvider.sendEvent(new GeneralErrorEvent(e, packedFunc.toString()));
diff --git a/app/src/main/java/de/kuschku/libquassel/events/CertificateAcceptedEvent.java b/app/src/main/java/de/kuschku/libquassel/events/CertificateAcceptedEvent.java
new file mode 100644
index 000000000..d654aeadb
--- /dev/null
+++ b/app/src/main/java/de/kuschku/libquassel/events/CertificateAcceptedEvent.java
@@ -0,0 +1,11 @@
+package de.kuschku.libquassel.events;
+
+import java.security.cert.X509Certificate;
+
+public class CertificateAcceptedEvent {
+    public final X509Certificate certificate;
+
+    public CertificateAcceptedEvent(X509Certificate certificate) {
+        this.certificate = certificate;
+    }
+}
diff --git a/app/src/main/java/de/kuschku/libquassel/events/UnknownCertificateEvent.java b/app/src/main/java/de/kuschku/libquassel/events/UnknownCertificateEvent.java
new file mode 100644
index 000000000..93a6e7883
--- /dev/null
+++ b/app/src/main/java/de/kuschku/libquassel/events/UnknownCertificateEvent.java
@@ -0,0 +1,20 @@
+package de.kuschku.libquassel.events;
+
+import java.security.cert.X509Certificate;
+
+import de.kuschku.libquassel.ssl.UnknownCertificateException;
+import de.kuschku.util.ServerAddress;
+
+public class UnknownCertificateEvent {
+    public final X509Certificate certificate;
+    public final ServerAddress address;
+
+    public UnknownCertificateEvent(X509Certificate certificate, ServerAddress address) {
+        this.certificate = certificate;
+        this.address = address;
+    }
+
+    public UnknownCertificateEvent(UnknownCertificateException cause) {
+        this(cause.certificate, cause.address);
+    }
+}
diff --git a/app/src/main/java/de/kuschku/libquassel/functions/serializers/HeartbeatReplySerializer.java b/app/src/main/java/de/kuschku/libquassel/functions/serializers/HeartbeatReplySerializer.java
index ffc10823f..881016095 100644
--- a/app/src/main/java/de/kuschku/libquassel/functions/serializers/HeartbeatReplySerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/functions/serializers/HeartbeatReplySerializer.java
@@ -5,11 +5,9 @@ import android.support.annotation.NonNull;
 import org.joda.time.DateTime;
 
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 
 import de.kuschku.libquassel.functions.FunctionType;
-import de.kuschku.libquassel.functions.types.Heartbeat;
 import de.kuschku.libquassel.functions.types.HeartbeatReply;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/QueryBuffer.java b/app/src/main/java/de/kuschku/libquassel/localtypes/QueryBuffer.java
index c07356b14..64c6bcc8c 100644
--- a/app/src/main/java/de/kuschku/libquassel/localtypes/QueryBuffer.java
+++ b/app/src/main/java/de/kuschku/libquassel/localtypes/QueryBuffer.java
@@ -31,9 +31,9 @@ public class QueryBuffer implements Buffer {
 
     @Override
     public BufferInfo.BufferStatus getStatus() {
-        return  (user == null) ?    BufferInfo.BufferStatus.OFFLINE :
-                (user.isAway()) ?   BufferInfo.BufferStatus.AWAY :
-                                    BufferInfo.BufferStatus.ONLINE;
+        return (user == null) ? BufferInfo.BufferStatus.OFFLINE :
+                (user.isAway()) ? BufferInfo.BufferStatus.AWAY :
+                        BufferInfo.BufferStatus.ONLINE;
     }
 
     @Nullable
diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/BacklogFilter.java b/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/BacklogFilter.java
index d36073bf8..90a005a47 100644
--- a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/BacklogFilter.java
+++ b/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/BacklogFilter.java
@@ -136,6 +136,14 @@ public class BacklogFilter implements UICallback {
         }
     }
 
+    public int getFilters() {
+        int filters = 0x00000000;
+        for (Message.Type type : filteredTypes) {
+            filters |= type.value;
+        }
+        return filters;
+    }
+
     public void setFilters(int filters) {
         Set<Message.Type> removed = new HashSet<>();
         for (Message.Type type : filteredTypes) {
@@ -152,12 +160,4 @@ public class BacklogFilter implements UICallback {
             }
         }
     }
-
-    public int getFilters() {
-        int filters = 0x00000000;
-        for (Message.Type type : filteredTypes) {
-            filters |= type.value;
-        }
-        return filters;
-    }
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/BacklogManager.java b/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/BacklogManager.java
index df71178de..6e10fd5d4 100644
--- a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/BacklogManager.java
+++ b/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/BacklogManager.java
@@ -2,15 +2,12 @@ package de.kuschku.libquassel.localtypes.backlogmanagers;
 
 import android.support.annotation.IntRange;
 import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v7.widget.RecyclerView;
 
 import java.util.List;
 
 import de.kuschku.libquassel.Client;
 import de.kuschku.libquassel.message.Message;
 import de.kuschku.libquassel.syncables.types.SyncableObject;
-import de.kuschku.util.observables.AutoScroller;
 import de.kuschku.util.observables.lists.ObservableSortedList;
 
 public abstract class BacklogManager<T extends BacklogManager<T>> extends SyncableObject<T> {
diff --git a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/SimpleBacklogManager.java b/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/SimpleBacklogManager.java
index e8236cc7a..c3bd0e981 100644
--- a/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/SimpleBacklogManager.java
+++ b/app/src/main/java/de/kuschku/libquassel/localtypes/backlogmanagers/SimpleBacklogManager.java
@@ -83,8 +83,8 @@ public class SimpleBacklogManager extends BacklogManager<SimpleBacklogManager> {
         ObservableSortedList<Message> backlog = backlogs.get(bufferId);
         int messageId =
                 (backlog == null) ? -1 :
-                (backlog.last() == null) ? -1 :
-                backlog.last().messageId;
+                        (backlog.last() == null) ? -1 :
+                                backlog.last().messageId;
 
         requestBacklog(bufferId, -1, messageId, count, 0);
     }
diff --git a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/MessageSerializer.java b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/MessageSerializer.java
index abe83b399..a5a0870a3 100644
--- a/app/src/main/java/de/kuschku/libquassel/primitives/serializers/MessageSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/primitives/serializers/MessageSerializer.java
@@ -3,7 +3,6 @@ package de.kuschku.libquassel.primitives.serializers;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 
-import de.kuschku.libquassel.primitives.types.BufferInfo;
 import org.joda.time.DateTime;
 
 import java.io.IOException;
@@ -11,6 +10,7 @@ import java.nio.ByteBuffer;
 import java.nio.channels.ByteChannel;
 
 import de.kuschku.libquassel.message.Message;
+import de.kuschku.libquassel.primitives.types.BufferInfo;
 
 import static de.kuschku.util.AndroidAssert.assertNotNull;
 
diff --git a/app/src/main/java/de/kuschku/libquassel/protocols/DatastreamPeer.java b/app/src/main/java/de/kuschku/libquassel/protocols/DatastreamPeer.java
index ed1322d3f..46edc8cc6 100644
--- a/app/src/main/java/de/kuschku/libquassel/protocols/DatastreamPeer.java
+++ b/app/src/main/java/de/kuschku/libquassel/protocols/DatastreamPeer.java
@@ -2,7 +2,6 @@ package de.kuschku.libquassel.protocols;
 
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.util.Log;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Lists;
@@ -14,11 +13,9 @@ import java.nio.BufferOverflowException;
 import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
@@ -52,6 +49,7 @@ import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.util.niohelpers.Helper;
 import de.kuschku.util.niohelpers.WrappedChannel;
 
+import static de.kuschku.util.AndroidAssert.assertFalse;
 import static de.kuschku.util.AndroidAssert.assertNotNull;
 
 /**
@@ -120,6 +118,7 @@ public class DatastreamPeer implements RemotePeer {
 
     public void onEventBackgroundThread(@NonNull SyncFunction func) {
         assertNotNull(connection.getOutputExecutor());
+        assertFalse(connection.getOutputExecutor().isShutdown());
         connection.getOutputExecutor().submit(new OutputRunnable<>(
                 VariantVariantListSerializer.<SyncFunction>get(),
                 UnpackedSyncFunctionSerializer.get().serialize(func)
@@ -128,6 +127,7 @@ public class DatastreamPeer implements RemotePeer {
 
     public void onEventBackgroundThread(@NonNull RpcCallFunction func) {
         assertNotNull(connection.getOutputExecutor());
+        assertFalse(connection.getOutputExecutor().isShutdown());
         connection.getOutputExecutor().submit(new OutputRunnable<>(
                 VariantVariantListSerializer.<RpcCallFunction>get(),
                 UnpackedRpcCallFunctionSerializer.get().serialize(func)
@@ -136,6 +136,7 @@ public class DatastreamPeer implements RemotePeer {
 
     public void onEventBackgroundThread(@NonNull InitRequestFunction func) {
         assertNotNull(connection.getOutputExecutor());
+        assertFalse(connection.getOutputExecutor().isShutdown());
         connection.getOutputExecutor().submit(new OutputRunnable<>(
                 VariantVariantListSerializer.<InitRequestFunction>get(),
                 InitRequestFunctionSerializer.get().serializePacked(func)
@@ -144,6 +145,7 @@ public class DatastreamPeer implements RemotePeer {
 
     public void onEventBackgroundThread(@NonNull InitDataFunction func) {
         assertNotNull(connection.getOutputExecutor());
+        assertFalse(connection.getOutputExecutor().isShutdown());
         connection.getOutputExecutor().submit(new OutputRunnable<>(
                 VariantVariantListSerializer.<InitDataFunction>get(),
                 InitDataFunctionSerializer.get().serialize(func)
@@ -152,6 +154,7 @@ public class DatastreamPeer implements RemotePeer {
 
     public void onEventBackgroundThread(@NonNull Heartbeat func) {
         assertNotNull(connection.getOutputExecutor());
+        assertFalse(connection.getOutputExecutor().isShutdown());
         connection.getOutputExecutor().submit(new OutputRunnable<>(
                 VariantVariantListSerializer.<InitDataFunction>get(),
                 HeartbeatSerializer.get().serialize(func)
@@ -160,6 +163,7 @@ public class DatastreamPeer implements RemotePeer {
 
     public void onEventBackgroundThread(@NonNull HeartbeatReply func) {
         assertNotNull(connection.getOutputExecutor());
+        assertFalse(connection.getOutputExecutor().isShutdown());
         connection.getOutputExecutor().submit(new OutputRunnable<>(
                 VariantVariantListSerializer.<InitDataFunction>get(),
                 HeartbeatReplySerializer.get().serialize(func)
@@ -168,6 +172,7 @@ public class DatastreamPeer implements RemotePeer {
 
     public void onEventBackgroundThread(@NonNull HandshakeFunction func) {
         assertNotNull(connection.getOutputExecutor());
+        assertFalse(connection.getOutputExecutor().isShutdown());
         Map<String, QVariant> variantMap = MessageTypeRegistry.toVariantMap(func.data).data;
         assertNotNull(variantMap);
         connection.getOutputExecutor().submit(new OutputRunnable<>(
@@ -273,6 +278,7 @@ public class DatastreamPeer implements RemotePeer {
 
     private class ParseRunnable implements Runnable {
         ByteBuffer buffer;
+
         public ParseRunnable(ByteBuffer buffer) {
             this.buffer = buffer;
         }
@@ -291,7 +297,7 @@ public class DatastreamPeer implements RemotePeer {
                 } else {
                     handlePackedFunc(data);
                 }
-            } catch (BufferUnderflowException|BufferOverflowException e) {
+            } catch (BufferUnderflowException | BufferOverflowException e) {
                 Helper.printHexDump(buffer.array());
                 busProvider.sendEvent(new GeneralErrorEvent(e));
             } catch (Exception e) {
diff --git a/app/src/main/java/de/kuschku/libquassel/protocols/LegacyPeer.java b/app/src/main/java/de/kuschku/libquassel/protocols/LegacyPeer.java
index 603fdd0c1..1d88e0ccd 100644
--- a/app/src/main/java/de/kuschku/libquassel/protocols/LegacyPeer.java
+++ b/app/src/main/java/de/kuschku/libquassel/protocols/LegacyPeer.java
@@ -13,7 +13,6 @@ import java.util.concurrent.Executors;
 
 import de.kuschku.libquassel.BusProvider;
 import de.kuschku.libquassel.CoreConnection;
-import de.kuschku.libquassel.events.ConnectionChangeEvent;
 import de.kuschku.libquassel.events.GeneralErrorEvent;
 import de.kuschku.libquassel.functions.FunctionType;
 import de.kuschku.libquassel.functions.serializers.HeartbeatReplySerializer;
@@ -37,12 +36,11 @@ import de.kuschku.libquassel.primitives.serializers.PrimitiveSerializer;
 import de.kuschku.libquassel.primitives.serializers.VariantSerializer;
 import de.kuschku.libquassel.primitives.serializers.VariantVariantListSerializer;
 import de.kuschku.libquassel.primitives.types.QVariant;
-import de.kuschku.util.AndroidAssert;
 import de.kuschku.util.niohelpers.WrappedChannel;
 
 import static de.kuschku.libquassel.primitives.QMetaType.Type.QVariantList;
 import static de.kuschku.libquassel.primitives.QMetaType.Type.QVariantMap;
-import static de.kuschku.util.AndroidAssert.*;
+import static de.kuschku.util.AndroidAssert.assertNotNull;
 
 /**
  * A helper class processing incoming and outgoing messages.
@@ -175,6 +173,7 @@ public class LegacyPeer implements RemotePeer {
 
     private class ParseRunnable implements Runnable {
         ByteBuffer buffer;
+
         public ParseRunnable(ByteBuffer buffer) {
             this.buffer = buffer;
         }
diff --git a/app/src/main/java/de/kuschku/libquassel/ssl/CertificateManager.java b/app/src/main/java/de/kuschku/libquassel/ssl/CertificateManager.java
new file mode 100644
index 000000000..dc9e947a3
--- /dev/null
+++ b/app/src/main/java/de/kuschku/libquassel/ssl/CertificateManager.java
@@ -0,0 +1,24 @@
+package de.kuschku.libquassel.ssl;
+
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import de.kuschku.util.ServerAddress;
+
+public interface CertificateManager {
+    boolean isTrusted(X509Certificate certificate, ServerAddress core);
+
+    boolean addCertificate(X509Certificate certificate, ServerAddress core);
+
+    boolean removeCertificate(X509Certificate certificate, ServerAddress core);
+
+    boolean removeAllCertificates(ServerAddress core);
+
+    List<String> findCertificates(ServerAddress core);
+
+    Map<String, Collection<String>> findAllCertificates();
+
+    void checkTrusted(X509Certificate certificate, ServerAddress address) throws UnknownCertificateException;
+}
diff --git a/app/src/main/java/de/kuschku/libquassel/ssl/QuasselTrustManager.java b/app/src/main/java/de/kuschku/libquassel/ssl/QuasselTrustManager.java
new file mode 100644
index 000000000..bcff02275
--- /dev/null
+++ b/app/src/main/java/de/kuschku/libquassel/ssl/QuasselTrustManager.java
@@ -0,0 +1,70 @@
+package de.kuschku.libquassel.ssl;
+
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import de.kuschku.util.ServerAddress;
+import de.kuschku.util.certificates.CertificateUtils;
+
+public class QuasselTrustManager implements X509TrustManager {
+    @NonNull
+    private final X509TrustManager wrapped;
+    @NonNull
+    private final CertificateManager certificateManager;
+    @NonNull
+    private ServerAddress address;
+
+    public QuasselTrustManager(@NonNull X509TrustManager wrapped, @NonNull CertificateManager certificateManager, @NonNull ServerAddress address) {
+        this.wrapped = wrapped;
+        this.certificateManager = certificateManager;
+        this.address = address;
+    }
+
+    public static QuasselTrustManager fromFactory(@NonNull TrustManagerFactory factory, @NonNull CertificateManager certificateManager, @NonNull ServerAddress address) throws GeneralSecurityException {
+        TrustManager[] managers = factory.getTrustManagers();
+        for (TrustManager manager : managers) {
+            if (manager instanceof X509TrustManager) {
+                return new QuasselTrustManager((X509TrustManager) manager, certificateManager, address);
+            }
+        }
+        throw new GeneralSecurityException("Couldn’t find trustmanager provided by factory");
+    }
+
+    public static QuasselTrustManager fromDefault(@NonNull CertificateManager certificateManager, @NonNull ServerAddress address) throws GeneralSecurityException {
+        TrustManagerFactory factory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+        factory.init((KeyStore) null);
+        return fromFactory(factory, certificateManager, address);
+    }
+
+    @Override
+    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+        wrapped.checkClientTrusted(chain, authType);
+    }
+
+    @Override
+    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+        try {
+            wrapped.checkServerTrusted(chain, authType);
+            chain[0].checkValidity();
+            if (!CertificateUtils.getHostnames(chain[0]).contains(address.host))
+                throw new CertificateException("Hostname not in certificate");
+        } catch (CertificateException e) {
+            certificateManager.checkTrusted(chain[0], address);
+        }
+    }
+
+    @Override
+    public X509Certificate[] getAcceptedIssuers() {
+        return wrapped.getAcceptedIssuers();
+    }
+}
diff --git a/app/src/main/java/de/kuschku/libquassel/ssl/UnknownCertificateException.java b/app/src/main/java/de/kuschku/libquassel/ssl/UnknownCertificateException.java
new file mode 100644
index 000000000..bec64bc1d
--- /dev/null
+++ b/app/src/main/java/de/kuschku/libquassel/ssl/UnknownCertificateException.java
@@ -0,0 +1,16 @@
+package de.kuschku.libquassel.ssl;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import de.kuschku.util.ServerAddress;
+
+public class UnknownCertificateException extends CertificateException {
+    public final X509Certificate certificate;
+    public final ServerAddress address;
+
+    public UnknownCertificateException(X509Certificate certificate, ServerAddress address) {
+        this.certificate = certificate;
+        this.address = address;
+    }
+}
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/AliasManagerSerializer.java b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/AliasManagerSerializer.java
index 2e089880b..9aab84c3f 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/AliasManagerSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/AliasManagerSerializer.java
@@ -17,15 +17,15 @@ public class AliasManagerSerializer implements ObjectSerializer<AliasManager> {
     @NonNull
     private static final AliasManagerSerializer serializer = new AliasManagerSerializer();
 
+    private AliasManagerSerializer() {
+
+    }
+
     @NonNull
     public static AliasManagerSerializer get() {
         return serializer;
     }
 
-    private AliasManagerSerializer() {
-
-    }
-
     @Nullable
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(@NonNull AliasManager data) {
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/IgnoreListManagerSerializer.java b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/IgnoreListManagerSerializer.java
index 2a114c537..dcd8885a5 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/IgnoreListManagerSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/IgnoreListManagerSerializer.java
@@ -15,14 +15,15 @@ import de.kuschku.libquassel.syncables.types.IgnoreListManager;
 
 public class IgnoreListManagerSerializer implements ObjectSerializer<IgnoreListManager> {
     private static IgnoreListManagerSerializer serializer = new IgnoreListManagerSerializer();
-    public static IgnoreListManagerSerializer get() {
-        return serializer;
-    }
 
     private IgnoreListManagerSerializer() {
 
     }
 
+    public static IgnoreListManagerSerializer get() {
+        return serializer;
+    }
+
     @Nullable
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(@NonNull IgnoreListManager data) {
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/NetworkConfigSerializer.java b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/NetworkConfigSerializer.java
index 277b08eaf..a0640ae66 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/serializers/NetworkConfigSerializer.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/serializers/NetworkConfigSerializer.java
@@ -11,18 +11,18 @@ import de.kuschku.libquassel.functions.types.UnpackedFunction;
 import de.kuschku.libquassel.objects.serializers.ObjectSerializer;
 import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.libquassel.syncables.types.NetworkConfig;
-import de.kuschku.libquassel.syncables.types.SyncableObject;
 
 public class NetworkConfigSerializer implements ObjectSerializer<NetworkConfig> {
     private static NetworkConfigSerializer serializer = new NetworkConfigSerializer();
-    public static NetworkConfigSerializer get() {
-        return serializer;
-    }
 
     private NetworkConfigSerializer() {
 
     }
 
+    public static NetworkConfigSerializer get() {
+        return serializer;
+    }
+
     @Nullable
     @Override
     public QVariant<Map<String, QVariant>> toVariantMap(@NonNull NetworkConfig data) {
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/BufferSyncer.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/BufferSyncer.java
index 40cae47dc..1e23c7246 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/BufferSyncer.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/BufferSyncer.java
@@ -11,7 +11,6 @@ import de.kuschku.libquassel.Client;
 import de.kuschku.libquassel.functions.types.InitDataFunction;
 import de.kuschku.libquassel.message.Message;
 import de.kuschku.libquassel.primitives.types.QVariant;
-import de.kuschku.libquassel.syncables.serializers.AliasManagerSerializer;
 import de.kuschku.libquassel.syncables.serializers.BufferSyncerSerializer;
 import de.kuschku.util.observables.lists.ObservableSortedList;
 
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/BufferViewConfig.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/BufferViewConfig.java
index 177f3d878..45aefb051 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/BufferViewConfig.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/BufferViewConfig.java
@@ -1,9 +1,7 @@
 package de.kuschku.libquassel.syncables.types;
 
 import android.support.annotation.NonNull;
-import android.util.Log;
 
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -290,7 +288,6 @@ public class BufferViewConfig extends SyncableObject<BufferViewConfig> {
     }
 
 
-
     @NonNull
     public IObservableList<ElementCallback<Integer>, Integer> getNetworkList() {
         return NetworkList;
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/BufferViewManager.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/BufferViewManager.java
index c6e927881..8c329eefa 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/BufferViewManager.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/BufferViewManager.java
@@ -10,7 +10,6 @@ import de.kuschku.libquassel.BusProvider;
 import de.kuschku.libquassel.Client;
 import de.kuschku.libquassel.functions.types.InitDataFunction;
 import de.kuschku.libquassel.primitives.types.QVariant;
-import de.kuschku.libquassel.syncables.serializers.BufferSyncerSerializer;
 import de.kuschku.libquassel.syncables.serializers.BufferViewManagerSerializer;
 
 public class BufferViewManager extends SyncableObject<BufferViewManager> {
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/Identity.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/Identity.java
index 49406768c..85d18f607 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/Identity.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/Identity.java
@@ -2,7 +2,6 @@ package de.kuschku.libquassel.syncables.types;
 
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.util.Log;
 
 import java.util.List;
 import java.util.Map;
@@ -12,7 +11,6 @@ import de.kuschku.libquassel.Client;
 import de.kuschku.libquassel.functions.types.InitDataFunction;
 import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.libquassel.syncables.Syncable;
-import de.kuschku.libquassel.syncables.serializers.BufferSyncerSerializer;
 import de.kuschku.libquassel.syncables.serializers.IdentitySerializer;
 
 public class Identity extends SyncableObject<Identity> {
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/IgnoreListManager.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/IgnoreListManager.java
index 8904263a1..876c9e565 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/IgnoreListManager.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/IgnoreListManager.java
@@ -11,7 +11,6 @@ import de.kuschku.libquassel.Client;
 import de.kuschku.libquassel.functions.types.InitDataFunction;
 import de.kuschku.libquassel.message.Message;
 import de.kuschku.libquassel.primitives.types.QVariant;
-import de.kuschku.libquassel.syncables.serializers.BufferSyncerSerializer;
 import de.kuschku.libquassel.syncables.serializers.IgnoreListManagerSerializer;
 
 import static de.kuschku.util.AndroidAssert.assertEquals;
@@ -22,7 +21,7 @@ public class IgnoreListManager extends SyncableObject<IgnoreListManager> {
     public IgnoreListManager(List<Integer> scope, List<Integer> ignoreType,
                              List<Boolean> isActive, List<String> scopeRule, List<Boolean> isRegEx,
                              List<Integer> strictness, List<String> ignoreRule) {
-        assertEquals(scope.size(),ignoreType.size(), isActive.size(),scopeRule.size(), isRegEx.size(), strictness.size(), ignoreRule.size());
+        assertEquals(scope.size(), ignoreType.size(), isActive.size(), scopeRule.size(), isRegEx.size(), strictness.size(), ignoreRule.size());
 
         for (int i = 0; i < scope.size(); i++) {
             ignoreRules.add(new IgnoreRule(
@@ -94,15 +93,21 @@ public class IgnoreListManager extends SyncableObject<IgnoreListManager> {
             HARD(2);
 
             public final int id;
+
             Strictness(int id) {
                 this.id = id;
             }
+
             public static Strictness of(int id) {
                 switch (id) {
-                    case 0: return UNMATCHED;
-                    case 1: return SOFT;
-                    case 2: return HARD;
-                    default: return INVALID;
+                    case 0:
+                        return UNMATCHED;
+                    case 1:
+                        return SOFT;
+                    case 2:
+                        return HARD;
+                    default:
+                        return INVALID;
                 }
             }
         }
@@ -114,15 +119,21 @@ public class IgnoreListManager extends SyncableObject<IgnoreListManager> {
             CTCP_IGNORE(2);
 
             public final int id;
+
             Type(int id) {
                 this.id = id;
             }
+
             public static Type of(int id) {
                 switch (id) {
-                    case 0: return SENDER_IGNORE;
-                    case 1: return MESSAGE_IGNORE;
-                    case 2: return CTCP_IGNORE;
-                    default: return INVALID;
+                    case 0:
+                        return SENDER_IGNORE;
+                    case 1:
+                        return MESSAGE_IGNORE;
+                    case 2:
+                        return CTCP_IGNORE;
+                    default:
+                        return INVALID;
                 }
             }
         }
@@ -134,15 +145,21 @@ public class IgnoreListManager extends SyncableObject<IgnoreListManager> {
             CHANNEL_SCOPE(2);
 
             public final int id;
+
             Scope(int id) {
                 this.id = id;
             }
+
             public static Scope of(int id) {
                 switch (id) {
-                    case 0: return GLOBAL_SCOPE;
-                    case 1: return NETWORK_SCOPE;
-                    case 2: return CHANNEL_SCOPE;
-                    default: return INVALID;
+                    case 0:
+                        return GLOBAL_SCOPE;
+                    case 1:
+                        return NETWORK_SCOPE;
+                    case 2:
+                        return CHANNEL_SCOPE;
+                    default:
+                        return INVALID;
                 }
             }
         }
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/IrcChannel.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/IrcChannel.java
index ab47a9728..5a1b8e9b7 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/IrcChannel.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/IrcChannel.java
@@ -11,7 +11,6 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 
 import de.kuschku.libquassel.BusProvider;
 import de.kuschku.libquassel.Client;
@@ -19,17 +18,22 @@ import de.kuschku.libquassel.functions.types.InitDataFunction;
 import de.kuschku.libquassel.primitives.types.QVariant;
 import de.kuschku.libquassel.syncables.Synced;
 import de.kuschku.libquassel.syncables.serializers.IrcChannelSerializer;
-import de.kuschku.util.AndroidAssert;
 
-import static de.kuschku.util.AndroidAssert.*;
+import static de.kuschku.util.AndroidAssert.assertNotNull;
 
 public class IrcChannel extends SyncableObject<IrcChannel> {
-    @Synced private String name;
-    @Synced private String topic;
-    @Synced private String password;
-    @Synced private Map<String, String> UserModes;
-    @Synced private Map<String, Object> ChanModes;
-    @Synced private boolean encrypted;
+    @Synced
+    private String name;
+    @Synced
+    private String topic;
+    @Synced
+    private String password;
+    @Synced
+    private Map<String, String> UserModes;
+    @Synced
+    private Map<String, Object> ChanModes;
+    @Synced
+    private boolean encrypted;
 
     @Nullable
     private Network network;
@@ -128,9 +132,11 @@ public class IrcChannel extends SyncableObject<IrcChannel> {
     public void addChannelMode(Character mode, String params) {
         addChannelMode(String.copyValueOf(new char[]{mode}), params);
     }
+
     public void addChannelMode(char mode, String params) {
         addChannelMode(String.copyValueOf(new char[]{mode}), params);
     }
+
     public void addChannelMode(String mode, String params) {
         assertNotNull(network);
 
@@ -157,12 +163,15 @@ public class IrcChannel extends SyncableObject<IrcChannel> {
                 break;
         }
     }
+
     public void removeChannelMode(Character mode, String params) {
         removeChannelMode(String.copyValueOf(new char[]{mode}), params);
     }
+
     public void removeChannelMode(char mode, String params) {
         removeChannelMode(String.copyValueOf(new char[]{mode}), params);
     }
+
     public void removeChannelMode(String mode, String params) {
         assertNotNull(network);
 
@@ -189,9 +198,11 @@ public class IrcChannel extends SyncableObject<IrcChannel> {
     public boolean hasMode(Character mode) {
         return hasMode(String.copyValueOf(new char[]{mode}));
     }
+
     public boolean hasMode(char mode) {
         return hasMode(String.copyValueOf(new char[]{mode}));
     }
+
     public boolean hasMode(String mode) {
         assertNotNull(network);
 
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/IrcUser.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/IrcUser.java
index 2aa1d62ab..e38da03c3 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/IrcUser.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/IrcUser.java
@@ -12,7 +12,6 @@ import de.kuschku.libquassel.BusProvider;
 import de.kuschku.libquassel.Client;
 import de.kuschku.libquassel.functions.types.InitDataFunction;
 import de.kuschku.libquassel.primitives.types.QVariant;
-import de.kuschku.libquassel.syncables.serializers.BufferSyncerSerializer;
 import de.kuschku.libquassel.syncables.serializers.IrcUserSerializer;
 
 import static de.kuschku.util.AndroidAssert.assertNotNull;
@@ -64,129 +63,129 @@ public class IrcUser extends SyncableObject<IrcUser> {
         return server;
     }
 
-    public String getIrcOperator() {
-        return ircOperator;
+    public void setServer(String server) {
+        this.server = server;
     }
 
-    public boolean isAway() {
-        return away;
+    public String getIrcOperator() {
+        return ircOperator;
     }
 
-    public int getLastAwayMessage() {
-        return lastAwayMessage;
+    public void setIrcOperator(String ircOperator) {
+        this.ircOperator = ircOperator;
     }
 
-    public DateTime getIdleTime() {
-        return idleTime;
+    public boolean isAway() {
+        return away;
     }
 
-    public String getWhoisServiceReply() {
-        return whoisServiceReply;
+    public void setAway(boolean away) {
+        this.away = away;
     }
 
-    public String getSuserHost() {
-        return suserHost;
+    public int getLastAwayMessage() {
+        return lastAwayMessage;
     }
 
-    public String getNick() {
-        return nick;
+    public void setLastAwayMessage(int lastAwayMessage) {
+        this.lastAwayMessage = lastAwayMessage;
     }
 
-    public String getRealName() {
-        return realName;
+    public DateTime getIdleTime() {
+        return idleTime;
     }
 
-    public String getAwayMessage() {
-        return awayMessage;
+    public void setIdleTime(DateTime idleTime) {
+        this.idleTime = idleTime;
     }
 
-    public DateTime getLoginTime() {
-        return loginTime;
+    public String getWhoisServiceReply() {
+        return whoisServiceReply;
     }
 
-    public boolean isEncrypted() {
-        return encrypted;
+    public void setWhoisServiceReply(String whoisServiceReply) {
+        this.whoisServiceReply = whoisServiceReply;
     }
 
-    @NonNull
-    public List<String> getChannels() {
-        return channels;
+    public String getSuserHost() {
+        return suserHost;
     }
 
-    public String getHost() {
-        return host;
+    public void setSuserHost(String suserHost) {
+        this.suserHost = suserHost;
     }
 
-    public String getUserModes() {
-        return userModes;
+    public String getNick() {
+        return nick;
     }
 
-    public String getUser() {
-        return user;
+    public void setNick(String nick) {
+        this.nick = nick;
     }
 
     /* BEGIN SYNC */
 
-    public void setServer(String server) {
-        this.server = server;
-    }
-
-    public void setIrcOperator(String ircOperator) {
-        this.ircOperator = ircOperator;
-    }
-
-    public void setAway(boolean away) {
-        this.away = away;
-    }
-
-    public void setLastAwayMessage(int lastAwayMessage) {
-        this.lastAwayMessage = lastAwayMessage;
-    }
-
-    public void setIdleTime(DateTime idleTime) {
-        this.idleTime = idleTime;
-    }
-
-    public void setWhoisServiceReply(String whoisServiceReply) {
-        this.whoisServiceReply = whoisServiceReply;
-    }
-
-    public void setSuserHost(String suserHost) {
-        this.suserHost = suserHost;
-    }
-
-    public void setNick(String nick) {
-        this.nick = nick;
+    public String getRealName() {
+        return realName;
     }
 
     public void setRealName(String realName) {
         this.realName = realName;
     }
 
+    public String getAwayMessage() {
+        return awayMessage;
+    }
+
     public void setAwayMessage(String awayMessage) {
         this.awayMessage = awayMessage;
     }
 
+    public DateTime getLoginTime() {
+        return loginTime;
+    }
+
     public void setLoginTime(DateTime loginTime) {
         this.loginTime = loginTime;
     }
 
+    public boolean isEncrypted() {
+        return encrypted;
+    }
+
     public void setEncrypted(boolean encrypted) {
         this.encrypted = encrypted;
     }
 
+    @NonNull
+    public List<String> getChannels() {
+        return channels;
+    }
+
     public void setChannels(@NonNull List<String> channels) {
         this.channels = channels;
     }
 
+    public String getHost() {
+        return host;
+    }
+
     public void setHost(String host) {
         this.host = host;
     }
 
+    public String getUserModes() {
+        return userModes;
+    }
+
     public void setUserModes(String userModes) {
         this.userModes = userModes;
     }
 
+    public String getUser() {
+        return user;
+    }
+
     public void setUser(String user) {
         this.user = user;
     }
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/Network.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/Network.java
index bb7ed09a3..cfb73a1c4 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/Network.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/Network.java
@@ -197,6 +197,10 @@ public class Network extends SyncableObject<Network> implements ContentComparabl
         return channels;
     }
 
+    public void setChannels(@NonNull Map<String, IrcChannel> channels) {
+        this.channels = channels;
+    }
+
     public void addIrcChannel(String channelName) {
         IrcChannel ircChannel = new IrcChannel(
                 channelName,
@@ -210,10 +214,6 @@ public class Network extends SyncableObject<Network> implements ContentComparabl
         channels.put(channelName, ircChannel);
     }
 
-    public void setChannels(@NonNull Map<String, IrcChannel> channels) {
-        this.channels = channels;
-    }
-
     @NonNull
     public List<NetworkServer> getServerList() {
         return ServerList;
@@ -562,25 +562,6 @@ public class Network extends SyncableObject<Network> implements ContentComparabl
         this.client = client;
     }
 
-    public static class IrcMode {
-        public final int rank;
-        public final String prefix;
-
-        public IrcMode(int rank, String prefix) {
-            this.rank = rank;
-            this.prefix = prefix;
-        }
-
-        @NonNull
-        @Override
-        public String toString() {
-            return "IrcMode{" +
-                    "rank=" + rank +
-                    ", prefix='" + prefix + '\'' +
-                    '}';
-        }
-    }
-
     public ChannelModeType channelModeType(char mode) {
         return channelModeType(String.copyValueOf(new char[]{mode}));
     }
@@ -597,11 +578,16 @@ public class Network extends SyncableObject<Network> implements ContentComparabl
         for (int i = 0; i < chanModes.length; i++) {
             if (chanModes[i].contains(mode)) {
                 switch (i) {
-                    case 0: return ChannelModeType.A_CHANMODE;
-                    case 1: return ChannelModeType.B_CHANMODE;
-                    case 2: return ChannelModeType.C_CHANMODE;
-                    case 3: return ChannelModeType.D_CHANMODE;
-                    default: return ChannelModeType.NOT_A_CHANMODE;
+                    case 0:
+                        return ChannelModeType.A_CHANMODE;
+                    case 1:
+                        return ChannelModeType.B_CHANMODE;
+                    case 2:
+                        return ChannelModeType.C_CHANMODE;
+                    case 3:
+                        return ChannelModeType.D_CHANMODE;
+                    default:
+                        return ChannelModeType.NOT_A_CHANMODE;
                 }
             }
         }
@@ -618,4 +604,23 @@ public class Network extends SyncableObject<Network> implements ContentComparabl
         C_CHANMODE,
         D_CHANMODE
     }
+
+    public static class IrcMode {
+        public final int rank;
+        public final String prefix;
+
+        public IrcMode(int rank, String prefix) {
+            this.rank = rank;
+            this.prefix = prefix;
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return "IrcMode{" +
+                    "rank=" + rank +
+                    ", prefix='" + prefix + '\'' +
+                    '}';
+        }
+    }
 }
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/NetworkConfig.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/NetworkConfig.java
index 1540f2994..32d885b45 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/NetworkConfig.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/NetworkConfig.java
@@ -8,7 +8,6 @@ import de.kuschku.libquassel.BusProvider;
 import de.kuschku.libquassel.Client;
 import de.kuschku.libquassel.functions.types.InitDataFunction;
 import de.kuschku.libquassel.primitives.types.QVariant;
-import de.kuschku.libquassel.syncables.serializers.BufferSyncerSerializer;
 import de.kuschku.libquassel.syncables.serializers.NetworkConfigSerializer;
 
 public class NetworkConfig extends SyncableObject<NetworkConfig> {
diff --git a/app/src/main/java/de/kuschku/libquassel/syncables/types/SyncableObject.java b/app/src/main/java/de/kuschku/libquassel/syncables/types/SyncableObject.java
index 1b7830386..3e5f6251f 100644
--- a/app/src/main/java/de/kuschku/libquassel/syncables/types/SyncableObject.java
+++ b/app/src/main/java/de/kuschku/libquassel/syncables/types/SyncableObject.java
@@ -10,7 +10,6 @@ import de.kuschku.libquassel.BusProvider;
 import de.kuschku.libquassel.Client;
 import de.kuschku.libquassel.functions.types.InitDataFunction;
 import de.kuschku.libquassel.functions.types.SyncFunction;
-import de.kuschku.libquassel.message.Message;
 import de.kuschku.libquassel.primitives.types.QVariant;
 
 import static de.kuschku.util.AndroidAssert.assertNotNull;
@@ -51,8 +50,10 @@ public abstract class SyncableObject<T extends SyncableObject<T>> {
 
     public abstract void init(@NonNull InitDataFunction function, @NonNull BusProvider provider, @NonNull Client client);
 
-    public void doInit() {}
+    public void doInit() {
+    }
 
     public abstract void update(T from);
+
     public abstract void update(Map<String, QVariant> from);
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/service/ClientBackgroundThread.java b/app/src/main/java/de/kuschku/quasseldroid_ng/service/ClientBackgroundThread.java
index cf6958773..41d02734c 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/service/ClientBackgroundThread.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/service/ClientBackgroundThread.java
@@ -1,5 +1,6 @@
 package de.kuschku.quasseldroid_ng.service;
 
+import android.content.Context;
 import android.support.annotation.NonNull;
 
 import java.io.IOException;
@@ -10,13 +11,15 @@ import de.kuschku.libquassel.CoreConnection;
 import de.kuschku.libquassel.ProtocolHandler;
 import de.kuschku.libquassel.events.GeneralErrorEvent;
 import de.kuschku.libquassel.protocols.RemotePeer;
+import de.kuschku.libquassel.ssl.CertificateManager;
 import de.kuschku.util.CompatibilityUtils;
 import de.kuschku.util.ServerAddress;
+import de.kuschku.util.certificates.SQLiteCertificateManager;
 
 public class ClientBackgroundThread implements Runnable {
     @NonNull
     private static final ClientData CLIENT_DATA = new ClientData(
-            new ClientData.FeatureFlags(false, true),
+            new ClientData.FeatureFlags(true, true),
             new byte[]{RemotePeer.DATASTREAM, RemotePeer.LEGACY},
             "QuasselDroid-ng 0.1 | libquassel 0.2",
             RemotePeer.PROTOCOL_VERSION_LEGACY
@@ -28,10 +31,13 @@ public class ClientBackgroundThread implements Runnable {
     public final CoreConnection connection;
     @NonNull
     public final ProtocolHandler handler;
+    @NonNull
+    public final CertificateManager certificateManager;
 
-    public ClientBackgroundThread(@NonNull BusProvider provider, @NonNull ServerAddress address) {
+    public ClientBackgroundThread(@NonNull BusProvider provider, @NonNull ServerAddress address, @NonNull Context context) {
         this.provider = provider;
-        this.connection = new CoreConnection(address, CLIENT_DATA, provider);
+        this.certificateManager = new SQLiteCertificateManager(context);
+        this.connection = new CoreConnection(address, CLIENT_DATA, provider, certificateManager);
         this.handler = new ProtocolHandler(provider);
         this.handler.client.setClientData(CLIENT_DATA);
         this.connection.setClient(handler.client);
@@ -47,10 +53,6 @@ public class ClientBackgroundThread implements Runnable {
     }
 
     public void close() {
-        try {
-            connection.close();
-        } catch (IOException e) {
-            provider.sendEvent(new GeneralErrorEvent(e));
-        }
+        connection.close();
     }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.java b/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.java
index 13fb9edac..b621d9aa2 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.java
@@ -39,7 +39,7 @@ public class QuasselService extends Service {
 
     public class LocalBinder extends Binder {
         public void startBackgroundThread(@NonNull BusProvider provider, @NonNull ServerAddress address) {
-            bgThread = new ClientBackgroundThread(provider, address);
+            bgThread = new ClientBackgroundThread(provider, address, QuasselService.this);
             new Thread(bgThread).start();
         }
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.java
index 38fb5b2e5..2f07658ca 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.java
@@ -4,7 +4,6 @@ import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.support.annotation.IntRange;
@@ -32,7 +31,6 @@ import com.afollestad.materialdialogs.MaterialDialog;
 import com.google.common.base.Splitter;
 import com.mikepenz.fastadapter.FastAdapter;
 import com.mikepenz.fastadapter.IExpandable;
-import com.mikepenz.fastadapter.IIdentifyable;
 import com.mikepenz.fastadapter.IItem;
 import com.mikepenz.fastadapter.adapters.ItemAdapter;
 import com.mikepenz.materialdrawer.AccountHeader;
@@ -46,7 +44,6 @@ import com.sothree.slidinguppanel.SlidingUpPanelLayout;
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -61,6 +58,7 @@ import de.kuschku.libquassel.events.BacklogReceivedEvent;
 import de.kuschku.libquassel.events.ConnectionChangeEvent;
 import de.kuschku.libquassel.events.GeneralErrorEvent;
 import de.kuschku.libquassel.events.LagChangedEvent;
+import de.kuschku.libquassel.events.UnknownCertificateEvent;
 import de.kuschku.libquassel.localtypes.Buffer;
 import de.kuschku.libquassel.localtypes.ChannelBuffer;
 import de.kuschku.libquassel.localtypes.backlogmanagers.BacklogFilter;
@@ -73,13 +71,12 @@ import de.kuschku.quasseldroid_ng.service.ClientBackgroundThread;
 import de.kuschku.quasseldroid_ng.service.QuasselService;
 import de.kuschku.quasseldroid_ng.ui.chat.chatview.MessageAdapter;
 import de.kuschku.quasseldroid_ng.ui.chat.drawer.BufferViewConfigWrapper;
-import de.kuschku.quasseldroid_ng.ui.chat.drawer.NetworkItem;
 import de.kuschku.quasseldroid_ng.ui.editor.AdvancedEditor;
 import de.kuschku.quasseldroid_ng.ui.theme.AppContext;
 import de.kuschku.quasseldroid_ng.ui.theme.AppTheme;
 import de.kuschku.quasseldroid_ng.ui.theme.ThemeUtil;
 import de.kuschku.util.ServerAddress;
-import de.kuschku.util.backports.Stream;
+import de.kuschku.util.certificates.CertificateUtils;
 import de.kuschku.util.instancestateutil.Storable;
 import de.kuschku.util.instancestateutil.Store;
 import de.kuschku.util.keyboardutils.DialogKeyboardUtil;
@@ -91,85 +88,38 @@ import static de.kuschku.util.AndroidAssert.assertNotNull;
 
 @UiThread
 public class ChatActivity extends AppCompatActivity {
+    @NonNull
+    private final Status status = new Status();
     @Bind(R.id.toolbar)
     Toolbar toolbar;
     @Bind(R.id.sliding_layout)
     SlidingUpPanelLayout slidingLayout;
     @Bind(R.id.sliding_layout_history)
     SlidingUpPanelLayout slidingLayoutHistory;
-
     @Bind(R.id.chatline_scroller)
     ScrollView chatlineScroller;
     @Bind(R.id.chatline)
     AppCompatEditText chatline;
     @Bind(R.id.send)
     AppCompatImageButton send;
-
     @Bind(R.id.msg_history)
     RecyclerView msgHistory;
-
     @Bind(R.id.swipe_view)
     SwipeRefreshLayout swipeView;
     @Bind(R.id.messages)
     RecyclerView messages;
-
     @Bind(R.id.formatting_menu)
     ActionMenuView formattingMenu;
     @Bind(R.id.formatting_toolbar)
     Toolbar formattingToolbar;
-
-    @PreferenceWrapper(BuildConfig.APPLICATION_ID)
-    public static abstract class Settings {
-        @StringPreference("QUASSEL_LIGHT")
-        String theme;
-        @BooleanPreference(false)   boolean fullHostmask;
-        @IntPreference(2)           int textSize;
-        @BooleanPreference(true)    boolean mircColors;
-
-        @StringPreference("")       String lastHost;
-        @IntPreference(4242)        int lastPort;
-        @StringPreference("")       String lastUsername;
-        @StringPreference("")       String lastPassword;
-    }
-
     private AppContext context = new AppContext();
-
-    @NonNull
-    private final Status status = new Status();
-    private static class Status extends Storable {
-        @Store int bufferId = -1;
-        @Store int bufferViewConfigId = -1;
-    }
-
     private ServiceInterface serviceInterface = new ServiceInterface();
-    private class ServiceInterface {
-        private void connect(@NonNull ServerAddress address) {
-            assertNotNull(binder);
-            disconnect();
-
-            BusProvider provider = new BusProvider();
-            provider.event.register(ChatActivity.this);
-            binder.startBackgroundThread(provider, address);
-            onConnectionEstablished();
-        }
-
-        private void disconnect() {
-            if (context.getProvider() != null)
-                context.getProvider().event.unregister(this);
-            context.setProvider(null);
-            context.setClient(null);
-        }
-    }
-
     private QuasselService.LocalBinder binder;
-
     private MessageAdapter messageAdapter;
-
     private AccountHeader accountHeader;
     private Drawer drawerLeft;
     private BufferViewConfigWrapper wrapper;
     private AdvancedEditor editor;
-
     private ServiceConnection serviceConnection = new ServiceConnection() {
         @UiThread
         public void onServiceConnected(@NonNull ComponentName cn, @NonNull IBinder service) {
@@ -197,6 +147,20 @@ public class ChatActivity extends AppCompatActivity {
         }
     };
 
+    private static void updateNoColor(Buffer buffer, Menu menu) {
+        boolean isNoColor = isNoColor(buffer);
+        menu.findItem(R.id.format_bold).setEnabled(!isNoColor);
+        menu.findItem(R.id.format_italic).setEnabled(!isNoColor);
+        menu.findItem(R.id.format_underline).setEnabled(!isNoColor);
+        menu.findItem(R.id.format_paint).setEnabled(!isNoColor);
+        menu.findItem(R.id.format_fill).setEnabled(!isNoColor);
+    }
+
+    public static boolean isNoColor(Buffer buffer) {
+        return buffer instanceof ChannelBuffer && ((ChannelBuffer) buffer).getChannel() != null &&
+                ((ChannelBuffer) buffer).getChannel().getD_ChanModes().contains("c");
+    }
+
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         setupContext();
@@ -381,17 +345,6 @@ public class ChatActivity extends AppCompatActivity {
         return super.onCreateOptionsMenu(menu);
     }
 
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        // Checks whether a hardware keyboard is available
-        if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
-
-        } else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
-
-        }
-    }
-
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         List<Integer> filterSettings = Arrays.asList(
@@ -403,7 +356,9 @@ public class ChatActivity extends AppCompatActivity {
                 Message.Type.Topic.value
         );
         int[] filterSettingsInts = new int[filterSettings.size()];
-        for (int i = 0; i < filterSettingsInts.length; i++) { filterSettingsInts[i] = filterSettings.get(i); }
+        for (int i = 0; i < filterSettingsInts.length; i++) {
+            filterSettingsInts[i] = filterSettings.get(i);
+        }
 
         switch (item.getItemId()) {
             case R.id.action_hide_events: {
@@ -436,12 +391,11 @@ public class ChatActivity extends AppCompatActivity {
                                 .onPositive((dialog, which) -> {
                                     int filters = 0x00000000;
                                     if (dialog.getSelectedIndices() != null)
-                                    for (int i : dialog.getSelectedIndices()) {
-                                        filters |= filterSettings.get(i);
-                                    }
+                                        for (int i : dialog.getSelectedIndices()) {
+                                            filters |= filterSettings.get(i);
+                                        }
                                     backlogFilter.setFilters(filters);
                                 })
-                                .buttonRippleColorAttr(R.attr.colorAccentFocus)
                                 .build()
                                 .show();
                     }
@@ -498,7 +452,6 @@ public class ChatActivity extends AppCompatActivity {
                     return true;
                 })
                 .negativeColor(context.getThemeUtil().res.colorForeground)
-                .buttonRippleColor(context.getThemeUtil().res.colorAccentFocus)
                 .build()
                 .show();
     }
@@ -563,23 +516,6 @@ public class ChatActivity extends AppCompatActivity {
         }
     }
 
-    private static void updateNoColor(Buffer buffer, Menu menu) {
-        boolean isNoColor = isNoColor(buffer);
-        menu.findItem(R.id.format_bold).setEnabled(!isNoColor);
-        menu.findItem(R.id.format_italic).setEnabled(!isNoColor);
-        menu.findItem(R.id.format_underline).setEnabled(!isNoColor);
-        menu.findItem(R.id.format_paint).setEnabled(!isNoColor);
-        menu.findItem(R.id.format_fill).setEnabled(!isNoColor);
-    }
-
-    public static boolean isNoColor(Buffer buffer) {
-        if (buffer instanceof ChannelBuffer && ((ChannelBuffer) buffer).getChannel() != null) {
-            return ((ChannelBuffer) buffer).getChannel().getD_ChanModes().contains("c");
-        } else {
-            return false;
-        }
-    }
-
     private void onConnectionEstablished() {
         assertNotNull(binder);
         assertNotNull(binder.getBackgroundThread());
@@ -653,7 +589,6 @@ public class ChatActivity extends AppCompatActivity {
                     Log.e("TIME", String.valueOf(System.currentTimeMillis()));
                 })
                 .negativeColor(context.getThemeUtil().res.colorForeground)
-                .buttonRippleColor(context.getThemeUtil().res.colorAccentFocus)
                 .positiveText("Login")
                 .negativeText("Cancel")
                 .build();
@@ -668,7 +603,8 @@ public class ChatActivity extends AppCompatActivity {
                 .title("Address")
                 .customView(R.layout.dialog_address, false)
                 .onPositive((dialog1, which) -> {
-                    if (binder != null && binder.getBackgroundThread() != null) binder.stopBackgroundThread();
+                    if (binder != null && binder.getBackgroundThread() != null)
+                        binder.stopBackgroundThread();
 
                     View parent = dialog1.getCustomView();
                     AppCompatEditText hostField = (AppCompatEditText) parent.findViewById(R.id.host);
@@ -699,6 +635,22 @@ public class ChatActivity extends AppCompatActivity {
         }
     }
 
+    public void onEventMainThread(UnknownCertificateEvent event) {
+        new MaterialDialog.Builder(this)
+                .content("Do you trust this certificate?\n" + CertificateUtils.certificateToFingerprint(event.certificate, ""))
+                .title("Unknown Certificate")
+                .onPositive((dialog, which) -> {
+                    if (binder.getBackgroundThread() != null) {
+                        binder.getBackgroundThread().certificateManager.addCertificate(event.certificate, event.address);
+                    }
+                })
+                .negativeColor(context.getThemeUtil().res.colorForeground)
+                .positiveText("Yes")
+                .negativeText("No")
+                .build()
+                .show();
+    }
+
     public void onEventMainThread(GeneralErrorEvent event) {
         Snackbar.make(messages, event.toString(), Snackbar.LENGTH_LONG).show();
         for (String line : Splitter.fixedLength(2048).split(event.toString())) {
@@ -719,4 +671,51 @@ public class ChatActivity extends AppCompatActivity {
             toolbar.setSubtitle("");
         }
     }
+
+    @PreferenceWrapper(BuildConfig.APPLICATION_ID)
+    public static abstract class Settings {
+        @StringPreference("QUASSEL_LIGHT")
+        String theme;
+        @BooleanPreference(false)
+        boolean fullHostmask;
+        @IntPreference(2)
+        int textSize;
+        @BooleanPreference(true)
+        boolean mircColors;
+
+        @StringPreference("")
+        String lastHost;
+        @IntPreference(4242)
+        int lastPort;
+        @StringPreference("")
+        String lastUsername;
+        @StringPreference("")
+        String lastPassword;
+    }
+
+    private static class Status extends Storable {
+        @Store
+        int bufferId = -1;
+        @Store
+        int bufferViewConfigId = -1;
+    }
+
+    private class ServiceInterface {
+        private void connect(@NonNull ServerAddress address) {
+            assertNotNull(binder);
+            disconnect();
+
+            BusProvider provider = new BusProvider();
+            provider.event.register(ChatActivity.this);
+            binder.startBackgroundThread(provider, address);
+            onConnectionEstablished();
+        }
+
+        private void disconnect() {
+            if (context.getProvider() != null)
+                context.getProvider().event.unregister(this);
+            context.setProvider(null);
+            context.setClient(null);
+        }
+    }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java
index cb07ea5ad..4000bcf78 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java
@@ -4,21 +4,13 @@ import android.content.Context;
 import android.graphics.Typeface;
 import android.support.annotation.ColorInt;
 import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
 import android.support.annotation.UiThread;
-import android.util.Log;
 
 import de.kuschku.libquassel.localtypes.Buffer;
 import de.kuschku.libquassel.message.Message;
-import de.kuschku.quasseldroid_ng.R;
 import de.kuschku.quasseldroid_ng.ui.theme.AppContext;
-import de.kuschku.util.annotationbind.AutoBinder;
-import de.kuschku.util.annotationbind.AutoString;
 import de.kuschku.util.irc.IrcFormatHelper;
 import de.kuschku.util.irc.IrcUserUtils;
-import de.kuschku.util.ui.MessageUtil;
-import de.kuschku.util.ui.SpanFormatter;
-import de.kuschku.quasseldroid_ng.ui.theme.ThemeUtil;
 
 import static de.kuschku.util.AndroidAssert.assertNotNull;
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/MessageAdapter.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/MessageAdapter.java
index 92ded6f6a..9a4d6deae 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/MessageAdapter.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/MessageAdapter.java
@@ -22,6 +22,7 @@ import static de.kuschku.util.AndroidAssert.assertNotNull;
 
 @UiThread
 public class MessageAdapter extends RecyclerView.Adapter<MessageViewHolder> {
+    private static ObservableSortedList<Message> emptyList = new ObservableComparableSortedList<Message>(Message.class);
     @NonNull
     private final ChatMessageRenderer renderer;
     @NonNull
@@ -37,6 +38,10 @@ public class MessageAdapter extends RecyclerView.Adapter<MessageViewHolder> {
         this.callback = new AdapterUICallbackWrapper(this, scroller);
     }
 
+    public static ObservableSortedList<Message> emptyList() {
+        return emptyList;
+    }
+
     public void setMessageList(@NonNull ObservableSortedList<Message> messageList) {
         this.messageList.removeCallback(callback);
         this.messageList = messageList;
@@ -62,9 +67,4 @@ public class MessageAdapter extends RecyclerView.Adapter<MessageViewHolder> {
     public int getItemCount() {
         return messageList.size();
     }
-
-    private static ObservableSortedList<Message> emptyList = new ObservableComparableSortedList<Message>(Message.class);
-    public static ObservableSortedList<Message> emptyList() {
-        return emptyList;
-    }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferItem.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferItem.java
index ea259ca33..ac9816842 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferItem.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferItem.java
@@ -117,7 +117,7 @@ public class BufferItem extends SecondaryDrawerItem implements IObservable<Gener
 
     @Override
     public ColorHolder getIconColor() {
-        return buffer.getStatus() == BufferInfo.BufferStatus.ONLINE  ?
+        return buffer.getStatus() == BufferInfo.BufferStatus.ONLINE ?
                 ColorHolder.fromColor(context.getThemeUtil().res.colorAccent) :
                 new ColorHolder();
     }
@@ -151,10 +151,10 @@ public class BufferItem extends SecondaryDrawerItem implements IObservable<Gener
         super.onPostBindView(drawerItem, view);
 
         if (getDescription() != null && getDescription().getText() != null)
-        ((TextView) view.findViewById(R.id.material_drawer_description)).setText(MessageUtil.parseStyleCodes(
-                context.getThemeUtil(),
-                getDescription().getText(),
-                context.getSettings().mircColors.or(true)
-        ));
+            ((TextView) view.findViewById(R.id.material_drawer_description)).setText(MessageUtil.parseStyleCodes(
+                    context.getThemeUtil(),
+                    getDescription().getText(),
+                    context.getSettings().mircColors.or(true)
+            ));
     }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferViewConfigWrapper.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferViewConfigWrapper.java
index 903e76bb3..83e4c0c57 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferViewConfigWrapper.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferViewConfigWrapper.java
@@ -1,22 +1,13 @@
 package de.kuschku.quasseldroid_ng.ui.chat.drawer;
 
-import android.util.Log;
-
-import com.mikepenz.fastadapter.IIdentifyable;
 import com.mikepenz.materialdrawer.Drawer;
 import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
 
 import de.kuschku.libquassel.syncables.types.BufferViewConfig;
 import de.kuschku.libquassel.syncables.types.Network;
 import de.kuschku.quasseldroid_ng.ui.theme.AppContext;
-import de.kuschku.quasseldroid_ng.ui.chat.drawer.NetworkItem;
-import de.kuschku.util.backports.Stream;
 import de.kuschku.util.observables.callbacks.ElementCallback;
 import de.kuschku.util.observables.lists.ObservableSortedList;
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/NetworkItem.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/NetworkItem.java
index 36706986d..ea4bfec59 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/NetworkItem.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/NetworkItem.java
@@ -1,6 +1,5 @@
 package de.kuschku.quasseldroid_ng.ui.chat.drawer;
 
-import android.util.Log;
 import android.util.SparseArray;
 
 import com.mikepenz.materialdrawer.holder.ColorHolder;
@@ -172,7 +171,7 @@ public class NetworkItem extends PrimaryDrawerItem implements IObservable<Genera
             return item1.getBuffer().getInfo().id == item2.getBuffer().getInfo().id;
         }
     }
-    
+
     class NoneComparator implements ObservableSortedList.ItemComparator<BufferItem> {
         @Override
         public int compare(BufferItem o1, BufferItem o2) {
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/AdvancedEditor.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/AdvancedEditor.java
index 8b1ed6384..ab9884be5 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/AdvancedEditor.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/AdvancedEditor.java
@@ -22,6 +22,7 @@ public class AdvancedEditor {
     public void toggleUnderline() {
         toggleUnderline(editText.getSelectionStart(), editText.getSelectionEnd());
     }
+
     public void toggleUnderline(int start, int end) {
         boolean isUnderline = false;
         for (UnderlineSpan span : editText.getText().getSpans(start, end, UnderlineSpan.class)) {
@@ -41,6 +42,7 @@ public class AdvancedEditor {
     public void toggleBold() {
         toggleBold(editText.getSelectionStart(), editText.getSelectionEnd());
     }
+
     public void toggleBold(int start, int end) {
         boolean isBold = false;
         for (BoldSpan span : editText.getText().getSpans(start, end, BoldSpan.class)) {
@@ -59,6 +61,7 @@ public class AdvancedEditor {
     public void toggleItalic() {
         toggleItalic(editText.getSelectionStart(), editText.getSelectionEnd());
     }
+
     public void toggleItalic(int start, int end) {
         boolean isItalic = false;
         for (ItalicSpan span : editText.getText().getSpans(start, end, ItalicSpan.class)) {
@@ -77,6 +80,7 @@ public class AdvancedEditor {
     public void toggleForeground(@IntRange(from = 0, to = 15) int color) {
         toggleForeground(editText.getSelectionStart(), editText.getSelectionEnd(), color);
     }
+
     public void toggleForeground(int start, int end, @ColorInt int color) {
         boolean isColored = false;
         for (ForegroundColorSpan span : editText.getText().getSpans(start, end, ForegroundColorSpan.class)) {
@@ -95,6 +99,7 @@ public class AdvancedEditor {
     public void toggleBackground(@IntRange(from = 0, to = 15) int color) {
         toggleBackground(editText.getSelectionStart(), editText.getSelectionEnd(), color);
     }
+
     public void toggleBackground(int start, int end, @ColorInt int color) {
         boolean isColored = false;
         for (BackgroundColorSpan span : editText.getText().getSpans(start, end, BackgroundColorSpan.class)) {
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/FormattingHelper.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/FormattingHelper.java
index 1e6be5d2b..74780cc7a 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/FormattingHelper.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/FormattingHelper.java
@@ -31,7 +31,7 @@ public class FormattingHelper {
     }
 
     private void withinParagraph(StringBuilder out, Spanned text,
-                                        int start, int end) {
+                                 int start, int end) {
         int next;
         for (int i = start; i < end; i = next) {
             next = text.nextSpanTransition(i, end, CharacterStyle.class);
@@ -87,7 +87,7 @@ public class FormattingHelper {
                 }
             }
 
-            out.append(text.subSequence(i,next));
+            out.append(text.subSequence(i, next));
 
             for (int j = style.length - 1; j >= 0; j--) {
                 if ((text.getSpanFlags(style[j]) & Spanned.SPAN_COMPOSING) != 0)
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/AppTheme.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/AppTheme.java
index 19f7f5231..7bfbbc77f 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/AppTheme.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/AppTheme.java
@@ -23,12 +23,16 @@ public enum AppTheme {
     public static AppTheme themeFromString(String s) {
         if (s == null) s = "";
         switch (s) {
-            case "MATERIAL_DARK": return MATERIAL_DARK;
-            case "MATERIAL_LIGHT": return MATERIAL_LIGHT;
-            case "QUASSEL_DARK": return QUASSEL_DARK;
+            case "MATERIAL_DARK":
+                return MATERIAL_DARK;
+            case "MATERIAL_LIGHT":
+                return MATERIAL_LIGHT;
+            case "QUASSEL_DARK":
+                return QUASSEL_DARK;
 
             default:
-            case "QUASSEL_LIGHT": return QUASSEL_LIGHT;
+            case "QUASSEL_LIGHT":
+                return QUASSEL_LIGHT;
         }
     }
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/ThemeUtil.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/ThemeUtil.java
index fa55f28ff..336fa5268 100644
--- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/ThemeUtil.java
+++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/ThemeUtil.java
@@ -6,7 +6,6 @@ import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.UiThread;
 import android.support.v7.view.ContextThemeWrapper;
-import android.util.Log;
 
 import de.kuschku.quasseldroid_ng.R;
 import de.kuschku.util.annotationbind.AutoBinder;
diff --git a/app/src/main/java/de/kuschku/util/ServerAddress.java b/app/src/main/java/de/kuschku/util/ServerAddress.java
index 33ba15f12..2d8f9b049 100644
--- a/app/src/main/java/de/kuschku/util/ServerAddress.java
+++ b/app/src/main/java/de/kuschku/util/ServerAddress.java
@@ -8,4 +8,8 @@ public class ServerAddress {
         this.host = host;
         this.port = port;
     }
+
+    public String print() {
+        return String.format("%s:%s", host, port);
+    }
 }
diff --git a/app/src/main/java/de/kuschku/util/certificates/CertificateDatabaseHandler.java b/app/src/main/java/de/kuschku/util/certificates/CertificateDatabaseHandler.java
new file mode 100644
index 000000000..f06452b5c
--- /dev/null
+++ b/app/src/main/java/de/kuschku/util/certificates/CertificateDatabaseHandler.java
@@ -0,0 +1,152 @@
+package de.kuschku.util.certificates;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteStatement;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+public class CertificateDatabaseHandler extends SQLiteOpenHelper {
+    private static final int DATABASE_VERSION = 1;
+    private static final String DATABASE_NAME = "certificates";
+    private static final String TABLE_CERTIFICATES = "certificates";
+
+    private static final String KEY_CORE_ADDRESS = "core_address";
+    private static final String KEY_FINGERPRINT = "fingerprint";
+
+    // Again we can only use String.format, as SQL doesn’t support table or column names to be bound
+    // in prepared statements
+    private static final String STATEMENT_INSERT =
+            String.format("INSERT OR IGNORE INTO %s(%s, %s) VALUES (?, ?)",
+                    TABLE_CERTIFICATES, KEY_CORE_ADDRESS, KEY_FINGERPRINT);
+    private static final String STATEMENT_DELETE =
+            String.format("DELETE FROM %s WHERE %s = ? AND %s = ?",
+                    TABLE_CERTIFICATES, KEY_CORE_ADDRESS, KEY_FINGERPRINT);
+    private static final String STATEMENT_DELETE_ALL =
+            String.format("DELETE FROM %s WHERE %s = ?",
+                    TABLE_CERTIFICATES, KEY_CORE_ADDRESS);
+
+    private static final String SPECIFIER_FIND_ALL = String.format("%s = ?", KEY_CORE_ADDRESS);
+
+    public CertificateDatabaseHandler(Context context) {
+        super(context, DATABASE_NAME, null, DATABASE_VERSION);
+    }
+
+    @Override
+    public void onCreate(SQLiteDatabase db) {
+        // Why do we use String.format and not prepared statements? Because we can’t bind table or
+        // column names in prepared statements
+        String statement = String.format("CREATE TABLE %s (%s, %s, PRIMARY KEY (%s, %s), UNIQUE(%s, %s));",
+                TABLE_CERTIFICATES,
+                KEY_CORE_ADDRESS, KEY_FINGERPRINT,
+                KEY_CORE_ADDRESS, KEY_FINGERPRINT,
+                KEY_CORE_ADDRESS, KEY_FINGERPRINT);
+        db.execSQL(statement);
+    }
+
+    @Override
+    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+
+    }
+
+    public boolean addCertificate(String fingerprint, String coreAddress) {
+        SQLiteDatabase db = this.getWritableDatabase();
+        SQLiteStatement statement = db.compileStatement(STATEMENT_INSERT);
+        statement.bindString(1, coreAddress);
+        statement.bindString(2, fingerprint);
+        // executeInsert returns -1 if unsuccessful
+        return statement.executeInsert() != -1;
+    }
+
+    public boolean removeCertificate(String fingerprint, String coreAddress) {
+        SQLiteDatabase db = this.getWritableDatabase();
+        SQLiteStatement statement = db.compileStatement(STATEMENT_DELETE);
+        statement.bindString(1, coreAddress);
+        statement.bindString(2, fingerprint);
+        // executeUpdateDelete returns amount of modified rows
+        return statement.executeUpdateDelete() > 0;
+    }
+
+    public boolean removeCertificates(String coreAddress) {
+        SQLiteDatabase db = this.getWritableDatabase();
+        SQLiteStatement statement = db.compileStatement(STATEMENT_DELETE_ALL);
+        statement.bindString(1, coreAddress);
+        // executeUpdateDelete returns amount of modified rows
+        return statement.executeUpdateDelete() > 0;
+    }
+
+    public Cursor cursorFindCertificates(String coreAddress) {
+        SQLiteDatabase db = this.getReadableDatabase();
+        return db.query(
+                // table name
+                TABLE_CERTIFICATES,
+                // column names
+                new String[]{KEY_FINGERPRINT},
+                // where clause
+                SPECIFIER_FIND_ALL,
+                // binds for where clause
+                new String[]{coreAddress},
+                null,
+                null,
+                null,
+                null
+        );
+    }
+
+    public Cursor cursorFindAllCertificates() {
+        SQLiteDatabase db = this.getReadableDatabase();
+        return db.query(
+                // table name
+                TABLE_CERTIFICATES,
+                // column names
+                new String[]{KEY_CORE_ADDRESS, KEY_FINGERPRINT},
+                // where clause
+                null,
+                // binds for where clause
+                new String[0],
+                null,
+                null,
+                null,
+                null
+        );
+    }
+
+    public List<String> findCertificates(String coreAddress) {
+        Cursor cursor = cursorFindCertificates(coreAddress);
+        List<String> certificates = new ArrayList<>();
+
+        if (cursor != null && cursor.moveToFirst()) {
+            do {
+                certificates.add(cursor.getString(0));
+            } while (cursor.moveToNext());
+        }
+
+        return certificates;
+    }
+
+    public Map<String, Collection<String>> findAllCertificates() {
+        Cursor cursor = cursorFindAllCertificates();
+
+        Map<String, Collection<String>> certificates = new HashMap<>();
+
+        if (cursor != null && cursor.moveToFirst()) {
+            do {
+                String coreid = cursor.getString(0);
+                String fingerprint = cursor.getString(1);
+                if (certificates.get(coreid) == null)
+                    certificates.put(coreid, new HashSet<>());
+
+                certificates.get(coreid).add(fingerprint);
+            } while (cursor.moveToNext());
+        }
+
+        return certificates;
+    }
+}
diff --git a/app/src/main/java/de/kuschku/util/certificates/CertificateUtils.java b/app/src/main/java/de/kuschku/util/certificates/CertificateUtils.java
new file mode 100644
index 000000000..2cdcef68e
--- /dev/null
+++ b/app/src/main/java/de/kuschku/util/certificates/CertificateUtils.java
@@ -0,0 +1,56 @@
+package de.kuschku.util.certificates;
+
+import com.google.common.base.Joiner;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import de.kuschku.util.Objects;
+
+public class CertificateUtils {
+    private CertificateUtils() {
+    }
+
+    public static String certificateToFingerprint(X509Certificate certificate) throws NoSuchAlgorithmException, CertificateEncodingException {
+        return hashToFingerprint(getHash(certificate));
+    }
+
+    public static String certificateToFingerprint(X509Certificate certificate, String defaultValue) {
+        try {
+            return certificateToFingerprint(certificate);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    private static byte[] getHash(X509Certificate certificate) throws NoSuchAlgorithmException, CertificateEncodingException {
+        MessageDigest digest = java.security.MessageDigest.getInstance("SHA1");
+        digest.update(certificate.getEncoded());
+        return digest.digest();
+    }
+
+    public static String hashToFingerprint(byte[] hash) {
+        String[] formattedBytes = new String[hash.length];
+        for (int i = 0; i < hash.length; i++) {
+            // Format each byte as hex string
+            formattedBytes[i] = Integer.toHexString(hash[i] & 0xff);
+        }
+        return Joiner.on(":").join(formattedBytes);
+    }
+
+    public static Collection<String> getHostnames(X509Certificate certificate) throws CertificateParsingException {
+        Set<String> hostnames = new HashSet<>();
+        for (List<?> data : certificate.getSubjectAlternativeNames()) {
+            if (Objects.equals(data.get(0), 2) && data.get(1) instanceof String)
+                hostnames.add((String) data.get(1));
+        }
+        return hostnames;
+    }
+}
diff --git a/app/src/main/java/de/kuschku/util/certificates/SQLiteCertificateManager.java b/app/src/main/java/de/kuschku/util/certificates/SQLiteCertificateManager.java
new file mode 100644
index 000000000..86c74a357
--- /dev/null
+++ b/app/src/main/java/de/kuschku/util/certificates/SQLiteCertificateManager.java
@@ -0,0 +1,67 @@
+package de.kuschku.util.certificates;
+
+import android.content.Context;
+
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import de.kuschku.libquassel.ssl.CertificateManager;
+import de.kuschku.libquassel.ssl.UnknownCertificateException;
+import de.kuschku.util.ServerAddress;
+
+public class SQLiteCertificateManager implements CertificateManager {
+    private CertificateDatabaseHandler handler;
+
+    public SQLiteCertificateManager(Context context) {
+        this.handler = new CertificateDatabaseHandler(context);
+    }
+
+    @Override
+    public boolean isTrusted(X509Certificate certificate, ServerAddress core) {
+        try {
+            certificate.checkValidity();
+            return handler.findCertificates(core.host).contains(CertificateUtils.certificateToFingerprint(certificate));
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean addCertificate(X509Certificate certificate, ServerAddress core) {
+        try {
+            return handler.addCertificate(CertificateUtils.certificateToFingerprint(certificate), core.host);
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean removeCertificate(X509Certificate certificate, ServerAddress core) {
+        try {
+            return handler.removeCertificate(CertificateUtils.certificateToFingerprint(certificate), core.host);
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean removeAllCertificates(ServerAddress core) {
+        return handler.removeCertificates(core.host);
+    }
+
+    @Override
+    public void checkTrusted(X509Certificate certificate, ServerAddress address) throws UnknownCertificateException {
+        if (!isTrusted(certificate, address))
+            throw new UnknownCertificateException(certificate, address);
+    }
+
+    public List<String> findCertificates(ServerAddress core) {
+        return handler.findCertificates(core.host);
+    }
+
+    public Map<String, Collection<String>> findAllCertificates() {
+        return handler.findAllCertificates();
+    }
+}
diff --git a/app/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java b/app/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java
index fc05b3521..adf2e8988 100644
--- a/app/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java
+++ b/app/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java
@@ -25,7 +25,6 @@ import java.util.regex.Pattern;
 
 import de.kuschku.quasseldroid_ng.R;
 import de.kuschku.quasseldroid_ng.ui.theme.AppContext;
-import de.kuschku.quasseldroid_ng.ui.theme.ThemeUtil;
 import de.kuschku.util.ui.MessageUtil;
 
 public class IrcFormatHelper {
diff --git a/app/src/main/java/de/kuschku/util/niohelpers/WrappedChannel.java b/app/src/main/java/de/kuschku/util/niohelpers/WrappedChannel.java
index 038786538..33769496b 100644
--- a/app/src/main/java/de/kuschku/util/niohelpers/WrappedChannel.java
+++ b/app/src/main/java/de/kuschku/util/niohelpers/WrappedChannel.java
@@ -14,10 +14,19 @@ import java.nio.ByteBuffer;
 import java.nio.channels.ByteChannel;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.InterruptibleChannel;
+import java.security.GeneralSecurityException;
 import java.util.zip.DeflaterOutputStream;
 import java.util.zip.InflaterInputStream;
 
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+
+import de.kuschku.libquassel.ssl.CertificateManager;
+import de.kuschku.libquassel.ssl.QuasselTrustManager;
 import de.kuschku.util.CompatibilityUtils;
+import de.kuschku.util.ServerAddress;
 
 public class WrappedChannel implements Flushable, ByteChannel, InterruptibleChannel {
     @Nullable
@@ -29,6 +38,9 @@ public class WrappedChannel implements Flushable, ByteChannel, InterruptibleChan
     @Nullable
     private DataOutputStream out;
 
+    @Nullable
+    private Socket socket = null;
+
     private WrappedChannel(@Nullable InputStream in, @Nullable OutputStream out) {
         this.rawIn = in;
         this.rawOut = out;
@@ -36,6 +48,11 @@ public class WrappedChannel implements Flushable, ByteChannel, InterruptibleChan
         if (this.rawOut != null) this.out = new DataOutputStream(rawOut);
     }
 
+    public WrappedChannel(Socket s) throws IOException {
+        this(s.getInputStream(), s.getOutputStream());
+        this.socket = s;
+    }
+
     @NonNull
     public static WrappedChannel ofStreams(@Nullable InputStream in, @Nullable OutputStream out) {
         return new WrappedChannel(in, out);
@@ -43,7 +60,7 @@ public class WrappedChannel implements Flushable, ByteChannel, InterruptibleChan
 
     @NonNull
     public static WrappedChannel ofSocket(@NonNull Socket s) throws IOException {
-        return new WrappedChannel(s.getInputStream(), s.getOutputStream());
+        return new WrappedChannel(s);
     }
 
     @Nullable
@@ -54,6 +71,20 @@ public class WrappedChannel implements Flushable, ByteChannel, InterruptibleChan
         );
     }
 
+
+    public static WrappedChannel withSSL(@NonNull WrappedChannel channel,
+                                         @NonNull CertificateManager certificateManager,
+                                         @NonNull ServerAddress address) throws GeneralSecurityException, IOException {
+        SSLContext context = SSLContext.getInstance("TLSv1.2");
+        TrustManager[] managers = new TrustManager[]{QuasselTrustManager.fromDefault(certificateManager, address)};
+        context.init(null, managers, null);
+        SSLSocketFactory factory = context.getSocketFactory();
+        SSLSocket socket = (SSLSocket) factory.createSocket(channel.socket, address.host, address.port, true);
+        socket.setUseClientMode(true);
+        socket.startHandshake();
+        return WrappedChannel.ofSocket(socket);
+    }
+
     /**
      * Reads a sequence of bytes from this channel into the given buffer.
      * <p>
diff --git a/app/src/main/java/de/kuschku/util/observables/ContentComparable.java b/app/src/main/java/de/kuschku/util/observables/ContentComparable.java
index 952ca7fea..7b6cb6204 100644
--- a/app/src/main/java/de/kuschku/util/observables/ContentComparable.java
+++ b/app/src/main/java/de/kuschku/util/observables/ContentComparable.java
@@ -2,5 +2,6 @@ package de.kuschku.util.observables;
 
 public interface ContentComparable<T extends ContentComparable<T>> extends Comparable<T> {
     boolean areItemsTheSame(T other);
+
     boolean areContentsTheSame(T other);
 }
diff --git a/app/src/main/java/de/kuschku/util/observables/lists/ChildParentObservableSortedList.java b/app/src/main/java/de/kuschku/util/observables/lists/ChildParentObservableSortedList.java
index 6485c2fd2..45466867a 100644
--- a/app/src/main/java/de/kuschku/util/observables/lists/ChildParentObservableSortedList.java
+++ b/app/src/main/java/de/kuschku/util/observables/lists/ChildParentObservableSortedList.java
@@ -3,7 +3,6 @@ package de.kuschku.util.observables.lists;
 import android.support.annotation.NonNull;
 
 import de.kuschku.util.observables.IObservable;
-import de.kuschku.util.observables.callbacks.UICallback;
 import de.kuschku.util.observables.callbacks.UIChildCallback;
 import de.kuschku.util.observables.callbacks.UIChildParentCallback;
 import de.kuschku.util.observables.callbacks.UIParentCallback;
diff --git a/app/src/main/java/de/kuschku/util/observables/lists/ObservableSortedList.java b/app/src/main/java/de/kuschku/util/observables/lists/ObservableSortedList.java
index 33a0de54b..41c8ffb83 100644
--- a/app/src/main/java/de/kuschku/util/observables/lists/ObservableSortedList.java
+++ b/app/src/main/java/de/kuschku/util/observables/lists/ObservableSortedList.java
@@ -213,6 +213,11 @@ public class ObservableSortedList<T> implements IObservableList<UICallback, T> {
         callback.notifyItemChanged(position);
     }
 
+    @Override
+    public String toString() {
+        return Arrays.toString(toArray());
+    }
+
     public interface ItemComparator<T> {
         int compare(T o1, T o2);
 
@@ -221,11 +226,6 @@ public class ObservableSortedList<T> implements IObservableList<UICallback, T> {
         boolean areItemsTheSame(T item1, T item2);
     }
 
-    @Override
-    public String toString() {
-        return Arrays.toString(toArray());
-    }
-
     class Callback extends SortedList.Callback<T> {
         @Override
         public int compare(T o1, T o2) {
diff --git a/app/src/main/java/de/kuschku/util/ui/DateTimeFormatHelper.java b/app/src/main/java/de/kuschku/util/ui/DateTimeFormatHelper.java
index bca319fad..9f9f324ea 100644
--- a/app/src/main/java/de/kuschku/util/ui/DateTimeFormatHelper.java
+++ b/app/src/main/java/de/kuschku/util/ui/DateTimeFormatHelper.java
@@ -17,42 +17,42 @@ public class DateTimeFormatHelper {
     }
 
     @NonNull
-    public DateTimeFormatter getTimeFormatter() {
-        return getTimeFormatter(context);
+    public static DateTimeFormatter getTimeFormatter(Context ctx) {
+        return DateTimeFormat.forPattern(((SimpleDateFormat) android.text.format.DateFormat.getTimeFormat(ctx)).toLocalizedPattern());
     }
 
     @NonNull
-    public DateTimeFormatter getDateFormatter() {
-        return getDateFormatter(context);
+    public static DateTimeFormatter getDateFormatter(Context ctx) {
+        return DateTimeFormat.forPattern(((SimpleDateFormat) android.text.format.DateFormat.getDateFormat(ctx)).toLocalizedPattern());
     }
 
     @NonNull
-    public DateTimeFormatter getLongDateFormatter() {
-        return getLongDateFormatter(context);
+    public static DateTimeFormatter getLongDateFormatter(Context ctx) {
+        return DateTimeFormat.forPattern(((SimpleDateFormat) android.text.format.DateFormat.getLongDateFormat(ctx)).toLocalizedPattern());
     }
 
     @NonNull
-    public  DateTimeFormatter getMediumDateFormatter() {
-        return getMediumDateFormatter(context);
+    public static DateTimeFormatter getMediumDateFormatter(Context ctx) {
+        return DateTimeFormat.forPattern(((SimpleDateFormat) android.text.format.DateFormat.getMediumDateFormat(ctx)).toLocalizedPattern());
     }
 
     @NonNull
-    public static DateTimeFormatter getTimeFormatter(Context ctx) {
-        return DateTimeFormat.forPattern(((SimpleDateFormat) android.text.format.DateFormat.getTimeFormat(ctx)).toLocalizedPattern());
+    public DateTimeFormatter getTimeFormatter() {
+        return getTimeFormatter(context);
     }
 
     @NonNull
-    public static DateTimeFormatter getDateFormatter(Context ctx) {
-        return DateTimeFormat.forPattern(((SimpleDateFormat) android.text.format.DateFormat.getDateFormat(ctx)).toLocalizedPattern());
+    public DateTimeFormatter getDateFormatter() {
+        return getDateFormatter(context);
     }
 
     @NonNull
-    public static DateTimeFormatter getLongDateFormatter(Context ctx) {
-        return DateTimeFormat.forPattern(((SimpleDateFormat) android.text.format.DateFormat.getLongDateFormat(ctx)).toLocalizedPattern());
+    public DateTimeFormatter getLongDateFormatter() {
+        return getLongDateFormatter(context);
     }
 
     @NonNull
-    public static DateTimeFormatter getMediumDateFormatter(Context ctx) {
-        return DateTimeFormat.forPattern(((SimpleDateFormat) android.text.format.DateFormat.getMediumDateFormat(ctx)).toLocalizedPattern());
+    public DateTimeFormatter getMediumDateFormatter() {
+        return getMediumDateFormatter(context);
     }
 }
diff --git a/app/src/main/java/de/kuschku/util/ui/MessageUtil.java b/app/src/main/java/de/kuschku/util/ui/MessageUtil.java
index cd301f37a..e14b2ea81 100644
--- a/app/src/main/java/de/kuschku/util/ui/MessageUtil.java
+++ b/app/src/main/java/de/kuschku/util/ui/MessageUtil.java
@@ -21,12 +21,12 @@ public class MessageUtil {
     public static SpannableString parseStyleCodes(ThemeUtil themeUtil, String content, boolean parse) {
         if (!parse) {
             return new SpannableString(content
-                    .replaceAll("\\x02","")
-                    .replaceAll("\\x0F","")
-                    .replaceAll("\\x1D","")
-                    .replaceAll("\\x1F","")
-                    .replaceAll("\\x03[0-9]{1,2}(,[0-9]{1,2})?","")
-                    .replaceAll("\\x03",""));
+                    .replaceAll("\\x02", "")
+                    .replaceAll("\\x0F", "")
+                    .replaceAll("\\x1D", "")
+                    .replaceAll("\\x1F", "")
+                    .replaceAll("\\x03[0-9]{1,2}(,[0-9]{1,2})?", "")
+                    .replaceAll("\\x03", ""));
         }
 
         final char boldIndicator = 2;
-- 
GitLab