diff --git a/pom.xml b/pom.xml
index 66bfdb6f6..e8c483adc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -116,6 +116,13 @@
${google.gson.version}
true
+
+ org.bouncycastle
+ bcprov-jdk15on
+ 1.64
+ true
+
+
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/.SQLServerColumnEncryptionAzureKeyVaultProvider.java.swo b/src/main/java/com/microsoft/sqlserver/jdbc/.SQLServerColumnEncryptionAzureKeyVaultProvider.java.swo
deleted file mode 100644
index d251366df..000000000
Binary files a/src/main/java/com/microsoft/sqlserver/jdbc/.SQLServerColumnEncryptionAzureKeyVaultProvider.java.swo and /dev/null differ
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerEnclaveProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerEnclaveProvider.java
index f18971bf3..5d4d106ef 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerEnclaveProvider.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerEnclaveProvider.java
@@ -38,7 +38,7 @@
import java.util.Hashtable;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import javax.crypto.KeyAgreement;
@@ -49,40 +49,41 @@
*
*/
public interface ISQLServerEnclaveProvider {
- static final String proc = "EXEC sp_describe_parameter_encryption ?,?,?";
+ static final String SDPE1 = "EXEC sp_describe_parameter_encryption ?,?";
+ static final String SDPE2 = "EXEC sp_describe_parameter_encryption ?,?,?";
default byte[] getEnclavePackage(String userSQL, ArrayList enclaveCEKs) throws SQLServerException {
EnclaveSession enclaveSession = getEnclaveSession();
if (null != enclaveSession) {
try {
ByteArrayOutputStream enclavePackage = new ByteArrayOutputStream();
- enclavePackage.writeBytes(enclaveSession.getSessionID());
+ enclavePackage.write(enclaveSession.getSessionID());
ByteArrayOutputStream keys = new ByteArrayOutputStream();
byte[] randomGUID = new byte[16];
SecureRandom.getInstanceStrong().nextBytes(randomGUID);
- keys.writeBytes(randomGUID);
- keys.writeBytes(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN)
- .putLong(enclaveSession.getCounter()).array());
- keys.writeBytes(MessageDigest.getInstance("SHA-256").digest((userSQL).getBytes(UTF_16LE)));
+ keys.write(randomGUID);
+ keys.write(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(enclaveSession.getCounter())
+ .array());
+ keys.write(MessageDigest.getInstance("SHA-256").digest((userSQL).getBytes(UTF_16LE)));
for (byte[] b : enclaveCEKs) {
- keys.writeBytes(b);
+ keys.write(b);
}
enclaveCEKs.clear();
SQLServerAeadAes256CbcHmac256EncryptionKey encryptedKey = new SQLServerAeadAes256CbcHmac256EncryptionKey(
enclaveSession.getSessionSecret(), SQLServerAeadAes256CbcHmac256Algorithm.algorithmName);
SQLServerAeadAes256CbcHmac256Algorithm algo = new SQLServerAeadAes256CbcHmac256Algorithm(encryptedKey,
SQLServerEncryptionType.Randomized, (byte) 0x1);
- enclavePackage.writeBytes(algo.encryptData(keys.toByteArray()));
+ enclavePackage.write(algo.encryptData(keys.toByteArray()));
return enclavePackage.toByteArray();
- } catch (GeneralSecurityException | SQLServerException e) {
+ } catch (GeneralSecurityException | SQLServerException | IOException e) {
SQLServerException.makeFromDriverError(null, this, e.getLocalizedMessage(), "0", false);
}
}
return null;
}
- default ResultSet executeProc(PreparedStatement stmt, String userSql, String preparedTypeDefinitions,
- BaseAttestationRequest req) throws SQLException {
+ default ResultSet executeSDPEv2(PreparedStatement stmt, String userSql, String preparedTypeDefinitions,
+ BaseAttestationRequest req) throws SQLException, IOException {
((SQLServerPreparedStatement) stmt).isInternalEncryptionQuery = true;
stmt.setNString(1, userSql);
if (preparedTypeDefinitions != null && preparedTypeDefinitions.length() != 0) {
@@ -94,7 +95,19 @@ default ResultSet executeProc(PreparedStatement stmt, String userSql, String pre
return ((SQLServerPreparedStatement) stmt).executeQueryInternal();
}
- default void processAev1SPDE(String userSql, String preparedTypeDefinitions, Parameter[] params,
+ default ResultSet executeSDPEv1(PreparedStatement stmt, String userSql,
+ String preparedTypeDefinitions) throws SQLException {
+ ((SQLServerPreparedStatement) stmt).isInternalEncryptionQuery = true;
+ stmt.setNString(1, userSql);
+ if (preparedTypeDefinitions != null && preparedTypeDefinitions.length() != 0) {
+ stmt.setNString(2, preparedTypeDefinitions);
+ } else {
+ stmt.setNString(2, "");
+ }
+ return ((SQLServerPreparedStatement) stmt).executeQueryInternal();
+ }
+
+ default void processSDPEv1(String userSql, String preparedTypeDefinitions, Parameter[] params,
ArrayList parameterNames, SQLServerConnection connection, PreparedStatement stmt, ResultSet rs,
ArrayList enclaveRequestedCEKs) throws SQLException {
Map cekList = new HashMap<>();
@@ -148,7 +161,7 @@ default void processAev1SPDE(String userSql, String preparedTypeDefinitions, Par
// Process the second resultset.
if (!stmt.getMoreResults()) {
- throw new SQLServerException(this, SQLServerException.getErrString("R_UnexpectedDescribeParamFormat"), null,
+ throw new SQLServerException(null, SQLServerException.getErrString("R_UnexpectedDescribeParamFormat"), null,
0, false);
}
@@ -164,7 +177,7 @@ default void processAev1SPDE(String userSql, String preparedTypeDefinitions, Par
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_InvalidEncryptionKeyOrdinal"));
Object[] msgArgs = {cekOrdinal, cekEntry.getSize()};
- throw new SQLServerException(this, form.format(msgArgs), null, 0, false);
+ throw new SQLServerException(null, form.format(msgArgs), null, 0, false);
}
SQLServerEncryptionType encType = SQLServerEncryptionType
.of((byte) rs.getInt(DescribeParameterEncryptionResultSet2.ColumnEncrytionType.value()));
@@ -180,7 +193,7 @@ default void processAev1SPDE(String userSql, String preparedTypeDefinitions, Par
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_ForceEncryptionTrue_HonorAETrue_UnencryptedColumn"));
Object[] msgArgs = {userSql, paramIndex + 1};
- SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), "0", true);
+ SQLServerException.makeFromDriverError(null, connection, form.format(msgArgs), "0", true);
}
}
}
@@ -241,7 +254,7 @@ abstract class BaseAttestationRequest {
protected byte[] x;
protected byte[] y;
- byte[] getBytes() {
+ byte[] getBytes() throws IOException {
return null;
};
@@ -388,13 +401,13 @@ byte[] getSessionID() {
class EnclaveSession {
private byte[] sessionID;
- private AtomicInteger counter;
+ private AtomicLong counter;
private byte[] sessionSecret;
EnclaveSession(byte[] cs, byte[] b) {
sessionID = cs;
sessionSecret = b;
- counter = new AtomicInteger(0);
+ counter = new AtomicLong(0);
}
byte[] getSessionID() {
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAASEnclaveProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAASEnclaveProvider.java
index 0fffc9bae..1c4f85bca 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAASEnclaveProvider.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAASEnclaveProvider.java
@@ -117,14 +117,18 @@ private ArrayList describeParameterEncryption(SQLServerConnection connec
ArrayList parameterNames) throws SQLServerException {
ArrayList enclaveRequestedCEKs = new ArrayList<>();
ResultSet rs = null;
- try (PreparedStatement stmt = connection.prepareStatement(proc)) {
- rs = executeProc(stmt, userSql, preparedTypeDefinitions, aasParams);
+ try (PreparedStatement stmt = connection.prepareStatement(connection.enclaveEstablished() ? SDPE1 : SDPE2)) {
+ if (connection.enclaveEstablished()) {
+ rs = executeSDPEv1(stmt, userSql, preparedTypeDefinitions);
+ } else {
+ rs = executeSDPEv2(stmt, userSql, preparedTypeDefinitions, aasParams);
+ }
if (null == rs) {
// No results. Meaning no parameter.
// Should never happen.
return enclaveRequestedCEKs;
}
- processAev1SPDE(userSql, preparedTypeDefinitions, params, parameterNames, connection, stmt, rs,
+ processSDPEv1(userSql, preparedTypeDefinitions, params, parameterNames, connection, stmt, rs,
enclaveRequestedCEKs);
// Process the third resultset.
if (connection.isAEv2() && stmt.getMoreResults()) {
@@ -139,7 +143,7 @@ private ArrayList describeParameterEncryption(SQLServerConnection connec
}
// Null check for rs is done already.
rs.close();
- } catch (SQLException e) {
+ } catch (SQLException | IOException e) {
if (e instanceof SQLServerException) {
throw (SQLServerException) e;
} else {
@@ -164,26 +168,26 @@ class AASAttestationParameters extends BaseAttestationRequest {
byte[] attestationUrlBytes = (attestationUrl + '\0').getBytes(UTF_16LE);
ByteArrayOutputStream os = new ByteArrayOutputStream();
- os.writeBytes(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(attestationUrlBytes.length).array());
- os.writeBytes(attestationUrlBytes);
- os.writeBytes(NONCE_LENGTH);
+ os.write(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(attestationUrlBytes.length).array());
+ os.write(attestationUrlBytes);
+ os.write(NONCE_LENGTH);
new SecureRandom().nextBytes(nonce);
- os.writeBytes(nonce);
+ os.write(nonce);
enclaveChallenge = os.toByteArray();
initBcryptECDH();
}
@Override
- byte[] getBytes() {
+ byte[] getBytes() throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
- os.writeBytes(ENCLAVE_TYPE);
- os.writeBytes(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(enclaveChallenge.length).array());
- os.writeBytes(enclaveChallenge);
- os.writeBytes(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(ENCLAVE_LENGTH).array());
- os.writeBytes(ECDH_MAGIC);
- os.writeBytes(x);
- os.writeBytes(y);
+ os.write(ENCLAVE_TYPE);
+ os.write(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(enclaveChallenge.length).array());
+ os.write(enclaveChallenge);
+ os.write(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(ENCLAVE_LENGTH).array());
+ os.write(ECDH_MAGIC);
+ os.write(x);
+ os.write(y);
return os.toByteArray();
}
@@ -293,12 +297,12 @@ void validateToken(String attestationUrl, byte[] nonce) throws SQLServerExceptio
String authorityUrl = new URL(attestationUrl).getAuthority();
URL wellKnownUrl = new URL("https://" + authorityUrl + "/.well-known/openid-configuration");
URLConnection con = wellKnownUrl.openConnection();
- String wellKnownUrlJson = new String(con.getInputStream().readAllBytes());
+ String wellKnownUrlJson = new String(Util.convertInputStreamToString(con.getInputStream()));
JsonObject attestationJson = JsonParser.parseString(wellKnownUrlJson).getAsJsonObject();
// Get our Keys
URL jwksUrl = new URL(attestationJson.get("jwks_uri").getAsString());
URLConnection jwksCon = jwksUrl.openConnection();
- String jwksUrlJson = new String(jwksCon.getInputStream().readAllBytes());
+ String jwksUrlJson = new String(Util.convertInputStreamToString(jwksCon.getInputStream()));
JsonObject jwksJson = JsonParser.parseString(jwksUrlJson).getAsJsonObject();
keys = jwksJson.get("keys").getAsJsonArray();
certificateCache.put(attestationUrl, new JWTCertificateEntry(keys));
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerVSMEnclaveProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerVSMEnclaveProvider.java
index 416a6a8a7..3c0f7fb53 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerVSMEnclaveProvider.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerVSMEnclaveProvider.java
@@ -11,6 +11,8 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CertificateException;
@@ -119,7 +121,9 @@ private byte[] getAttestationCertificates() throws IOException {
if (null == certData) {
java.net.URL url = new java.net.URL(attestationUrl + "/attestationservice.svc/v2.0/signingCertificates/");
java.net.URLConnection con = url.openConnection();
- String s = new String(con.getInputStream().readAllBytes());
+ byte[] buff = new byte[con.getInputStream().available()];
+ con.getInputStream().read(buff, 0, buff.length);
+ String s = new String(buff);
// omit the square brackets that come with the JSON
String[] bytesString = s.substring(1, s.length() - 1).split(",");
certData = new byte[bytesString.length];
@@ -136,14 +140,18 @@ private ArrayList describeParameterEncryption(SQLServerConnection connec
ArrayList parameterNames) throws SQLServerException {
ArrayList enclaveRequestedCEKs = new ArrayList<>();
ResultSet rs = null;
- try (PreparedStatement stmt = connection.prepareStatement(proc)) {
- rs = executeProc(stmt, userSql, preparedTypeDefinitions, vsmParams);
+ try (PreparedStatement stmt = connection.prepareStatement(connection.enclaveEstablished() ? SDPE1 : SDPE2)) {
+ if (connection.enclaveEstablished()) {
+ rs = executeSDPEv1(stmt, userSql, preparedTypeDefinitions);
+ } else {
+ rs = executeSDPEv2(stmt, userSql, preparedTypeDefinitions, vsmParams);
+ }
if (null == rs) {
// No results. Meaning no parameter.
// Should never happen.
return enclaveRequestedCEKs;
}
- processAev1SPDE(userSql, preparedTypeDefinitions, params, parameterNames, connection, stmt, rs,
+ processSDPEv1(userSql, preparedTypeDefinitions, params, parameterNames, connection, stmt, rs,
enclaveRequestedCEKs);
// Process the third resultset.
if (connection.isAEv2() && stmt.getMoreResults()) {
@@ -158,7 +166,7 @@ private ArrayList describeParameterEncryption(SQLServerConnection connec
}
// Null check for rs is done already.
rs.close();
- } catch (SQLException e) {
+ } catch (SQLException | IOException e) {
if (e instanceof SQLServerException) {
throw (SQLServerException) e;
} else {
@@ -181,14 +189,14 @@ class VSMAttestationParameters extends BaseAttestationRequest {
}
@Override
- byte[] getBytes() {
+ byte[] getBytes() throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
- os.writeBytes(ENCLAVE_TYPE);
- os.writeBytes(enclaveChallenge);
- os.writeBytes(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(ENCLAVE_LENGTH).array());
- os.writeBytes(ECDH_MAGIC);
- os.writeBytes(x);
- os.writeBytes(y);
+ os.write(ENCLAVE_TYPE);
+ os.write(enclaveChallenge);
+ os.write(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(ENCLAVE_LENGTH).array());
+ os.write(ECDH_MAGIC);
+ os.write(x);
+ os.write(y);
return os.toByteArray();
}
}
@@ -319,7 +327,17 @@ void validateStatementSignature() throws SQLServerException, GeneralSecurityExce
SQLServerResource.getResource("R_EnclavePackageLengthError"), "0", false);
}
- Signature sig = Signature.getInstance("RSASSA-PSS");
+ Signature sig = null;
+ try {
+ sig = Signature.getInstance("RSASSA-PSS");
+ } catch (NoSuchAlgorithmException e) {
+ /*
+ * RSASSA-PSS was added in JDK 11, the user might be using an older version of Java. Use BC as backup.
+ * Remove this logic if JDK 8 stops being supported or backports RSASSA-PSS
+ */
+ Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+ sig = Signature.getInstance("RSASSA-PSS");
+ }
PSSParameterSpec pss = new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1);
sig.setParameter(pss);
sig.initVerify(healthCert);
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java
index bbb597491..02c79d25a 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java
@@ -5,6 +5,7 @@
package com.microsoft.sqlserver.jdbc;
+import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
@@ -991,6 +992,16 @@ static boolean use43Wrapper() {
static String escapeSingleQuotes(String name) {
return name.replace("'", "''");
}
+
+ static String convertInputStreamToString(java.io.InputStream is) throws IOException {
+ java.io.ByteArrayOutputStream result = new java.io.ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int length;
+ while ((length = is.read(buffer)) != -1) {
+ result.write(buffer, 0, length);
+ }
+ return result.toString();
+ }
}