diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java
index b2ea581c2..f8e69533c 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java
@@ -23,6 +23,7 @@ import org.jivesoftware.smack.util.DNSUtil;
import org.jivesoftware.smack.util.dns.HostAddress;
import javax.net.SocketFactory;
+import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.security.auth.callback.CallbackHandler;
@@ -80,6 +81,8 @@ public class ConnectionConfiguration implements Cloneable {
private boolean useDnsSrvRr = true;
private SecurityMode securityMode = SecurityMode.enabled;
+ private HostnameVerifier hostnameVerifier;
+
/**
* Permanent store for the Roster, needed for roster versioning
*/
@@ -310,6 +313,29 @@ public class ConnectionConfiguration implements Cloneable {
this.customSSLContext = context;
}
+ /**
+ * Set the HostnameVerifier used to verify the hostname of SSLSockets used by XMPP connections
+ * created with this ConnectionConfiguration.
+ *
+ * @param verifier
+ */
+ public void setHostnameVerifier(HostnameVerifier verifier) {
+ hostnameVerifier = verifier;
+ }
+
+ /**
+ * Returns the configured HostnameVerifier of this ConnectionConfiguration or the Smack default
+ * HostnameVerifier configured with
+ * {@link SmackConfiguration#setDefaultHostnameVerifier(HostnameVerifier)}.
+ *
+ * @return a configured HostnameVerifier or null
+ */
+ public HostnameVerifier getHostnameVerifier() {
+ if (hostnameVerifier != null)
+ return hostnameVerifier;
+ return SmackConfiguration.getDefaultHostnameVerifier();
+ }
+
/**
* Returns true if the connection is going to use stream compression. Stream compression
* will be requested after TLS was established (if TLS was enabled) and only if the server
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java
index 374c1c8b5..11f225b04 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java
@@ -31,6 +31,8 @@ import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.net.ssl.HostnameVerifier;
+
import org.jivesoftware.smack.compression.Java7ZlibInputOutputStream;
import org.jivesoftware.smack.compression.XMPPInputOutputStream;
import org.jivesoftware.smack.initializer.SmackInitializer;
@@ -178,6 +180,8 @@ public final class SmackConfiguration {
*/
private static ParsingExceptionCallback defaultCallback = new ExceptionThrowingCallback();
+ private static HostnameVerifier defaultHostnameVerififer;
+
/**
* Returns the Smack version information, eg "1.3.0".
*
@@ -319,6 +323,25 @@ public final class SmackConfiguration {
return res;
}
+ /**
+ * Set the default HostnameVerifier that will be used by XMPP connections to verify the hostname
+ * of a TLS certificate. XMPP connections are able to overwrite this settings by supplying a
+ * HostnameVerifier in their ConnecitonConfiguration with
+ * {@link ConnectionConfiguration#setHostnameVerifier(HostnameVerifier)}.
+ */
+ public static void setDefaultHostnameVerifier(HostnameVerifier verifier) {
+ defaultHostnameVerififer = verifier;
+ }
+
+ /**
+ * Get the default HostnameVerifier
+ *
+ * @return the default HostnameVerifier or null
if none was set
+ */
+ static HostnameVerifier getDefaultHostnameVerifier() {
+ return defaultHostnameVerififer;
+ }
+
public static void processConfigFile(InputStream cfgFileStream,
Collection exceptions) throws Exception {
processConfigFile(cfgFileStream, exceptions, SmackConfiguration.class.getClassLoader());
diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
index 10a11054f..264140540 100644
--- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
+++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
@@ -35,6 +35,7 @@ import org.jivesoftware.smack.parsing.ParsingExceptionCallback;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.dns.HostAddress;
+import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
@@ -61,6 +62,7 @@ import java.net.Socket;
import java.security.KeyStore;
import java.security.Provider;
import java.security.Security;
+import java.security.cert.CertificateException;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
@@ -647,14 +649,21 @@ public class XMPPTCPConnection extends XMPPConnection {
// Initialize the reader and writer with the new secured version
initReaderAndWriter();
+ final SSLSocket sslSocket = (SSLSocket) socket;
try {
// Proceed to do the handshake
- ((SSLSocket) socket).startHandshake();
+ sslSocket.startHandshake();
}
catch (IOException e) {
setConnectionException(e);
throw e;
}
+
+ final HostnameVerifier verifier = getConfiguration().getHostnameVerifier();
+ if (verifier != null && !verifier.verify(getServiceName(), sslSocket.getSession())) {
+ throw new CertificateException("Hostname verification of certificate failed. Certificate does not authenticate " + getServiceName());
+ }
+
//if (((SSLSocket) socket).getWantClientAuth()) {
// System.err.println("XMPPConnection wants client auth");
//}