summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/com/isode/stroke/client/ClientOptions.java8
-rw-r--r--src/com/isode/stroke/client/ClientSession.java8
-rw-r--r--src/com/isode/stroke/client/CoreClient.java9
-rw-r--r--src/com/isode/stroke/component/ComponentSession.java6
-rw-r--r--src/com/isode/stroke/component/CoreComponent.java8
-rw-r--r--src/com/isode/stroke/session/BasicSessionStream.java73
-rw-r--r--src/com/isode/stroke/session/Session.java59
-rw-r--r--src/com/isode/stroke/session/SessionStream.java20
-rw-r--r--src/com/isode/stroke/streamstack/TLSLayer.java9
-rw-r--r--src/com/isode/stroke/tls/BlindCertificateTrustChecker.java30
-rw-r--r--src/com/isode/stroke/tls/Certificate.java5
-rw-r--r--src/com/isode/stroke/tls/CertificateVerificationError.java11
-rw-r--r--src/com/isode/stroke/tls/ServerIdentityVerifier.java15
-rw-r--r--src/com/isode/stroke/tls/SimpleCertificate.java78
-rw-r--r--src/com/isode/stroke/tls/TLSContext.java3
-rw-r--r--src/com/isode/stroke/tls/TLSContextFactory.java4
-rw-r--r--src/com/isode/stroke/tls/TLSError.java36
-rw-r--r--src/com/isode/stroke/tls/TLSOptions.java25
-rw-r--r--src/com/isode/stroke/tls/java/JSSEContext.java7
-rw-r--r--src/com/isode/stroke/tls/java/JSSEContextFactory.java3
-rw-r--r--test/com/isode/stroke/component/ComponentSessionTest.java8
-rw-r--r--test/com/isode/stroke/tls/CertificateTest.java36
-rw-r--r--test/com/isode/stroke/tls/ServerIdentityVerifierTest.java185
23 files changed, 567 insertions, 79 deletions
diff --git a/src/com/isode/stroke/client/ClientOptions.java b/src/com/isode/stroke/client/ClientOptions.java
index 4144214..b410094 100644
--- a/src/com/isode/stroke/client/ClientOptions.java
+++ b/src/com/isode/stroke/client/ClientOptions.java
@@ -8,6 +8,9 @@
*/
package com.isode.stroke.client;
+
+import com.isode.stroke.tls.TLSOptions;
+
/**
* Options for a client connection
*/
@@ -58,6 +61,11 @@ public class ClientOptions {
public int manualPort;
+ /**
+ * Options passed to the TLS stack
+ */
+ public TLSOptions tlsOptions = new TLSOptions();
+
public enum UseTLS {
NeverUseTLS,
UseTLSWhenAvailable,
diff --git a/src/com/isode/stroke/client/ClientSession.java b/src/com/isode/stroke/client/ClientSession.java
index fe9481d..b8ec4a9 100644
--- a/src/com/isode/stroke/client/ClientSession.java
+++ b/src/com/isode/stroke/client/ClientSession.java
@@ -212,8 +212,8 @@ public class ClientSession {
handleElement(p1);
}
});
- streamClosedConnection = stream.onClosed.connect(new Slot1<SessionStream.Error>(){
- public void call(final SessionStream.Error p1) {
+ streamClosedConnection = stream.onClosed.connect(new Slot1<com.isode.stroke.base.Error>(){
+ public void call(final com.isode.stroke.base.Error p1) {
handleStreamClosed(p1);
}
});
@@ -535,7 +535,7 @@ public class ClientSession {
checkTrustOrFinish(certificateChain, verificationError);
}
else {
- final ServerIdentityVerifier identityVerifier = new ServerIdentityVerifier(localJID);
+ final ServerIdentityVerifier identityVerifier = new ServerIdentityVerifier(localJID, idnConverter);
if (identityVerifier.certificateVerifies(peerCertificate)) {
continueAfterTLSEncrypted();
}
@@ -560,7 +560,7 @@ public class ClientSession {
sendStreamHeader();
}
- private void handleStreamClosed(final SessionStream.Error streamError) {
+ private void handleStreamClosed(final com.isode.stroke.base.Error streamError) {
final State previousState = state;
state = State.Finished;
diff --git a/src/com/isode/stroke/client/CoreClient.java b/src/com/isode/stroke/client/CoreClient.java
index 39229a3..74ba031 100644
--- a/src/com/isode/stroke/client/CoreClient.java
+++ b/src/com/isode/stroke/client/CoreClient.java
@@ -29,6 +29,7 @@ import com.isode.stroke.signals.Slot2;
import com.isode.stroke.tls.CertificateTrustChecker;
import com.isode.stroke.tls.CertificateVerificationError;
import com.isode.stroke.tls.CertificateWithKey;
+import com.isode.stroke.tls.TLSOptions;
/**
* The central class for communicating with an XMPP server.
@@ -246,7 +247,7 @@ public class CoreClient {
connection_ = connection;
assert (sessionStream_ == null);
- sessionStream_ = new BasicSessionStream(StreamType.ClientStreamType, connection_, payloadParserFactories_, payloadSerializers_, networkFactories.getTLSContextFactory(), networkFactories.getTimerFactory());
+ sessionStream_ = new BasicSessionStream(StreamType.ClientStreamType, connection_, payloadParserFactories_, payloadSerializers_, networkFactories.getTLSContextFactory(), networkFactories.getTimerFactory(), options.tlsOptions);
if (certificate_ != null && !certificate_.isNull()) {
sessionStream_.setTLSCertificate(certificate_);
}
@@ -352,8 +353,8 @@ public class CoreClient {
break;
/* Note: no case clause for "StreamError" */
}
- } else if (error instanceof SessionStream.Error) {
- SessionStream.Error actualError = (SessionStream.Error) error;
+ } else if (error instanceof SessionStream.SessionStreamError) {
+ SessionStream.SessionStreamError actualError = (SessionStream.SessionStreamError) error;
switch (actualError.type) {
case ParseError:
clientError = new ClientError(ClientError.Type.XMLError);
@@ -373,7 +374,7 @@ public class CoreClient {
}
} else if (error instanceof CertificateVerificationError) {
CertificateVerificationError verificationError = (CertificateVerificationError)error;
- switch (verificationError.type) {
+ switch (verificationError.getType()) {
case UnknownError:
clientError = new ClientError(ClientError.Type.UnknownCertificateError);
break;
diff --git a/src/com/isode/stroke/component/ComponentSession.java b/src/com/isode/stroke/component/ComponentSession.java
index 40bb391..4fd2d3a 100644
--- a/src/com/isode/stroke/component/ComponentSession.java
+++ b/src/com/isode/stroke/component/ComponentSession.java
@@ -84,9 +84,9 @@ public class ComponentSession {
handleElement(e1);
}
});
- onClosedConnection = stream.onClosed.connect(new Slot1<SessionStream.Error>() {
+ onClosedConnection = stream.onClosed.connect(new Slot1<com.isode.stroke.base.Error>() {
@Override
- public void call(SessionStream.Error e1) {
+ public void call(com.isode.stroke.base.Error e1) {
handleStreamClosed(e1);
}
});
@@ -172,7 +172,7 @@ public class ComponentSession {
stream.writeElement(new ComponentHandshake(ComponentHandshakeGenerator.getHandshake(header.getID(), secret, crypto)));
}
- private void handleStreamClosed(SessionStream.Error streamError) {
+ private void handleStreamClosed(com.isode.stroke.base.Error streamError) {
State oldState = state;
state = State.Finished;
stream.setWhitespacePingEnabled(false);
diff --git a/src/com/isode/stroke/component/CoreComponent.java b/src/com/isode/stroke/component/CoreComponent.java
index 63b9cd5..cc51873 100644
--- a/src/com/isode/stroke/component/CoreComponent.java
+++ b/src/com/isode/stroke/component/CoreComponent.java
@@ -35,6 +35,7 @@ import com.isode.stroke.signals.Signal;
import com.isode.stroke.signals.SignalConnection;
import com.isode.stroke.signals.Slot1;
import com.isode.stroke.client.StanzaChannel;
+import com.isode.stroke.tls.TLSOptions;
/**
* The central class for communicating with an XMPP server as a component.
@@ -178,8 +179,7 @@ public class CoreComponent extends Entity {
connection_ = connection;
assert(sessionStream_ == null);
- //TODO: PORT TLSOPTIONS.
- sessionStream_ = new BasicSessionStream(StreamType.ComponentStreamType, connection_, getPayloadParserFactories(), getPayloadSerializers(), null, networkFactories.getTimerFactory());
+ sessionStream_ = new BasicSessionStream(StreamType.ComponentStreamType, connection_, getPayloadParserFactories(), getPayloadSerializers(), null, networkFactories.getTimerFactory(), new TLSOptions());
onDataReadConnection = sessionStream_.onDataRead.connect(new Slot1<SafeByteArray>() {
@Override
public void call(SafeByteArray s1) {
@@ -235,8 +235,8 @@ public class CoreComponent extends Entity {
break;
}
}
- else if(error instanceof SessionStream.Error) {
- SessionStream.Error actualError = (SessionStream.Error)error;
+ else if(error instanceof SessionStream.SessionStreamError) {
+ SessionStream.SessionStreamError actualError = (SessionStream.SessionStreamError)error;
switch(actualError.type) {
case ParseError:
componentError = new ComponentError(ComponentError.Type.XMLError);
diff --git a/src/com/isode/stroke/session/BasicSessionStream.java b/src/com/isode/stroke/session/BasicSessionStream.java
index 9ba862d..4048363 100644
--- a/src/com/isode/stroke/session/BasicSessionStream.java
+++ b/src/com/isode/stroke/session/BasicSessionStream.java
@@ -20,6 +20,7 @@ import com.isode.stroke.parser.PayloadParserFactoryCollection;
import com.isode.stroke.serializer.PayloadSerializerCollection;
import com.isode.stroke.signals.Slot;
import com.isode.stroke.signals.Slot1;
+import com.isode.stroke.signals.SignalConnection;
import com.isode.stroke.streamstack.CompressionLayer;
import com.isode.stroke.streamstack.ConnectionLayer;
import com.isode.stroke.streamstack.StreamStack;
@@ -29,6 +30,8 @@ import com.isode.stroke.streamstack.XMPPLayer;
import com.isode.stroke.tls.Certificate;
import com.isode.stroke.tls.CertificateVerificationError;
import com.isode.stroke.tls.TLSContextFactory;
+import com.isode.stroke.tls.TLSOptions;
+import com.isode.stroke.tls.TLSError;
public class BasicSessionStream extends SessionStream {
@@ -38,7 +41,8 @@ public class BasicSessionStream extends SessionStream {
PayloadParserFactoryCollection payloadParserFactories,
PayloadSerializerCollection payloadSerializers,
TLSContextFactory tlsContextFactory,
- TimerFactory timerFactory) {
+ TimerFactory timerFactory,
+ TLSOptions tlsOptions) {
available = false;
this.connection = connection;
this.payloadParserFactories = payloadParserFactories;
@@ -52,40 +56,40 @@ public class BasicSessionStream extends SessionStream {
this.compressionLayer = null;
this.tlsLayer = null;
this.whitespacePingLayer = null;
-
+ this.tlsOptions_ = tlsOptions;
xmppLayer = new XMPPLayer(payloadParserFactories, payloadSerializers, streamType);
- xmppLayer.onStreamStart.connect(new Slot1<ProtocolHeader>() {
+ onStreamStartConnection = xmppLayer.onStreamStart.connect(new Slot1<ProtocolHeader>() {
public void call(ProtocolHeader p1) {
handleStreamStartReceived(p1);
}
});
- xmppLayer.onElement.connect(new Slot1<Element>() {
+ onElementConnection = xmppLayer.onElement.connect(new Slot1<Element>() {
public void call(Element p1) {
handleElementReceived(p1);
}
});
- xmppLayer.onError.connect(new Slot() {
+ onErrorConnection = xmppLayer.onError.connect(new Slot() {
public void call() {
handleXMPPError();
}
});
- xmppLayer.onDataRead.connect(new Slot1<SafeByteArray>() {
+ onDataReadConnection = xmppLayer.onDataRead.connect(new Slot1<SafeByteArray>() {
public void call(SafeByteArray p1) {
handleDataRead(p1);
}
});
- xmppLayer.onWriteData.connect(new Slot1<SafeByteArray>() {
+ onWriteDataConnection = xmppLayer.onWriteData.connect(new Slot1<SafeByteArray>() {
public void call(SafeByteArray p1) {
handleDataWritten(p1);
}
});
- connection.onDisconnected.connect(new Slot1<Connection.Error>() {
+ onDisconnectedConnection = connection.onDisconnected.connect(new Slot1<Connection.Error>() {
public void call(Connection.Error p1) {
handleConnectionFinished(p1);
@@ -99,6 +103,28 @@ public class BasicSessionStream extends SessionStream {
}
+ /**
+ * User will have to call disconnect() method to free up the resources.
+ */
+ @Override
+ public void disconnect() {
+ if(tlsLayer != null) {
+ onErrorConnection.disconnect();
+ onConnectedConnection.disconnect();
+ tlsLayer = null;
+ }
+ whitespacePingLayer = null;
+ streamStack = null;
+ onDisconnectedConnection.disconnect();
+ connectionLayer = null;
+ onStreamStartConnection.disconnect();
+ onElementConnection.disconnect();
+ onErrorConnection.disconnect();
+ onDataReadConnection.disconnect();
+ onWriteDataConnection.disconnect();
+ xmppLayer = null;
+ }
+
public void writeHeader(ProtocolHeader header) {
assert available;
xmppLayer.writeHeader(header);
@@ -133,18 +159,18 @@ public class BasicSessionStream extends SessionStream {
public void addTLSEncryption() {
assert available;
- tlsLayer = new TLSLayer(tlsContextFactory);
+ tlsLayer = new TLSLayer(tlsContextFactory, tlsOptions_);
if (hasTLSCertificate() && !tlsLayer.setClientCertificate(getTLSCertificate())) {
- onClosed.emit(new Error(Error.Type.InvalidTLSCertificateError));
+ onClosed.emit(new SessionStreamError(SessionStreamError.Type.InvalidTLSCertificateError));
} else {
streamStack.addLayer(tlsLayer);
- tlsLayer.onError.connect(new Slot() {
+ onErrorConnection = tlsLayer.onError.connect(new Slot1<TLSError>() {
- public void call() {
- handleTLSError();
+ public void call(TLSError e) {
+ handleTLSError(e);
}
});
- tlsLayer.onConnected.connect(new Slot() {
+ onConnectedConnection = tlsLayer.onConnected.connect(new Slot() {
public void call() {
handleTLSConnected();
@@ -211,25 +237,25 @@ public class BasicSessionStream extends SessionStream {
private void handleXMPPError() {
available = false;
- onClosed.emit(new Error(Error.Type.ParseError));
+ onClosed.emit(new SessionStreamError(SessionStreamError.Type.ParseError));
}
private void handleTLSConnected() {
onTLSEncrypted.emit();
}
- private void handleTLSError() {
+ private void handleTLSError(TLSError error) {
available = false;
- onClosed.emit(new Error(Error.Type.TLSError));
+ onClosed.emit(error);
}
private void handleConnectionFinished(Connection.Error error) {
available = false;
if (Connection.Error.ReadError.equals(error)) {
- onClosed.emit(new Error(Error.Type.ConnectionReadError));
+ onClosed.emit(new SessionStreamError(SessionStreamError.Type.ConnectionReadError));
}
else if (error != null) {
- onClosed.emit(new Error(Error.Type.ConnectionWriteError));
+ onClosed.emit(new SessionStreamError(SessionStreamError.Type.ConnectionWriteError));
}
else {
onClosed.emit(null);
@@ -262,5 +288,12 @@ public class BasicSessionStream extends SessionStream {
private TLSLayer tlsLayer;
private WhitespacePingLayer whitespacePingLayer;
private StreamStack streamStack;
-
+ private TLSOptions tlsOptions_;
+ private SignalConnection onErrorConnection;
+ private SignalConnection onConnectedConnection;
+ private SignalConnection onDisconnectedConnection;
+ private SignalConnection onStreamStartConnection;
+ private SignalConnection onElementConnection;
+ private SignalConnection onDataReadConnection;
+ private SignalConnection onWriteDataConnection;
}
diff --git a/src/com/isode/stroke/session/Session.java b/src/com/isode/stroke/session/Session.java
index 815be02..0255627 100644
--- a/src/com/isode/stroke/session/Session.java
+++ b/src/com/isode/stroke/session/Session.java
@@ -22,6 +22,7 @@ import com.isode.stroke.serializer.PayloadSerializerCollection;
import com.isode.stroke.signals.Signal1;
import com.isode.stroke.signals.Slot;
import com.isode.stroke.signals.Slot1;
+import com.isode.stroke.signals.SignalConnection;
import com.isode.stroke.streamstack.ConnectionLayer;
import com.isode.stroke.streamstack.StreamStack;
import com.isode.stroke.streamstack.XMPPLayer;
@@ -46,27 +47,31 @@ public abstract class Session {
public Session(
final Connection connection,
final PayloadParserFactoryCollection payloadParserFactories,
- final PayloadSerializerCollection payloadSerializers,
- final EventLoop eventLoop) {
+ final PayloadSerializerCollection payloadSerializers) {
this.connection = connection;
- this.eventLoop = eventLoop;
this.payloadParserFactories = payloadParserFactories;
this.payloadSerializers = payloadSerializers;
+ xmppLayer = null;
+ connectionLayer = null;
+ streamStack = null;
finishing = false;
}
public void startSession() {
initializeStreamStack();
- handleSessionStarted();
+ handleSessionStarted();
}
public void finishSession() {
+ if (finishing) {
+ return;
+ }
finishing = true;
- connection.disconnect();
- handleSessionFinished(null);
- finishing = false;
- onSessionFinished.emit(null);
+ if (xmppLayer != null) {
+ xmppLayer.writeFooter();
+ }
+ connection.disconnect();
}
public void sendElement(Element stanza) {
@@ -94,11 +99,14 @@ public abstract class Session {
}
protected void finishSession(SessionError error) {
+ if (finishing) {
+ return;
+ }
finishing = true;
- connection.disconnect();
- handleSessionFinished(error);
- finishing = false;
- onSessionFinished.emit(error);
+ if (xmppLayer != null) {
+ xmppLayer.writeFooter();
+ }
+ connection.disconnect();
}
protected void handleSessionStarted() {
@@ -133,7 +141,7 @@ public abstract class Session {
});
xmppLayer.onDataRead.connect(onDataRead);
xmppLayer.onWriteData.connect(onDataWritten);
- connection.onDisconnected.connect(new Slot1<Connection.Error>() {
+ onDisconnectedConnection = connection.onDisconnected.connect(new Slot1<Connection.Error>() {
public void call(Connection.Error p1) {
handleDisconnected(p1);
@@ -151,28 +159,33 @@ public abstract class Session {
public StreamStack getStreamStack() {
return streamStack;
-
-
}
- /*void setFinished();*/ /* This seems to be unused in Swiften*/
+ /*protected void setFinished();*/ /* This seems to be unused in Swiften*/
private void handleDisconnected(Connection.Error connectionError) {
+ onDisconnectedConnection.disconnect();
if (connectionError != null) {
switch (connectionError) {
case ReadError:
- finishSession(SessionError.ConnectionReadError);
+ handleSessionFinished(SessionError.ConnectionReadError);
+ onSessionFinished.emit(SessionError.ConnectionReadError);
break;
case WriteError:
- finishSession(SessionError.ConnectionWriteError);
+ handleSessionFinished(SessionError.ConnectionWriteError);
+ onSessionFinished.emit(SessionError.ConnectionWriteError);
break;
}
- } else {
- finishSession();
+ }
+ else {
+ SessionError error = null;
+ handleSessionFinished(error);
+ onSessionFinished.emit(error);
}
}
- private JID localJID;
- private JID remoteJID;
+
+ private JID localJID = new JID();
+ private JID remoteJID = new JID();
private Connection connection;
private PayloadParserFactoryCollection payloadParserFactories;
private PayloadSerializerCollection payloadSerializers;
@@ -180,5 +193,5 @@ public abstract class Session {
private ConnectionLayer connectionLayer;
private StreamStack streamStack;
private boolean finishing;
- private final EventLoop eventLoop;
+ private SignalConnection onDisconnectedConnection;
}
diff --git a/src/com/isode/stroke/session/SessionStream.java b/src/com/isode/stroke/session/SessionStream.java
index 3171ec0..78d5588 100644
--- a/src/com/isode/stroke/session/SessionStream.java
+++ b/src/com/isode/stroke/session/SessionStream.java
@@ -6,12 +6,13 @@
* Copyright (c) 2010-2014, Isode Limited, London, England.
* All rights reserved.
*/
-package com.isode.stroke.session;
+package com.isode.stroke.session;
import java.util.List;
import com.isode.stroke.base.SafeByteArray;
import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.base.Error;
import com.isode.stroke.elements.Element;
import com.isode.stroke.elements.ProtocolHeader;
import com.isode.stroke.signals.Signal;
@@ -22,7 +23,7 @@ import com.isode.stroke.tls.CertificateWithKey;
public abstract class SessionStream {
- public static class Error implements com.isode.stroke.base.Error {
+ public static class SessionStreamError implements Error {
public enum Type {
@@ -33,7 +34,7 @@ public abstract class SessionStream {
ConnectionWriteError
}
- public Error(Type type) {
+ public SessionStreamError(Type type) {
this.type = type;
}
public final Type type;
@@ -64,7 +65,18 @@ public abstract class SessionStream {
public abstract void setWhitespacePingEnabled(boolean enabled);
public abstract void resetXMPPParser();
-
+
+ public abstract void disconnect();
+
+ protected void finalize() throws Throwable {
+ try {
+ disconnect();
+ }
+ finally {
+ super.finalize();
+ }
+ }
+
public void setTLSCertificate(CertificateWithKey cert) {
certificate = cert;
}
diff --git a/src/com/isode/stroke/streamstack/TLSLayer.java b/src/com/isode/stroke/streamstack/TLSLayer.java
index 8244766..c7b40aa 100644
--- a/src/com/isode/stroke/streamstack/TLSLayer.java
+++ b/src/com/isode/stroke/streamstack/TLSLayer.java
@@ -13,17 +13,20 @@ import java.util.List;
import com.isode.stroke.base.SafeByteArray;
import com.isode.stroke.signals.Signal;
+import com.isode.stroke.signals.Signal1;
import com.isode.stroke.signals.Slot1;
import com.isode.stroke.tls.Certificate;
import com.isode.stroke.tls.CertificateVerificationError;
import com.isode.stroke.tls.CertificateWithKey;
import com.isode.stroke.tls.TLSContext;
+import com.isode.stroke.tls.TLSOptions;
+import com.isode.stroke.tls.TLSError;
import com.isode.stroke.tls.TLSContextFactory;
public class TLSLayer extends StreamLayer {
- public TLSLayer(TLSContextFactory factory) {
- context = factory.createTLSContext();
+ public TLSLayer(TLSContextFactory factory, TLSOptions tlsOptions) {
+ context = factory.createTLSContext(tlsOptions);
context.onDataForNetwork.connect(new Slot1<SafeByteArray>() {
public void call(SafeByteArray p1) {
@@ -72,7 +75,7 @@ public class TLSLayer extends StreamLayer {
return context;
}
- public final Signal onError = new Signal();//needs port
+ public final Signal1<TLSError> onError = new Signal1<TLSError>();
public final Signal onConnected = new Signal();
private final TLSContext context;
diff --git a/src/com/isode/stroke/tls/BlindCertificateTrustChecker.java b/src/com/isode/stroke/tls/BlindCertificateTrustChecker.java
new file mode 100644
index 0000000..46866f2
--- /dev/null
+++ b/src/com/isode/stroke/tls/BlindCertificateTrustChecker.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2010-2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.tls;
+
+import com.isode.stroke.tls.CertificateTrustChecker;
+import java.util.List;
+
+/**
+ * A certificate trust checker that trusts any ceritficate.
+ *
+ * This can be used to ignore any TLS certificate errors occurring
+ * during connection.
+ *
+ * @link Client#setAlwaysTrustCertificates()
+ */
+public class BlindCertificateTrustChecker implements CertificateTrustChecker {
+
+ public boolean isCertificateTrusted(final List<Certificate> certificate) {
+ return true;
+ }
+} \ No newline at end of file
diff --git a/src/com/isode/stroke/tls/Certificate.java b/src/com/isode/stroke/tls/Certificate.java
index de23f94..fdd64c0 100644
--- a/src/com/isode/stroke/tls/Certificate.java
+++ b/src/com/isode/stroke/tls/Certificate.java
@@ -9,6 +9,7 @@
package com.isode.stroke.tls;
import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.crypto.CryptoProvider;
import com.isode.stroke.stringcodecs.Hexify;
import com.isode.stroke.stringcodecs.SHA1;
import java.util.List;
@@ -31,8 +32,8 @@ public abstract class Certificate {
public abstract ByteArray toDER();
- public String getSHA1Fingerprint() {
- ByteArray hash = SHA1.getHash(toDER());
+ public static String getSHA1Fingerprint(Certificate certificate, CryptoProvider crypto) {
+ ByteArray hash = crypto.getSHA1Hash(certificate.toDER());
StringBuilder s = new StringBuilder();
for (int i = 0; i < hash.getSize(); ++i) {
if (i > 0) {
diff --git a/src/com/isode/stroke/tls/CertificateVerificationError.java b/src/com/isode/stroke/tls/CertificateVerificationError.java
index d76dc00..baff374 100644
--- a/src/com/isode/stroke/tls/CertificateVerificationError.java
+++ b/src/com/isode/stroke/tls/CertificateVerificationError.java
@@ -29,12 +29,21 @@ public class CertificateVerificationError implements Error {
RevocationCheckFailed
}
+ public CertificateVerificationError() {
+ this(Type.UnknownError);
+ }
+
public CertificateVerificationError(Type type) {
if (type == null) {
throw new IllegalStateException();
}
this.type = type;
}
- public final Type type;
+
+ public Type getType() {
+ return type;
+ }
+
+ private final Type type;
}
diff --git a/src/com/isode/stroke/tls/ServerIdentityVerifier.java b/src/com/isode/stroke/tls/ServerIdentityVerifier.java
index 20caae8..86ce803 100644
--- a/src/com/isode/stroke/tls/ServerIdentityVerifier.java
+++ b/src/com/isode/stroke/tls/ServerIdentityVerifier.java
@@ -9,15 +9,20 @@
*/
package com.isode.stroke.tls;
-import com.isode.stroke.idn.IDNA;
+import com.isode.stroke.idn.IDNConverter;
import com.isode.stroke.jid.JID;
import java.util.List;
public class ServerIdentityVerifier {
- public ServerIdentityVerifier(JID jid) {
+ public ServerIdentityVerifier(JID jid, IDNConverter idnConverter) {
+ this.domainValid = false;
domain = jid.getDomain();
- encodedDomain = IDNA.getEncoded(domain);
+ String domainResult = idnConverter.getIDNAEncoded(domain);
+ if (domainResult != null) {
+ encodedDomain = domainResult;
+ domainValid = true;
+ }
}
public boolean certificateVerifies(Certificate certificate) {
@@ -69,6 +74,9 @@ public class ServerIdentityVerifier {
}
boolean matchesDomain(String s) {
+ if (!domainValid) {
+ return false;
+ }
if (s.startsWith("*.")) {
String matchString = s.substring(2);
String matchDomain = encodedDomain;
@@ -88,4 +96,5 @@ public class ServerIdentityVerifier {
}
private String domain;
private String encodedDomain;
+ private boolean domainValid;
}
diff --git a/src/com/isode/stroke/tls/SimpleCertificate.java b/src/com/isode/stroke/tls/SimpleCertificate.java
new file mode 100644
index 0000000..178d36d
--- /dev/null
+++ b/src/com/isode/stroke/tls/SimpleCertificate.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2010-2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.tls;
+
+import java.util.List;
+import java.util.ArrayList;
+import com.isode.stroke.base.ByteArray;
+
+public class SimpleCertificate extends Certificate {
+
+ private String subjectName = "";
+ private ByteArray der = new ByteArray();
+ private List<String> commonNames = new ArrayList<String>();
+ private List<String> dnsNames = new ArrayList<String>();
+ private List<String> xmppAddresses = new ArrayList<String>();
+ private List<String> srvNames = new ArrayList<String>();
+
+ public void setSubjectName(final String name) {
+ subjectName = name;
+ }
+
+ public String getSubjectName() {
+ return subjectName;
+ }
+
+ public List<String> getCommonNames() {
+ return commonNames;
+ }
+
+ public void addCommonName(final String name) {
+ commonNames.add(name);
+ }
+
+ public void addSRVName(final String name) {
+ srvNames.add(name);
+ }
+
+ public void addDNSName(final String name) {
+ dnsNames.add(name);
+ }
+
+ public void addXMPPAddress(final String addr) {
+ xmppAddresses.add(addr);
+ }
+
+ public List<String> getSRVNames() {
+ return srvNames;
+ }
+
+ public List<String> getDNSNames() {
+ return dnsNames;
+ }
+
+ public List<String> getXMPPAddresses() {
+ return xmppAddresses;
+ }
+
+ public ByteArray toDER() {
+ return der;
+ }
+
+ public void setDER(final ByteArray der) {
+ this.der = der;
+ }
+
+ private void parse() {
+
+ }
+} \ No newline at end of file
diff --git a/src/com/isode/stroke/tls/TLSContext.java b/src/com/isode/stroke/tls/TLSContext.java
index 3f5e8d7..cd9f90d 100644
--- a/src/com/isode/stroke/tls/TLSContext.java
+++ b/src/com/isode/stroke/tls/TLSContext.java
@@ -15,6 +15,7 @@ import com.isode.stroke.base.ByteArray;
import com.isode.stroke.base.SafeByteArray;
import com.isode.stroke.signals.Signal;
import com.isode.stroke.signals.Signal1;
+import com.isode.stroke.tls.TLSError;
public abstract class TLSContext {
@@ -41,6 +42,6 @@ public abstract class TLSContext {
public Signal1<SafeByteArray> onDataForNetwork = new Signal1<SafeByteArray>();
public Signal1<SafeByteArray> onDataForApplication = new Signal1<SafeByteArray>();
- public Signal onError = new Signal();
+ public Signal1<TLSError> onError = new Signal1<TLSError>();
public Signal onConnected = new Signal();
}
diff --git a/src/com/isode/stroke/tls/TLSContextFactory.java b/src/com/isode/stroke/tls/TLSContextFactory.java
index 27e322f..f33539b 100644
--- a/src/com/isode/stroke/tls/TLSContextFactory.java
+++ b/src/com/isode/stroke/tls/TLSContextFactory.java
@@ -11,5 +11,7 @@ package com.isode.stroke.tls;
public interface TLSContextFactory {
boolean canCreate();
- TLSContext createTLSContext();
+ TLSContext createTLSContext(TLSOptions tlsOptions);
+ //void setCheckCertificateRevocation(boolean b);
+ //void setDisconnectOnCardRemoval(boolean b);
}
diff --git a/src/com/isode/stroke/tls/TLSError.java b/src/com/isode/stroke/tls/TLSError.java
new file mode 100644
index 0000000..619b747
--- /dev/null
+++ b/src/com/isode/stroke/tls/TLSError.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012-2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.tls;
+
+import com.isode.stroke.base.Error;
+
+public class TLSError implements Error {
+
+ private Type type;
+
+ public enum Type {
+ UnknownError,
+ CertificateCardRemoved
+ };
+
+ public TLSError() {
+ this(Type.UnknownError);
+ }
+
+ public TLSError(Type type) {
+ this.type = type;
+ }
+
+ public Type getType() {
+ return type;
+ }
+} \ No newline at end of file
diff --git a/src/com/isode/stroke/tls/TLSOptions.java b/src/com/isode/stroke/tls/TLSOptions.java
new file mode 100644
index 0000000..9c0b647
--- /dev/null
+++ b/src/com/isode/stroke/tls/TLSOptions.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.tls;
+
+public class TLSOptions {
+
+ /**
+ * This flag is not used in java, and is purely here to maintain
+ * consistency with Swiften
+ */
+ public boolean schannelTLS1_0Workaround;
+
+ public TLSOptions() {
+ schannelTLS1_0Workaround = false;
+ }
+} \ No newline at end of file
diff --git a/src/com/isode/stroke/tls/java/JSSEContext.java b/src/com/isode/stroke/tls/java/JSSEContext.java
index 17b7d4d..02f3b4d 100644
--- a/src/com/isode/stroke/tls/java/JSSEContext.java
+++ b/src/com/isode/stroke/tls/java/JSSEContext.java
@@ -53,6 +53,7 @@ import com.isode.stroke.tls.CertificateVerificationError.Type;
import com.isode.stroke.tls.CertificateWithKey;
import com.isode.stroke.tls.PKCS12Certificate;
import com.isode.stroke.tls.TLSContext;
+import com.isode.stroke.tls.TLSError;
@@ -121,7 +122,7 @@ public class JSSEContext extends TLSContext {
*/
logger_.log(Level.WARNING, jsseContextError.toString(), e);
errorsEmitted.add(jsseContextError);
- onError.emit();
+ onError.emit(null);
}
@Override
@@ -921,7 +922,7 @@ public class JSSEContext extends TLSContext {
public void handleDataFromNetwork(SafeByteArray data) {
if (hasError()) {
/* We have previously seen, and reported, an error. Emit again */
- onError.emit();
+ onError.emit(null);
return;
}
@@ -999,7 +1000,7 @@ public class JSSEContext extends TLSContext {
public void handleDataFromApplication(SafeByteArray data) {
if (hasError()) {
/* We have previously seen, and reported, an error. Emit again */
- onError.emit();
+ onError.emit(null);
return;
}
if (closeNotifyReceived) {
diff --git a/src/com/isode/stroke/tls/java/JSSEContextFactory.java b/src/com/isode/stroke/tls/java/JSSEContextFactory.java
index 63b184d..666ee77 100644
--- a/src/com/isode/stroke/tls/java/JSSEContextFactory.java
+++ b/src/com/isode/stroke/tls/java/JSSEContextFactory.java
@@ -14,6 +14,7 @@ import java.util.HashSet;
import java.util.Set;
import com.isode.stroke.tls.TLSContext;
+import com.isode.stroke.tls.TLSOptions;
import com.isode.stroke.tls.TLSContextFactory;
/**
@@ -33,7 +34,7 @@ public class JSSEContextFactory implements TLSContextFactory {
}
@Override
- public TLSContext createTLSContext() {
+ public TLSContext createTLSContext(TLSOptions tlsOptions) {
return new JSSEContext(restrictedCipherSuites);
}
diff --git a/test/com/isode/stroke/component/ComponentSessionTest.java b/test/com/isode/stroke/component/ComponentSessionTest.java
index c7fa36b..5d6d21e 100644
--- a/test/com/isode/stroke/component/ComponentSessionTest.java
+++ b/test/com/isode/stroke/component/ComponentSessionTest.java
@@ -65,13 +65,17 @@ public class ComponentSessionTest {
}
public void close() {
- onClosed.emit((SessionStream.Error)null);
+ onClosed.emit((SessionStream.SessionStreamError)null);
}
public boolean isOpen() {
return available;
}
+ public void disconnect() {
+
+ }
+
public void writeHeader(final ProtocolHeader header) {
receivedEvents.add(new Event(header));
}
@@ -133,7 +137,7 @@ public class ComponentSessionTest {
}
public void breakConnection() {
- onClosed.emit(new SessionStream.Error(SessionStream.Error.Type.ConnectionReadError));
+ onClosed.emit(new SessionStream.SessionStreamError(SessionStream.SessionStreamError.Type.ConnectionReadError));
}
public void sendStreamStart() {
diff --git a/test/com/isode/stroke/tls/CertificateTest.java b/test/com/isode/stroke/tls/CertificateTest.java
new file mode 100644
index 0000000..44d6701
--- /dev/null
+++ b/test/com/isode/stroke/tls/CertificateTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2010-2013 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.tls;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.junit.Before;
+import com.isode.stroke.crypto.CryptoProvider;
+import com.isode.stroke.crypto.JavaCryptoProvider;
+import com.isode.stroke.tls.SimpleCertificate;
+import com.isode.stroke.tls.Certificate;
+import com.isode.stroke.base.ByteArray;
+
+public class CertificateTest {
+
+ @Test
+ public void testGetSHA1Fingerprint() {
+ SimpleCertificate testling = new SimpleCertificate();
+ testling.setDER(new ByteArray("abcdefg"));
+
+ assertEquals("2f:b5:e1:34:19:fc:89:24:68:65:e7:a3:24:f4:76:ec:62:4e:87:40", Certificate.getSHA1Fingerprint(testling, new JavaCryptoProvider()));
+ }
+}
diff --git a/test/com/isode/stroke/tls/ServerIdentityVerifierTest.java b/test/com/isode/stroke/tls/ServerIdentityVerifierTest.java
new file mode 100644
index 0000000..17a8c5a
--- /dev/null
+++ b/test/com/isode/stroke/tls/ServerIdentityVerifierTest.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2010 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.tls;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.junit.Before;
+import com.isode.stroke.crypto.CryptoProvider;
+import com.isode.stroke.crypto.JavaCryptoProvider;
+import com.isode.stroke.tls.SimpleCertificate;
+import com.isode.stroke.tls.Certificate;
+import com.isode.stroke.tls.ServerIdentityVerifier;
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.idn.IDNConverter;
+import com.isode.stroke.idn.ICUConverter;
+import com.isode.stroke.jid.JID;
+
+public class ServerIdentityVerifierTest {
+
+ private IDNConverter idnConverter;
+
+ @Before
+ public void setUp() {
+ idnConverter = new ICUConverter();
+ }
+
+ @Test
+ public void testCertificateVerifies_WithoutMatchingDNSName() {
+ ServerIdentityVerifier testling = new ServerIdentityVerifier(new JID("foo@bar.com/baz"), idnConverter);
+ SimpleCertificate certificate = new SimpleCertificate();
+ certificate.addDNSName("foo.com");
+
+ assertFalse(testling.certificateVerifies(certificate));
+ }
+
+ @Test
+ public void testCertificateVerifies_WithMatchingDNSName() {
+ ServerIdentityVerifier testling = new ServerIdentityVerifier(new JID("foo@bar.com/baz"), idnConverter);
+ SimpleCertificate certificate = new SimpleCertificate();
+ certificate.addDNSName("bar.com");
+
+ assertTrue(testling.certificateVerifies(certificate));
+ }
+
+ @Test
+ public void testCertificateVerifies_WithSecondMatchingDNSName() {
+ ServerIdentityVerifier testling = new ServerIdentityVerifier(new JID("foo@bar.com/baz"), idnConverter);
+ SimpleCertificate certificate = new SimpleCertificate();
+ certificate.addDNSName("foo.com");
+ certificate.addDNSName("bar.com");
+
+ assertTrue(testling.certificateVerifies(certificate));
+ }
+
+ @Test
+ public void testCertificateVerifies_WithMatchingInternationalDNSName() {
+ ServerIdentityVerifier testling = new ServerIdentityVerifier(new JID("foo@tronçon.com/baz"), idnConverter);
+ SimpleCertificate certificate = new SimpleCertificate();
+ certificate.addDNSName("xn--tronon-zua.com");
+
+ assertTrue(testling.certificateVerifies(certificate));
+ }
+
+ @Test
+ public void testCertificateVerifies_WithMatchingDNSNameWithWildcard() {
+ ServerIdentityVerifier testling = new ServerIdentityVerifier(new JID("foo@im.bar.com/baz"), idnConverter);
+ SimpleCertificate certificate = new SimpleCertificate();
+ certificate.addDNSName("*.bar.com");
+
+ assertTrue(testling.certificateVerifies(certificate));
+ }
+
+ @Test
+ public void testCertificateVerifies_WithMatchingDNSNameWithWildcardMatchingNoComponents() {
+ ServerIdentityVerifier testling = new ServerIdentityVerifier(new JID("foo@bar.com/baz"), idnConverter);
+ SimpleCertificate certificate = new SimpleCertificate();
+ certificate.addDNSName("*.bar.com");
+
+ assertFalse(testling.certificateVerifies(certificate));
+ }
+
+ @Test
+ public void testCertificateVerifies_WithDNSNameWithWildcardMatchingTwoComponents() {
+ ServerIdentityVerifier testling = new ServerIdentityVerifier(new JID("foo@xmpp.im.bar.com/baz"), idnConverter);
+ SimpleCertificate certificate = new SimpleCertificate();
+ certificate.addDNSName("*.bar.com");
+
+ assertFalse(testling.certificateVerifies(certificate));
+ }
+
+ @Test
+ public void testCertificateVerifies_WithMatchingSRVNameWithoutService() {
+ ServerIdentityVerifier testling = new ServerIdentityVerifier(new JID("foo@bar.com/baz"), idnConverter);
+ SimpleCertificate certificate = new SimpleCertificate();
+ certificate.addSRVName("bar.com");
+
+ assertFalse(testling.certificateVerifies(certificate));
+ }
+
+ @Test
+ public void testCertificateVerifies_WithMatchingSRVNameWithService() {
+ ServerIdentityVerifier testling = new ServerIdentityVerifier(new JID("foo@bar.com/baz"), idnConverter);
+ SimpleCertificate certificate = new SimpleCertificate();
+ certificate.addSRVName("_xmpp-client.bar.com");
+
+ assertTrue(testling.certificateVerifies(certificate));
+ }
+
+ @Test
+ public void testCertificateVerifies_WithMatchingSRVNameWithServiceAndWildcard() {
+ ServerIdentityVerifier testling = new ServerIdentityVerifier(new JID("foo@im.bar.com/baz"), idnConverter);
+ SimpleCertificate certificate = new SimpleCertificate();
+ certificate.addSRVName("_xmpp-client.*.bar.com");
+
+ assertTrue(testling.certificateVerifies(certificate));
+ }
+
+ @Test
+ public void testCertificateVerifies_WithMatchingSRVNameWithDifferentService() {
+ ServerIdentityVerifier testling = new ServerIdentityVerifier(new JID("foo@bar.com/baz"), idnConverter);
+ SimpleCertificate certificate = new SimpleCertificate();
+ certificate.addSRVName("_xmpp-server.bar.com");
+
+ assertFalse(testling.certificateVerifies(certificate));
+ }
+
+ @Test
+ public void testCertificateVerifies_WithMatchingXmppAddr() {
+ ServerIdentityVerifier testling = new ServerIdentityVerifier(new JID("foo@bar.com/baz"), idnConverter);
+ SimpleCertificate certificate = new SimpleCertificate();
+ certificate.addXMPPAddress("bar.com");
+
+ assertTrue(testling.certificateVerifies(certificate));
+ }
+
+ @Test
+ public void testCertificateVerifies_WithMatchingXmppAddrWithWildcard() {
+ ServerIdentityVerifier testling = new ServerIdentityVerifier(new JID("foo@im.bar.com/baz"), idnConverter);
+ SimpleCertificate certificate = new SimpleCertificate();
+ certificate.addXMPPAddress("*.bar.com");
+
+ assertFalse(testling.certificateVerifies(certificate));
+ }
+
+ @Test
+ public void testCertificateVerifies_WithMatchingInternationalXmppAddr() {
+ ServerIdentityVerifier testling = new ServerIdentityVerifier(new JID("foo@tronçon.com/baz"), idnConverter);
+ SimpleCertificate certificate = new SimpleCertificate();
+ certificate.addXMPPAddress("tronçon.com");
+
+ assertTrue(testling.certificateVerifies(certificate));
+ }
+
+ @Test
+ public void testCertificateVerifies_WithMatchingCNWithoutSAN() {
+ ServerIdentityVerifier testling = new ServerIdentityVerifier(new JID("foo@bar.com/baz"), idnConverter);
+ SimpleCertificate certificate = new SimpleCertificate();
+ certificate.addCommonName("bar.com");
+
+ assertTrue(testling.certificateVerifies(certificate));
+ }
+
+ @Test
+ public void testCertificateVerifies_WithMatchingCNWithSAN() {
+ ServerIdentityVerifier testling = new ServerIdentityVerifier(new JID("foo@bar.com/baz"), idnConverter);
+ SimpleCertificate certificate = new SimpleCertificate();
+ certificate.addSRVName("foo.com");
+ certificate.addCommonName("bar.com");
+
+ assertFalse(testling.certificateVerifies(certificate));
+ }
+} \ No newline at end of file