Skip to content

Commit 5d3d922

Browse files
author
Maik Scheibler
committed
Merge remote-tracking branch 'eclipse/develop' into NetworkModule_SPI
2 parents 556fa63 + d64e7cb commit 5d3d922

45 files changed

Lines changed: 945 additions & 395 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
/**
2+
* Copyright (c) 2011, 2017 IBM
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License v1.0
6+
* and Eclipse Distribution License v1.0 which accompany this distribution.
7+
*
8+
* The Eclipse Public License is available at
9+
* http://www.eclipse.org/legal/epl-v10.html
10+
* and the Eclipse Distribution License is available at
11+
* http://www.eclipse.org/org/documents/edl-v10.php.
12+
*
13+
* Contributors:
14+
* James Sutton - IBM Adding HTTPS Hostname verification tests.
15+
*/
16+
17+
package org.eclipse.paho.client.mqttv3.test;
18+
19+
import java.io.InputStream;
20+
import java.security.KeyStore;
21+
import java.security.SecureRandom;
22+
import java.security.cert.Certificate;
23+
import java.security.cert.CertificateFactory;
24+
import java.util.logging.Level;
25+
import java.util.logging.Logger;
26+
27+
import javax.net.ssl.SSLContext;
28+
import javax.net.ssl.SSLHandshakeException;
29+
import javax.net.ssl.SSLSocketFactory;
30+
import javax.net.ssl.TrustManagerFactory;
31+
32+
import org.eclipse.paho.client.mqttv3.MqttClient;
33+
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
34+
import org.eclipse.paho.client.mqttv3.test.logging.LoggingUtilities;
35+
import org.eclipse.paho.client.mqttv3.test.utilities.Utility;
36+
import org.junit.Assert;
37+
import org.junit.BeforeClass;
38+
import org.junit.Test;
39+
40+
/**
41+
* This test aims to run some basic SSL functionality tests of the MQTT client
42+
*/
43+
44+
public class TLSHostnameVerificationTest {
45+
46+
static final Class<?> cclass = TLSHostnameVerificationTest.class;
47+
private static final String className = cclass.getName();
48+
private static final Logger log = Logger.getLogger(className);
49+
50+
private static String serverHost;
51+
private static int serverPort;
52+
private static String serverIP;
53+
private static String certificateName;
54+
private static int websocketPort;
55+
56+
/**
57+
* @throws Exception
58+
*/
59+
@BeforeClass
60+
public static void setUpBeforeClass() throws Exception {
61+
62+
try {
63+
String methodName = Utility.getMethodName();
64+
LoggingUtilities.banner(log, cclass, methodName);
65+
// Overriding with iot.eclipse.org for the time being.
66+
67+
serverHost = "iot.eclipse.org";
68+
serverIP = "198.41.30.241";
69+
serverPort = 8883;
70+
websocketPort = 443;
71+
72+
certificateName = "iot.eclipse.org.crt";
73+
74+
} catch (Exception exception) {
75+
log.log(Level.SEVERE, "caught exception:", exception);
76+
throw exception;
77+
}
78+
}
79+
80+
/**
81+
* This test checks that the Java 7 HTTPS Style Hostname Verification works with
82+
* the Paho Client. This will verify that the Hostname we are connecting to
83+
* matches the one recorded in the Certificate
84+
*
85+
* @throws Exception
86+
*/
87+
@Test(timeout = 10000)
88+
public void testValidHTTPSStyleHostnameVerification() throws Exception {
89+
90+
SSLSocketFactory socketFactory = getSocketFactory(certificateName);
91+
92+
MqttConnectOptions connOpts = new MqttConnectOptions();
93+
connOpts.setHttpsHostnameVerificationEnabled(true);
94+
connOpts.setSocketFactory(socketFactory);
95+
String serverURI = "ssl://" + serverHost + ":" + serverPort;
96+
97+
MqttClient mqttClient = new MqttClient(serverURI, MqttClient.generateClientId());
98+
99+
log.info("Connecting to: " + serverURI);
100+
mqttClient.connect(connOpts);
101+
102+
Thread.sleep(2000);
103+
104+
log.info("Disconnetting...");
105+
mqttClient.disconnect();
106+
mqttClient.close();
107+
108+
}
109+
110+
/**
111+
* This test checks that the Java 7 HTTPS Style Hostname Verification works with
112+
* the Paho Client. This will verify that when connecting to a server via the IP
113+
* address, the Hostname verification will fail
114+
*
115+
* @throws Exception
116+
*/
117+
@Test(timeout = 10000)
118+
public void testInvalidHTTPSStyleHostnameVerification() throws Exception {
119+
120+
SSLSocketFactory socketFactory = getSocketFactory(certificateName);
121+
122+
MqttConnectOptions connOpts = new MqttConnectOptions();
123+
connOpts.setHttpsHostnameVerificationEnabled(true);
124+
connOpts.setSocketFactory(socketFactory);
125+
String serverIPURI = "ssl://" + serverIP + ":" + serverPort;
126+
127+
MqttClient mqttClient = new MqttClient(serverIPURI, MqttClient.generateClientId());
128+
129+
log.info("Connecting to: " + serverIPURI);
130+
try {
131+
mqttClient.connect(connOpts);
132+
} catch (Exception ex) {
133+
// We want to make sure that the returned exception is an SSLHandshakeException
134+
Assert.assertEquals(SSLHandshakeException.class, ex.getCause().getClass());
135+
log.info("Expected Exception thrown: " + ex.getCause().getClass());
136+
} finally {
137+
mqttClient.close();
138+
}
139+
}
140+
141+
142+
/**
143+
* This test checks that the Java 7 HTTPS Style Hostname Verification works with
144+
* the Paho Client. This will verify that the Hostname we are connecting to
145+
* matches the one recorded in the Certificate
146+
*
147+
* @throws Exception
148+
*/
149+
@Test(timeout = 10000)
150+
public void testValidWebSocketHTTPSStyleHostnameVerification() throws Exception {
151+
152+
SSLSocketFactory socketFactory = getSocketFactory(certificateName);
153+
154+
MqttConnectOptions connOpts = new MqttConnectOptions();
155+
connOpts.setHttpsHostnameVerificationEnabled(true);
156+
connOpts.setSocketFactory(socketFactory);
157+
String serverURI = "wss://" + serverHost + ":" + websocketPort + "/ws";
158+
159+
MqttClient mqttClient = new MqttClient(serverURI, MqttClient.generateClientId());
160+
161+
log.info("Connecting to: " + serverURI);
162+
mqttClient.connect(connOpts);
163+
164+
Thread.sleep(2000);
165+
166+
log.info("Disconnetting...");
167+
mqttClient.disconnect();
168+
mqttClient.close();
169+
170+
}
171+
172+
/**
173+
* This test checks that the Java 7 HTTPS Style Hostname Verification works with
174+
* the Paho Client. This will verify that when connecting to a server via the IP
175+
* address, the Hostname verification will fail
176+
*
177+
* @throws Exception
178+
*/
179+
@Test(timeout = 10000)
180+
public void testInvalidWebSocketHTTPSStyleHostnameVerification() throws Exception {
181+
182+
SSLSocketFactory socketFactory = getSocketFactory(certificateName);
183+
184+
MqttConnectOptions connOpts = new MqttConnectOptions();
185+
connOpts.setHttpsHostnameVerificationEnabled(true);
186+
connOpts.setSocketFactory(socketFactory);
187+
String serverIPURI = "wss://" + serverIP + ":" + websocketPort + "/ws";
188+
189+
190+
MqttClient mqttClient = new MqttClient(serverIPURI, MqttClient.generateClientId());
191+
192+
log.info("Connecting to: " + serverIPURI);
193+
try {
194+
mqttClient.connect(connOpts);
195+
} catch (Exception ex) {
196+
// We want to make sure that the returned exception is an SSLHandshakeException
197+
Assert.assertEquals(SSLHandshakeException.class, ex.getCause().getClass());
198+
log.info("Expected Exception thrown: " + ex.getCause().getClass());
199+
} finally {
200+
mqttClient.close();
201+
}
202+
}
203+
204+
205+
206+
private static SSLSocketFactory getSocketFactory(String certificateName) throws Exception {
207+
// Load the certificate from src/test/resources and create a Certificate object
208+
InputStream certStream = cclass.getClassLoader().getResourceAsStream(certificateName);
209+
CertificateFactory certFactory = CertificateFactory.getInstance("X509");
210+
Certificate certificate = certFactory.generateCertificate(certStream);
211+
SSLContext sslContext = SSLContext.getInstance("TLS");
212+
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
213+
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
214+
keyStore.load(null);
215+
keyStore.setCertificateEntry("alias", certificate);
216+
trustManagerFactory.init(keyStore);
217+
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
218+
return sslContext.getSocketFactory();
219+
}
220+
}
Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
11
-----BEGIN CERTIFICATE-----
2-
MIIEVDCCAwygAwIBAgIEVYPx2jANBgkqhkiG9w0BAQsFADCBhzELMAkGA1UEBhMC
3-
Q0ExJTAjBgNVBAoTHEVjbGlwc2Uub3JnIEZvdW5kYXRpb24sIEluYy4xFDASBgNV
4-
BAsTC2lvdC10ZXN0aW5nMQ8wDQYDVQQHEwZOZXBlYW4xEDAOBgNVBAgTB09udGFy
5-
aW8xGDAWBgNVBAMTD2lvdC5lY2xpcHNlLm9yZzAeFw0xNTA2MTkxMDQxMzFaFw0y
6-
MDA1MjMxMDQxMzRaMIGHMQswCQYDVQQGEwJDQTElMCMGA1UEChMcRWNsaXBzZS5v
7-
cmcgRm91bmRhdGlvbiwgSW5jLjEUMBIGA1UECxMLaW90LXRlc3RpbmcxDzANBgNV
8-
BAcTBk5lcGVhbjEQMA4GA1UECBMHT250YXJpbzEYMBYGA1UEAxMPaW90LmVjbGlw
9-
c2Uub3JnMIIBUjANBgkqhkiG9w0BAQEFAAOCAT8AMIIBOgKCATEA0xUc7iXRrDG3
10-
WBCM6kzBv7eDVJUj8fMt7yhVS+os3QwKXfirzvRn1kj3KQkBzR/Nkeqk4go5EkYI
11-
ZCvG3svmfbyxJaWxl8VBhhiVf8ytd0CkNbbho5VlV2BrDuMpAMiQ1MZZqXV544wf
12-
BXAChXPgDjsjP/2QDAR52jJjqwoGm+KFAp9ZTFpHqi1Yajt2J7M1EOacnkasDdcD
13-
GzgxDIA1oo6XOM1sisOc11d+L1JyOwtSHIaQRO9BChU5CAZCTRF9wITdmBnhb+ha
14-
mEgHPFIeF8uPXnjvsXJgHM/GqOoIlb671DOVNdZPxDhg+Pp47qGQJrcTz+ptTLw4
15-
bgcSCbHQXK+TDk2GxUS27PbZ9trK2Rfo6MLe4x0kT8k+9zZkPDKdJzCYDVzfcakf
16-
Qj8cDxplrwIDAQABo2YwZDAMBgNVHRMBAf8EAjAAMCAGA1UdEQQZMBeCD2lvdC5l
17-
Y2xpcHNlLm9yZ4cExike8TATBgNVHSUEDDAKBggrBgEFBQcDATAdBgNVHQ4EFgQU
18-
sXXbkSdzImOC1cQcnXOPeGdghvowDQYJKoZIhvcNAQELBQADggExAHueO/jU8LNE
19-
M5GyA0ENlPLoeBgtsa2HGXGH1vrc9HEESaFLlQX/V0hEa6MtFTtZTGz4J4zfJK0s
20-
Zb7aMxQqlyDE0tGHb6p4XiyjyEcIpAaRuOdMFvsELBugLQIGyJU+FRhrJ6we0G/9
21-
aZg+PPhiLRsRQtxgS6zWJ3zQt8Vxj7gnjMjUzuJ30LeborKOZih0By1zY127TbgK
22-
0+7CjI3kzUzgX5gy6/YKJLoEl7In7GgLCXy4TdRAC4jrmiBZA3xhn2YbpEGG383z
23-
JL7gwrTxilGPdpUkPqsh3QPFybe//IuZnv0asPY4t9GgVqObomUGkafBcz1kDnlh
24-
eCQzrplSaCcePR3cawnX/x6ZjnRSfKGJzKYWBVtxLT6TW95AyGyv0bpsH5d3TpKO
25-
ayb6IYpBphw=
26-
-----END CERTIFICATE-----
2+
MIIFATCCA+mgAwIBAgISBPTNy6fmaq/MmFsgFgDdQI6IMA0GCSqGSIb3DQEBCwUA
3+
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
4+
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xODAxMjYxNDI1MzlaFw0x
5+
ODA0MjYxNDI1MzlaMBoxGDAWBgNVBAMTD2lvdC5lY2xpcHNlLm9yZzCCASIwDQYJ
6+
KoZIhvcNAQEBBQADggEPADCCAQoCggEBANbm+sfVzGMN3ppXf0DGAAtRjiAOzmN5
7+
htxj8TXSgSq/+dCgf7rC2Iq/pdZXWnfo9Eln+lZGprmvOr5dRSWCucRc4T+itHw7
8+
wyrsThxvp/yLv0OrmdRlarQM+TDZS/ee/BD65Zt5ByOtzBasJRnfnOiBQEBfsrjR
9+
yz4XPJn2vJg/0q6XELp3wkZHOy0b8vC73j9zZ6fXIxQ5cBg60jZkvclPmxm+2kDL
10+
Icw8Hsee/cN9RxtWapyNzW+N8NW1XSRSgiDauYZjrcE4RoUXt1eGJ2IIwTrnGc7l
11+
+ucRKYpAN9tZFA4fPmKzNCCZRJsabwT1KfCfKQKrt3mpgyv/+CyP2ycCAwEAAaOC
12+
Ag8wggILMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
13+
BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU/IKAPfc1/rKRW0mYo+V8OHvV
14+
O38wHwYDVR0jBBgwFoAUqEpqYwR93brm0Tm3pkVl7/Oo7KEwbwYIKwYBBQUHAQEE
15+
YzBhMC4GCCsGAQUFBzABhiJodHRwOi8vb2NzcC5pbnQteDMubGV0c2VuY3J5cHQu
16+
b3JnMC8GCCsGAQUFBzAChiNodHRwOi8vY2VydC5pbnQteDMubGV0c2VuY3J5cHQu
17+
b3JnLzAaBgNVHREEEzARgg9pb3QuZWNsaXBzZS5vcmcwgf4GA1UdIASB9jCB8zAI
18+
BgZngQwBAgEwgeYGCysGAQQBgt8TAQEBMIHWMCYGCCsGAQUFBwIBFhpodHRwOi8v
19+
Y3BzLmxldHNlbmNyeXB0Lm9yZzCBqwYIKwYBBQUHAgIwgZ4MgZtUaGlzIENlcnRp
20+
ZmljYXRlIG1heSBvbmx5IGJlIHJlbGllZCB1cG9uIGJ5IFJlbHlpbmcgUGFydGll
21+
cyBhbmQgb25seSBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIENlcnRpZmljYXRlIFBv
22+
bGljeSBmb3VuZCBhdCBodHRwczovL2xldHNlbmNyeXB0Lm9yZy9yZXBvc2l0b3J5
23+
LzANBgkqhkiG9w0BAQsFAAOCAQEAaaW5HwoTLMMFsYOYszLwCaZY1j3XlytqYXSI
24+
hJrPhlBkIKKgN/K5OYr0zbWZl8z1cTFXux9XgLfSaJoxZIv1YW9QKvU3Yqj2GbZz
25+
k1dasPMJi1pP9fTn2KC3PbbjsRI8boIfgd/9Zw+pQlk5ibFFa7hmtq7zpMGp4CPe
26+
tRmVDxOmlAOsT40DFdOIh/Q76uGz9XN65SIaHov90yivhQBumx62RNQgMJ4WJwS4
27+
ElAkOhTYTlsE5fiYsxG+0ddBx8qhqyQcgHCPHiMzT7gc7d/rrY+QHMbqNww852Dn
28+
mIfsVQ1QxiFKerEC/9qnxaa6YxXorIeBFy/bewiFiOAcULYMYQ==
29+
-----END CERTIFICATE-----

org.eclipse.paho.client.mqttv3/pom.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<plugin>
2020
<groupId>org.codehaus.mojo</groupId>
2121
<artifactId>templating-maven-plugin</artifactId>
22-
<version>1.0-alpha-3</version>
22+
<version>1.0.0</version>
2323
<executions>
2424
<execution>
2525
<id>filter-src</id>
@@ -32,7 +32,7 @@
3232
<plugin>
3333
<groupId>org.apache.maven.plugins</groupId>
3434
<artifactId>maven-resources-plugin</artifactId>
35-
<version>2.4.2</version>
35+
<version>3.0.2</version>
3636
<executions>
3737
<execution>
3838
<id>default-copy-resources</id>
@@ -59,7 +59,7 @@
5959
<plugin>
6060
<groupId>org.eclipse.tycho</groupId>
6161
<artifactId>tycho-compiler-plugin</artifactId>
62-
<version>0.20.0</version>
62+
<version>${tycho.version}</version>
6363
<configuration>
6464
<source>${mqttclient.java.version}</source>
6565
<target>${mqttclient.java.version}</target>
@@ -69,7 +69,7 @@
6969
<plugin>
7070
<groupId>org.apache.maven.plugins</groupId>
7171
<artifactId>maven-javadoc-plugin</artifactId>
72-
<version>2.10.4</version>
72+
<version>3.0.0</version>
7373
<executions>
7474
<execution>
7575
<id>attach-javadocs</id>

org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public class MqttConnectOptions {
6565
private char[] password;
6666
private SocketFactory socketFactory;
6767
private Properties sslClientProps = null;
68+
private boolean httpsHostnameVerificationEnabled = false;
6869
private HostnameVerifier sslHostnameVerifier = null;
6970
private boolean cleanSession = CLEAN_SESSION_DEFAULT;
7071
private int connectionTimeout = CONNECTION_TIMEOUT_DEFAULT;
@@ -401,6 +402,14 @@ public Properties getSSLProperties() {
401402
public void setSSLProperties(Properties props) {
402403
this.sslClientProps = props;
403404
}
405+
406+
public boolean isHttpsHostnameVerificationEnabled() {
407+
return httpsHostnameVerificationEnabled;
408+
}
409+
410+
public void setHttpsHostnameVerificationEnabled(boolean httpsHostnameVerificationEnabled) {
411+
this.httpsHostnameVerificationEnabled = httpsHostnameVerificationEnabled;
412+
}
404413

405414
/**
406415
* Returns the HostnameVerifier for the SSL connection.

org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.io.IOException;
1919

2020
import javax.net.ssl.HostnameVerifier;
21+
import javax.net.ssl.SSLParameters;
2122
import javax.net.ssl.SSLSession;
2223
import javax.net.ssl.SSLSocket;
2324
import javax.net.ssl.SSLSocketFactory;
@@ -36,6 +37,9 @@ public class SSLNetworkModule extends TCPNetworkModule {
3637
private String[] enabledCiphers;
3738
private int handshakeTimeoutSecs;
3839
private HostnameVerifier hostnameVerifier;
40+
private boolean httpsHostnameVerificationEnabled = false;
41+
42+
3943

4044
private String host;
4145
private int port;
@@ -108,15 +112,30 @@ public HostnameVerifier getSSLHostnameVerifier() {
108112
public void setSSLHostnameVerifier(HostnameVerifier hostnameVerifier) {
109113
this.hostnameVerifier = hostnameVerifier;
110114
}
115+
116+
public boolean isHttpsHostnameVerificationEnabled() {
117+
return httpsHostnameVerificationEnabled;
118+
}
119+
120+
public void setHttpsHostnameVerificationEnabled(boolean httpsHostnameVerificationEnabled) {
121+
this.httpsHostnameVerificationEnabled = httpsHostnameVerificationEnabled;
122+
}
111123

112124
public void start() throws IOException, MqttException {
113125
super.start();
114126
setEnabledCiphers(enabledCiphers);
115127
int soTimeout = socket.getSoTimeout();
116128
// RTC 765: Set a timeout to avoid the SSL handshake being blocked indefinitely
117129
socket.setSoTimeout(this.handshakeTimeoutSecs * 1000);
130+
131+
// If default Hostname verification is enabled, use the same method that is used with HTTPS
132+
if(this.httpsHostnameVerificationEnabled) {
133+
SSLParameters sslParams = new SSLParameters();
134+
sslParams.setEndpointIdentificationAlgorithm("HTTPS");
135+
((SSLSocket) socket).setSSLParameters(sslParams);
136+
}
118137
((SSLSocket) socket).startHandshake();
119-
if (hostnameVerifier != null) {
138+
if (hostnameVerifier != null && !this.httpsHostnameVerificationEnabled) {
120139
SSLSession session = ((SSLSocket) socket).getSession();
121140
hostnameVerifier.verify(host, session);
122141
}

org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModuleFactory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public NetworkModule createNetworkModule(URI brokerUri, MqttConnectOptions optio
7474
SSLNetworkModule netModule = new SSLNetworkModule((SSLSocketFactory) factory, host, port, clientId);
7575
netModule.setSSLhandshakeTimeout(options.getConnectionTimeout());
7676
netModule.setSSLHostnameVerifier(options.getSSLHostnameVerifier());
77+
netModule.setHttpsHostnameVerificationEnabled(options.isHttpsHostnameVerificationEnabled());
7778
// Ciphers suites need to be set, if they are available
7879
if (factoryFactory != null) {
7980
String[] enabledCiphers = factoryFactory.getEnabledCipherSuites(null);

0 commit comments

Comments
 (0)