diff --git a/.gitmodules b/.gitmodules
index 17cbe617..d0f56166 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -5,3 +5,6 @@
[submodule "libs/openpgp-api-lib"]
path = libs/openpgp-api-lib
url = https://github.com/open-keychain/openpgp-api-lib.git
+[submodule "libs/MemorizingTrustManager"]
+ path = libs/MemorizingTrustManager
+ url = https://github.com/ge0rg/MemorizingTrustManager
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a755c98a..44bfd18d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -105,6 +105,7 @@
+
diff --git a/libs/MemorizingTrustManager b/libs/MemorizingTrustManager
new file mode 160000
index 00000000..7610570f
--- /dev/null
+++ b/libs/MemorizingTrustManager
@@ -0,0 +1 @@
+Subproject commit 7610570fd89194af108b417b47951b8e97814245
diff --git a/project.properties b/project.properties
index 86ae5d0c..7276fb94 100644
--- a/project.properties
+++ b/project.properties
@@ -14,3 +14,4 @@
target=android-19
android.library.reference.1=libs/minidns
android.library.reference.2=libs/openpgp-api-lib
+android.library.reference.3=libs/MemorizingTrustManager
diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java
index 0fecbb35..03d1a48d 100644
--- a/src/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/eu/siacs/conversations/services/XmppConnectionService.java
@@ -15,6 +15,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection;
+import de.duenndns.ssl.MemorizingTrustManager;
+
import net.java.otr4j.OtrException;
import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus;
@@ -90,6 +92,8 @@ public class XmppConnectionService extends Service {
public static final long CARBON_GRACE_PERIOD = 60000L;
private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
+
+ private MemorizingTrustManager mMemorizingTrustManager;
private MessageParser mMessageParser = new MessageParser(this);
private PresenceParser mPresenceParser = new PresenceParser(this);
@@ -106,7 +110,6 @@ public class XmppConnectionService extends Service {
private int convChangedListenerCount = 0;
private OnAccountUpdate mOnAccountUpdate = null;
private OnRosterUpdate mOnRosterUpdate = null;
- private OnTLSExceptionReceived tlsException = null;
public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() {
@Override
@@ -121,11 +124,6 @@ public class XmppConnectionService extends Service {
}
};
- public void setOnTLSExceptionReceivedListener(
- OnTLSExceptionReceived listener) {
- tlsException = listener;
- }
-
private SecureRandom mRandom;
private ContentObserver contactObserver = new ContentObserver(null) {
@@ -368,6 +366,9 @@ public class XmppConnectionService extends Service {
ExceptionHelper.init(getApplicationContext());
PRNGFixes.apply();
this.mRandom = new SecureRandom();
+ this.mMemorizingTrustManager = new MemorizingTrustManager(getApplicationContext());
+ this.mMemorizingTrustManager.wrapHostnameVerifier(
+ new org.apache.http.conn.ssl.StrictHostnameVerifier());
this.databaseBackend = DatabaseBackend
.getInstance(getApplicationContext());
this.fileBackend = new FileBackend(getApplicationContext());
@@ -467,19 +468,6 @@ public class XmppConnectionService extends Service {
connection
.setOnUnregisteredIqPacketReceivedListener(this.mIqParser);
connection.setOnJinglePacketReceivedListener(this.jingleListener);
- connection
- .setOnTLSExceptionReceivedListener(new OnTLSExceptionReceived() {
-
- @Override
- public void onTLSExceptionReceived(String fingerprint,
- Account account) {
- Log.d(LOGTAG, "tls exception arrived in service");
- if (tlsException != null) {
- tlsException.onTLSExceptionReceived(fingerprint,
- account);
- }
- }
- });
connection.setOnBindListener(this.mOnBindListener);
return connection;
}
@@ -1214,10 +1202,6 @@ public class XmppConnectionService extends Service {
this.databaseBackend.updateConversation(conversation);
}
- public void removeOnTLSExceptionReceivedListener() {
- this.tlsException = null;
- }
-
public void reconnectAccount(final Account account, final boolean force) {
new Thread(new Runnable() {
@@ -1338,6 +1322,10 @@ public class XmppConnectionService extends Service {
public SecureRandom getRNG() {
return this.mRandom;
}
+
+ public MemorizingTrustManager getMemorizingTrustManager() {
+ return this.mMemorizingTrustManager;
+ }
public PowerManager getPowerManager() {
return this.pm;
diff --git a/src/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/eu/siacs/conversations/ui/ManageAccountActivity.java
index 6fa1ec31..ed657902 100644
--- a/src/eu/siacs/conversations/ui/ManageAccountActivity.java
+++ b/src/eu/siacs/conversations/ui/ManageAccountActivity.java
@@ -59,57 +59,6 @@ public class ManageAccountActivity extends XmppActivity {
}
};
- protected OnTLSExceptionReceived tlsExceptionReceived = new OnTLSExceptionReceived() {
-
- @Override
- public void onTLSExceptionReceived(final String fingerprint,
- final Account account) {
- activity.runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- AlertDialog.Builder builder = new AlertDialog.Builder(
- activity);
- builder.setTitle(getString(R.string.account_status_error));
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- View view = (View) getLayoutInflater().inflate(
- R.layout.cert_warning, null);
- TextView sha = (TextView) view.findViewById(R.id.sha);
- TextView hint = (TextView) view.findViewById(R.id.hint);
- StringBuilder humanReadableSha = new StringBuilder();
- humanReadableSha.append(fingerprint);
- for (int i = 2; i < 59; i += 3) {
- if ((i == 14) || (i == 29) || (i == 44)) {
- humanReadableSha.insert(i, "\n");
- } else {
- humanReadableSha.insert(i, ":");
- }
-
- }
- hint.setText(getString(R.string.untrusted_cert_hint,
- account.getServer()));
- sha.setText(humanReadableSha.toString());
- builder.setView(view);
- builder.setNegativeButton(
- getString(R.string.certif_no_trust), null);
- builder.setPositiveButton(getString(R.string.certif_trust),
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- account.setSSLCertFingerprint(fingerprint);
- activity.xmppConnectionService
- .updateAccount(account);
- }
- });
- builder.create().show();
- }
- });
-
- }
- };
-
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -471,7 +420,6 @@ public class ManageAccountActivity extends XmppActivity {
protected void onStop() {
if (xmppConnectionServiceBound) {
xmppConnectionService.removeOnAccountListChangedListener();
- xmppConnectionService.removeOnTLSExceptionReceivedListener();
}
super.onStop();
}
@@ -479,8 +427,6 @@ public class ManageAccountActivity extends XmppActivity {
@Override
void onBackendConnected() {
xmppConnectionService.setOnAccountListChangedListener(accountChanged);
- xmppConnectionService
- .setOnTLSExceptionReceivedListener(tlsExceptionReceived);
this.accountList.clear();
this.accountList.addAll(xmppConnectionService.getAccounts());
accountListViewAdapter.notifyDataSetChanged();
diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java
index 72018394..6d2dec20 100644
--- a/src/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -21,6 +21,7 @@ import java.util.Hashtable;
import java.util.List;
import java.util.Map.Entry;
+import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
@@ -28,8 +29,12 @@ import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
+import org.bouncycastle.pqc.math.linearalgebra.GoppaCode.MaMaPe;
import org.xmlpull.v1.XmlPullParserException;
+import de.duenndns.ssl.MemorizingTrustManager;
+
+import android.content.Context;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
@@ -97,11 +102,12 @@ public class XmppConnection implements Runnable {
private OnIqPacketReceived unregisteredIqListener = null;
private OnMessagePacketReceived messageListener = null;
private OnStatusChanged statusListener = null;
- private OnTLSExceptionReceived tlsListener = null;
private OnBindListener bindListener = null;
+ private MemorizingTrustManager mMemorizingTrustManager;
public XmppConnection(Account account, XmppConnectionService service) {
this.mRandom = service.getRNG();
+ this.mMemorizingTrustManager = service.getMemorizingTrustManager();
this.account = account;
this.wakeLock = service.getPowerManager().newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
account.getJid());
@@ -440,67 +446,19 @@ public class XmppConnection implements Runnable {
tagReader.readTag();
try {
SSLContext sc = SSLContext.getInstance("TLS");
- TrustManagerFactory tmf = TrustManagerFactory
- .getInstance(TrustManagerFactory.getDefaultAlgorithm());
- try {
- tmf.init((KeyStore) null);
- } catch (KeyStoreException e1) {
- e1.printStackTrace();
- }
-
- TrustManager[] trustManagers = tmf.getTrustManagers();
- final X509TrustManager origTrustmanager = (X509TrustManager) trustManagers[0];
-
- TrustManager[] wrappedTrustManagers = new TrustManager[] { new X509TrustManager() {
-
- @Override
- public void checkClientTrusted(X509Certificate[] chain,
- String authType) throws CertificateException {
- origTrustmanager.checkClientTrusted(chain, authType);
- }
-
- @Override
- public void checkServerTrusted(X509Certificate[] chain,
- String authType) throws CertificateException {
- try {
- origTrustmanager.checkServerTrusted(chain, authType);
- } catch (CertificateException e) {
- if (e.getCause() instanceof CertPathValidatorException) {
- String sha;
- try {
- MessageDigest sha1 = MessageDigest
- .getInstance("SHA1");
- sha1.update(chain[0].getEncoded());
- sha = CryptoHelper.bytesToHex(sha1.digest());
- if (!sha.equals(account.getSSLFingerprint())) {
- changeStatus(Account.STATUS_TLS_ERROR);
- if (tlsListener != null) {
- tlsListener.onTLSExceptionReceived(sha,
- account);
- }
- throw new CertificateException();
- }
- } catch (NoSuchAlgorithmException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
- } else {
- throw new CertificateException();
- }
- }
- }
-
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return origTrustmanager.getAcceptedIssuers();
- }
-
- } };
- sc.init(null, wrappedTrustManagers, null);
+ sc.init(null, new X509TrustManager[] { this.mMemorizingTrustManager }, mRandom);
SSLSocketFactory factory = sc.getSocketFactory();
+
+ HostnameVerifier verifier = this.mMemorizingTrustManager.wrapHostnameVerifier(new org.apache.http.conn.ssl.StrictHostnameVerifier());
SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket,
socket.getInetAddress().getHostAddress(), socket.getPort(),
true);
+
+ if (verifier != null && !verifier.verify(account.getServer(), sslSocket.getSession())) {
+ Log.d(LOGTAG, account.getJid() + ": host mismatch in TLS connection");
+ sslSocket.close();
+ throw new IOException();
+ }
tagReader.setInputStream(sslSocket.getInputStream());
tagWriter.setOutputStream(sslSocket.getOutputStream());
sendStartStream();
@@ -508,10 +466,8 @@ public class XmppConnection implements Runnable {
processStream(tagReader.readTag());
sslSocket.close();
} catch (NoSuchAlgorithmException e1) {
- // TODO Auto-generated catch block
e1.printStackTrace();
} catch (KeyManagementException e) {
- // TODO Auto-generated catch block
e.printStackTrace();
}
}
@@ -844,11 +800,6 @@ public class XmppConnection implements Runnable {
this.statusListener = listener;
}
- public void setOnTLSExceptionReceivedListener(
- OnTLSExceptionReceived listener) {
- this.tlsListener = listener;
- }
-
public void setOnBindListener(OnBindListener listener) {
this.bindListener = listener;
}