Använda Always Encrypted med JDBC-drivrutinen
Den här sidan innehåller information om hur du utvecklar Java-program för användning Always Encrypted med Microsoft JDBC Driver 6.0 (eller senare) för SQL Server.
Always Encrypted gör att klienter kan kryptera känsliga data och aldrig avslöja data eller krypteringsnycklar till SQL Server eller Azure SQL Database. En Always Encrypted-aktiverad drivrutin, till exempel Microsoft JDBC Driver 6.0 (eller senare) för SQL Server, uppnår detta beteende genom att transparent kryptera och dekryptera känsliga data i klientprogrammet. Drivrutinen räknar ut vilka frågeparametrar som motsvarar Always Encrypted-databaskolumner och krypterar värdena för dessa parametrar innan de skickas till databasen. På samma sätt dekrypterar drivrutinen data genomskinligt som hämtas från krypterade databaskolumner i frågeresultat. Mer information finns i Always Encrypted (Database Engine) och Always Encrypted API-referens för JDBC-drivrutinen.
Förutsättningar
- Kontrollera att Microsoft JDBC Driver 6.0 (eller senare) för SQL Server är installerat på utvecklingsdatorn.
- Ladda ned och installera JCE-filen (Java Cryptography Extension) Unlimited Strength Jurisdiction Policy Files. Se till att läsa Readme som ingår i zip-filen för installationsinstruktioner och relevant information om möjliga export- eller importproblem.
- För mssql-jdbc-X.X.X.jre7.jar eller sqljdbc41.jar kan principfilerna laddas ned från Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 7 Download
- För mssql-jdbc-X.X.X.jre8.jar eller sqljdbc42.jar kan principfilerna laddas ned från Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 8 Download
- För alla JRE version 9 eller senare (till exempel mssql-jdbc-X.X.X.jre9.jar) behöver ingen principfil laddas ned. Jurisdiktionsprincipen i Java 9 och senare är som standard inställd på obegränsad styrkekryptering.
Arbeta med förråd för kolumnmästarnycklar
För att kryptera eller dekryptera data för krypterade kolumner underhåller SQL Server kolumnkrypteringsnycklar. Kolumnkrypteringsnycklar lagras i krypterad form i databasmetadata. Varje kolumnkrypteringsnyckel har en motsvarande kolumnhuvudnyckel som används för att kryptera kolumnkrypteringsnyckeln.
Databasmetadata innehåller inte kolumnhuvudnycklarna. Dessa nycklar innehas endast av klienten. Databasmetadata innehåller dock information om var kolumnhuvudnycklarna lagras i förhållande till klienten. Till exempel kan databasmetadata säga att nyckelarkivet som innehåller en kolumnhuvudnyckel är Windows Certificate Store, och det specifika certifikatet som används för att kryptera och dekryptera finns på en specifik sökväg i Windows Certificate Store.
Om klienten har åtkomst till certifikatet i Windows Certificate Store kan den hämta certifikatet. Certifikatet kan sedan användas för att dekryptera kolumnkrypteringsnyckeln. Sedan kan krypteringsnyckeln användas för att dekryptera eller kryptera data för krypterade kolumner som använder den kolumnkrypteringsnyckeln.
Microsoft JDBC-drivrutinen för SQL Server kommunicerar med ett nyckelarkiv som använder en huvudnyckellagringsleverantör för kolumn, vilken är en instans av en klass som ärvts från SQLServerColumnEncryptionKeyStoreProvider
.
Använd inbyggda lagringsleverantörer för huvudnycklar för kolumner
Microsoft JDBC-drivrutinen för SQL Server levereras med följande inbyggda huvudnyckellagringsprovider för kolumner. Vissa av dessa leverantörer är förregistrerade med de specifika providernamnen (används för att leta upp providern) och vissa kräver antingen extra autentiseringsuppgifter eller explicit registrering.
Klass | Beskrivning | Providernamn (sökning) | Är förregistrerad? | Plattform |
---|---|---|---|---|
SQLServerColumnEncryptionAzureKeyVaultProvider |
En leverantör för ett nyckellagringstjänst för Azure Key Vault. | AZURE_KEY_VAULT | Ingen före JDBC-drivrutinen version 7.4.1, men ja från och med JDBC-drivrutinsversion 7.4.1. | Windows, Linux, macOS |
SQLServerColumnEncryptionCertificateStoreProvider |
En leverantör för Windows certifikatsbutik. | MSSQL_CERTIFICATE_STORE | Ja | Windows |
SQLServerColumnEncryptionJavaKeyStoreProvider |
En tillhandahållare för Java-nyckelarkivet. | MSSQL_JAVA_KEYSTORE | Ja | Windows, Linux, macOS |
För de förregistrerade nyckellagringsprovidrar behöver du inga programkodändringar för att använda dessa leverantörer, men observera följande:
- Du måste se till att providernamnet som har konfigurerats i kolumnens huvudnyckelmetadata är korrekt och att kolumnhuvudnyckelsökvägen följer det nyckelsökvägsformat som är giltigt för en viss provider. Vi rekommenderar att du konfigurerar nycklarna med verktyg som SQL Server Management Studio, som automatiskt genererar giltiga providernamn och nyckelsökvägar för att utfärda instruktionen
CREATE COLUMN MASTER KEY
(Transact-SQL). - Se till att programmet kan komma åt nyckeln i nyckelarkivet. Den här uppgiften kan innebära att ge programmet åtkomst till nyckeln och/eller nyckelarkivet. Beroende på nyckelarkivet kan detta omfatta andra nyckellagringsspecifika konfigurationssteg. Om du till exempel vill använda
SQLServerColumnEncryptionJavaKeyStoreProvider
måste du ange platsen och lösenordet för nyckelarkivet i anslutningsegenskaperna.
Alla dessa nyckelarkivleverantörer beskrivs mer detaljerat i de avsnitt som följer. Du behöver bara implementera en nyckellagringsprovider för att använda Always Encrypted.
Använda Azure Key Vault-provider
Azure Key Vault är ett praktiskt alternativ för att lagra och hantera kolumnhuvudnycklar för Always Encrypted (särskilt om ditt program finns i Azure). Microsoft JDBC-drivrutinen för SQL Server innehåller en inbyggd provider, SQLServerColumnEncryptionAzureKeyVaultProvider
, för program som har nycklar lagrade i Azure Key Vault. Namnet på den här providern är AZURE_KEY_VAULT.
Not
Azure Key Vault-providern som är inbyggd i JDBC-drivrutinen stöder både -valv och hanterade HSM:er i Azure Key Vault.
Om du vill använda Azure Key Vault Store-providern måste en programutvecklare skapa valvet och nycklarna i Azure Key Vault och skapa en appregistrering i Microsoft Entra-ID (tidigare Azure Active Directory). Det registrerade programmet måste beviljas behörigheterna Get, Decrypt, Encrypt, Unwrap Key, Wrap Key och Verify i de åtkomstprinciper som definierats för nyckelvalvet som skapats för användning med Always Encrypted. Mer information om hur du konfigurerar nyckelvalvet och skapar en kolumnhuvudnyckel finns i Azure Key Vault – Steg för steg och Skapa kolumnhuvudnycklar i Azure Key Vault.
För Azure Key Vault-providern validerar JDBC-drivrutinen kolumnhuvudnyckelsökvägen mot listan över betrodda slutpunkter. Från och med version 8.2.2 kan den här listan konfigureras: skapa en mssql-jdbc.properties
fil i arbetskatalogen för programmet, ange egenskapen AKVTrustedEndpoints
till en semikolonavgränsad lista. Om värdet börjar med ett semikolon utökas standardlistan. Annars ersätter den standardlistan.
Standardvärdena för betrodda slutpunkter är:
*vault.azure.net
*vault.azure.cn
*vault.usgovcloudapi.net
*vault.microsoftazure.de
-
*managedhsm.azure.net
(v9.2+) -
*managedhsm.azure.cn
(v9.2+) -
*managedhsm.usgovcloudapi.net
(v9.2+) -
*managedhsm.microsoftazure.de
(v9.2+)
Om du har skapat en Azure Key Vault-baserad kolumnhuvudnyckel och kolumnkrypteringsnyckel med SQL Server Management Studio kan T-SQL-skriptet för att återskapa dem se ut ungefär som i det här exemplet med egna specifika KEY_PATH och ENCRYPTED_VALUE:
CREATE COLUMN MASTER KEY [MyCMK]
WITH
(
KEY_STORE_PROVIDER_NAME = N'AZURE_KEY_VAULT',
KEY_PATH = N'https://<MyKeyVaultName>.vault.azure.net:443/keys/Always-Encrypted-Auto1/c61f01860f37302457fa512bb7e7f4e8'
);
CREATE COLUMN ENCRYPTION KEY [MyCEK]
WITH VALUES
(
COLUMN_MASTER_KEY = [MyCMK],
ALGORITHM = 'RSA_OAEP',
ENCRYPTED_VALUE = 0x01BA000001680074507400700073003A002F002F006400610076006...
);
Ett program som använder JDBC-drivrutinen kan använda Azure Key Vault. Syntaxen eller instruktionerna för den här användningen av Azure Key Vault har ändrats från och med JDBC-drivrutinsversion 7.4.1.
JDBC-drivrutin 7.4.1 eller senare
Det här avsnittet omfattar JDBC-drivrutinsversion 7.4.1 eller senare.
Ett klientprogram som använder JDBC-drivrutinen kan konfigurera att använda Azure Key Vault genom att nämna keyVaultProviderClientId=<ClientId>;keyVaultProviderClientKey=<ClientKey>
i JDBC-anslutningssträngen.
Här är ett exempel som innehåller den här konfigurationsinformationen i en JDBC-anslutningssträng.
String connectionUrl = "jdbc:sqlserver://<server>:<port>;encrypt=true;user=<user>;password=<password>;columnEncryptionSetting=Enabled;keyVaultProviderClientId=<ClientId>;keyVaultProviderClientKey=<ClientKey>";
JDBC-drivrutinen instansierar automatiskt ett SQLServerColumnEncryptionAzureKeyVaultProvider
objekt när dessa autentiseringsuppgifter finns bland anslutningsegenskaperna.
Viktig
Anslutningsegenskaperna keyVaultProviderClientId
och keyVaultProviderClientKey
har föråldrats från och med v8.4.1. Användarna uppmanas att använda keyStoreAuthentication
, KeyStorePrincipalId
och KeyStoreSecret
i stället.
JDBC-drivrutinsversioner före 7.4.1
Det här avsnittet omfattar JDBC-drivrutinsversioner före 7.4.1.
Ett klientprogram som använder JDBC-drivrutinen måste instansiera ett SQLServerColumnEncryptionAzureKeyVaultProvider
objekt och sedan registrera objektet med drivrutinen.
SQLServerColumnEncryptionAzureKeyVaultProvider akvProvider = new SQLServerColumnEncryptionAzureKeyVaultProvider(clientID, clientKey);
clientID
är program-ID för en appregistrering i en Microsoft Entra-klientorganisation.
clientKey
är ett nyckellösenord som registrerats under programmet, vilket ger API-åtkomst till Azure Key Vault.
När programmet har skapat en instans av SQLServerColumnEncryptionAzureKeyVaultProvider
måste programmet registrera instansen med drivrutinen med metoden SQLServerConnection.registerColumnEncryptionKeyStoreProviders()
. Vi rekommenderar starkt att instansen registreras med standarduppslagsnamnet, AZURE_KEY_VAULT, som kan hämtas av SQLServerColumnEncryptionAzureKeyVaultProvider.getName()
-API:et. Med standardnamnet kan du använda verktyg som SQL Server Management Studio eller PowerShell för att etablera och hantera Always Encrypted-nycklar (verktygen använder standardnamnet för att generera metadataobjektet till kolumnhuvudnyckeln). I följande exempel visas registrering av Azure Key Vault-providern. Mer information om metoden SQLServerConnection.registerColumnEncryptionKeyStoreProviders()
finns i Always Encrypted API-referens för JDBC-drivrutinen.
Map<String, SQLServerColumnEncryptionKeyStoreProvider> keyStoreMap = new HashMap<String, SQLServerColumnEncryptionKeyStoreProvider>();
keyStoreMap.put(akvProvider.getName(), akvProvider);
SQLServerConnection.registerColumnEncryptionKeyStoreProviders(keyStoreMap);
Viktig
Om du använder Azure Key Vault-nyckelarkivprovidern har Azure Key Vault-implementeringen av JDBC-drivrutinen beroenden för dessa bibliotek (från GitHub) som måste ingå i ditt program:
bibliotek med microsoft-authentication-library-for-java
Ett exempel på hur du inkluderar dessa beroenden i ett Maven-projekt finns i Ladda ned MSAL4J- och AKV-beroenden med Apache Maven-
Använda Azure Key Vault-autentisering med hanterade identiteter
Från och med JDBC-drivrutinen 8.4.1har drivrutinen lagt till stöd för att autentisera till Azure Key Vaults med hanterade identiteter.
Du kan använda hanterade identiteter för att autentisera till Azure Key Vault om programmet finns i Azure. Detta eliminerar behovet av att ange och exponera eventuella autentiseringsuppgifter i koden.
Anslutningsegenskaper för Key Vault-autentisering med hanterade identiteter
För JDBC Driver 8.4.1 och senare introducerade drivrutinen följande anslutningsegenskaper:
Anslutningsegenskap | Möjligt värdepar 1 | Möjligt värdepar 2 | Möjligt värdepar 3 |
---|---|---|---|
Nyckellagerautentisering | KeyVaultClientSecret | KeyVaultManagedIdentity | JavaKeyStorePassword |
keyStorePrincipalId | <Microsoft Entra-programklient-ID> | <Objekt-ID för Microsoft Entra-program> (valfritt) | n/a |
keyStoreSecret | <Microsoft Entra-programklienthemlighet> | n/a | <hemlighet/lösenord för Java Key Store> |
I följande exempel visas hur anslutningsegenskaperna används i en anslutningssträng.
Använda hanterad identitet för att autentisera till AKV
"jdbc:sqlserver://<server>:<port>;encrypt=true;columnEncryptionSetting=Enabled;keyStoreAuthentication=KeyVaultManagedIdentity;"
Använd hanterad identitet och huvud-ID för att autentisera till AKV
"jdbc:sqlserver://<server>:<port>;encrypt=true;columnEncryptionSetting=Enabled;keyStoreAuthentication=KeyVaultManagedIdentity;keyStorePrincipal=<principalId>"
Använda clientId och clientSecret för autentisering till AKV
"jdbc:sqlserver://<server>:<port>;encrypt=true;columnEncryptionSetting=Enabled;keyStoreAuthentication=KeyVaultClientSecret;keyStorePrincipalId=<clientId>;keyStoreSecret=<clientSecret>"
Användarna uppmanas att använda dessa anslutningsegenskaper för att ange vilken typ av autentisering som används för nyckelarkiven i stället för SQLServerColumnEncryptionAzureKeyVaultProvider
-API:et.
Tidigare tillagda anslutningsegenskaper keyVaultProviderClientId
och keyVaultProviderClientKey
är inaktuella och ersätts av de anslutningsegenskaper som beskrevs tidigare.
Information om hur du konfigurerar hanterade identiteter finns i Konfigurera hanterade identiteter för Azure-resurser på en virtuell dator med azure-portalen.
Använda Windows Certificate Store-providern
SQLServerColumnEncryptionCertificateStoreProvider
kan användas för att lagra kolumnhuvudnycklar i Windows Certificate Store. Använd guiden Always Encrypted för SQL Server Management Studio (SSMS) eller andra verktyg som stöds för att skapa kolumnhuvudnyckeln och kolumnkrypteringsnyckeldefinitionerna i databasen. Samma guide kan användas för att generera ett självsignerat certifikat i Windows Certificate Store som kan användas som en kolumnhuvudnyckel för Always Encrypted-data. Mer information om T-SQL-syntaxen för kolumnhuvudnyckeln och kolumnkrypteringsnyckeln finns i CREATE COLUMN MASTER KEY and CREATE COLUMN ENCRYPTION KEY respectively .
Namnet på SQLServerColumnEncryptionCertificateStoreProvider
är MSSQL_CERTIFICATE_STORE och kan efterfrågas av getName()-API:et för providerobjektet. Den registreras automatiskt av drivrutinen och kan användas sömlöst utan någon programändring.
För exemplen på den här sidan, om du har skapat en Windows Certificate Store-baserad kolumnhuvudnyckel och kolumnkrypteringsnyckel med SQL Server Management Studio, kan T-SQL-skriptet för att återskapa dem se ut ungefär som i det här exemplet med sina egna specifika KEY_PATH och ENCRYPTED_VALUE:
CREATE COLUMN MASTER KEY [MyCMK]
WITH
(
KEY_STORE_PROVIDER_NAME = N'MSSQL_CERTIFICATE_STORE',
KEY_PATH = N'CurrentUser/My/A2A91F59C461B559E4D962DA9D2BC6131B32CB91'
);
CREATE COLUMN ENCRYPTION KEY [MyCEK]
WITH VALUES
(
COLUMN_MASTER_KEY = [MyCMK],
ALGORITHM = 'RSA_OAEP',
ENCRYPTED_VALUE = 0x016E000001630075007200720065006E0074007500730065007200...
);
Viktig
Även om de andra nyckellagringsleverantörerna i den här artikeln är tillgängliga på alla plattformar som stöds av drivrutinen, är den SQLServerColumnEncryptionCertificateStoreProvider
implementeringen av JDBC-drivrutinen endast tillgänglig på Windows-operativsystem. Den har ett beroende av den mssql-jdbc_auth-<version>-<arch>.dll
som är tillgänglig i drivrutinspaketet. Om du vill använda den här providern kopierar du filen mssql-jdbc_auth-<version>-<arch>.dll
till en katalog på Windows-systemsökvägen på datorn där JDBC-drivrutinen är installerad. Du kan också ange systemegenskapen java.library.path för att ange katalogen för mssql-jdbc_auth-<version>-<arch>.dll
. Om du kör en 32-bitars Java Virtual Machine (JVM) använder du filen mssql-jdbc_auth-<version>-x86.dll
i mappen x86, även om operativsystemet är x64-versionen. Om du kör en 64-bitars JVM på en x64-processor använder du filen mssql-jdbc_auth-<version>-x64.dll
i mappen x64. Om du till exempel använder 32-bitars JVM och JDBC-drivrutinen är installerad i standardkatalogen kan du ange platsen för DLL med följande virtuella datorargument (VM) när Java-programmet startas: -Djava.library.path=C:\Microsoft JDBC Driver <version> for SQL Server\sqljdbc_<version>\enu\auth\x86
Använda Java Key Store-provider
JDBC-drivrutinen levereras med en inbyggd nyckellagringsproviderimplementering för Java Key Store. Om egenskapen keyStoreAuthentication
i anslutningssträngen finns och är inställd på JavaKeyStorePassword
, instansierar och registrerar drivrutinen automatiskt providern för Java Key Store. Namnet på Java Key Store-providern är MSSQL_JAVA_KEYSTORE. Det här namnet kan också efterfrågas av SQLServerColumnEncryptionJavaKeyStoreProvider.getName()
-API:et.
Det finns tre egenskaper för anslutningssträngar som gör att ett klientprogram kan ange de autentiseringsuppgifter som drivrutinen behöver för att autentisera till Java Key Store. Drivrutinen initierar providern baserat på värdena för dessa tre egenskaper i anslutningssträngen.
keyStoreAuthentication
: Identifierar Java Key Store som ska användas. Med Microsoft JDBC Driver 6.0 och senare för SQL Server kan du endast autentisera till Java Key Store via den här egenskapen. För Java Key Store måste värdet för den här egenskapen vara JavaKeyStorePassword
.
keyStoreLocation
: Sökvägen till Java Key Store-filen som lagrar kolumnhuvudnyckeln. Sökvägen innehåller nyckelarkivets filnamn.
keyStoreSecret
: Hemligheten/lösenordet som ska användas för nyckelarkivet och nyckeln. Om du vill använda Java Key Store måste nyckelarkivet och nyckellösenordet vara desamma.
Här är ett exempel på hur du anger dessa autentiseringsuppgifter i anslutningssträngen:
String connectionUrl = "jdbc:sqlserver://<server>:<port>;encrypt=true;user=<user>;password=<password>;columnEncryptionSetting=Enabled;keyStoreAuthentication=JavaKeyStorePassword;keyStoreLocation=<path_to_the_keystore_file>;keyStoreSecret=<keystore_key_password>";
Du kan också hämta eller ange de här inställningarna med SQLServerDataSource
-objektet. Mer information finns i Always Encrypted API-referens för JDBC-drivrutinen.
JDBC-drivrutinen instansierar automatiskt SQLServerColumnEncryptionJavaKeyStoreProvider
när dessa autentiseringsuppgifter finns i anslutningsegenskaperna.
Skapa en kolumnhuvudnyckel för Java Key Store
SQLServerColumnEncryptionJavaKeyStoreProvider
kan användas med JKS- eller PKCS12-nyckellagringstyper. Om du vill skapa eller importera en nyckel som ska användas med den här providern använder du verktyget Java keytool. Nyckeln måste ha samma lösenord som själva nyckelarkivet. Här är ett exempel på hur du skapar en offentlig nyckel och dess associerade privata nyckel med verktyget keytool
. Ersätt <password>
med ett giltigt lösenord.
keytool -genkeypair -keyalg RSA -alias AlwaysEncryptedKey -keystore keystore.jks -storepass <password> -validity 360 -keysize 2048 -storetype jks
Det här kommandot skapar en offentlig nyckel och omsluter den i ett självsignerat X.509-certifikat som lagras i nyckelarkivet keystore.jks
tillsammans med dess associerade privata nyckel. Den här posten i nyckelarkivet identifieras av aliaset AlwaysEncryptedKey
.
Här är ett exempel på samma sak med en PKCS12-butikstyp. Ersätt <password>
med ett giltigt lösenord.
keytool -genkeypair -keyalg RSA -alias AlwaysEncryptedKey -keystore keystore.pfx -storepass <password> -validity 360 -keysize 2048 -storetype pkcs12 -keypass <password>
Om nyckelarkivet är av typen PKCS12 frågar inte keytool-verktyget efter ett nyckellösenord och nyckellösenordet måste anges med -keypass
alternativ eftersom SQLServerColumnEncryptionJavaKeyStoreProvider
kräver att nyckelarkivet och nyckeln har samma lösenord.
Du kan också exportera ett certifikat från Windows-certifikatarkivet i .pfx-format och använda det med SQLServerColumnEncryptionJavaKeyStoreProvider
. Det exporterade certifikatet kan också importeras till Java Key Store som en JKS-nyckellagringstyp.
När du har skapat keytool-posten skapar du kolumnhuvudnyckelmetadata i databasen, som behöver nyckellagringsproviderns namn och nyckelsökvägen. Mer information om hur du skapar huvudnyckelmetadata för kolumner finns i CREATE COLUMN MASTER KEY. För SQLServerColumnEncryptionJavaKeyStoreProvider
är nyckelsökvägen bara aliaset för nyckeln och namnet på SQLServerColumnEncryptionJavaKeyStoreProvider
är MSSQL_JAVA_KEYSTORE
. Du kan också köra frågor mot det här namnet med det getName()
offentliga API:et för klassen SQLServerColumnEncryptionJavaKeyStoreProvider
.
T-SQL-syntaxen för att skapa kolumnhuvudnyckeln är:
CREATE COLUMN MASTER KEY [<CMK_name>]
WITH
(
KEY_STORE_PROVIDER_NAME = N'MSSQL_JAVA_KEYSTORE',
KEY_PATH = N'<key_alias>'
);
För "AlwaysEncryptedKey" som skapades tidigare skulle kolumnhuvudnyckeldefinitionen vara:
CREATE COLUMN MASTER KEY [MyCMK]
WITH
(
KEY_STORE_PROVIDER_NAME = N'MSSQL_JAVA_KEYSTORE',
KEY_PATH = N'AlwaysEncryptedKey'
);
Note
Den inbyggda SQL Server Management Studio-funktionen kan inte skapa kolumnhuvudnyckeldefinitioner för Java Key Store. T-SQL-kommandon måste användas programmatiskt.
Skapa en kolumnkrypteringsnyckel för Java Key Store
SQL Server Management Studio eller något annat verktyg kan inte användas för att skapa kolumnkrypteringsnycklar med hjälp av kolumnhuvudnycklar i Java Key Store. Klientprogrammet måste skapa kolumnkrypteringsnyckeln programmatiskt med klassen SQLServerColumnEncryptionJavaKeyStoreProvider
. För mer information, se Använda kolumnhuvudnyckellagringsleverantörer för programmatisk etablering av nycklar.
Implementera en leverantör för huvudnyckellager för anpassade kolumner
Om du vill lagra kolumnhuvudnycklar i ett nyckelarkiv som inte stöds av en befintlig provider kan du implementera en anpassad provider genom att utöka SQLServerColumnEncryptionKeyStoreProvider
-klassen och registrera providern med någon av följande metoder:
SQLServerConnection.registerColumnEncryptionKeyStoreProviders
-
SQLServerConnection.registerColumnEncryptionKeyStoreProvidersOnConnection
(har lagts till i JDBC version 10.2) -
SQLServerStatement.registerColumnEncryptionKeyStoreProvidersOnStatement
(har lagts till i JDBC version 10.2)
public class MyCustomKeyStore extends SQLServerColumnEncryptionKeyStoreProvider{
private String name = "MY_CUSTOM_KEYSTORE";
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public byte[] encryptColumnEncryptionKey(String masterKeyPath, String encryptionAlgorithm, byte[] plainTextColumnEncryptionKey)
{
// Logic for encrypting the column encryption key
}
public byte[] decryptColumnEncryptionKey(String masterKeyPath, String encryptionAlgorithm, byte[] encryptedColumnEncryptionKey)
{
// Logic for decrypting the column encryption key
}
}
Registrera providern med SQLServerConnection.registerColumnEncryptionKeyStoreProviders
:
SQLServerColumnEncryptionKeyStoreProvider storeProvider = new MyCustomKeyStore();
Map<String, SQLServerColumnEncryptionKeyStoreProvider> keyStoreMap = new HashMap<String, SQLServerColumnEncryptionKeyStoreProvider>();
keyStoreMap.put(storeProvider.getName(), storeProvider);
SQLServerConnection.registerColumnEncryptionKeyStoreProviders(keyStoreMap);
Prioritet för kolumnkrypteringsnyckelcache
Det här avsnittet gäller för JDBC-drivrutinsversion 10.2 och senare.
Kolumnkrypteringsnycklarna (CEK) som dekrypteras av anpassade nyckellagringsproviders som är registrerade på en anslutnings- eller instruktionsinstans cachelagras inte av Microsoft JDBC-drivrutin för SQL Server. Leverantörer av anpassade nyckellager bör implementera sin egen CEK-cachelagringsmekanism.
Från och med version 10.2 har SQLServerColumnEncryptionAzureKeyVaultProvider
en egen CEK-cachelagringsimplementering. När CEK:er registreras på en anslutnings- eller instruktioninstans och dekrypteras av en instans av SQLServerColumnEncryptionAzureKeyVaultProvider
, kommer dessa CEK:er att rensas när instansen hamnar utanför sitt omfång.
try (SQLServerConnection conn = getConnection(); SQLServerStatement stmt = (SQLServerStatement) conn.createStatement()) {
Map<String, SQLServerColumnEncryptionKeyStoreProvider> customKeyStoreProviders = new HashMap<>();
SQLServerColumnEncryptionAzureKeyVaultProvider akvProvider = new SQLServerColumnEncryptionAzureKeyVaultProvider(clientID, clientKey);
customKeyStoreProviders.put(akvProvider.getName(), akvProvider);
stmt.registerColumnEncryptionKeyStoreProvidersOnStatement(customKeyStoreProviders);
// Perform database operation with Azure Key Vault Provider
// Any decrypted column encryption keys will be cached
} // Column encryption key cache of "akvProvider" is cleared when "akvProvider" goes out of scope
Not
CEK-cachelagring (nyckelkryptering) som implementeras av anpassade nyckellagringsleverantörer, inaktiveras av drivrutinen om instansen av nyckellagringsleverantören registreras globalt i drivrutinen med metoden SQLServerConnection.registerColumnEncryptionKeyStoreProviders
. En CEK-cacheimplementering ska referera till värdet för livslängd innan en CEK lagras i cache och inte lagra den i cache om värdet är noll. Detta kommer att undvika duplicerad cachelagring och eventuell användarförvirring när de försöker konfigurera cachelagring av nycklar. Time-to-live-värdet för cacheminnet kan anges med metoden SQLServerColumnEncryptionKeyStoreProvider.setColumnEncryptionCacheTtl
.
Registrera en anpassad leverantör av huvudnyckelarkivet för kolumner
Det här avsnittet gäller för JDBC-drivrutinsversion 10.2 och senare.
Anpassade huvudnyckellagringsleverantörer kan registreras hos drivrutinen i tre olika lager. Prioriteten för de tre registreringarna är följande:
- Registreringen per uttryck kontrolleras om den inte är tom.
- Om registreringen per instruktion är tom kontrolleras registreringen per anslutning om den inte är tom.
- Om registreringen per anslutning är tom kontrolleras den globala registreringen.
När någon nyckellagerprovider hittas på registreringsnivå drivrutinen INTE återgå till de andra registreringarna för att söka efter en provider. Om leverantörer är registrerade men rätt leverantör inte hittas på en viss nivå, genereras ett undantag som endast innehåller de leverantörer som var registrerade i den specifika registreringen som kontrollerades.
Den inbyggda kolumnhuvudnyckellagringsprovidern som är tillgänglig för Windows Certificate Store är förregistrerad. Microsoft Java Keystore-providern och Azure Key Vault Keystore-providern kan implicit förregistreras med en anslutningsinstans om autentiseringsuppgifter anges i förväg.
De tre registreringsnivåerna stöder olika scenarier vid frågor mot krypterade data. Lämplig metod kan användas för att säkerställa att en användare av ett program kan komma åt klartextdata. Åtkomst till okrypterade data sker bara om de kan tillhandahålla den nödvändiga kolumnhuvudnyckeln genom att autentisera mot nyckelarkivet som innehåller kolumnhuvudnyckeln.
Program som delar en SQLServerConnection
instans mellan flera användare kanske vill använda SQLServerStatement.registerColumnEncryptionKeyStoreProvidersOnStatement
. Varje användare måste registrera en nyckellagerprovider på en SQLServerStatement
instans innan en fråga körs för att få åtkomst till en krypterad kolumn. Om nyckellagringsprovidern kan komma åt den nödvändiga kolumnhuvudnyckeln i nyckelarkivet som använder användarens angivna autentiseringsuppgifter lyckas frågan.
Program som skapar en SQLServerConnection
instans för varje användare kanske vill använda SQLServerConnection.registerColumnEncryptionKeyStoreProvidersOnConnection
. Nyckellagringsproviders som är registrerade med den här metoden kan användas av anslutningen för frågor som har åtkomst till krypterade data.
Nyckellagringsprovidrar som är registrerade med SQLServerConnection.registerColumnEncryptionKeyStoreProviders
använder den identitet som programmet anger när de autentiserar mot nyckelarkivet.
Exemplet nedan visar prioriteten för anpassade masternyckellagringsleverantörer som är registrerade på en anslutningsinstans.
Map<String, SQLServerColumnEncryptionKeyStoreProvider> customKeyStoreProviders = new HashMap<>();
MyCustomKeyStore myProvider = new MyCustomKeyStore();
customKeyStoreProviders.put(myProvider.getName(), myProvider);
// Registers the provider globally
SQLServerConnection.registerColumnEncryptionKeyStoreProviders(customKeyStoreProviders);
try (SQLServerConnection conn = getConnection()) {
customKeyStoreProviders.clear();
SQLServerColumnEncryptionAzureKeyVaultProvider akvProvider = new SQLServerColumnEncryptionAzureKeyVaultProvider(clientID, clientKey);
customKeyStoreProviders.put(akvProvider.getName(), akvProvider);
// Registers the provider on the connection
// These providers will take precedence over globally registered providers
conn.registerColumnEncryptionKeyStoreProvidersOnConnection(customKeyStoreProviders);
}
I följande exempel visas prioriteten för anpassade huvudnyckellagringsproviders för kolumner som är registrerade på en instruktionsinstans:
Map<String, SQLServerColumnEncryptionKeyStoreProvider> customKeyStoreProviders = new HashMap<>();
MyCustomKeyStore firstProvider = new MyCustomKeyStore();
customKeyStoreProviders.put("FIRST_CUSTOM_STORE", firstProvider);
// Registers the provider globally
SQLServerConnection.registerColumnEncryptionKeyStoreProviders(customKeyStoreProviders);
try (SQLServerConnection conn = getConnection()) {
customKeyStoreProviders.clear();
MyCustomKeyStore secondProvider = new MyCustomKeyStore();
customKeyStoreProviders.put("SECOND_CUSTOM_STORE", secondProvider);
// Registers the provider on the connection
conn.registerColumnEncryptionKeyStoreProvidersOnConnection(customKeyStoreProviders);
try (SQLServerStatement stmt = (SQLServerStatement) conn.createStatement()) {
customKeyStoreProviders.clear();
SQLServerColumnEncryptionAzureKeyVaultProvider akvProvider = new SQLServerColumnEncryptionAzureKeyVaultProvider(clientID, clientKey);
customKeyStoreProviders.put(akvProvider.getName(), akvProvider);
// Registers the provider on the statement
// These providers will take precedence over connection-level providers and globally registered providers
stmt.registerColumnEncryptionKeyStoreProvidersOnStatement(customKeyStoreProviders);
}
}
Använd kolumnhuvudnyckellagringsleverantörer för programmatisk nyckeletablering
För att få åtkomst till krypterade kolumner, hittar och anropar Microsoft JDBC-drivrutinen för SQL Server med transparens den korrekta leverantören av huvudnyckellagringsprovider för att dekryptera kolumnkrypteringsnycklar. Normalt anropar din normala programkod inte kolumnhuvudnyckellagringsprovidrar direkt. Du kan dock instansiera och anropa en provider programmatiskt för att etablera och hantera Always Encrypted-nycklar. Det här steget kan göras för att generera en krypterad kolumnnyckel och dekryptera en kolumnnyckel som en del av rotationen av huvudnyckeln, till exempel. Mer information finns i Översikt över nyckelhantering för Always Encrypted.
Om du använder en anpassad nyckellagringsprovider kan det krävas implementering av dina egna nyckelhanteringsverktyg. Om du vill använda nycklar som lagras i Windows Certificate Store eller i Azure Key Vault kan du använda befintliga verktyg, till exempel SQL Server Management Studio eller PowerShell, för att hantera och etablera nycklar. Om du vill använda nycklar som lagras i Java Key Store måste du etablera nycklar programmatiskt. I följande exempel visas hur du använder klassen SQLServerColumnEncryptionJavaKeyStoreProvider
för att kryptera nyckeln med en nyckel som lagras i Java Key Store.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionJavaKeyStoreProvider;
import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionKeyStoreProvider;
import com.microsoft.sqlserver.jdbc.SQLServerException;
/**
* This program demonstrates how to create a column encryption key programmatically for the Java Key Store.
*/
public class AlwaysEncrypted {
// Alias of the key stored in the keystore.
private static String keyAlias = "<provide key alias>";
// Name by which the column master key will be known in the database.
private static String columnMasterKeyName = "MyCMK";
// Name by which the column encryption key will be known in the database.
private static String columnEncryptionKey = "MyCEK";
// The location of the keystore.
private static String keyStoreLocation = "C:\\Dev\\Always Encrypted\\keystore.jks";
// The password of the keystore and the key.
private static char[] keyStoreSecret = "<password>".toCharArray();
/**
* Name of the encryption algorithm used to encrypt the value of the column encryption key. The algorithm for the system providers must be
* RSA_OAEP.
*/
private static String algorithm = "RSA_OAEP";
public static void main(String[] args) {
String connectionUrl = "jdbc:sqlserver://<server>:<port>;encrypt=true;databaseName=<databaseName>;user=<user>;password=<password>;columnEncryptionSetting=Enabled;";
try (Connection connection = DriverManager.getConnection(connectionUrl);
Statement statement = connection.createStatement();) {
// Instantiate the Java Key Store provider.
SQLServerColumnEncryptionKeyStoreProvider storeProvider = new SQLServerColumnEncryptionJavaKeyStoreProvider(keyStoreLocation,
keyStoreSecret);
byte[] encryptedCEK = getEncryptedCEK(storeProvider);
/**
* Create column encryption key For more details on the syntax, see:
* https://learn.microsoft.com/sql/t-sql/statements/create-column-encryption-key-transact-sql Encrypted column encryption key first needs
* to be converted into varbinary_literal from bytes, for which byteArrayToHex() is used.
*/
String createCEKSQL = "CREATE COLUMN ENCRYPTION KEY "
+ columnEncryptionKey
+ " WITH VALUES ( "
+ " COLUMN_MASTER_KEY = "
+ columnMasterKeyName
+ " , ALGORITHM = '"
+ algorithm
+ "' , ENCRYPTED_VALUE = 0x"
+ byteArrayToHex(encryptedCEK)
+ " ) ";
statement.executeUpdate(createCEKSQL);
System.out.println("Column encryption key created with name : " + columnEncryptionKey);
}
// Handle any errors that may have occurred.
catch (SQLException e) {
e.printStackTrace();
}
}
private static byte[] getEncryptedCEK(SQLServerColumnEncryptionKeyStoreProvider storeProvider) throws SQLServerException {
String plainTextKey = "You need to give your plain text";
// plainTextKey has to be 32 bytes with current algorithm supported
byte[] plainCEK = plainTextKey.getBytes();
// This will give us encrypted column encryption key in bytes
byte[] encryptedCEK = storeProvider.encryptColumnEncryptionKey(keyAlias, algorithm, plainCEK);
return encryptedCEK;
}
public static String byteArrayToHex(byte[] a) {
StringBuilder sb = new StringBuilder(a.length * 2);
for (byte b : a)
sb.append(String.format("%02x", b).toUpperCase());
return sb.toString();
}
}
Aktivera Always Encrypted för programfrågor
Det enklaste sättet att aktivera kryptering av parametrar och dekryptering av frågeresultat för krypterade kolumner är genom att ange värdet för nyckelordet columnEncryptionSetting
anslutningssträng till Enabled
.
Följande anslutningssträng är ett exempel på hur du aktiverar Always Encrypted i JDBC-drivrutinen:
String connectionUrl = "jdbc:sqlserver://<server>:<port>;user=<user>;encrypt=true;password=<password>;databaseName=<database>;columnEncryptionSetting=Enabled;";
SQLServerConnection connection = (SQLServerConnection) DriverManager.getConnection(connectionUrl);
Följande kod är ett motsvarande exempel för att använda objektet SQLServerDataSource
:
SQLServerDataSource ds = new SQLServerDataSource();
ds.setServerName("<server>");
ds.setPortNumber(<port>);
ds.setUser("<user>");
ds.setPassword("<password>");
ds.setDatabaseName("<database>");
ds.setColumnEncryptionSetting("Enabled");
SQLServerConnection con = (SQLServerConnection) ds.getConnection();
Always Encrypted kan också aktiveras för enskilda frågor. Mer information finns i Kontrollera prestandapåverkan för Always Encrypted. Det räcker inte att aktivera Always Encrypted för att kryptering eller dekryptering ska lyckas. Du måste också se till att:
- Programmet har
VIEW ANY COLUMN MASTER KEY DEFINITION
- ochVIEW ANY COLUMN ENCRYPTION KEY DEFINITION
databasbehörigheter som krävs för att få åtkomst till metadata om Always Encrypted-nycklar i databasen. Mer information finns i Behörigheter i Always Encrypted (Database Engine). - Programmet kan komma åt kolumnhuvudnyckeln som skyddar kolumnkrypteringsnycklarna, som krypterar de efterfrågade databaskolumnerna. Om du vill använda Java Key Store-providern måste du ange extra autentiseringsuppgifter i anslutningssträngen. Mer information finns i Använd Java Key Store-providern.
Konfigurera hur java.sql.Time-värden skickas till servern
Egenskapen sendTimeAsDatetime
-anslutning används för att konfigurera hur värdet java.sql.Time skickas till servern. När värdet är inställt på false skickas tidsvärdet som en SQL Server-tidstyp. När värdet är inställt på true skickas tidsvärdet som en datetime-typ. Om en tidskolumn krypteras måste egenskapen sendTimeAsDatetime
vara falsk eftersom krypterade kolumner inte stöder konverteringen från tid till datum. Observera också att den här egenskapen är som standard true, så om du vill använda krypterade tidskolumner anger du den till false. Annars utlöser drivrutinen ett undantag. Från och med version 6.0 av drivrutinen har klassen SQLServerConnection
två metoder för att konfigurera värdet för den här egenskapen programmatiskt:
- offentlig void setSendTimeAsDatetime(boolean sendTimeAsDateTimeValue)
- public boolean getSendTimeAsDatetime()
Mer information om den här egenskapen finns i Konfigurera hur java.sql.Time-värden skickas till servern.
Konfigurera hur strängvärden skickas till servern
Egenskapen sendStringParametersAsUnicode
-anslutning används för att konfigurera hur Strängvärden skickas till SQL Server. Om värdet är true skickas strängparametrar till servern i Unicode-format. Om värdet är falskt skickas strängparametrar i icke-Unicode-format, till exempel ASCII eller MBCS, i stället för Unicode. Standardvärdet för den här egenskapen är sant. När Always Encrypted är aktiverat och en char
/varchar
/varchar(max)
kolumn krypteras måste värdet för sendStringParametersAsUnicode
anges till false. Om den här egenskapen är inställd på true utlöser drivrutinen ett undantag när data dekrypteras från en krypterad char
/varchar
/varchar(max)
kolumn med Unicode-tecken. Mer information om den här egenskapen finns i Ange anslutningsegenskaper.
Viktig
Om sendStringParametersAsUnicode
är inställt på true
och unicode-data infogas i en char
/varchar
kolumn krypterad med Always Encrypted kan dataförlust inträffa utan att ett fel rapporteras. Dataförlusten kan bara identifieras när du försöker dekryptera data när du har läst tillbaka dem från servern. Ett fel som Decryption failed. The last 10 bytes of the encrypted column encryption key are: 'C3-D9-10-4E-C1-45-8B-94-A2-43'. The first 10 bytes of ciphertext are: '01-9B-9D-A6-3E-40-22-53-15-9B'.
kan vara resultatet.
Det är viktigt att använda rätt kolumndatatyper och ange rätt datatyp för parametrar vid infogning av krypterade data. Om unicode-data förväntas använder du nchar
/nvarchar
kolumner och setNString()
metoder. Servern kan inte utföra implicita datakonverteringar och har begränsad möjlighet att identifiera datafel när Always Encrypted är aktiverat.
Hämta och ändra data i krypterade kolumner
När du aktiverar Always Encrypted för programfrågor kan du använda JDBC-standard-API:er för att hämta eller ändra data i krypterade databaskolumner. Om ditt program har nödvändiga databasbehörigheter och kan komma åt kolumnhuvudnyckeln krypterar drivrutinen alla frågeparametrar som riktar sig mot krypterade kolumner och dekrypterar data som hämtas från krypterade kolumner.
Om Always Encrypted inte är aktiverat misslyckas frågor med parametrar som riktar sig mot krypterade kolumner. Frågor kan fortfarande hämta data från krypterade kolumner så länge frågan inte har några parametrar för krypterade kolumner. Drivrutinen försöker dock inte dekryptera några värden som hämtas från krypterade kolumner och programmet tar emot binära krypterade data (som bytematriser).
I följande tabell sammanfattas beteendet för frågor beroende på om Always Encrypted är aktiverat eller inte:
Frågeegenskaper | Always Encrypted är aktiverat och programmet kan komma åt nycklar och nyckelmetadata | Always Encrypted är aktiverat och programmet kan inte komma åt nycklar eller nyckelmetadata | Always Encrypted är inaktiverat |
---|---|---|---|
Frågor med parametrar som riktar sig till krypterade kolumner. | Parametervärden krypteras transparent. | Fel | Fel |
Frågor som hämtar data från krypterade kolumner utan parametrar som riktar sig mot krypterade kolumner. | Resultat från krypterade kolumner dekrypteras transparent. Programmet tar emot oformaterade värden för JDBC-datatyper som motsvarar de SQL Server-typer som konfigurerats för de krypterade kolumnerna. | Fel | Resultat från krypterade kolumner dekrypteras inte. Programmet tar emot krypterade värden som bytematriser (byte[]). |
Infoga och hämta krypterade dataexempel
I följande exempel visas hur du hämtar och ändrar data i krypterade kolumner. Exemplen förutsätter att måltabellen har följande schema och krypterade SSN- och BirthDate-kolumner. Om du har konfigurerat en kolumnhuvudnyckel med namnet "MyCMK" och en kolumnkrypteringsnyckel med namnet "MyCEK" (som beskrivs i föregående avsnitt för nyckellagringsproviders) kan du skapa tabellen med det här skriptet:
CREATE TABLE [dbo].[Patients]([PatientId] [int] IDENTITY(1,1),
[SSN] [char](11) COLLATE Latin1_General_BIN2
ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC,
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256',
COLUMN_ENCRYPTION_KEY = MyCEK) NOT NULL,
[FirstName] [nvarchar](50) NULL,
[LastName] [nvarchar](50) NULL,
[BirthDate] [date]
ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED,
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256',
COLUMN_ENCRYPTION_KEY = MyCEK) NOT NULL
PRIMARY KEY CLUSTERED ([PatientId] ASC) ON [PRIMARY]);
GO
För varje Java-kodexempel måste du infoga keystore-specifik kod på den angivna platsen.
Så här använder du en Azure Key Vault-nyckellagringsprovider:
String clientID = "<Azure Application ID>";
String clientKey = "<Azure Application API Key Password>";
SQLServerColumnEncryptionAzureKeyVaultProvider akvProvider = new SQLServerColumnEncryptionAzureKeyVaultProvider(clientID, clientKey);
Map<String, SQLServerColumnEncryptionKeyStoreProvider> keyStoreMap = new HashMap<String, SQLServerColumnEncryptionKeyStoreProvider>();
keyStoreMap.put(akvProvider.getName(), akvProvider);
SQLServerConnection.registerColumnEncryptionKeyStoreProviders(keyStoreMap);
String connectionUrl = "jdbc:sqlserver://<server>:<port>;encrypt=true;databaseName=<databaseName>;user=<user>;password=<password>;columnEncryptionSetting=Enabled;";
Så här använder du en Nyckelarkivprovider för Windows Certificate Store:
String connectionUrl = "jdbc:sqlserver://<server>:<port>;encrypt=true;databaseName=<databaseName>;user=<user>;password=<password>;columnEncryptionSetting=Enabled;";
Så här använder du en Java Key Store-nyckellagringsprovider:
String connectionUrl = "jdbc:sqlserver://<server>:<port>;encrypt=true;databaseName=<databaseName>;user=<user>;password=<password>;columnEncryptionSetting=Enabled;keyStoreAuthentication=JavaKeyStorePassword;keyStoreLocation=<path to jks or pfx file>;keyStoreSecret=<keystore secret/password>";
Att infoga ett dataexempel
I det här exemplet infogas en rad i tabellen Patienter. Observera följande:
- Det finns inget specifikt för kryptering i exempelkoden. Microsoft JDBC-drivrutinen för SQL Server identifierar och krypterar automatiskt de parametrar som riktar sig mot krypterade kolumner. Det här beteendet gör kryptering transparent för programmet.
- Värdena som infogas i databaskolumner, inklusive de krypterade kolumnerna, skickas som parametrar med
SQLServerPreparedStatement
. Även om parametrar är valfria när du skickar värden till icke-krypterade kolumner (även om det rekommenderas starkt eftersom det hjälper till att förhindra SQL-inmatning), krävs det för värden som riktar sig mot krypterade kolumner. Om värdena som infogats i de krypterade kolumnerna skickades som literaler inbäddade i frågeutsatsen skulle frågan misslyckas eftersom drivrutinen inte skulle kunna fastställa värdena i målkrypterade kolumner och inte kryptera värdena. Därför skulle servern avvisa dem som inkompatibla med de krypterade kolumnerna. - Alla värden som skrivs ut av programmet är i klartext eftersom Microsoft JDBC-drivrutinen för SQL Server transparent dekrypterar data som hämtats från de krypterade kolumnerna.
- Om du gör en sökning med en WHERE-sats måste värdet som används i WHERE-satsen skickas som en parameter så att drivrutinen transparent kan kryptera den innan den skickas till databasen. I följande exempel skickas SSN som en parameter, men LastName skickas som en literal eftersom LastName inte är krypterat.
- Setter-metoden som används för parametern som riktar sig mot SSN-kolumnen är
setString()
, som mappar till datatypenchar
/varchar
SQL Server. Om den metod som användes för den här parametern varsetNString()
, som mappar tillnchar
/nvarchar
, skulle frågan misslyckas eftersom Always Encrypted inte stöder konverteringar från krypteradenchar
/nvarchar
värden till krypteradechar
/varchar
värden.
// <Insert keystore-specific code here>
try (Connection sourceConnection = DriverManager.getConnection(connectionUrl);
PreparedStatement insertStatement = sourceConnection.prepareStatement("INSERT INTO [dbo].[Patients] VALUES (?, ?, ?, ?)")) {
insertStatement.setString(1, "795-73-9838");
insertStatement.setString(2, "Catherine");
insertStatement.setString(3, "Abel");
insertStatement.setDate(4, Date.valueOf("1996-09-10"));
insertStatement.executeUpdate();
System.out.println("1 record inserted.\n");
}
// Handle any errors that may have occurred.
catch (SQLException e) {
e.printStackTrace();
}
Hämtar exempel på klartextdata
I följande exempel visas filtrering av data baserat på krypterade värden och hämtning av klartextdata från krypterade kolumner. Observera följande:
- Värdet som används i WHERE-satsen för att filtrera på SSN-kolumnen måste skickas som en parameter så att Microsoft JDBC-drivrutinen för SQL Server transparent kan kryptera den innan den skickas till databasen.
- Alla värden som skrivs ut av programmet är i klartext eftersom Microsoft JDBC-drivrutinen för SQL Server transparent dekrypterar data som hämtats från kolumnerna SSN och BirthDate.
Notera
Om kolumner krypteras med deterministisk kryptering kan frågor utföra likhetsjämförelser på dem. Mer information finns i deterministisk kryptering.
// <Insert keystore-specific code here>
try (Connection connection = DriverManager.getConnection(connectionUrl);
PreparedStatement selectStatement = connection
.prepareStatement("\"SELECT [SSN], [FirstName], [LastName], [BirthDate] FROM [dbo].[Patients] WHERE SSN = ?;\"");) {
selectStatement.setString(1, "795-73-9838");
ResultSet rs = selectStatement.executeQuery();
while (rs.next()) {
System.out.println("SSN: " + rs.getString("SSN") + ", FirstName: " + rs.getString("FirstName") + ", LastName:"
+ rs.getString("LastName") + ", Date of Birth: " + rs.getString("BirthDate"));
}
}
// Handle any errors that may have occurred.
catch (SQLException e) {
e.printStackTrace();
}
Exempel på hämtning av krypterade data
Om Always Encrypted inte är aktiverat kan en fråga fortfarande hämta data från krypterade kolumner, så länge frågan inte har några parametrar för krypterade kolumner.
I följande exempel visas hur du hämtar binära krypterade data från krypterade kolumner. Observera följande:
- Eftersom Always Encrypted inte är aktiverat i anslutningssträngen returnerar frågan krypterade värden för SSN och BirthDate som bytematriser (programmet konverterar värdena till strängar).
- En fråga som hämtar data från krypterade kolumner med Always Encrypted inaktiverad kan ha parametrar, så länge ingen av parametrarna riktar sig mot en krypterad kolumn. Följande fråga filtreras efter LastName, som inte är krypterad i databasen. Om frågan filtreras efter SSN eller BirthDate misslyckas frågan.
try (Connection sourceConnection = DriverManager.getConnection(connectionUrl);
PreparedStatement selectStatement = sourceConnection
.prepareStatement("SELECT [SSN], [FirstName], [LastName], [BirthDate] FROM [dbo].[Patients] WHERE LastName = ?;");) {
selectStatement.setString(1, "Abel");
ResultSet rs = selectStatement.executeQuery();
while (rs.next()) {
System.out.println("SSN: " + rs.getString("SSN") + ", FirstName: " + rs.getString("FirstName") + ", LastName:"
+ rs.getString("LastName") + ", Date of Birth: " + rs.getString("BirthDate"));
}
}
// Handle any errors that may have occurred.
catch (SQLException e) {
e.printStackTrace();
}
Undvika vanliga problem vid frågor mot krypterade kolumner
I det här avsnittet beskrivs vanliga felkategorier när du kör frågor mot krypterade kolumner från Java-program och några riktlinjer för hur du undviker dem.
Konverteringsfel av datatyp som inte stöds
Always Encrypted stöder få konverteringar för krypterade datatyper. Se Always Encrypted (Databasmotor) för detaljerad lista över typkonverteringar som stöds. Här är vad du kan göra för att undvika datatypkonverteringsfel. Kontrollera att:
du använder rätt sättningsmetoder när du skickar värden för parametrar som är avsedda för krypterade kolumner. Kontrollera att SQL Server-datatypen för parametern är exakt samma som typen av målkolumn eller en konvertering av SQL Server-datatypen för parametern till måltypen för kolumnen stöds. API-metoder har lagts till i
SQLServerPreparedStatement
,SQLServerCallableStatement
ochSQLServerResultSet
klasser för att skicka parametrar som motsvarar specifika SQL Server-datatyper. En fullständig lista över nya API:er finns i Always Encrypted API-referens för JDBC-drivrutinen. Om datatypsdefinitionerna inte uppfylls resulterar det sannolikt i konfliktfel av operandtyp. Följande är några exempel på justeringar som kan behövas när du använder Always Encrypted:- Du kan använda metoden
setTimestamp()
för att skicka en parameter till en icke-krypterad datetime2- eller datetime-kolumn. Men när en kolumn är krypterad måste du använda den exakta metoden som representerar typen av kolumn i databasen. AnvändsetTimestamp()
för att skicka värden till en krypterad datetime2-kolumn och användsetDateTime()
för att skicka värden till en krypterad datetime-kolumn. - Du kan använda metoden
setBinary()
för att skicka en parameter till en icke-krypteradvarbinary(max)
ellerbinary
kolumn. Drivrutinen använder som standard datatypenBINARY
försetBinary()
parametrar och servern kan implicit konvertera data till envarbinary(max)
kolumn. Men när envarbinary(max)
kolumn krypteras måste du ange en mer exakt typ för parameterdata. Exempel:preparedStatement.setObject(1, binaryData, java.sql.JDBCType.LONGVARBINARY)
- Du kan använda metoden
Precisionen och skalan för parametrar som riktar sig till kolumner med decimal- och numeriska SQL Server-datatyper är samma som precisionen och skalan som konfigurerats för målkolumnen. API-metoder har lagts till i klasserna
SQLServerPreparedStatement
,SQLServerCallableStatement
ochSQLServerResultSet
för att acceptera precision och skalning tillsammans med datavärden för parametrar/kolumner som representerar decimal- och numeriska datatyper. Se Always Encrypted API-referens för JDBC-drivrutinen för en fullständig lista över nya/överbelastade API:er.- När du till exempel använder Javas
BigDecimal
som parametertyp för en angiven decimalkolumn i databasen måste du ange precisionen och skala till antingen metodensetBigDecimal()
ellersetValue()
metoden. Om du inte anger rätt precision och skalning kan det leda till ett fel som liknar följande:
Operand type clash: decimal(18,0) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'myCek', column_encryption_key_database_name = 'issue2169') is incompatible with decimal(20,4) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'myCek', column_encryption_key_database_name = 'myDatabase')
- När du till exempel använder Javas
Precisionen/skalan för bråksekunder av parametrar som riktar sig mot kolumner av
datetime2
,datetimeoffset
eller SQL Server-datatyper för tid är inte större än bråksekundernas precision/skala för målkolumnen i frågeställningar som förändrar värdena för målkolumnen. API-metoder har lagts till iSQLServerPreparedStatement
,SQLServerCallableStatement
ochSQLServerResultSet
klasser för att acceptera bråkdels sekunders precision/skala tillsammans med datavärden för parametrar som representerar dessa datatyper. En fullständig lista över nya/överbelastade API:er finns i Always Encrypted API-referens för JDBC-drivrutinen.
Fel på grund av felaktiga anslutningsegenskaper
I det här avsnittet beskrivs hur du konfigurerar anslutningsinställningarna korrekt för att använda Always Encrypted-data. Eftersom krypterade datatyper stöder begränsade konverteringar behöver anslutningsinställningarna för sendTimeAsDatetime
och sendStringParametersAsUnicode
korrekt konfiguration för att använda krypterade kolumner. Kontrollera att:
- sendTimeAsDatetime-inställningen är inställd på falsk när data infogas i krypterade tidskolumner. Mer information finns i Konfigurera hur java.sql.Time-värden skickas till servern.
- sendStringParametersAsUnicode anslutningsinställningen är inställd på true (eller lämnas som standard) när data infogas i krypterade kolumner med tecken/varchar/varchar(max).
Fel som beror på att klartext skickas i stället för krypterade värden
Alla värden som är inriktade på en krypterad kolumn måste krypteras i programmet. Ett försök att infoga/ändra eller filtrera efter ett oformaterat värde i en krypterad kolumn resulterar i ett fel som liknar det här:
com.microsoft.sqlserver.jdbc.SQLServerException: Operand type clash: varchar is incompatible with varchar(8000) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'MyCEK', column_encryption_key_database_name = 'ae') collation_name = 'SQL_Latin1_General_CP1_CI_AS'
Om du vill förhindra sådana fel kontrollerar du att:
- Always Encrypted är aktiverat för programfrågor som riktar sig till krypterade kolumner (för anslutningssträngen eller för en specifik fråga).
- du använder förberedda instruktioner och parametrar för att skicka data som riktar sig mot krypterade kolumner. I följande exempel visas en fråga som felaktigt filtreras av en literal/konstant i en krypterad kolumn (SSN), i stället för att skicka literalen inuti som en parameter. Den här frågan misslyckas:
ResultSet rs = connection.createStatement().executeQuery("SELECT * FROM Customers WHERE SSN='795-73-9838'");
Tvinga kryptering på indataparametrar
Funktionen Force Encryption framtvingar kryptering av en parameter med Always Encrypted. Om framtvingad kryptering används och SQL Server informerar drivrutinen om att parametern inte behöver krypteras misslyckas frågan som använder parametern. Den här egenskapen ger ett extra skydd mot säkerhetsattacker som innebär att en komprometterad SQL Server tillhandahåller felaktiga krypteringsmetadata till klienten, vilket kan leda till att data avslöjas. Set*-metoderna i klasserna SQLServerPreparedStatement
och SQLServerCallableStatement
och update*
i klassen SQLServerResultSet
är överbelastade för att acceptera ett booleskt argument för att ange inställningen för framtvingad kryptering. Om värdet för det här argumentet är falskt framtvingar drivrutinen inte kryptering på parametrar. Om tvingad kryptering är inställd på true skickas frågeparametern endast om målkolumnen är krypterad och Always Encrypted är aktiverad på anslutningen eller på -instruktionen. Den här egenskapen ger ett extra säkerhetslager, vilket säkerställer att drivrutinen inte av misstag skickar data till SQL Server som klartext när den förväntas krypteras.
Mer information om de SQLServerPreparedStatement
- och SQLServerCallableStatement
metoder som är överbelastade med inställningen för tvingad kryptering finns i Always Encrypted API-referens för JDBC-drivrutinen
Kontrollera prestandapåverkan för Always Encrypted
Eftersom Always Encrypted är en krypteringsteknik på klientsidan observeras de flesta prestandakostnaderna på klientsidan, inte i databasen. Förutom kostnaden för krypterings- och dekrypteringsåtgärder är andra källor till prestandakostnader på klientsidan:
- Det blev fler rundresor till databasen för att hämta metadata för frågeparametrar.
- Anropar ett huvudnyckelarkiv för kolumner för att få åtkomst till en kolumnhuvudnyckel.
I det här avsnittet beskrivs de inbyggda prestandaoptimeringarna i Microsoft JDBC-drivrutinen för SQL Server och hur du kan styra effekten av de tidigare nämnda två faktorerna på prestanda.
Kontrollera rundturer för att hämta metadata för frågeparametrar
Om Always Encrypted är aktiverat för en anslutning anropar drivrutinen som standard sys.sp_describe_parameter_encryption för varje parametriserad fråga och skickar frågeuttryck (utan parametervärden) till databasen. sys.sp_describe_parameter_encryption analyserar frågeuttryck för att ta reda på om några parametrar behöver krypteras, och i så fall returnerar den krypteringsrelaterad information som gör att drivrutinen kan kryptera parametervärden. Det här beteendet säkerställer en hög transparensnivå för klientprogrammet. Så länge programmet använder parametrar för att skicka värden som riktar krypterade kolumner till drivrutinen behöver programmet (och programutvecklaren) inte veta vilka frågor som har åtkomst till krypterade kolumner.
Ange Always Encrypted på frågenivå
Om du vill kontrollera prestandapåverkan vid hämtning av krypteringsmetadata för parametriserade frågor kan du aktivera Always Encrypted för enskilda frågor i stället för att konfigurera den för anslutningen. På så sätt kan du se till att sys.sp_describe_parameter_encryption endast anropas för frågor som du vet har parametrar som riktar sig till krypterade kolumner. Observera dock att genom att göra det minskar du transparensen för kryptering: om du ändrar krypteringsegenskaperna för dina databaskolumner kan du behöva ändra koden för ditt program för att justera det med schemaändringarna.
För att styra always encrypted-beteendet för enskilda frågor måste du konfigurera enskilda instruktionsobjekt genom att skicka en Enum, SQLServerStatementColumnEncryptionSetting, som anger hur data ska skickas och tas emot när du läser och skriver krypterade kolumner för den specifika instruktionen. Här följer några användbara riktlinjer:
Om de flesta frågor som ett klientprogram skickar via en databasanslutning får åtkomst till krypterade kolumner använder du följande riktlinjer:
- Ange nyckelordet för anslutningssträngen
columnEncryptionSetting
tillEnabled
. - Ange
SQLServerStatementColumnEncryptionSetting.Disabled
för enskilda frågor som inte har åtkomst till några krypterade kolumner. Den här inställningen inaktiverar både anrop avsys.sp_describe_parameter_encryption
och dekriptar alla värden i resultatuppsättningen. - Ange
SQLServerStatementColumnEncryptionSetting.ResultSet
för enskilda frågor som inte har några parametrar som kräver kryptering men hämtar data från krypterade kolumner. Den här inställningen inaktiverar anrop avsys.sp_describe_parameter_encryption
och parameterkryptering. Frågan dekrypterar resultatet från krypteringskolumner.
- Ange nyckelordet för anslutningssträngen
Om de flesta frågor som ett klientprogram skickar via en databasanslutning inte har åtkomst till krypterade kolumner använder du följande riktlinjer:
- Ställ in nyckelordet
columnEncryptionSetting
för anslutningssträngen påDisabled
. - Ange
SQLServerStatementColumnEncryptionSetting.Enabled
för enskilda frågor som har parametrar som behöver krypteras. Med den här inställningen kan både anropasys.sp_describe_parameter_encryption
och dekryptera eventuella frågeresultat som hämtats från krypterade kolumner. - Ange
SQLServerStatementColumnEncryptionSetting.ResultSet
för frågor som inte har några parametrar som kräver kryptering men hämtar data från krypterade kolumner. Den här inställningen inaktiverar anrop avsys.sp_describe_parameter_encryption
och parameterkryptering. Frågan dekrypterar resultatet från krypteringskolumner.
- Ställ in nyckelordet
Inställningarna för SQLServerStatementColumnEncryptionSetting
kan inte användas för att kringgå kryptering och få åtkomst till klartextdata. Mer information om hur du konfigurerar kolumnkryptering för en -instruktion finns i Always Encrypted API-referens för JDBC-drivrutinen.
I följande exempel inaktiveras Always Encrypted för databasanslutningen. Frågan som programmet skickar ut har en parameter som riktar sig mot kolumnen LastName som inte är krypterad. Frågan hämtar data från kolumnerna SSN och BirthDate som båda är krypterade. I sådana fall krävs det inte att du anropar sys.sp_describe_parameter_encryption
för att hämta krypteringsmetadata. Dekrypteringen av frågeresultatet måste dock aktiveras så att programmet kan ta emot klartextvärden från de två krypterade kolumnerna. Inställningen SQLServerStatementColumnEncryptionSetting.ResultSet
används för att säkerställa detta.
// Assumes the same table definition as in Section "Retrieving and modifying data in encrypted columns"
// where only SSN and BirthDate columns are encrypted in the database.
String connectionUrl = "jdbc:sqlserver://<server>:<port>;encrypt=true;databaseName=<database>;user=<user>;password=<password>;"
+ "keyStoreAuthentication=JavaKeyStorePassword;"
+ "keyStoreLocation=<keyStoreLocation>"
+ "keyStoreSecret=<keyStoreSecret>;";
String filterRecord = "SELECT FirstName, LastName, SSN, BirthDate FROM " + tableName + " WHERE LastName = ?";
try (SQLServerConnection connection = (SQLServerConnection) DriverManager.getConnection(connectionUrl);
PreparedStatement selectStatement = connection.prepareStatement(filterRecord, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
connection.getHoldability(), SQLServerStatementColumnEncryptionSetting.ResultSetOnly);) {
selectStatement.setString(1, "Abel");
ResultSet rs = selectStatement.executeQuery();
while (rs.next()) {
System.out.println("First name: " + rs.getString("FirstName"));
System.out.println("Last name: " + rs.getString("LastName"));
System.out.println("SSN: " + rs.getString("SSN"));
System.out.println("Date of Birth: " + rs.getDate("BirthDate"));
}
}
// Handle any errors that may have occurred.
catch (SQLException e) {
e.printStackTrace();
}
Cachelagring av metadata för frågeparametrar
För att minska antalet turer till databasen kan Microsoft JDBC-drivrutinen för SQL Server cache:a krypteringsrelaterad information för frågeparametrar. Från och med version 11.2.0 cachelagras krypteringsrelaterad information för parametrar som returneras från sys.sp_describe_parameter_encryption-anrop av drivrutinen om den associerade SQL Server-processen inte använder säkra enklaver. För cachelagring med hjälp av säkra enklaver måste servern ha stöd för att återupprätta enklaversessionen i de fall då sessionen inte längre är giltig.
Cachelagring av kolumnkrypteringsnyckel
För att minska antalet anrop till ett kolumnhuvudnyckellager för att dekryptera kolumnkrypteringsnycklar cachelagrar Microsoft JDBC-drivrutinen för SQL Server de oformaterade kolumnkrypteringsnycklarna i minnet. När drivrutinen tar emot krypteringsnyckelvärdet för krypterad kolumn från databasmetadata försöker drivrutinen först hitta krypteringsnyckeln för oformaterad kolumn som motsvarar det krypterade nyckelvärdet. Drivrutinen anropar nyckelarkivet som innehåller kolumnhuvudnyckeln endast om den inte kan hitta krypteringsnyckelvärdet för krypterad kolumn i cacheminnet.
Du kan konfigurera ett time-to-live-värde för kolumnkrypteringsnyckelposterna i cacheminnet med API:et setColumnEncryptionKeyCacheTtl()
i klassen SQLServerConnection
. Standardvärdet för time-to-live för kolumnkrypteringsnyckelposterna i cacheminnet är två timmar. Om du vill inaktivera cachelagring använder du värdet 0. Om du vill ange ett time-to-live-värde använder du följande API:
SQLServerConnection.setColumnEncryptionKeyCacheTtl (int columnEncryptionKeyCacheTTL, TimeUnit unit)
Om du till exempel vill ange ett time-to-live-värde på 10 minuter använder du:
SQLServerConnection.setColumnEncryptionKeyCacheTtl (10, TimeUnit.MINUTES)
Endast DAGAR, TIMMAR, MINUTER eller SEKUNDER stöds som tidsenhet.
Kopiera krypterade data med SQLServerBulkCopy
Med SQLServerBulkCopy
kan du kopiera data som redan är krypterade och lagrade i en tabell till en annan tabell utan att dekryptera data. Så här gör du:
- Kontrollera att krypteringskonfigurationen för måltabellen är identisk med konfigurationen av källtabellen. I synnerhet måste båda tabellerna ha samma kolumner krypterade och kolumnerna måste krypteras med samma krypteringstyper och samma krypteringsnycklar. Om en målkolumn krypteras på ett annat sätt än motsvarande källkolumn kan du inte dekryptera data i måltabellen efter kopieringsåtgärden. Data kommer att skadas.
- Konfigurera båda databasanslutningarna till källtabellen och till måltabellen utan Always Encrypted aktiverat.
- Ange alternativet
allowEncryptedValueModifications
. Mer information finns i Använda masskopiering med JDBC-drivrutinen.
Note
Var försiktig när du anger AllowEncryptedValueModifications
eftersom det här alternativet kan leda till att databasen skadas eftersom Microsoft JDBC-drivrutinen för SQL Server inte kontrollerar om data verkligen är krypterade eller om de är korrekt krypterade med samma krypteringstyp, algoritm och nyckel som målkolumnen.