summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Hudson <nick.hudson@isode.com>2014-07-10 13:31:24 (GMT)
committerSwift Review <review@swift.im>2014-07-22 11:43:14 (GMT)
commitf86f1c1df0fc8bfd72306d55d370e202378652b2 (patch)
treeb06484367c13f4b0e704e0adbcf4809f8d961386
parentb6b5d495636f14d776087d9e255e7d7528522734 (diff)
downloadstroke-f86f1c1df0fc8bfd72306d55d370e202378652b2.zip
stroke-f86f1c1df0fc8bfd72306d55d370e202378652b2.tar.bz2
Make Stroke return peer certificate chain, rather then just EE certificate
Since the initial Stroke TLS implementation was done, some changes were made in Swiften, starting with "Show Certificate dialog from certificate error window." 159e773b156f531575d0d7e241e2d20c85ee6d7cA which mean that certificate verification uses the peer's certificate chain, and not just the peer's EE certificate. This change updates Stroke so that its API now more closely matches what Swiften does. Note that any current Stroke clients that implement the "CertificateTrustChecker" interface will break, as this patch makes an incompatible change to that interface, requiring implementing classes to handle a certificate chain rather than a single certificate. Isode copyright notices are updated; Remko copyright notices are updated to reflect the current copyright notices in any equivalent Swiften source files. Test-information: Used MLC (after having patched it for CertificateTrustChecker changes) and verified that it sees the entire certificate chain coming back. Ran self-tests for Stroke and saw no junit failures Change-Id: I3d863f929bfed3324446cadf3bb4d6b9ff916660
-rw-r--r--src/com/isode/stroke/client/ClientSession.java20
-rw-r--r--src/com/isode/stroke/session/BasicSessionStream.java14
-rw-r--r--src/com/isode/stroke/session/SessionStream.java8
-rw-r--r--src/com/isode/stroke/streamstack/TLSLayer.java8
-rw-r--r--src/com/isode/stroke/tls/CertificateTrustChecker.java8
-rw-r--r--src/com/isode/stroke/tls/TLSContext.java13
-rw-r--r--src/com/isode/stroke/tls/java/JSSEContext.java24
7 files changed, 69 insertions, 26 deletions
diff --git a/src/com/isode/stroke/client/ClientSession.java b/src/com/isode/stroke/client/ClientSession.java
index f6082b7..c0caeb6 100644
--- a/src/com/isode/stroke/client/ClientSession.java
+++ b/src/com/isode/stroke/client/ClientSession.java
@@ -1,9 +1,9 @@
/*
- * Copyright (c) 2010-2012 Isode Limited, London, England.
+ * Copyright (c) 2010-2014 Isode Limited, London, England.
* All rights reserved.
*/
/*
- * Copyright (c) 2010-2011 Remko Tronçon.
+ * Copyright (c) 2010-2014 Remko Tronçon.
* All rights reserved.
*/
package com.isode.stroke.client;
@@ -48,6 +48,8 @@ import com.isode.stroke.tls.Certificate;
import com.isode.stroke.tls.CertificateTrustChecker;
import com.isode.stroke.tls.CertificateVerificationError;
import com.isode.stroke.tls.ServerIdentityVerifier;
+
+import java.util.List;
import java.util.UUID;
public class ClientSession {
@@ -513,24 +515,26 @@ public class ClientSession {
if (!checkState(State.Encrypting)) {
return;
}
- final Certificate certificate = stream.getPeerCertificate();
+ final List<Certificate> certificateChain = stream.getPeerCertificateChain();
+ final Certificate peerCertificate =
+ (certificateChain == null || certificateChain.isEmpty() ? null : certificateChain.get(0));
final CertificateVerificationError verificationError = stream.getPeerCertificateVerificationError();
if (verificationError != null) {
- checkTrustOrFinish(certificate, verificationError);
+ checkTrustOrFinish(certificateChain, verificationError);
}
else {
final ServerIdentityVerifier identityVerifier = new ServerIdentityVerifier(localJID);
- if (identityVerifier.certificateVerifies(certificate)) {
+ if (identityVerifier.certificateVerifies(peerCertificate)) {
continueAfterTLSEncrypted();
}
else {
- checkTrustOrFinish(certificate, new CertificateVerificationError(CertificateVerificationError.Type.InvalidServerIdentity));
+ checkTrustOrFinish(certificateChain, new CertificateVerificationError(CertificateVerificationError.Type.InvalidServerIdentity));
}
}
}
- private void checkTrustOrFinish(final Certificate certificate, final CertificateVerificationError error) {
- if (certificateTrustChecker != null && certificateTrustChecker.isCertificateTrusted(certificate)) {
+ private void checkTrustOrFinish(final List<Certificate> certificateChain, final CertificateVerificationError error) {
+ if (certificateTrustChecker != null && certificateTrustChecker.isCertificateTrusted(certificateChain)) {
continueAfterTLSEncrypted();
}
else {
diff --git a/src/com/isode/stroke/session/BasicSessionStream.java b/src/com/isode/stroke/session/BasicSessionStream.java
index dbe13f7..f1e7bf1 100644
--- a/src/com/isode/stroke/session/BasicSessionStream.java
+++ b/src/com/isode/stroke/session/BasicSessionStream.java
@@ -1,17 +1,18 @@
/*
- * Copyright (c) 2010 Remko Tronçon
+ * Copyright (c) 2010-2014 Remko Tronçon
* All rights reserved. */
/*
- * Copyright (c) 2010-2012, Isode Limited, London, England.
+ * Copyright (c) 2010-2014, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.session;
+import java.util.List;
+
import com.isode.stroke.base.ByteArray;
import com.isode.stroke.elements.Element;
import com.isode.stroke.elements.ProtocolHeader;
import com.isode.stroke.elements.StreamType;
-import com.isode.stroke.eventloop.EventLoop;
import com.isode.stroke.network.Connection;
import com.isode.stroke.network.TimerFactory;
import com.isode.stroke.parser.PayloadParserFactoryCollection;
@@ -23,10 +24,10 @@ import com.isode.stroke.streamstack.ConnectionLayer;
import com.isode.stroke.streamstack.StreamStack;
import com.isode.stroke.streamstack.TLSLayer;
import com.isode.stroke.streamstack.WhitespacePingLayer;
-import com.isode.stroke.tls.TLSContextFactory;
import com.isode.stroke.streamstack.XMPPLayer;
import com.isode.stroke.tls.Certificate;
import com.isode.stroke.tls.CertificateVerificationError;
+import com.isode.stroke.tls.TLSContextFactory;
public class BasicSessionStream extends SessionStream {
@@ -156,6 +157,11 @@ public class BasicSessionStream extends SessionStream {
return tlsLayer != null;
}
+ @Override
+ public List<Certificate> getPeerCertificateChain() {
+ return tlsLayer.getPeerCertificateChain();
+ }
+ @Override
public Certificate getPeerCertificate() {
return tlsLayer.getPeerCertificate();
}
diff --git a/src/com/isode/stroke/session/SessionStream.java b/src/com/isode/stroke/session/SessionStream.java
index 5dbb0fc..2b9932b 100644
--- a/src/com/isode/stroke/session/SessionStream.java
+++ b/src/com/isode/stroke/session/SessionStream.java
@@ -1,13 +1,15 @@
/*
- * Copyright (c) 2010 Remko Tronçon
+ * Copyright (c) 2010-2014 Remko Tronçon
* All rights reserved.
*/
/*
- * Copyright (c) 2010-2012, Isode Limited, London, England.
+ * Copyright (c) 2010-2014, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.session;
+import java.util.List;
+
import com.isode.stroke.base.ByteArray;
import com.isode.stroke.elements.Element;
import com.isode.stroke.elements.ProtocolHeader;
@@ -16,7 +18,6 @@ import com.isode.stroke.signals.Signal1;
import com.isode.stroke.tls.Certificate;
import com.isode.stroke.tls.CertificateVerificationError;
import com.isode.stroke.tls.CertificateWithKey;
-import com.isode.stroke.tls.PKCS12Certificate;
public abstract class SessionStream {
@@ -69,6 +70,7 @@ public abstract class SessionStream {
return certificate != null && !certificate.isNull();
}
+ public abstract List<Certificate> getPeerCertificateChain();
public abstract Certificate getPeerCertificate();
public abstract CertificateVerificationError getPeerCertificateVerificationError();
diff --git a/src/com/isode/stroke/streamstack/TLSLayer.java b/src/com/isode/stroke/streamstack/TLSLayer.java
index 1f213fc..70bcd1a 100644
--- a/src/com/isode/stroke/streamstack/TLSLayer.java
+++ b/src/com/isode/stroke/streamstack/TLSLayer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2012, Isode Limited, London, England.
+ * Copyright (c) 2010-2014, Isode Limited, London, England.
* All rights reserved.
*/
/*
@@ -9,6 +9,8 @@
package com.isode.stroke.streamstack;
+import java.util.List;
+
import com.isode.stroke.base.ByteArray;
import com.isode.stroke.signals.Signal;
import com.isode.stroke.signals.Slot1;
@@ -54,6 +56,10 @@ public class TLSLayer extends StreamLayer {
return context.setClientCertificate(certificate);
}
+ public List<Certificate> getPeerCertificateChain() {
+ return context.getPeerCertificateChain();
+ }
+
public Certificate getPeerCertificate() {
return context.getPeerCertificate();
}
diff --git a/src/com/isode/stroke/tls/CertificateTrustChecker.java b/src/com/isode/stroke/tls/CertificateTrustChecker.java
index 2fcf3c0..7f4753b 100644
--- a/src/com/isode/stroke/tls/CertificateTrustChecker.java
+++ b/src/com/isode/stroke/tls/CertificateTrustChecker.java
@@ -4,11 +4,14 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
/*
- * Copyright (c) 2011, Isode Limited, London, England.
+ * Copyright (c) 2011-2014, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.tls;
+import java.util.List;
+
+
/**
* A class to implement a check for certificate trust.
*/
@@ -19,5 +22,6 @@ public interface CertificateTrustChecker {
* trusted. This usually happens when a certificate's validation
* fails, to check whether to proceed with the connection or not.
*/
- boolean isCertificateTrusted(Certificate certificate);
+ public boolean isCertificateTrusted(List<Certificate> chain);
+
}
diff --git a/src/com/isode/stroke/tls/TLSContext.java b/src/com/isode/stroke/tls/TLSContext.java
index ec39a3b..738c8b6 100644
--- a/src/com/isode/stroke/tls/TLSContext.java
+++ b/src/com/isode/stroke/tls/TLSContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, Isode Limited, London, England.
+ * Copyright (c) 2011-2014, Isode Limited, London, England.
* All rights reserved.
*/
/*
@@ -9,6 +9,8 @@
package com.isode.stroke.tls;
+import java.util.List;
+
import com.isode.stroke.base.ByteArray;
import com.isode.stroke.signals.Signal;
import com.isode.stroke.signals.Signal1;
@@ -22,7 +24,16 @@ public abstract class TLSContext {
public abstract void handleDataFromNetwork(ByteArray data);
public abstract void handleDataFromApplication(ByteArray data);
+ /**
+ * The peer certificate, as presented by the remote entity
+ * @return the peer certificate, which may be null
+ */
public abstract Certificate getPeerCertificate();
+ /**
+ * The peer's certificate chain, as presented by the remote entity
+ * @return the peer certificate chain, which may be null.
+ */
+ public abstract List<Certificate> getPeerCertificateChain();
public abstract CertificateVerificationError getPeerCertificateVerificationError();
public abstract ByteArray getFinishMessage();
diff --git a/src/com/isode/stroke/tls/java/JSSEContext.java b/src/com/isode/stroke/tls/java/JSSEContext.java
index 2928498..13904e8 100644
--- a/src/com/isode/stroke/tls/java/JSSEContext.java
+++ b/src/com/isode/stroke/tls/java/JSSEContext.java
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, Isode Limited, London, England.
+/* Copyright (c) 2012-2014, Isode Limited, London, England.
* All rights reserved.
*
* Acquisition and use of this software and related materials for any
@@ -26,7 +26,9 @@ import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.logging.Level;
@@ -627,8 +629,10 @@ public class JSSEContext extends TLSContext {
if (certs == null || certs.length == 0) {
return;
}
-
- peerCertificate = new JavaCertificate(certs[0]);
+ peerCertificateChain = new ArrayList<Certificate>(certs.length);
+ for (X509Certificate x509:certs) {
+ peerCertificateChain.add(new JavaCertificate(x509));
+ }
/* Swiften uses SSL_get_verify_result() for this, and the documentation
* for that says it "while the verification of a certificate can fail
@@ -1052,10 +1056,16 @@ public class JSSEContext extends TLSContext {
}
-
+ @Override
+ public List<Certificate> getPeerCertificateChain() {
+ return peerCertificateChain;
+ }
@Override
public Certificate getPeerCertificate() {
- return peerCertificate;
+ if (peerCertificateChain == null || peerCertificateChain.isEmpty()) {
+ return null;
+ }
+ return (peerCertificateChain.get(0));
}
@Override
@@ -1161,9 +1171,9 @@ public class JSSEContext extends TLSContext {
private Object recvMutex = new Object();
/**
- * The server certificate as obtained from the TLS handshake
+ * The server certificate chain as obtained from the TLS handshake
*/
- private JavaCertificate peerCertificate = null;
+ private List<Certificate> peerCertificateChain = null;
/**
* The CertificateVerificationError derived from the peerCertificate. This