Skip to content

Commit c394a1b

Browse files
authored
Fixed server certificate validation for encrypt=strict (#2174)
1 parent 46a0fb0 commit c394a1b

File tree

11 files changed

+96
-65
lines changed

11 files changed

+96
-65
lines changed

pom.xml

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
xSQLv12 - - - - - - For tests not compatible with SQL Server 2008 R2 - 2014
3939
xSQLv14 - - - - - - For tests not compatible with SQL Server 2016 - 2017
4040
xSQLv15 - - - - - - For tests not compatible with SQL Server 2019 - - - -
41+
xSQLv16 - - - - - - For tests not compatible with SQL Server 2022 - - - -
4142
xAzureSQLDB - - - - For tests not compatible with Azure SQL Database - -
4243
xAzureSQLDW - - - - For tests not compatible with Azure Data Warehouse -
4344
xAzureSQLMI - - - - For tests not compatible with Azure SQL Managed Instance

src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1693,7 +1693,7 @@ else if (con.getTrustManagerClass() != null) {
16931693
// Otherwise, we'll validate the certificate using a real TrustManager obtained
16941694
// from the a security provider that is capable of validating X.509 certificates.
16951695
else {
1696-
if (isTDS8) {
1696+
if (isTDS8 && serverCert != null) {
16971697
if (logger.isLoggable(Level.FINEST))
16981698
logger.finest(toString() + " Verify server certificate for TDS 8");
16991699

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCertificateUtils.java

+3-5
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,8 @@ static void validateServerNameInCertificate(X509Certificate cert, String hostNam
281281
*/
282282
static void validateServerCerticate(X509Certificate cert, String certFile) throws CertificateException {
283283
try (InputStream is = fileToStream(certFile)) {
284-
if (!CertificateFactory.getInstance("X509").generateCertificate(is).getPublicKey()
285-
.equals(cert.getPublicKey())) {
286-
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_publicKeyMismatch"));
284+
if (!CertificateFactory.getInstance("X509").generateCertificate(is).equals(cert)) {
285+
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_serverCertError"));
287286
Object[] msgArgs = {certFile};
288287
throw new CertificateException(form.format(msgArgs));
289288
}
@@ -353,8 +352,7 @@ static KeyManager[] readPKCS8Certificate(String certPath, String keyPath,
353352
}
354353

355354
private static KeyManager[] readPKCS12Certificate(String certPath,
356-
String keyPassword) throws NoSuchAlgorithmException, CertificateException, IOException,
357-
UnrecoverableKeyException, KeyStoreException, SQLServerException {
355+
String keyPassword) throws NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException, KeyStoreException, SQLServerException {
358356

359357
KeyStore keyStore = loadPKCS12KeyStore(certPath, keyPassword);
360358
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(SUN_X_509);

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java

-1
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,6 @@ protected Object[][] getContents() {
530530
{"R_illegalArgumentTrustManager", "Interal error. Peer certificate chain or key exchange algorithem can not be null or empty."},
531531
{"R_serverCertExpired", "Server Certificate has expired: {0}: {1}"},
532532
{"R_serverCertNotYetValid", "Server Certificate is not yet valid: {0}: {1}"},
533-
{"R_publicKeyMismatch", "Error validating Server Certificate: public key mismatch: {0}"},
534533
{"R_serverCertError", "Error validating Server Certificate: {0}: \n{1}:\n{2}."},
535534
{"R_SecureStringInitFailed", "Failed to initialize SecureStringUtil to store secure strings"},
536535
{"R_ALPNFailed", "Failed to negotiate Application-Layer Protocol {0}. Server returned: {1}."},

src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/MSITest.java

+6
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public class MSITest extends AESetup {
5151
@Tag(Constants.xSQLv12)
5252
@Tag(Constants.xSQLv14)
5353
@Tag(Constants.xSQLv15)
54+
@Tag(Constants.xSQLv16)
5455
@Test
5556
public void testManagedIdentityAuth() throws SQLException {
5657
String connStr = connectionString;
@@ -78,6 +79,7 @@ private void testSimpleConnect(String connStr) {
7879
@Tag(Constants.xSQLv12)
7980
@Tag(Constants.xSQLv14)
8081
@Tag(Constants.xSQLv15)
82+
@Tag(Constants.xSQLv16)
8183
@Test
8284
public void testManagedIdentityAuthWithManagedIdentityClientId() throws SQLException {
8385
String connStr = connectionString;
@@ -116,6 +118,7 @@ public void testManagedIdentityAuthWithManagedIdentityClientId() throws SQLExcep
116118
@Tag(Constants.xSQLv12)
117119
@Tag(Constants.xSQLv14)
118120
@Tag(Constants.xSQLv15)
121+
@Tag(Constants.xSQLv16)
119122
@Test
120123
public void testDSManagedIdentityAuth() throws SQLException {
121124
String connStr = connectionString;
@@ -141,6 +144,7 @@ public void testDSManagedIdentityAuth() throws SQLException {
141144
@Tag(Constants.xSQLv12)
142145
@Tag(Constants.xSQLv14)
143146
@Tag(Constants.xSQLv15)
147+
@Tag(Constants.xSQLv16)
144148
@Test
145149
public void testDSManagedIdentityAuthWithManagedIdentityClientId() throws SQLException {
146150
String connStr = connectionString;
@@ -169,6 +173,7 @@ public void testDSManagedIdentityAuthWithManagedIdentityClientId() throws SQLExc
169173
@Tag(Constants.xSQLv12)
170174
@Tag(Constants.xSQLv14)
171175
@Tag(Constants.xSQLv15)
176+
@Tag(Constants.xSQLv16)
172177
@Test
173178
public void testActiveDirectoryDefaultAuth() throws SQLException {
174179
String connStr = connectionString;
@@ -188,6 +193,7 @@ public void testActiveDirectoryDefaultAuth() throws SQLException {
188193
@Tag(Constants.xSQLv12)
189194
@Tag(Constants.xSQLv14)
190195
@Tag(Constants.xSQLv15)
196+
@Tag(Constants.xSQLv16)
191197
@Test
192198
public void testActiveDirectoryDefaultAuthDS() throws SQLException {
193199
String connStr = connectionString;

src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java

+72-58
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,22 @@ public void testEncryptedConnection() throws SQLException {
333333
try (Connection con = ds.getConnection()) {}
334334
}
335335

336+
@Tag(Constants.xSQLv11)
337+
@Tag(Constants.xSQLv12)
338+
@Tag(Constants.xSQLv14)
339+
@Tag(Constants.xSQLv15)
340+
@Tag(Constants.xAzureSQLDW)
341+
@Tag(Constants.xAzureSQLDB)
342+
@Test
343+
public void testEncryptedStrictConnection() throws SQLException {
344+
SQLServerDataSource ds = new SQLServerDataSource();
345+
ds.setURL(connectionString);
346+
ds.setServerCertificate(serverCertificate);
347+
ds.setEncrypt(Constants.STRICT);
348+
349+
try (Connection con = ds.getConnection()) {}
350+
}
351+
336352
@Test
337353
public void testJdbcDataSourceMethod() throws SQLFeatureNotSupportedException {
338354
SQLServerDataSource fxds = new SQLServerDataSource();
@@ -936,65 +952,63 @@ public void run() {
936952
assertTrue(status && future.isCancelled(), TestResource.getResource("R_threadInterruptNotSet"));
937953
}
938954

955+
/**
956+
* Test thread count when finding socket using threading.
957+
*/
958+
@Test
959+
@Tag(Constants.xAzureSQLDB)
960+
@Tag(Constants.xAzureSQLDW)
961+
public void testThreadCountWhenFindingSocket() {
962+
ExecutorService executor = null;
963+
ManagementFactory.getThreadMXBean().resetPeakThreadCount();
964+
965+
// First, check to see if there is a reachable local host, or else test will fail.
966+
try {
967+
SQLServerDataSource ds = new SQLServerDataSource();
968+
ds.setServerName("localhost");
969+
Connection con = ds.getConnection();
970+
} catch (SQLServerException e) {
971+
// Assume this will be an error different than 'localhost is unreachable'. If it is 'localhost is
972+
// unreachable' abort and skip the test.
973+
Assume.assumeFalse(e.getMessage().startsWith(TestResource.getResource("R_tcpipConnectionToHost")));
974+
}
975+
976+
try {
977+
executor = Executors.newSingleThreadExecutor(r -> new Thread(r, ""));
978+
executor.submit(() -> {
979+
try {
980+
SQLServerDataSource ds = new SQLServerDataSource();
981+
ds.setServerName("localhost");
982+
Thread.sleep(5000);
983+
Connection conn2 = ds.getConnection();
984+
} catch (Exception e) {
985+
if (!(e instanceof SQLServerException)) {
986+
fail(TestResource.getResource("R_unexpectedException") + e.getMessage());
987+
}
988+
}
989+
});
990+
SQLServerDataSource ds = new SQLServerDataSource();
991+
ds.setServerName("localhost");
992+
Connection conn = ds.getConnection();
993+
Thread.sleep(5000);
994+
} catch (Exception e) {
995+
if (!(e instanceof SQLServerException)) {
996+
fail(TestResource.getResource("R_unexpectedException") + e.getMessage());
997+
}
998+
} finally {
999+
if (executor != null) {
1000+
executor.shutdown();
1001+
}
1002+
}
1003+
1004+
// At this point, thread count has returned to normal. If the peak was more
1005+
// than 2 times the current, this is an issue and the test should fail.
1006+
if (ManagementFactory.getThreadMXBean().getPeakThreadCount() > 2
1007+
* ManagementFactory.getThreadMXBean().getThreadCount()) {
1008+
fail(TestResource.getResource("R_unexpectedThreadCount"));
1009+
}
1010+
}
9391011

940-
/**
941-
* Test thread count when finding socket using threading.
942-
*/
943-
@Test
944-
@Tag(Constants.xAzureSQLDB)
945-
@Tag(Constants.xAzureSQLDW)
946-
public void testThreadCountWhenFindingSocket() {
947-
ExecutorService executor = null;
948-
ManagementFactory.getThreadMXBean().resetPeakThreadCount();
949-
950-
// First, check to see if there is a reachable local host, or else test will fail.
951-
try {
952-
SQLServerDataSource ds = new SQLServerDataSource();
953-
ds.setServerName("localhost");
954-
Connection con = ds.getConnection();
955-
} catch (SQLServerException e) {
956-
// Assume this will be an error different than 'localhost is unreachable'. If it is 'localhost is
957-
// unreachable' abort and skip the test.
958-
Assume.assumeFalse(e.getMessage().startsWith(TestResource.getResource("R_tcpipConnectionToHost")));
959-
}
960-
961-
try {
962-
executor = Executors.newSingleThreadExecutor(r -> new Thread(r, ""));
963-
executor.submit(() -> {
964-
try {
965-
SQLServerDataSource ds = new SQLServerDataSource();
966-
ds.setServerName("localhost");
967-
Thread.sleep(5000);
968-
Connection conn2 = ds.getConnection();
969-
} catch (Exception e) {
970-
if (!(e instanceof SQLServerException)) {
971-
fail(TestResource.getResource("R_unexpectedException") + e.getMessage());
972-
}
973-
}
974-
});
975-
SQLServerDataSource ds = new SQLServerDataSource();
976-
ds.setServerName("localhost");
977-
Connection conn = ds.getConnection();
978-
Thread.sleep(5000);
979-
} catch (Exception e) {
980-
if (!(e instanceof SQLServerException)) {
981-
fail(TestResource.getResource("R_unexpectedException") + e.getMessage());
982-
}
983-
} finally {
984-
if (executor != null) {
985-
executor.shutdown();
986-
}
987-
}
988-
989-
// At this point, thread count has returned to normal. If the peak was more
990-
// than 2 times the current, this is an issue and the test should fail.
991-
if (ManagementFactory.getThreadMXBean().getPeakThreadCount()
992-
> 2 * ManagementFactory.getThreadMXBean().getThreadCount()) {
993-
fail(TestResource.getResource("R_unexpectedThreadCount"));
994-
}
995-
}
996-
997-
9981012
/**
9991013
* Test calling method to get redirected server string.
10001014
*/

src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,7 @@ public void testGetColumns() throws SQLException {
758758
@Tag(Constants.xSQLv12)
759759
@Tag(Constants.xSQLv14)
760760
@Tag(Constants.xSQLv15)
761+
@Tag(Constants.xSQLv16)
761762
@Tag(Constants.xAzureSQLDB)
762763
@Tag(Constants.xAzureSQLMI)
763764
public void testGetImportedKeysDW() throws SQLException {

src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ public void testPooledConnectionLang() throws SQLException {
296296
@Tag(Constants.xSQLv12)
297297
@Tag(Constants.xSQLv14)
298298
@Tag(Constants.xSQLv15)
299+
@Tag(Constants.xSQLv16)
299300
@Tag(Constants.xAzureSQLDW)
300301
@Tag(Constants.reqExternalSetup)
301302
@Test

src/test/java/com/microsoft/sqlserver/jdbc/resultset/DataClassificationTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ public void testDataClassificationMetadata() throws Exception {
167167
@Tag(Constants.xAzureSQLDB)
168168
@Tag(Constants.xAzureSQLDW)
169169
@Tag(Constants.xSQLv15)
170+
@Tag(Constants.xSQLv16)
170171
@Test
171172
public void testDataClassificationNotSupported() throws Exception {
172173
try (Statement stmt = connection.createStatement();) {

src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java

+8
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ public abstract class AbstractTest {
7373
protected static String trustStore = "";
7474
protected static String trustStorePassword = "";
7575

76+
protected static String serverCertificate = "";
77+
7678
protected static String encrypt = "";
7779
protected static String trustServerCertificate = "";
7880

@@ -202,6 +204,12 @@ public static void setup() throws Exception {
202204
trustStorePassword);
203205
}
204206

207+
serverCertificate = getConfiguredProperty("serverCertificate", "");
208+
if (!serverCertificate.trim().isEmpty()) {
209+
connectionString = TestUtils.addOrOverrideProperty(connectionString, "serverCertificate",
210+
serverCertificate);
211+
}
212+
205213
Map<String, SQLServerColumnEncryptionKeyStoreProvider> map = new HashMap<String, SQLServerColumnEncryptionKeyStoreProvider>();
206214
if (null == jksProvider) {
207215
jksProvider = new SQLServerColumnEncryptionJavaKeyStoreProvider(javaKeyPath,

src/test/java/com/microsoft/sqlserver/testframework/Constants.java

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ private Constants() {}
2020
* xSQLv12 - - - - - - For tests not compatible with SQL Server 2008 R2 - 2014
2121
* xSQLv14 - - - - - - For tests not compatible with SQL Server 2016 - 2017
2222
* xSQLv15 - - - - - - For tests not compatible with SQL Server 2019
23+
* xSQLv16 - - - - - - For tests not compatible with SQL Server 2022
2324
* xAzureSQLDB - - - - For tests not compatible with Azure SQL Database
2425
* xAzureSQLDW - - - - For tests not compatible with Azure Data Warehouse
2526
* xAzureSQLMI - - - - For tests not compatible with Azure SQL Managed Instance
@@ -35,6 +36,7 @@ private Constants() {}
3536
public static final String xSQLv12 = "xSQLv12";
3637
public static final String xSQLv14 = "xSQLv14";
3738
public static final String xSQLv15 = "xSQLv15";
39+
public static final String xSQLv16 = "xSQLv16";
3840
public static final String xAzureSQLDB = "xAzureSQLDB";
3941
public static final String xAzureSQLDW = "xAzureSQLDW";
4042
public static final String xAzureSQLMI = "xAzureSQLMI";

0 commit comments

Comments
 (0)