Skip to content

Commit 179c609

Browse files
authored
Honor user-configured hostname verification for MSSQL SSL connections (#1661)
Respects the hostname verification algorithm when explicitly configured by users via ClientSSLOptions. The client continues to disable hostname verification by default (empty algorithm) for backward compatibility, but now honors custom verification settings when provided. Some portions of this content were created with the assistance of Claude Code. Signed-off-by: Thomas Segismont <tsegismont@gmail.com>
1 parent a98b31b commit 179c609

File tree

4 files changed

+98
-5
lines changed

4 files changed

+98
-5
lines changed

vertx-mssql-client/src/main/java/io/vertx/mssqlclient/impl/MSSQLSocketConnection.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ Future<Void> enableSslForTds7x(boolean clientConfigSsl, byte encryptionLevel, MS
106106
if (!clientConfigSsl) {
107107
sslOptions.setTrustAll(true);
108108
}
109-
sslOptions.setHostnameVerificationAlgorithm("");
109+
if (sslOptions.getHostnameVerificationAlgorithm() == null) {
110+
sslOptions.setHostnameVerificationAlgorithm("");
111+
}
110112

111113
// 2. Create and set up an SSLHelper and SSLHandler
112114
// options.getApplicationLayerProtocols()

vertx-mssql-client/src/test/java/io/vertx/tests/mssqlclient/MSSQLStrictEncryptionTest.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,23 @@
1111

1212
package io.vertx.tests.mssqlclient;
1313

14+
import io.vertx.core.Vertx;
15+
import io.vertx.core.VertxOptions;
1416
import io.vertx.core.buffer.Buffer;
17+
import io.vertx.core.dns.AddressResolverOptions;
1518
import io.vertx.core.net.ClientSSLOptions;
1619
import io.vertx.core.net.PemTrustOptions;
1720
import io.vertx.ext.unit.TestContext;
1821
import io.vertx.ext.unit.junit.VertxUnitRunner;
1922
import io.vertx.mssqlclient.EncryptionMode;
23+
import io.vertx.mssqlclient.MSSQLConnection;
2024
import io.vertx.tests.mssqlclient.junit.MSSQLRule;
2125
import org.junit.ClassRule;
2226
import org.junit.Test;
2327
import org.junit.runner.RunWith;
2428

29+
import javax.net.ssl.SSLHandshakeException;
30+
2531
import static io.vertx.tests.mssqlclient.junit.MSSQLRule.Config.STRICT_ENCRYPTION;
2632

2733
/**
@@ -60,6 +66,51 @@ public void testEncryptionWithValidCertificate(TestContext ctx) {
6066
asyncAssertConnectionEncrypted(ctx);
6167
}
6268

69+
@Test
70+
public void testHostnameValidationFailsWithInvalidHostname(TestContext ctx) {
71+
// Even with valid certificate, wrong hostname should fail
72+
// Certificate has CN=sql1, but we connect to 'localhost'
73+
Buffer certValue = vertx.fileSystem().readFileBlocking("mssql.pem");
74+
setOptions(rule.options()
75+
.setHost("localhost")
76+
.setEncryptionMode(EncryptionMode.STRICT)
77+
.setSslOptions(new ClientSSLOptions()
78+
.setHostnameVerificationAlgorithm("HTTPS")
79+
.setTrustOptions(new PemTrustOptions().addCertValue(certValue))));
80+
connect(ctx.asyncAssertFailure(t -> {
81+
ctx.assertTrue(t instanceof SSLHandshakeException,
82+
"Expected SSLHandshakeException due to hostname mismatch (localhost != sql1)");
83+
}));
84+
}
85+
86+
@Test
87+
public void testHostnameValidationSucceedsWithCorrectHostname(TestContext ctx) {
88+
// Use custom DNS resolution to map sql1 -> 127.0.0.1
89+
// This allows connecting to 'sql1' (which matches the certificate CN)
90+
// while actually reaching localhost
91+
Vertx customVertx = Vertx.vertx(
92+
new VertxOptions()
93+
.setAddressResolverOptions(
94+
new AddressResolverOptions()
95+
.setHostsValue(Buffer.buffer("127.0.0.1 sql1\n"))
96+
)
97+
);
98+
99+
Buffer certValue = vertx.fileSystem().readFileBlocking("mssql.pem");
100+
MSSQLConnection.connect(customVertx, rule.options()
101+
.setHost("sql1")
102+
.setEncryptionMode(EncryptionMode.STRICT)
103+
.setSslOptions(new ClientSSLOptions()
104+
.setHostnameVerificationAlgorithm("HTTPS")
105+
.setTrustOptions(new PemTrustOptions().addCertValue(certValue))))
106+
.onComplete(ctx.asyncAssertSuccess(conn -> {
107+
ctx.assertTrue(conn.isSSL());
108+
conn.close().onComplete(ctx.asyncAssertSuccess(v -> {
109+
customVertx.close().onComplete(ctx.asyncAssertSuccess());
110+
}));
111+
}));
112+
}
113+
63114
@Test
64115
public void testEncryptionRejectsNonStrictClient(TestContext ctx) {
65116
setOptions(rule.options()

vertx-mssql-client/src/test/java/io/vertx/tests/mssqlclient/MSSQLTds7EncryptionTestBase.java

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,14 @@
1111

1212
package io.vertx.tests.mssqlclient;
1313

14+
import io.vertx.core.Vertx;
15+
import io.vertx.core.VertxOptions;
1416
import io.vertx.core.buffer.Buffer;
17+
import io.vertx.core.dns.AddressResolverOptions;
1518
import io.vertx.core.net.ClientSSLOptions;
1619
import io.vertx.core.net.PemTrustOptions;
1720
import io.vertx.ext.unit.TestContext;
21+
import io.vertx.mssqlclient.MSSQLConnection;
1822
import org.junit.Test;
1923

2024
import javax.net.ssl.SSLHandshakeException;
@@ -26,15 +30,49 @@ public abstract class MSSQLTds7EncryptionTestBase extends MSSQLEncryptionTestBas
2630

2731
@Test
2832
public void testHostnameValidationFails(TestContext ctx) {
29-
// If the client requires SSL
30-
// Hostname validation must be performed
33+
// Certificate has CN=sql1, but we connect to 'localhost'
34+
// With hostname verification enabled, this should fail
35+
Buffer certValue = vertx.fileSystem().readFileBlocking("mssql.pem");
3136
setOptions(rule().options()
32-
.setSsl(true));
37+
.setHost("localhost")
38+
.setSsl(true)
39+
.setSslOptions(new ClientSSLOptions()
40+
.setHostnameVerificationAlgorithm("HTTPS")
41+
.setTrustOptions(new PemTrustOptions().addCertValue(certValue))));
3342
connect(ctx.asyncAssertFailure(t -> {
34-
ctx.assertTrue(t instanceof SSLHandshakeException);
43+
ctx.assertTrue(t instanceof SSLHandshakeException,
44+
"Expected SSLHandshakeException due to hostname mismatch (localhost != sql1)");
3545
}));
3646
}
3747

48+
@Test
49+
public void testHostnameValidationSucceeds(TestContext ctx) {
50+
// Use custom DNS resolution to map sql1 -> 127.0.0.1
51+
// This allows connecting to 'sql1' (which matches the certificate CN)
52+
// while actually reaching localhost
53+
Vertx customVertx = Vertx.vertx(
54+
new VertxOptions()
55+
.setAddressResolverOptions(
56+
new AddressResolverOptions()
57+
.setHostsValue(Buffer.buffer("127.0.0.1 sql1\n"))
58+
)
59+
);
60+
61+
Buffer certValue = vertx.fileSystem().readFileBlocking("mssql.pem");
62+
MSSQLConnection.connect(customVertx, rule().options()
63+
.setHost("sql1")
64+
.setSsl(true)
65+
.setSslOptions(new ClientSSLOptions()
66+
.setHostnameVerificationAlgorithm("HTTPS")
67+
.setTrustOptions(new PemTrustOptions().addCertValue(certValue))))
68+
.onComplete(ctx.asyncAssertSuccess(conn -> {
69+
ctx.assertTrue(conn.isSSL());
70+
conn.close().onComplete(ctx.asyncAssertSuccess(v -> {
71+
customVertx.close().onComplete(ctx.asyncAssertSuccess());
72+
}));
73+
}));
74+
}
75+
3876
@Test
3977
public void testTrustAll(TestContext ctx) {
4078
setOptions(rule().options()

vertx-mssql-client/src/test/resources/ssl.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-docker-container-sec
33

44
Certificate and key created with the following command:
55
openssl req -x509 -nodes -newkey rsa:2048 -subj '/CN=sql1' -keyout mssql.key -out mssql.pem -days 36500
6+
7+
The certificate CN is 'sql1' to test hostname verification.

0 commit comments

Comments
 (0)