Skip to content

Commit 0042429

Browse files
authored
Add ALPN for TDSS connections (#1795)
1 parent 37f6feb commit 0042429

File tree

5 files changed

+77
-33
lines changed

5 files changed

+77
-33
lines changed

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

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import javax.net.SocketFactory;
6767
import javax.net.ssl.KeyManager;
6868
import javax.net.ssl.SSLContext;
69+
import javax.net.ssl.SSLParameters;
6970
import javax.net.ssl.SSLSocket;
7071
import javax.net.ssl.TrustManager;
7172
import javax.net.ssl.TrustManagerFactory;
@@ -1763,6 +1764,11 @@ else if (con.getTrustManagerClass() != null) {
17631764

17641765
if (isTDSS) {
17651766
sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(host, port);
1767+
1768+
// set ALPN values
1769+
SSLParameters sslParam = sslSocket.getSSLParameters();
1770+
sslParam.setApplicationProtocols(new String[] {"tds", "/", "8.0"});
1771+
sslSocket.setSSLParameters(sslParam);
17661772
} else {
17671773
// don't close proxy when SSL socket is closed
17681774
sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(proxySocket, host, port, false);
@@ -1772,18 +1778,26 @@ else if (con.getTrustManagerClass() != null) {
17721778
if (logger.isLoggable(Level.FINER))
17731779
logger.finer(toString() + " Starting SSL handshake");
17741780

1775-
// TLS 1.2 intermittent exception happens here.
1781+
// TLS 1.2 intermittent exception may happen here.
17761782
handshakeState = SSLHandhsakeState.SSL_HANDHSAKE_STARTED;
17771783
sslSocket.startHandshake();
17781784
handshakeState = SSLHandhsakeState.SSL_HANDHSAKE_COMPLETE;
17791785

1780-
// After SSL handshake is complete, rewire proxy socket to use raw TCP/IP streams ...
1786+
if (isTDSS) {
1787+
if (logger.isLoggable(Level.FINEST)) {
1788+
String negotiatedProtocol = sslSocket.getApplicationProtocol();
1789+
logger.finest(toString() + " Application Protocol negotiated: "
1790+
+ ((negotiatedProtocol == null) ? "null" : negotiatedProtocol));
1791+
}
1792+
}
1793+
1794+
// After SSL handshake is complete, re-wire proxy socket to use raw TCP/IP streams ...
17811795
if (logger.isLoggable(Level.FINEST))
17821796
logger.finest(toString() + " Rewiring proxy streams after handshake");
17831797

17841798
proxySocket.setStreams(inputStream, outputStream);
17851799

1786-
// ... and rewire TDSChannel to use SSL streams.
1800+
// ... and re-wire TDSChannel to use SSL streams.
17871801
if (logger.isLoggable(Level.FINEST))
17881802
logger.finest(toString() + " Getting SSL InputStream");
17891803

@@ -2669,15 +2683,17 @@ private SocketFactory getSocketFactory() throws IOException {
26692683

26702684
/**
26712685
* Helper function which traverses through list of InetAddresses to find a resolved one
2672-
* @param hostName
2686+
*
2687+
* @param hostName
26732688
*
26742689
* @param portNumber
26752690
* Port Number
26762691
* @return First resolved address or unresolved address if none found
26772692
* @throws IOException
26782693
* @throws SQLServerException
26792694
*/
2680-
private InetSocketAddress getInetAddressByIPPreference(String hostName, int portNumber) throws IOException, SQLServerException {
2695+
private InetSocketAddress getInetAddressByIPPreference(String hostName,
2696+
int portNumber) throws IOException, SQLServerException {
26812697
InetSocketAddress addr = InetSocketAddress.createUnresolved(hostName, portNumber);
26822698
for (int i = 0; i < addressList.size(); i++) {
26832699
addr = new InetSocketAddress(addressList.get(i), portNumber);
@@ -2760,8 +2776,11 @@ private Socket getSocketByIPPreference(String hostName, int portNumber, int time
27602776

27612777
/**
27622778
* Fills static array of IP Addresses with addresses of the preferred protocol version.
2763-
* @param addresses Array of all addresses
2764-
* @param ipv6first Boolean switch for IPv6 first
2779+
*
2780+
* @param addresses
2781+
* Array of all addresses
2782+
* @param ipv6first
2783+
* Boolean switch for IPv6 first
27652784
*/
27662785
private void fillAddressList(InetAddress[] addresses, boolean ipv6first) {
27672786
addressList.clear();

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

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1765,14 +1765,14 @@ private void registerKeyStoreProviderOnConnection(String keyStoreAuth, String ke
17651765
}
17661766
}
17671767

1768-
private void setKeyStoreSecretAndLocation(String keyStoreSecret, String keyStoreLocation) throws SQLServerException {
1768+
private void setKeyStoreSecretAndLocation(String keyStoreSecret,
1769+
String keyStoreLocation) throws SQLServerException {
17691770
// both secret and location must be set for JKS.
17701771
if ((null == keyStoreSecret) || (null == keyStoreLocation)) {
1771-
throw new SQLServerException(
1772-
SQLServerException.getErrString("R_keyStoreSecretOrLocationNotSet"), null);
1772+
throw new SQLServerException(SQLServerException.getErrString("R_keyStoreSecretOrLocationNotSet"), null);
17731773
} else {
17741774
SQLServerColumnEncryptionJavaKeyStoreProvider provider = new SQLServerColumnEncryptionJavaKeyStoreProvider(
1775-
keyStoreLocation, keyStoreSecret.toCharArray());
1775+
keyStoreLocation, keyStoreSecret.toCharArray());
17761776
systemColumnEncryptionKeyStoreProvider.put(provider.getName(), provider);
17771777
}
17781778
}
@@ -1959,14 +1959,15 @@ Connection connectInternal(Properties propsIn,
19591959
if (null != sPropValuePort) {
19601960
trustedServerNameAE += ":" + sPropValuePort;
19611961
}
1962-
1962+
19631963
sPropKey = SQLServerDriverStringProperty.IPADDRESS_PREFERENCE.toString();
19641964
sPropValue = activeConnectionProperties.getProperty(sPropKey);
19651965
if (null == sPropValue) {
19661966
sPropValue = SQLServerDriverStringProperty.IPADDRESS_PREFERENCE.getDefaultValue();
19671967
activeConnectionProperties.setProperty(sPropKey, sPropValue);
19681968
} else {
1969-
activeConnectionProperties.setProperty(sPropKey, IPAddressPreference.valueOfString(sPropValue).toString());
1969+
activeConnectionProperties.setProperty(sPropKey,
1970+
IPAddressPreference.valueOfString(sPropValue).toString());
19701971
}
19711972

19721973
sPropKey = SQLServerDriverStringProperty.APPLICATION_NAME.toString();
@@ -2016,8 +2017,8 @@ Connection connectInternal(Properties propsIn,
20162017

20172018
// enclave requires columnEncryption=enabled, enclaveAttestationUrl and enclaveAttestationProtocol
20182019
if (
2019-
// An attestation URL requires a protocol
2020-
(null != enclaveAttestationUrl && !enclaveAttestationUrl.isEmpty()
2020+
// An attestation URL requires a protocol
2021+
(null != enclaveAttestationUrl && !enclaveAttestationUrl.isEmpty()
20212022
&& (null == enclaveAttestationProtocol || enclaveAttestationProtocol.isEmpty()))
20222023

20232024
// An attestation protocol that is not NONE requires a URL
@@ -2073,7 +2074,8 @@ Connection connectInternal(Properties propsIn,
20732074
SQLServerException.getErrString("R_keyVaultProviderClientKeyNotSet"), null);
20742075
}
20752076
String keyVaultColumnEncryptionProviderClientKey = sPropValue;
2076-
setKeyVaultProvider(keyVaultColumnEncryptionProviderClientId, keyVaultColumnEncryptionProviderClientKey);
2077+
setKeyVaultProvider(keyVaultColumnEncryptionProviderClientId,
2078+
keyVaultColumnEncryptionProviderClientKey);
20772079
}
20782080

20792081
sPropKey = SQLServerDriverBooleanProperty.MULTI_SUBNET_FAILOVER.toString();
@@ -3265,7 +3267,8 @@ private InetSocketAddress connectHelper(ServerPortPlaceHolder serverInfo, int ti
32653267

32663268
// if the timeout is infinite slices are infinite too.
32673269
tdsChannel = new TDSChannel(this);
3268-
String iPAddressPreference = activeConnectionProperties.getProperty(SQLServerDriverStringProperty.IPADDRESS_PREFERENCE.toString());
3270+
String iPAddressPreference = activeConnectionProperties
3271+
.getProperty(SQLServerDriverStringProperty.IPADDRESS_PREFERENCE.toString());
32693272

32703273
InetSocketAddress inetSocketAddress = tdsChannel.open(serverInfo.getParsedServerName(),
32713274
serverInfo.getPortNumber(), (0 == timeOutFullInSeconds) ? 0 : timeOutSliceInMillis, useParallel,
@@ -3688,7 +3691,7 @@ void prelogin(String serverName, int portNumber) throws SQLServerException {
36883691
// If we say we don't support SSL and the server doesn't accept unencrypted connections,
36893692
// then terminate the connection.
36903693
if (TDS.ENCRYPT_NOT_SUP == requestedEncryptionLevel
3691-
&& TDS.ENCRYPT_NOT_SUP != negotiatedEncryptionLevel) {
3694+
&& TDS.ENCRYPT_NOT_SUP != negotiatedEncryptionLevel && !isTDSS) {
36923695
// If the server required an encrypted connection then terminate with an appropriate error.
36933696
if (TDS.ENCRYPT_REQ == negotiatedEncryptionLevel)
36943697
terminate(SQLServerException.DRIVER_ERROR_SSL_FAILED,
@@ -4558,8 +4561,8 @@ int writeAEFeatureRequest(boolean write, /* if false just calculates the length
45584561
if (write) {
45594562
tdsWriter.writeByte(TDS.TDS_FEATURE_EXT_AE); // FEATUREEXT_TC
45604563
tdsWriter.writeInt(1); // length of version
4561-
if (null == enclaveAttestationUrl || enclaveAttestationUrl.isEmpty() || (enclaveAttestationProtocol != null
4562-
&& !enclaveAttestationProtocol.equalsIgnoreCase(AttestationProtocol.NONE.toString()))) {
4564+
if (null == enclaveAttestationUrl || enclaveAttestationUrl.isEmpty() || (enclaveAttestationProtocol != null
4565+
&& !enclaveAttestationProtocol.equalsIgnoreCase(AttestationProtocol.NONE.toString()))) {
45634566
tdsWriter.writeByte(TDS.COLUMNENCRYPTION_VERSION1);
45644567
} else {
45654568
tdsWriter.writeByte(TDS.COLUMNENCRYPTION_VERSION2);
@@ -5678,8 +5681,8 @@ private void onFeatureExtAck(byte featureId, byte[] data) throws SQLServerExcept
56785681

56795682
serverColumnEncryptionVersion = ColumnEncryptionVersion.AE_V1;
56805683

5681-
if (null != enclaveAttestationUrl || (enclaveAttestationProtocol != null
5682-
&& enclaveAttestationProtocol.equalsIgnoreCase(AttestationProtocol.NONE.toString()))) {
5684+
if (null != enclaveAttestationUrl || (enclaveAttestationProtocol != null
5685+
&& enclaveAttestationProtocol.equalsIgnoreCase(AttestationProtocol.NONE.toString()))) {
56835686
if (aeVersion < TDS.COLUMNENCRYPTION_VERSION2) {
56845687
throw new SQLServerException(SQLServerException.getErrString("R_enclaveNotSupported"), null);
56855688
} else {
@@ -7666,8 +7669,9 @@ String getServerName() {
76667669

76677670
@Override
76687671
public void setIPAddressPreference(String iPAddressPreference) {
7669-
activeConnectionProperties.setProperty(SQLServerDriverStringProperty.IPADDRESS_PREFERENCE.toString(), iPAddressPreference);
7670-
7672+
activeConnectionProperties.setProperty(SQLServerDriverStringProperty.IPADDRESS_PREFERENCE.toString(),
7673+
iPAddressPreference);
7674+
76717675
}
76727676

76737677
@Override

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ protected Object[][] getContents() {
506506
{"R_InvalidIPAddressPreference", "IP address preference {0} is not valid."},
507507
{"R_UnableLoadAuthDll", "Unable to load authentication DLL {0}"},
508508
{"R_illegalArgumentTrustManager", "Interal error. Peer certificate chain or key exchange algorithem can not be null or empty."},
509-
{"R_serverCertError", "Error validating Server Certificate: {0}."},
509+
{"R_serverCertError", "Error validating Server Certificate: {0}: {1}."},
510510
{"R_SecureStringInitFailed", "Failed to initialize SecureStringUtil to store secure strings"},
511511
};
512512
}

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.microsoft.sqlserver.jdbc;
22

33
import java.security.cert.CertificateException;
4-
import java.security.cert.CertificateExpiredException;
54
import java.security.cert.X509Certificate;
65
import java.text.MessageFormat;
76
import java.util.Locale;
@@ -154,8 +153,8 @@ public void checkServerTrusted(X509Certificate[] chain, String authType) throws
154153
}
155154
} catch (Exception e) {
156155
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_serverCertError"));
157-
Object[] msgArgs = {serverCert, e.getMessage()};
158-
throw new CertificateExpiredException(form.format(msgArgs));
156+
Object[] msgArgs = {serverCert != null ? serverCert : hostName, e.getMessage()};
157+
throw new CertificateException(form.format(msgArgs));
159158
}
160159
}
161160

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

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,14 @@ public static void setupTests() throws Exception {
5656

5757
/**
5858
* Test connection properties with SQLServerDataSource
59+
*
60+
* @throws SQLServerException
5961
*/
6062
@Test
61-
public void testDataSource() {
63+
public void testDataSource() throws SQLServerException {
6264
SQLServerDataSource ds = new SQLServerDataSource();
6365
String stringPropValue = "stringPropValue";
6466
boolean booleanPropValue = true;
65-
String booleanStringValue = "true";
6667
int intPropValue = 1;
6768

6869
ds.setInstanceName(stringPropValue);
@@ -82,7 +83,7 @@ public void testDataSource() {
8283

8384
ds.setPortNumber(intPropValue);
8485
assertEquals(intPropValue, ds.getPortNumber(), TestResource.getResource("R_valuesAreDifferent"));
85-
86+
8687
ds.setIPAddressPreference(stringPropValue);
8788
assertEquals(stringPropValue, ds.getIPAddressPreference(), TestResource.getResource("R_valuesAreDifferent"));
8889

@@ -162,8 +163,29 @@ public void testDataSource() {
162163
ds.setTrustStorePassword(stringPropValue);
163164
assertEquals(stringPropValue, ds.getTrustStorePassword(), TestResource.getResource("R_valuesAreDifferent"));
164165

165-
ds.setEncrypt(booleanStringValue);
166-
assertEquals(booleanStringValue, ds.getEncrypt(), TestResource.getResource("R_valuesAreDifferent"));
166+
// verify encrypt=true options
167+
ds.setEncrypt(EncryptOption.Mandatory.toString());
168+
assertEquals("True", EncryptOption.valueOfString(ds.getEncrypt()).toString(),
169+
TestResource.getResource("R_valuesAreDifferent"));
170+
ds.setEncrypt(EncryptOption.True.toString());
171+
assertEquals("True", EncryptOption.valueOfString(ds.getEncrypt()).toString(),
172+
TestResource.getResource("R_valuesAreDifferent"));
173+
174+
// verify encrypt=false options
175+
ds.setEncrypt(EncryptOption.Optional.toString());
176+
assertEquals("False", EncryptOption.valueOfString(ds.getEncrypt()).toString(),
177+
TestResource.getResource("R_valuesAreDifferent"));
178+
ds.setEncrypt(EncryptOption.False.toString());
179+
assertEquals("False", EncryptOption.valueOfString(ds.getEncrypt()).toString(),
180+
TestResource.getResource("R_valuesAreDifferent"));
181+
ds.setEncrypt(EncryptOption.No.toString());
182+
assertEquals("False", EncryptOption.valueOfString(ds.getEncrypt()).toString(),
183+
TestResource.getResource("R_valuesAreDifferent"));
184+
185+
// verify enrypt=strict options
186+
ds.setEncrypt(EncryptOption.Strict.toString());
187+
assertEquals("Strict", EncryptOption.valueOfString(ds.getEncrypt()).toString(),
188+
TestResource.getResource("R_valuesAreDifferent"));
167189

168190
ds.setEncrypt(booleanPropValue);
169191
assertEquals(Boolean.toString(booleanPropValue), ds.getEncrypt(),

0 commit comments

Comments
 (0)