Verwenden von Always Encrypted mit dem JDBC-Treiber

JDBC-Treiber herunterladen

Dieser Artikel enthält Informationen zum Entwickeln von Java-Anwendungen zur Verwendung von Always Encrypted mit dem Microsoft JDBC-Treiber 6.0 (oder höher) für SQL Server.

Always Encrypted ermöglicht Clients das Verschlüsseln von vertraulichen Daten in einer Weise, dass weder die Daten noch die Verschlüsselungsschlüssel zu irgendeinem Zeitpunkt gegenüber SQL Server oder Azure SQL-Datenbank offengelegt werden. Ein Treiber, bei dem Always Encrypted aktiviert ist, z.B. der Microsoft JDBC-Treiber 6.0 (oder höher) für SQL Server, erreicht dieses Verhalten durch die transparente Ver- und Entschlüsselung von sensiblen Daten in der Clientanwendung. Der Treiber ermittelt, welche Abfrageparameter Always Encrypted-Datenbankspalten entsprechen, und verschlüsselt die Werte dieser Parameter, bevor sie an die Datenbank gesendet werden. Auf ähnliche Weise entschlüsselt der Treiber die Daten transparent, die von verschlüsselten Datenbankspalten in Abfrageergebnissen empfangen werden. Weitere Informationen finden Sie unter Always Encrypted (Datenbank-Engine) und Always Encrypted: API-Referenz für den JDBC-Treiber.

Voraussetzungen

  • Stellen Sie sicher, dass der Microsoft JDBC-Treiber 6.0 (oder höher) für SQL Server auf dem Entwicklungscomputer installiert ist.
  • Laden Sie die Richtliniendateien Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction herunter und installieren Sie sie. Achten Sie darauf, die in der ZIP-Datei enthaltene Infodatei zu lesen, um Installationsanweisungen und relevante Details zu möglichen Export- oder Importproblemen zu erhalten.

Arbeiten mit Spaltenhauptschlüssel-Speichern

Zum Verschlüsseln oder Entschlüsseln von Daten für verschlüsselte Spalten verwaltet SQL Server die Spaltenverschlüsselungsschlüssel. Spaltenverschlüsselungsschlüssel werden in der verschlüsselten Form in den Datenbankmetadaten gespeichert. Jeder Spaltenverschlüsselungsschlüssel weist einen entsprechenden Spaltenhauptschlüssel auf, mit dem der Spaltenverschlüsselungsschlüssel verschlüsselt wurde.

Die Datenbankmetadaten enthalten die Spaltenhauptschlüssel nicht. Diese Schlüssel werden nur vom Client gespeichert. Die Datenbankmetadaten enthalten jedoch Informationen dazu, wo die Spaltenhauptschlüssel relativ zum Client gespeichert werden. Beispielsweise können die Datenbankmetadaten die Information enthalten, dass der Keystore, der einen Spaltenhauptschlüssel enthält, der Windows-Zertifikatspeicher ist, und dass das zum Verschlüsseln und Entschlüsseln verwendete Zertifikat sich in einem bestimmten Pfad innerhalb des Windows-Zertifikatspeichers befindet.

Wenn der Client Zugriff auf dieses Zertifikat im Windows-Zertifikatspeicher hat, kann er das Zertifikat abrufen. Das Zertifikat kann dann verwendet werden, um den Spaltenverschlüsselungsschlüssel zu entschlüsseln. Dieser Verschlüsselungsschlüssel kann dann zum Entschlüsseln oder Verschlüsseln von Daten für verschlüsselte Spalten verwendet werden, die diesen Spaltenverschlüsselungsschlüssel verwenden.

Der Microsoft JDBC-Treiber für SQL Server kommuniziert mit einem Keystore, wobei ein Speicheranbieter für einen Spaltenhauptschlüssel verwendet wird, der eine Instanz einer Klasse darstellt, die von SQLServerColumnEncryptionKeyStoreProvider abgeleitet wird.

Verwenden integrierter Speicheranbieter für Spaltenhauptschlüssel

Der Microsoft JDBC-Treiber für SQL Server verfügt über die folgenden integrierten Spaltenhauptschlüssel-Speicheranbieter. Einige dieser Anbieter sind bereits mit den spezifischen (für die Suche nach dem Anbieter verwendeten) Anbieternamen registriert, und einige erfordern entweder zusätzliche Anmeldeinformationen oder eine explizite Registrierung.

Klasse BESCHREIBUNG Anbietername (Suche) Bereits registriert? Plattform
SQLServerColumnEncryptionAzureKeyVaultProvider Ein Anbieter für einen Keystore für die Azure Key Vault-Instanz. AZURE_KEY_VAULT Nein vor Version 7.4.1 des JDBC-Treibers, aber Ja ab Version 7.4.1 des JDBC-Treibers Windows, Linux, macOS
SQLServerColumnEncryptionCertificateStoreProvider Ein Anbieter für den Windows-Zertifikatspeicher. MSSQL_CERTIFICATE_STORE Ja Windows
SQLServerColumnEncryptionJavaKeyStoreProvider Ein Anbieter für den Java-Keystore. MSSQL_JAVA_KEYSTORE Ja Windows, Linux, macOS

Um bereits registrierte Keystore-Anbieter zu verwenden, sind keine Änderungen am Anwendungscode erforderlich. Beachten Sie jedoch die folgenden Punkte:

  • Sie müssen sicherstellen, dass der in den Metadaten des Spaltenhauptschlüssels konfigurierte Anbietername korrekt ist und der Pfad des Spaltenhauptschlüssels das Schlüsselpfadformat befolgt, das für einen angegebenen Anbieter gültig ist. Es wird empfohlen, dass Sie die Schlüssel mithilfe von Tools wie SQL Server Management Studio konfigurieren, die die gültigen Anbieternamen und Schlüsselpfade automatisch generieren, wenn die Anweisung CREATE COLUMN MASTER KEY (Transact-SQL) ausgegeben wird.
  • Stellen Sie sicher, dass die Anwendung auf den Schlüssel im Schlüsselspeicher zugreifen kann. Diese Aufgabe kann das Gewähren des Zugriffs auf den Schlüssel und bzw. oder den Keystore für Ihre Anwendung umfassen. Je nach Keystore kann dies andere Keystore-spezifische Konfigurationsschritte umfassen. Um beispielsweise den SQLServerColumnEncryptionJavaKeyStoreProvider zu verwenden, müssen Sie den Speicherort und das Kennwort des Keystores in den Verbindungseigenschaften angeben.

Alle diese Keystore-Anbieter werden in den folgenden Abschnitten ausführlicher beschrieben. Sie müssen nur einen einzigen Keystore-Anbieter implementieren, um Always Encrypted zu verwenden.

Verwenden des Azure Key Vault-Anbieters

Azure Key Vault ist eine praktische Möglichkeit zum Speichern von Spaltenhauptschlüsseln für Always Encrypted (insbesondere, wenn Ihre Anwendung in Azure gehostet wird). Der Microsoft JDBC-Treiber für SQL Server enthält den integrierten Anbieter SQLServerColumnEncryptionAzureKeyVaultProvider für Anwendungen, die über in Azure Key Vault gespeicherte Schlüssel verfügen. Der Name dieses Anbieters ist AZURE_KEY_VAULT.

Hinweis

Der Azure Key Vault-Anbieter, der im JDBC-Treiber integriert ist, unterstützt sowohl Tresore als auch verwaltete HSMs in Azure Key Vault.

Um den Azure Key Vault-Speicheranbieter zu verwenden, muss ein*e Anwendungsentwickler*in den Tresor und die Schlüssel in Azure Key Vault und eine App-Registrierung in Microsoft Entra ID (ehemals Azure Active Directory) erstellen. Der registrierten Anwendung müssen in den Zugriffsrichtlinien die Berechtigungen „Abrufen“, „Entschlüsseln“, „Verschlüsseln“, „Schlüssel entpacken“, „Schlüssel packen“ und „Überprüfen“ gewährt werden, die für den Schlüsseltresor zur Verwendung mit Always Encrypted definiert sind. Weitere Informationen zum Einrichten des Schlüsseltresors und zum Erstellen eines Spaltenhauptschlüssels finden Sie unter Azure Key Vault – Schritt für Schritt und Erstellen von Spaltenhauptschlüsseln in Azure Key Vault.

Bei Verwendung eines Azure Key Vault-Anbieters gleicht der JDBC-Treiber den Pfad des Spaltenhauptschlüssels mit der Liste vertrauenswürdiger Endpunkte ab. Ab der Version 8.2.2 kann diese Liste konfiguriert werden: Erstellen Sie dazu eine mssql-jdbc.properties-Datei im Arbeitsverzeichnis der Anwendung, und legen Sie die AKVTrustedEndpoints-Eigenschaft auf eine durch Semikolons getrennte Liste fest. Wenn der Wert mit einem Semikolon beginnt, wie die Standardliste erweitert. Andernfalls wird die Standardliste ersetzt.

Die vertrauenswürdigen Standardendpunkte sind die folgenden:

  • *vault.azure.net
  • *vault.azure.cn
  • *vault.usgovcloudapi.net
  • *vault.microsoftazure.de
  • *managedhsm.azure.net (Version 9.2 und höher)
  • *managedhsm.azure.cn (Version 9.2 und höher)
  • *managedhsm.usgovcloudapi.net (Version 9.2 und höher)
  • *managedhsm.microsoftazure.de (Version 9.2 und höher)

Wenn Sie in den Beispielen auf dieser Seite mit SQL Server Management Studio einen Azure Key Vault-basierten Spaltenhauptschlüssel und einen Spaltenverschlüsselungsschlüssel erstellt haben, könnte das T-SQL-Skript zum erneuten Erstellen in etwa wie in diesem Beispiel mit seinem eigenen spezifischen KEY_PATH und ENCRYPTED_VALUE aussehen:

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...
);

Eine Anwendung, die den JDBC-Treiber verwendet, kann die Azure Key Vault-Instanz verwenden. Die Syntax oder Anweisungen für diese Verwendung von Azure Key Vault wurden ab der JDBC-Treiberversion 7.4.1 geändert.

JDBC-Treiber 7.4.1 oder höher

Dieser Abschnitt umfasst die JDBC-Treiberversion 7.4.1 oder höher.

Um eine Clientanwendung, die den JDBC-Treiber verwendet, für die Verwendung von Azure Key Vault zu konfigurieren, muss keyVaultProviderClientId=<ClientId>;keyVaultProviderClientKey=<ClientKey> in der JDBC-Verbindungszeichenfolge enthalten sein.

Nachstehend finden Sie ein Beispiel, in dem die Konfigurationsinformationen in einer JDBC-Verbindungszeichenfolge angegeben werden.

String connectionUrl = "jdbc:sqlserver://<server>:<port>;encrypt=true;user=<user>;password=<password>;columnEncryptionSetting=Enabled;keyVaultProviderClientId=<ClientId>;keyVaultProviderClientKey=<ClientKey>";

Der JDBC-Treiber instanziiert automatisch ein SQLServerColumnEncryptionAzureKeyVaultProvider -Objekt, wenn diese Anmeldeinformationen in den Verbindungseigenschaften vorhanden sind.

Wichtig

Die Verbindungseigenschaften keyVaultProviderClientId und keyVaultProviderClientKey sind seit v8.4.1 veraltet. Benutzern sollten stattdessen keyStoreAuthentication , KeyStorePrincipalId und KeyStoreSecret verwenden.

JDBC-Treiberversionen vor 7.4.1

Dieser Abschnitt betrifft JDBC-Treiberversionen vor 7.4.1.

Eine Clientanwendung, die den JDBC-Treiber verwendet, muss ein SQLServerColumnEncryptionAzureKeyVaultProvider -Objekt instanziieren und das Objekt dann beim Treiber registrieren.

SQLServerColumnEncryptionAzureKeyVaultProvider akvProvider = new SQLServerColumnEncryptionAzureKeyVaultProvider(clientID, clientKey);

clientID ist die Anwendungs-ID einer App-Registrierung in einem Microsoft Entra-Mandanten. clientKey ist ein Schlüsselkennwort, das unter dieser Anwendung registriert ist und API-Zugriff auf die Azure Key Vault-Instanz ermöglicht.

Nachdem die Anwendung eine Instanz des SQLServerColumnEncryptionAzureKeyVaultProvider erstellt hat, muss die Anwendung die Instanz mit der SQLServerConnection.registerColumnEncryptionKeyStoreProviders()-Methode beim Treiber registrieren. Es wird dringen empfohlen, die Instanz mit dem Standardlookupnamen „AZURE_KEY_VAULT“ zu registrieren, der über die SQLServerColumnEncryptionAzureKeyVaultProvider.getName()-API abgerufen werden kann. Wenn Sie den Standardnamen verwenden, können Sie Tools wie SQL Server Management Studio oder PowerShell verwenden, um Always Encrypted-Schlüssel bereitzustellen und zu verwalten (die Tools verwenden den Standardnamen, um das Metadatenobjekt für den Spaltenhauptschlüssel zu generieren). Im folgenden Beispiel wird die Registrierung des Azure Key Vault-Anbieters veranschaulicht. Weitere Informationen zur Methode SQLServerConnection.registerColumnEncryptionKeyStoreProviders() finden Sie unter Always Encrypted – API-Referenz für den JDBC-Treiber.

Map<String, SQLServerColumnEncryptionKeyStoreProvider> keyStoreMap = new HashMap<String, SQLServerColumnEncryptionKeyStoreProvider>();
keyStoreMap.put(akvProvider.getName(), akvProvider);
SQLServerConnection.registerColumnEncryptionKeyStoreProviders(keyStoreMap);

Wichtig

Wenn Sie den Azure Key Vault-Keystore-Anbieter verwenden, müssen Sie die Abhängigkeiten der Azure Key Vault-Implementierung des JDBC-Treibers von diesen Bibliotheken (von GitHub) in die Anwendung einbeziehen:

azure-sdk-for-java

microsoft-authentication-library-for-java libraries

Ein Beispiel für das Einbeziehen dieser Abhängigkeiten in ein Maven-Projekt finden Sie unter Herunterladen von MSAL4J- und AKV-Abhängigkeiten mit Apache Maven.

Verwenden der Azure Key Vault-Authentifizierung mit verwalteten Identitäten

Ab Version 8.4.1 bietet der JDBC-Treiber Unterstützung für die Azure Key Vault-Authentifizierung mit verwalteten Identitäten.

Sie können verwaltete Identitäten verwenden, um sich beim Azure Key Vault zu authentifizieren, wenn die Anwendung in Azure gehostet wird. Dadurch müssen keine Anmeldeinformationen im Code eingegeben und verfügbar gemacht werden.

Verbindungseigenschaften für die Key Vault-Authentifizierung mit verwalteten Identitäten

Ab Version 8.4.1 des JDBC-Treibers wurden die folgenden Verbindungseigenschaften eingeführt:

ConnectionProperty Mögliche Wertpaare 1 Mögliche Wertpaare 2 Mögliche Wertpaare 3
keyStoreAuthentication KeyVaultClientSecret KeyVaultManagedIdentity JavaKeyStorePassword
keyStorePrincipalId <Anwendungsclient-ID in Microsoft Entra> <Applikationsobjekt-ID in Microsoft Entra> (optional)
keyStoreSecret <Geheimer Clientschlüssel der Microsoft Entra-Anwendung> <Geheimnis/Passwort für den Java Key Store>

In den folgenden Beispielen wird veranschaulicht, wie die Verbindungseigenschaften in einer Verbindungszeichenfolge verwendet werden.

Verwenden einer verwalteten Identität zur Authentifizierung bei AKV

"jdbc:sqlserver://<server>:<port>;encrypt=true;columnEncryptionSetting=Enabled;keyStoreAuthentication=KeyVaultManagedIdentity;"

Verwenden einer verwalteten Identität und der Prinzipal-ID zur Authentifizierung bei AKV

"jdbc:sqlserver://<server>:<port>;encrypt=true;columnEncryptionSetting=Enabled;keyStoreAuthentication=KeyVaultManagedIdentity;keyStorePrincipal=<principalId>"

Verwenden von clientID und clientSecret zur Authentifizierung bei AKV

"jdbc:sqlserver://<server>:<port>;encrypt=true;columnEncryptionSetting=Enabled;keyStoreAuthentication=KeyVaultClientSecret;keyStorePrincipalId=<clientId>;keyStoreSecret=<clientSecret>"

Benutzer*innen wird empfohlen, diese Verbindungseigenschaften zu verwenden, um den für die Keystores verwendeten Authentifizierungstyp anzugeben, anstatt die SQLServerColumnEncryptionAzureKeyVaultProvider-API zu nutzen.

Die zuvor hinzugefügten Verbindungseigenschaften keyVaultProviderClientId und keyVaultProviderClientKey sind veraltet und wurden durch die oben beschriebenen Verbindungseigenschaften ersetzt.

Informationen zum Konfigurieren verwalteter Identitäten finden Sie unter Konfigurieren von verwalteten Identitäten für Azure-Ressourcen auf einem virtuellen Computer über das Azure-Portal.

Verwenden des Windows-Zertifikatspeicheranbieters

Zum Speichern von Spaltenhauptschlüsseln im Windows-Zertifikatspeicher kann SQLServerColumnEncryptionCertificateStoreProvider verwendet werden. Verwenden Sie den SQL Server Management Studio Always Encrypted-Assistenten (SSMS) oder andere unterstützte Tools, um die Spaltenhauptschlüssel- und Spaltenverschlüsselungsschlüssel-Definitionen in der Datenbank zu erstellen. Der gleiche Assistent kann verwendet werden, um ein selbstsigniertes Zertifikat im Windows-Zertifikatspeicher zu generieren, das als Spaltenhauptschlüssel für die Always Encrypted-Daten verwendet werden kann. Weitere Informationen zum Spaltenhauptschlüssel und der T-SQL-Syntax für den Spaltenverschlüsselungsschlüssel finden Sie unter ERSTELLEN DES SPALTENHAUPTSCHLÜSSELS bzw. ERSTELLEN DES SPALTENVERSCHLÜSSELUNGSSCHLÜSSELS.

Der Name von SQLServerColumnEncryptionCertificateStoreProvider ist MSSQL_CERTIFICATE_STORE und kann mit der getName()-API des Anbieterobjekts abgefragt werden. Er wird automatisch vom Treiber registriert und kann nahtlos ohne Änderung der Anwendung verwendet werden.

Wenn Sie in den Beispielen auf dieser Seite mit SQL Server Management Studio einen auf dem Windows-Zertifikatspeicher basierenden Spaltenhauptschlüssel und einen Spaltenverschlüsselungsschlüssel erstellt haben, kann das T-SQL-Skript zum erneuten Erstellen in etwa wie in diesem Beispiel mit seinem eigenen spezifischen KEY_PATH und ENCRYPTED_VALUE aussehen:

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...
);

Wichtig

Während die anderen Keystore-Anbieter in diesem Artikel auf allen Plattformen verfügbar sind, die vom Treiber unterstützt werden, ist die SQLServerColumnEncryptionCertificateStoreProvider-Implementierung des JDBC-Treibers nur auf Windows-Betriebssystemen verfügbar. Es besteht eine Abhängigkeit von mssql-jdbc_auth-<version>-<arch>.dll, die im Treiberpaket verfügbar ist. Wenn Sie diesen Anbieter verwenden möchten, müssen Sie die Datei mssql-jdbc_auth-<version>-<arch>.dll in ein Verzeichnis im Windows-Systempfad des Computers kopieren, auf dem der JDBC-Treiber installiert ist. Alternativ können Sie mit der java.libary.path-Systemeigenschaft das Verzeichnis von mssql-jdbc_auth-<version>-<arch>.dll angeben. Wenn Sie eine 32-Bit-JVM (Java Virtual Machine) ausführen, verwenden Sie die Datei mssql-jdbc_auth-<version>-x86.dll im Ordner „x86“, auch wenn es sich bei dem Betriebssystem um die x64-Version handelt. Wenn Sie eine 64-Bit-JVM mit einem x64-Prozessor ausführen, verwenden Sie die Datei mssql-jdbc_auth-<version>-x64.dll im Ordner „x64“. Wenn Sie beispielsweise die 32-Bit-JVM-Umgebung verwenden und der JDBC-Treiber im Standardverzeichnis installiert ist, können Sie den Speicherort der DLL beim Start der Java-Anwendung mit dem folgenden VM-Argument (Virtual Machine) angeben: -Djava.library.path=C:\Microsoft JDBC Driver <version> for SQL Server\sqljdbc_<version>\enu\auth\x86

Verwenden des Java KeyStore-Anbieters

Der JDBC-Treiber ist mit einer integrierten Schlüsselspeicheranbieter-Implementierung für den Java Key Store ausgestattet. Wenn die Eigenschaft keyStoreAuthentication der Verbindungszeichenfolge vorhanden und auf JavaKeyStorePassword festgelegt ist, instanziiert und registriert der Treiber den Anbieter für Java Key Store automatisch. Der Name des Java Key Store-Anbieters ist MSSQL_JAVA_KEYSTORE. Dieser Name kann auch von der SQLServerColumnEncryptionJavaKeyStoreProvider.getName()-API abgefragt werden.

Es gibt drei Eigenschaften der Verbindungszeichenfolge, die einer Clientanwendung ermöglichen, die Anmeldeinformationen anzugeben, die der Treiber für die Authentifizierung beim Java Key Store benötigt. Der Treiber initialisiert den Anbieter auf der Grundlage der Werte dieser drei Eigenschaften in der Verbindungszeichenfolge.

keyStoreAuthentication: Identifiziert den zu verwendenden Java Key Store. Mit dem Microsoft JDBC-Treiber 6.0 und höher für SQL Server können Sie sich nur über diese Eigenschaft beim Java Key Store authentifizieren. Für den Java Key Store muss der Wert für diese Eigenschaft JavaKeyStorePassword sein.

keyStoreLocation: Der Pfad zur Java Key Store-Datei, in der der Spaltenhauptschlüssel gespeichert wird. Der Pfad enthält den Keystoredateinamen.

keyStoreSecret: Das Geheimnis/Kennwort, das für den Keystore und für den Schlüssel verwendet werden soll. Beachten Sie, dass zur Verwendung des Java KeyStore der Keystore und das Schlüsselkennwort übereinstimmen müssen.

Nachstehend finden Sie ein Beispiel für die Angabe dieser Anmeldeinformationen in der Verbindungszeichenfolge:

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>";

Sie können diese Einstellungen auch mit dem SQLServerDataSource-Objekt abrufen oder festlegen. Weitere Informationen finden Sie unter Always Encrypted – API-Referenz für den JDBC-Treiber.

Der JDBC-Treiber instanziiert SQLServerColumnEncryptionJavaKeyStoreProvider automatisch, wenn diese Anmeldeinformationen in den Verbindungseigenschaften vorhanden sind.

Erstellen eines Spaltenhauptschlüssels für den Java Key Store

SQLServerColumnEncryptionJavaKeyStoreProvider kann mit JKS- oder PKCS12-Keystores verwendet werden. Um einen Schlüssel zu erstellen oder zu importieren, der mit diesem Anbieter verwendet werden soll, verwenden Sie das Java-Hilfsprogramm keytool. Der Schlüssel muss das gleiche Kennwort wie der Keystore selbst aufweisen. Nachstehend finden Sie ein Beispiel für das Erstellen eines öffentlichen Schlüssels und des zugehörigen privaten Schlüssels mit dem Hilfsprogramm keytool:

keytool -genkeypair -keyalg RSA -alias AlwaysEncryptedKey -keystore keystore.jks -storepass mypassword -validity 360 -keysize 2048 -storetype jks

Mit diesem Befehl wird ein öffentlicher Schlüssel erstellt und von einem selbstsignierten X.509-Zertifikat umschlossen, das im Keystore keystore.jks zusammen mit dem zugehörigen privaten Schlüssel gespeichert wird. Dieser Eintrag im Keystore wird durch den Alias AlwaysEncryptedKey identifiziert.

Nachstehend finden Sie ein Beispiel für den gleichen Vorgang mit einem PKCS12-Speichertyp:

keytool -genkeypair -keyalg RSA -alias AlwaysEncryptedKey -keystore keystore.pfx -storepass mypassword -validity 360 -keysize 2048 -storetype pkcs12 -keypass mypassword

Bei einem Keystore des Typs PKCS12 fordert das keytool-Hilfsprogramm nicht zur Eingabe eines Schlüsselkennworts auf, und das Schlüsselkennwort muss mit der -keypass-Option angegeben werden, weil der SQLServerColumnEncryptionJavaKeyStoreProvider erfordert, dass Keystore und Schlüssel das gleiche Kennwort aufweisen.

Sie können auch ein Zertifikat aus dem Windows-Zertifikatspeicher im PFX-Format exportieren und mit dem SQLServerColumnEncryptionJavaKeyStoreProvider verwenden. Das exportierte Zertifikat kann auch als JKS-Keystoretyp in den Java Key Store importiert werden.

Nachdem Sie den Keytool-Eintrag erstellt haben, erstellen Sie die Metadaten des Spaltenhauptschlüssels in der Datenbank, die den Namen des Keystore-Anbieters und den Schlüsselpfad benötigt. Weitere Informationen zum Erstellen von Spaltenhauptschlüssel-Metadaten finden Sie unter CREATE COLUMN MASTER KEY. Für SQLServerColumnEncryptionJavaKeyStoreProvider ist der Schlüsselpfad lediglich der Alias des Schlüssels, und der Name von SQLServerColumnEncryptionJavaKeyStoreProvider ist MSSQL_JAVA_KEYSTORE. Sie können diesen Namen auch mit der öffentlichen getName()-API der SQLServerColumnEncryptionJavaKeyStoreProvider-Klasse abfragen.

Die T-SQL-Syntax zum Erstellen des Spaltenhauptschlüssels lautet:

CREATE COLUMN MASTER KEY [<CMK_name>]
WITH
(
    KEY_STORE_PROVIDER_NAME = N'MSSQL_JAVA_KEYSTORE',
    KEY_PATH = N'<key_alias>'
);

Für den oben erstellten „AlwaysEncryptedKey“ lautet die Definition des Spaltenhauptschlüssels wie folgt:

CREATE COLUMN MASTER KEY [MyCMK]
WITH
(
    KEY_STORE_PROVIDER_NAME = N'MSSQL_JAVA_KEYSTORE',
    KEY_PATH = N'AlwaysEncryptedKey'
);

Hinweis

Mit der integrierten SQL Server Management Studio-Funktionalität können keine Spaltenhauptschlüssel-Definitionen für den Java Key Store erstellt werden. T-SQL-Befehle müssen programmgesteuert verwendet werden.

Erstellen eines Spaltenverschlüsselungsschlüssels für den Java KeyStore

SQL Server Management Studio oder andere Tools können nicht zum Erstellen von Spaltenverschlüsselungsschlüsseln verwendet werden, die Spaltenhauptschlüssel im Java Key Store verwenden. Die Clientanwendung muss den Spaltenverschlüsselungsschlüssel programmgesteuert mithilfe der SQLServerColumnEncryptionJavaKeyStoreProvider-Klasse erstellen. Weitere Informationen finden Sie unter Verwenden eines Speicheranbieters für Spaltenhauptschlüssel zur programmgesteuerten Schlüsselbereitstellung.

Implementieren eines benutzerdefinierten Speicheranbieters für den Spaltenhauptschlüssel

Wenn Sie Spaltenhauptschlüssel in einem Keystore speichern möchten, der nicht von einem vorhandenen Anbieter unterstützt wird, können Sie einen benutzerdefinierten Anbieter implementieren, indem Sie die SQLServerColumnEncryptionKeyStoreProvider-Klasse erweitern und den Anbieter mithilfe einer der folgenden Methoden registrieren:

  • SQLServerConnection.registerColumnEncryptionKeyStoreProviders
  • SQLServerConnection.registerColumnEncryptionKeyStoreProvidersOnConnection (Hinzugefügt in der JDBC-Version 10.2)
  • SQLServerStatement.registerColumnEncryptionKeyStoreProvidersOnStatement (Hinzugefügt in der 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
    }
}

Registrieren Sie den Anbieter mit SQLServerConnection.registerColumnEncryptionKeyStoreProviders:

SQLServerColumnEncryptionKeyStoreProvider storeProvider = new MyCustomKeyStore();
Map<String, SQLServerColumnEncryptionKeyStoreProvider> keyStoreMap = new HashMap<String, SQLServerColumnEncryptionKeyStoreProvider>();
keyStoreMap.put(storeProvider.getName(), storeProvider);
SQLServerConnection.registerColumnEncryptionKeyStoreProviders(keyStoreMap);

Priorität des Cache für Spaltenverschlüsselungsschlüssel

Dieser Abschnitt gilt für JDBC-Treiber der Version 10.2 oder höher.

Die Spaltenverschlüsselungsschlüssel (CEK), die von benutzerdefinierten Keystore-Anbietern entschlüsselt werden und für eine Verbindungs- oder Anweisungsinstanz registriert sind, werden vom Microsoft JDBC-Treiber für SQL Server nicht zwischengespeichert. Benutzerdefinierte Schlüsselspeicheranbieter sollten einen eigenen CEK-Cachemechanismus implementieren.

Ab Version 10.2 verfügt der SQLServerColumnEncryptionAzureKeyVaultProvider über eigene Implementierungen zur CEK-Zwischenspeicherung. Wenn CEKs, die von einer Instanz von SQLServerColumnEncryptionAzureKeyVaultProvider verschlüsselt werden, für eine Verbindungs- oder eine Anweisungsinstanz registriert werden, werden sie gelöscht, wenn die Instanz den Geltungsbereich verlässt:

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

Hinweis

Die von benutzerdefinierten Keystore-Anbietern implementierte CEK-Zwischenspeicherung wird vom Treiber deaktiviert, wenn die Keystore-Anbieterinstanz global mit der SQLServerConnection.registerColumnEncryptionKeyStoreProviders-Methode im Treiber registriert ist. Jede Implementierung zum CEK-Cache sollte auf den Wert der Gültigkeitsdauer verweisen, bevor ein CEK zwischengespeichert wird, und nicht zwischenspeichern, wenn der Wert Null beträgt. So können doppeltes Zwischenspeichern und mögliche Benutzerkonfusionen beim Versuch, Zwischenspeichermechanismen für Schlüssel zu konfigurieren, vermieden werden. Der Wert der Gültigkeitsdauer für den Cache kann mit der SQLServerColumnEncryptionKeyStoreProvider.setColumnEncryptionCacheTtl-Methode festgelegt werden.

Registrieren eines benutzerdefinierten Speicheranbieters für Spaltenhauptschlüssel

Dieser Abschnitt gilt für JDBC-Treiber der Version 10.2 oder höher.

Benutzerdefinierte Anbieter von Speicher für Spaltenhauptschlüssel können beim Treiber auf drei verschiedenen Ebenen registriert werden. Für die drei Registrierungen gilt folgende Priorität:

  • Die Registrierung pro Anweisung wird überprüft, wenn sie nicht leer ist.
  • Wenn die Registrierung pro Anweisung leer ist, wird die Registrierung pro Verbindung überprüft, wenn sie nicht leer ist.
  • Wenn die Registrierung pro Verbindung leer ist, wird die globale Registrierung überprüft.

Wenn ein Schlüsselspeicheranbieter auf Registrierungsebene gefunden wurde, greift der Treiber bei der Suche nach einem Anbieter NICHT auf die anderen Registrierungen zurück. Wenn Anbieter registriert sind, der richtige Anbieter jedoch nicht auf einer Ebene gefunden wurde, wird eine Ausnahme ausgelöst, die besagt, dass nur die registrierten Anbieter in der überprüften Registrierung enthalten sind.

Der integrierte Speicheranbieter für Spaltenhauptschlüssel, der für den Windows-Zertifikatspeicher verfügbar ist, ist bereits registriert. Der Microsoft Java KeyStore-Anbieter und der Keystore-Anbieter von Azure Key Vault können implizit bei einer Verbindungsinstanz vorab registriert werden, wenn im Voraus Anmeldeinformationen bereitgestellt werden.

Die drei Registrierungsebenen unterstützen verschiedene Szenarios beim Abfragen verschlüsselter Daten. Die entsprechende Methode kann verwendet werden, um sicherzustellen, dass ein*e Benutzer*in einer Anwendung auf die Klartextdaten zugreifen kann. Der Zugriff auf unverschlüsselte Daten erfolgt nur, wenn sie den erforderlichen Spaltenhauptschlüssel bereitstellen können, indem sie sich bei dem Keystore authentifizieren, der den Spaltenhauptschlüssel enthält.

Anwendungen, die eine SQLServerConnection-Instanz mit mehreren Benutzer*innen gemeinsam nutzen, sollten SQLServerStatement.registerColumnEncryptionKeyStoreProvidersOnStatement verwenden. Jede*r Benutzer*in muss einen Keystore-Anbieter für eine SQLServerStatement-Instanz registrieren, bevor eine Abfrage ausgeführt wird, um auf eine verschlüsselte Spalte zuzugreifen. Wenn der Keystore-Anbieter auf den erforderlichen Spaltenhauptschlüssel im Keystore zugreifen kann, der die angegebenen Anmeldeinformationen des Benutzers bzw. der Benutzerin verwendet, wird die Abfrage erfolgreich ausgeführt.

Anwendungen, die eine SQLServerConnection-Instanz für jede*n Benutzer*n erstellen, sollten SQLServerConnection.registerColumnEncryptionKeyStoreProvidersOnConnection verwenden. Schlüsselspeicheranbieter, die mit dieser Methode registriert wurden, können von der Verbindung für jede Abfrage verwendet werden, die auf verschlüsselte Daten zugreift.

Keystore-Anbieter, die bei SQLServerConnection.registerColumnEncryptionKeyStoreProviders registriert sind, verwenden die Identität, die von der Anwendung bei der Authentifizierung beim Keystore angegeben wird.

Das folgende Beispiel zeigt die Rangfolge von benutzerdefinierten Speicheranbietern für Spaltenhauptschlüssel, die für eine Verbindungsinstanz registriert sind:

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);              
}

Im folgenden Beispiel wird die Rangfolge von benutzerdefinierten Speicheranbietern für Spaltenhauptschlüssel demonstriert, die für eine Anweisungsinstanz registriert sind:

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);
    }             
}

Verwenden von Speicheranbietern für Spaltenhauptschlüssel zur programmgesteuerten Schlüsselbereitstellung

Beim Zugriff auf verschlüsselte Spalten sucht der Microsoft JDBC-Treiber für SQL Server den richtigen Speicheranbieter für Spaltenhauptschlüssel transparent und ruft ihn anschließend auf, um die Spaltenverschlüsselungsschlüssel zu entschlüsseln. In der Regel ruft der normale Anwendungscode die Speicheranbieter für Spaltenhauptschlüssel nicht direkt auf. Sie können einen Anbieter jedoch programmgesteuert instanziieren und abrufen, um Always Encrypted-Schlüssel bereitzustellen und zu verwalten. Dieser Schritt kann z. B. ausgeführt werden, um einen verschlüsselten Spaltenverschlüsselungsschlüssel zu generieren und einen Spaltenverschlüsselungsschlüssel als Teil der Spaltenhauptschlüssel-Rotation zu entschlüsseln. Weitere Informationen finden Sie unter Overview of Key Management for Always Encrypted(Übersicht über die Schlüsselverwaltung für Always Encrypted).

Die Implementierung Ihrer eigenen Schlüsselverwaltungstools ist möglicherweise erforderlich, wenn Sie einen benutzerdefinierten Schlüsselspeicheranbieter verwenden. Bei der Verwendung von Schlüsseln, die im Windows-Zertifikatspeicher oder in Azure Key Vault gespeichert werden, können Sie vorhandene Tools wie SQL Server Management Studio oder PowerShell verwenden, um Schlüssel zu verwalten und bereitzustellen. Wenn Sie Schlüssel verwenden, die im Java KeyStore gespeichert sind, müssen Sie Schlüssel programmgesteuert bereitstellen. Im folgenden Beispiel wird die Verwendung der SQLServerColumnEncryptionJavaKeyStoreProvider-Klasse zum Verschlüsseln des Schlüssels mit einem Schlüssel veranschaulicht, der im Java KeyStore gespeichert ist.

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 = "********".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();
    }
}

Aktivieren von Always Encrypted für Anwendungsabfragen

Die einfachste Möglichkeit, die Verschlüsselung von Parametern und die Entschlüsselung von Abfrageergebnissen verschlüsselter Spalten zu aktivieren, besteht darin, den Wert des Schlüsselworts der Verbindungszeichenfolge columnEncryptionSetting auf Enabled festzulegen.

Die folgende Verbindungszeichenfolge ist ein Beispiel für das Aktivieren von Always Encrypted im JDBC-Speicher:

String connectionUrl = "jdbc:sqlserver://<server>:<port>;user=<user>;encrypt=true;password=<password>;databaseName=<database>;columnEncryptionSetting=Enabled;";
SQLServerConnection connection = (SQLServerConnection) DriverManager.getConnection(connectionUrl);

Der folgende Code ist ein gleichwertiges Beispiel für die Verwendung des SQLServerDataSource-Objekts:

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 kann auch für einzelne Abfragen aktiviert werden. Weitere Informationen finden Sie weiter unten im Abschnitt Kontrollieren der Auswirkungen von Always Encrypted auf die Leistung. Die Aktivierung von Always Encrypted ist für eine erfolgreiche Verschlüsselung und Entschlüsselung nicht ausreichend. Sie müssen auch Folgendes sicherstellen:

  • Die Anwendung verfügt über die Datenbankberechtigungen VIEW ANY COLUMN MASTER KEY DEFINITION und VIEW ANY COLUMN ENCRYPTION KEY DEFINITION , die für den Zugriff auf die Metadaten für Always Encrypted-Schlüssel in der Datenbank erforderlich sind. Weitere Informationen finden Sie unter Berechtigungen in Always Encrypted (Datenbank-Engine).
  • Die Anwendung kann auf den Hauptschlüssel der Spalte zugreifen, der die Spaltenverschlüsselungsschlüssel schützt, mit denen die abgefragten Datenbankspalten verschlüsselt werden. Wenn Sie den Java Key Store-Anbieter verwenden möchten, müssen Sie zusätzliche Anmeldeinformationen in der Verbindungszeichenfolge angeben. Weitere Informationen finden Sie unter Verwenden des Java KeyStore-Anbieters.

Konfigurieren der Art und Weise, wie java.sql.Time-Werte an den Server gesendet werden

Die sendTimeAsDatetime -Verbindungseigenschaft wird dazu verwendet, die Art und Weise zu konfigurieren, auf die der java.sql.Time-Wert an den Server gesendet wird. Wenn der Wert auf „false“ festgelegt ist, wird der Zeitwert als SQL Server-Zeittyp gesendet. Wenn der Wert auf „true“ festgelegt ist, wird der Zeitwert als datetime-Typ gesendet. Wenn eine Zeitspalte verschlüsselt wird, muss die sendTimeAsDatetime -Eigenschaft „false“ sein, da verschlüsselte Spalten die Konvertierung von „time“ in „datetime“ nicht unterstützen. Beachten Sie auch, dass diese Eigenschaft standardmäßig „true“ ist. Wenn Sie verschlüsselte Zeitspalten verwenden, müssen Sie diese Eigenschaft also auf „false“ festlegen. Andernfalls löst der Treiber eine Ausnahme aus. Ab Version 6.0 des Treibers verfügt die SQLServerConnection-Klasse über zwei Methoden, um den Wert dieser Eigenschaft programmgesteuert zu konfigurieren:

  • public void setSendTimeAsDatetime(boolean sendTimeAsDateTimeValue)
  • öffentlicher boolescher Wert getSendTimeAsDatetime()

Weitere Informationen zu dieser Eigenschaft finden Sie unter Konfigurieren der Art und Weise, wie java.sql.Time-Werte an den Server gesendet werden.

Konfigurieren der Art und Weise, wie Zeichenfolgenwerte an den Server gesendet werden

Mit der sendStringParametersAsUnicode -Verbindungseigenschaft wird konfiguriert, wie Zeichenfolgenwerte an SQL Server gesendet werden. Wenn die Eigenschaft auf „TRUE“ festgelegt ist, werden String-Parameter im Unicode-Format an den Server gesendet. Wenn die Eigenschaft auf „false“ festgelegt ist, werden Zeichenfolgenparameter nicht im Unicode-Format, sondern in einem anderen Format wie z. B. ASCII oder MBCS an den Server gesendet. Der Standardwert dieser Eigenschaft ist „TRUE“. Wenn Always Encrypted aktiviert ist, und eine char/varchar/varchar(max)-Spalte verschlüsselt ist, muss der Wert von sendStringParametersAsUnicode auf „false“ festgelegt werden. Wenn diese Eigenschaft auf „true“ festgelegt ist, löst der Treiber eine Ausnahme aus, wenn Daten aus einer verschlüsselten char/varchar/varchar(max)-Spalte mit Unicode-Zeichen entschlüsselt werden. Weitere Informationen zu dieser Eigenschaft finden Sie unter Festlegen von Verbindungseigenschaften.

Wichtig

Wenn sendStringParametersAsUnicode auf true eingestellt und Unicode-Daten in eine char/varchar-Spalte eingefügt wird, die mit Always Encrypted verschlüsselt ist, kann ein Datenverlust auftreten, ohne dass ein Fehler gemeldet wird. Der Datenverlust kann nur erkannt werden, indem versucht wird, die Daten nach dem Zurücklesen vom Server zu entschlüsseln. Ein Fehler wie 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'. kann dann gemeldet werden.

Es ist wichtig, die richtigen Spaltendatentypen zu verwenden und den richtigen Datentyp unter den Parametern anzugeben, wenn verschlüsselte Daten eingefügt werden. Wenn Unicode-Daten erwartet werden, verwenden Sie nchar/nvarchar-Spalten und setNString()-Methoden. Der Server kann keine impliziten Datenkonvertierungen ausführen und hat nur eingeschränkte Fähigkeiten, Datenfehler zu erkennen, wenn Always Encrypted aktiviert ist.

Abrufen und Ändern von Daten in verschlüsselten Spalten

Nachdem Sie Always Encrypted für Anwendungsabfragen aktiviert haben, können Sie JDBC-Standard-APIs verwenden, um Daten in verschlüsselten Datenbankspalten abzurufen oder zu ändern. Wenn die Anwendung über die erforderlichen Datenbankberechtigungen verfügt und auf den Spaltenhauptschlüssel zugreifen kann, verschlüsselt der Treiber alle Abfrageparameter für verschlüsselte Spalten und entschlüsselt Daten, die aus verschlüsselten Spalten abgerufen werden.

Wenn Always Encrypted nicht aktiviert ist, tritt bei Abfragen mit Parametern, die verschlüsselte Spalten anzielen, ein Fehler auf. Abfragen können weiterhin Daten aus verschlüsselten Spalten abrufen, solange die Abfrage keine Parameter für verschlüsselte Spalten enthält. Der Treiber versucht jedoch nicht, die aus verschlüsselten Spalten abgerufenen Werte zu entschlüsseln, sodass die Anwendung binär verschlüsselte Daten erhält (als Bytearrays).

In der folgenden Tabelle wird das Verhalten von Abfragen in Abhängigkeit davon zusammengefasst, ob Always Encrypted aktiviert ist:

Abfragemerkmal Always Encrypted ist aktiviert und die Anwendung kann auf die Schlüssel und Schlüsselmetadaten zugreifen. Always Encrypted ist aktiviert, und die Anwendung kann nicht auf die Schlüssel oder Schlüsselmetadaten zugreifen. Always Encrypted ist deaktiviert
Abfragen mit Parametern, die auf verschlüsselte Spalten ausgerichtet sind. Parameterwerte werden transparent verschlüsselt. Fehler Fehler
Abfragen, bei denen Daten von verschlüsselten Spalten ohne Parameter abgerufen werden, die auf verschlüsselte Spalten ausgerichtet sind. Ergebnisse von verschlüsselten Spalten werden transparent entschlüsselt. Die Anwendung erhält Klartextwerte der JDBC-Datentypen, die den SQL Server-Datentypen entsprechen, die für die verschlüsselten Spalten konfiguriert wurden. Fehler Ergebnisse von verschlüsselten Spalten werden nicht entschlüsselt. Die Anwendung erhält verschlüsselte Werte als Bytearrays (byte[]).

Beispiel zum Einfügen und Abrufen von verschlüsselten Daten

Die folgenden Beispiele veranschaulichen das Abrufen und Ändern von Daten in verschlüsselten Spalten. In den Beispielen wird davon ausgegangen, dass die Zieltabelle das folgende Schema und die verschlüsselten Spalten „SSN“ und BirthDate aufweist. Wenn Sie über einen Spaltenhauptschlüssel mit dem Namen „MyCMK“ und einen Spaltenverschlüsselungsschlüssel mit dem Namen „MyCEK“ konfiguriert verfügen (wie in den vorherigen Abschnitten zu Keystore-Anbietern beschrieben), können Sie die Tabelle mit diesem Skript erstellen:

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 jedes Java-Codebeispiel müssen Sie für den Keystore spezifischen Code an der angegebenen Position einfügen.

So verwenden Sie einen Keystore-Anbieter für Azure Key Vault:

    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;";

So verwenden Sie einen Keystore-Anbieter für den Windows-Zertifikatspeicher:

    String connectionUrl = "jdbc:sqlserver://<server>:<port>;encrypt=true;databaseName=<databaseName>;user=<user>;password=<password>;columnEncryptionSetting=Enabled;";

So verwenden Sie einen Java KeyStore-Anbieter:

    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>";

Beispiel zum Einfügen von Daten

In diesem Beispiel wird eine Zeile in die Tabelle „Patients“ eingefügt. Beachten Sie Folgendes:

  • Es erfolgt keine spezielle Verschlüsselung im Beispielcode. Der Microsoft JDBC-Treiber für SQL Server erkennt und verschlüsselt automatisch die Parameter, die auf verschlüsselte Spalten ausgerichtet sind. Durch dieses Verhalten wird die Verschlüsselung für die Anwendung transparent.
  • Die in die Datenbankspalten eingefügten Werte, einschließlich der verschlüsselten Spalten, werden mit SQLServerPreparedStatement als Parameter übergeben. Während die Verwendung von Parametern optional ist, wenn Werte an nicht verschlüsselte Spalten gesendet werden (obwohl es dringend empfohlen wird, da es eine Einschleusung von SQL-Befehlen verhindert), ist sie für Werte für verschlüsselte Spalten erforderlich. Wenn die in die verschlüsselten Spalten eingefügten Werte als Literale übergeben würden, die in die Abfrageanweisung eingebettet sind, würde bei der Abfrage ein Fehler auftreten, da der Treiber die Werte in den verschlüsselten Zielspalten nicht ermitteln könnte und nicht verschlüsseln würde. Daher würde der Server sie zurückweisen, da sie mit den verschlüsselten Spalten inkompatibel sind.
  • Alle Werte werden vom Programm als Klartext ausgegeben, da der JDBC-Treiber für SQL Server die aus den verschlüsselten Spalten abgerufenen Daten transparent entschlüsselt.
  • Wenn Sie ein Lookup mit einer WHERE-Klausel durchführen, muss der in der WHERE-Klausel verwendete Wert als Parameter übergeben werden, damit der Treiber den Wert vor dem Senden an die Datenbank transparent verschlüsseln kann. Im folgenden Beispiel wird die SSN als Parameter übergeben, aber LastName als Literalwert, da LastName nicht verschlüsselt ist.
  • Für den auf die Spalte „SSN“ ausgerichteten Parameter wird die Settermethode setString() verwendet, die dem SQL Server-Datentyp char/varchar zugeordnet ist. Wenn für diesen Parameter die Settermethode setNString() verwendet wird, die nchar/nvarchar zugeordnet ist, schlägt die Abfrage fehl, da Always Encrypted keine Konvertierungen von verschlüsselten nchar/nvarchar-Werten in verschlüsselte char/varchar-Werte unterstützt.
// <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();
}

Beispiel zum Abrufen von Klartextdaten

Im folgenden Beispiel wird das Filtern von Daten auf Basis verschlüsselter Werte und das Abrufen von Klartextdaten aus verschlüsselten Spalten veranschaulicht. Beachten Sie Folgendes:

  • Der in der WHERE-Klausel zum Filtern der Spalte „SSN“ verwendete Wert muss als Parameter übergeben werden, damit er vom Microsoft JDBC-Treiber für SQL Server vor dem Senden an die Datenbank transparent verschlüsselt werden kann.
  • Alle Werte werden vom Programm als Klartext ausgegeben, da der JDBC-Treiber für SQL Server die aus den Spalten „SSN“ und „BirthDate“ abgerufenen Daten transparent entschlüsselt.

Hinweis

Abfragen können für Spalten Übereinstimmungsvergleiche ausführen, wenn diese mittels deterministischer Verschlüsselung verschlüsselt sind. Weitere Informationen finden Sie unter Deterministische Verschlüsselung.

// <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();
}

Beispiel zum Abrufen von verschlüsselten Daten

Wenn Always Encrypted nicht aktiviert ist, können Abfragen weiterhin Daten aus verschlüsselten Spalten abrufen, so lange die Abfrage keine Parameter für verschlüsselte Spalten enthält.

In den folgenden Beispielen wird das Abrufen von binär verschlüsselten Daten aus verschlüsselten Spalten veranschaulicht. Beachten Sie Folgendes:

  • Da Always Encrypted in der Verbindungszeichenfolge nicht aktiviert ist, gibt die Abfrage verschlüsselte Werte von „SSN“ und „BirthDate“ als Bytearrays zurück (das Programm konvertiert die Werte in Zeichenfolgen).
  • Eine Abfrage, die Daten aus verschlüsselten Spalten mit deaktiviertem Always Encrypted abruft, kann Parameter aufweisen, so lange keiner der Parameter auf eine verschlüsselte Spalte ausgerichtet ist. In der folgenden Abfrage wird nach der Spalte „LastName“ gefiltert, die in der Datenbank nicht verschlüsselt ist. Wenn die Abfrage nach „SSN“ oder „BirthDate“ filtert, würde ein Fehler auftreten.
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();
}

Vermeiden allgemeiner Probleme beim Abfragen von verschlüsselten Spalten

Dieser Abschnitt beschreibt die allgemeinen Kategorien von Fehlern bei der Abfrage verschlüsselter Spalten über Java-Anwendungen sowie einige Grundsätze zum Vermeiden dieser Fehler.

Konvertierungsfehler durch nicht unterstützte Datentypen

Always Encrypted unterstützt einige Konvertierungen für verschlüsselte Datentypen. Eine ausführliche Liste der unterstützten Typkonvertierungen finden Sie unter Always Encrypted (Datenbank-Engine). Sie können Fehler bei der Datentypkonvertierung folgendermaßen vermeiden. Stellen Sie Folgendes sicher:

  • Verwenden Sie beim Übergeben von Werten für Parameter, die auf verschlüsselte Spalten abzielen, die richtigen Settermethoden. Stellen Sie sicher, dass der SQL Server-Datentyp des Parameters genau dem Typ der Zielspalte entspricht, oder dass eine Konvertierung des SQL Server-Datentyps des Parameters in den Zieltyp der Spalte unterstützt wird. Zu den Klassen SQLServerPreparedStatement, SQLServerCallableStatement und SQLServerResultSet wurden API-Methoden hinzugefügt, um Parameter zu übergeben, die bestimmten SQL Server-Datentypen entsprechen. Eine vollständige Liste neuer APIs finden Sie unter Always Encrypted-API-Referenz für den JDBC-Treiber. Wenn Sie die Datentypdefinitionen nicht einhalten, treten wahrscheinlich Fehler beim Operandentypkonflikt auf. Im Folgenden finden Sie einige Beispiele für Anpassungen, die bei Verwendung von Always Encrypted erforderlich sein können:

    • Wenn eine Spalte z. B. nicht verschlüsselt ist, können Sie mit der setTimestamp()-Methode einen Parameter an eine datetime2- oder Date/time-Spalte übergeben. Aber wenn eine Spalte verschlüsselt ist, müssen Sie die exakte Methode verwenden, die den Typ der Spalte in der Datenbank darstellt. Verwenden Sie z. B. setTimestamp(), um Werte an eine verschlüsselte datetime2-Spalte zu übergeben, und setDateTime(), um Werte an eine verschlüsselte date/time-Spalte zu übergeben.
    • Sie können die setBinary()-Methode verwenden, um einen Parameter an eine nicht verschlüsselte Spalte varbinary(max) oder binary zu übergeben. Der Treiber wird standardmäßig für den Datentyp BINARY für die Parameter setBinary() verwendet, und der Server kann die Daten implizit in eine varbinary(max)-Spalte konvertieren. Aber wenn eine varbinary(max)-Spalte verschlüsselt ist, müssen Sie einen genaueren Typ für die Parameterdaten angeben. Beispiel: preparedStatement.setObject(1, binaryData, java.sql.JDBCType.LONGVARBINARY)
  • Die Genauigkeit und Dezimalstellenanzahl von Parametern, die auf Spalten der SQL Server-Datentypen „decimal“ und „numeric“ ausgerichtet sind, ist mit der für die Zielspalte konfigurierten Genauigkeit und Dezimalstellenanzahl identisch. Zu den Klassen SQLServerPreparedStatement, SQLServerCallableStatement und SQLServerResultSet wurden API-Methoden hinzugefügt, um Genauigkeit und Dezimalstellenanzahl sowie Datenwerte für Parameter/Spalten zu akzeptieren, die dezimale und numerische Datentypen darstellen. Eine vollständige Liste neuer/überladener APIs finden Sie unter Always Encrypted – API-Referenz für den JDBC-Treiber.

    • Wenn Sie z. B. Java BigDecimal als Parametertyp für eine angegebene Dezimalspalte in der Datenbank verwenden, müssen Sie die Genauigkeit und Skalierung entweder für die setBigDecimal() Methode oder die setValue() Methode angeben. Fehler beim Angeben der korrekten Genauigkeit und Skalierung können zu einem Fehler wie den folgenden führen:
    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')
    
  • Die Genauigkeit/Dezimalstellenanzahl von Sekundenbruchteilen von Parametern, die auf Spalten der SQL Server-Datentypen datetime2, datetimeoffset oder „time“ ausgerichtet sind, ist in Abfragen, in denen Werte der Zielspalte geändert werden, nicht größer als die Genauigkeit/Dezimalstellenanzahl von Sekundenbruchteilen für die Zielspalte. Zu den Klassen SQLServerPreparedStatement, SQLServerCallableStatement und SQLServerResultSet wurden API-Methoden hinzugefügt, um Genauigkeit/Dezimalstellenanzahl von Sekundenbruchteilen zusammen mit Datenwerten für Parameter zu akzeptieren, die diese Datentypen darstellen. Eine vollständige Liste neuer/überladener APIs finden Sie unter Always Encrypted – API-Referenz für den JDBC-Treiber.

Fehler aufgrund falscher Verbindungseigenschaften

In diesem Abschnitt wird beschrieben, wie Verbindungseinstellungen ordnungsgemäß zur Verwendung von Always Encrypted-Daten konfiguriert werden. Da verschlüsselte Datentypen eingeschränkte Konvertierungen unterstützen, müssen die Verbindungseinstellungen sendTimeAsDatetime und sendStringParametersAsUnicode bei Verwendung verschlüsselter Spalten ordnungsgemäß konfiguriert werden. Stellen Sie Folgendes sicher:

Fehler aufgrund der Übergabe von Klartext anstelle von verschlüsselten Werten

Jeder Wert, der auf eine verschlüsselte Spalte ausgerichtet ist, muss in der Anwendung verschlüsselt werden. Ein Versuch, einen Klartextwert einzufügen bzw. zu ändern oder nach einem Klartextwert für eine verschlüsselte Spalte zu filtern, führt zu einem Fehler wie dem folgenden:

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'

Stellen Sie Folgendes sicher, um solche Fehler zu vermeiden:

  • Always Encrypted ist für Anwendungsabfragen aktiviert, die auf verschlüsselte Spalten ausgerichtet sind (für die Verbindungszeichenfolge oder für eine bestimmte Abfrage).
  • Sie verwenden vorbereitete Anweisungen und Parameter zum Senden von Daten, die auf verschlüsselte Spalten ausgerichtet sind. Das folgende Beispiel zeigt eine Abfrage, die falsch nach einem Literal bzw. einer Konstante einer verschlüsselten Spalte (SSN) filtert, anstatt das Literal innerhalb eines Parameters zu übergeben. Diese Abfrage kann unter folgenden Voraussetzungen nicht ausgeführt werden:
ResultSet rs = connection.createStatement().executeQuery("SELECT * FROM Customers WHERE SSN='795-73-9838'");

Erzwingen der Verschlüsselung von Eingabeparametern

Das Feature „Verschlüsselung erzwingen“ erzwingt die Verschlüsselung eines Parameters bei Verwendung von Always Encrypted. Wenn „Verschlüsselung erzwingen“ verwendet wird und SQL Server den Treiber darüber informiert, dass der Parameter nicht verschlüsselt werden muss, tritt bei der Abfrage, die diesen Parameter verwendet, ein Fehler auf. Diese Eigenschaft bietet zusätzlichen Schutz vor Angriffen, bei denen ein kompromittierter SQL Server dem Client falsche Verschlüsselungsmetadaten bereitstellt, was zur Offenlegung von Daten führen kann. Die set*-Methoden in den Klassen SQLServerPreparedStatement und SQLServerCallableStatement sowie die update*-Methoden in der SQLServerResultSet-Klasse werden überladen, um ein boolesches Argument zu akzeptieren, das die Einstellung „Verschlüsselung erzwingen“ festlegt. Wenn der Wert dieses Arguments „false“ ist, wird die Verschlüsselung von Parametern vom Treiber nicht erzwungen. Wenn „Verschlüsselung erzwingen“ auf „true“ festgelegt ist, wird der Abfrageparameter nur festgelegt, wenn die Zielspalte verschlüsselt und Always Encrypted in der Verbindung oder Anweisung aktiviert ist. Die Verwendung dieser Eigenschaft bietet zusätzliche Sicherheit, wodurch sichergestellt wird, dass der Treiber nicht fälschlicherweise Daten als Klartext an SQL Server sendet, wenn sie verschlüsselt werden sollen.

Weitere Informationen zu den Methoden SQLServerPreparedStatement und SQLServerCallableStatement, die mit der Einstellung „Verschlüsselung erzwingen“ überladen werden, finden Sie unter Always Encrypted – API-Referenz für den JDBC-Treiber.

Kontrollieren der Auswirkungen von Always Encrypted auf die Leistung

Da Always Encrypted eine clientseitige Verschlüsselungstechnologie ist, werden die meisten Leistungseinbußen auf der Clientseite und nicht in der Datenbank beobachtet. Neben den Kosten für Verschlüsselungs- und Entschlüsselungsvorgänge ergeben sich folgende andere Quellen für Leistungseinbußen auf der Clientseite:

  • Zusätzliche Roundtrips zur Datenbank zum Abrufen von Metadaten für Abfrageparameter.
  • Aufrufe an einen Spaltenhauptschlüsselspeicher für den Zugriff auf einen Spaltenhauptschlüssel.

In diesem Abschnitt werden die integrierten Leistungsoptimierungen JDBC-Treiber für SQL Server und die Steuerung der Auswirkungen der beiden oben genannten Faktoren auf die Leistung beschrieben.

Kontrollieren von Roundtrips zum Abrufen von Metadaten für Abfrageparameter

Wenn Always Encrypted für eine Verbindung aktiviert ist, ruft der Treiber standardmäßig sys.sp_describe_parameter_encryption für jede parametrisierte Abfrage auf, wobei die Abfrageanweisung (ohne Parameterwerte) an die Datenbank übergeben wird. Die Abfrageanweisung wird vonsys.sp_describe_parameter_encryption analysiert, um zu ermitteln, ob Parameter verschlüsselt werden müssen. In diesem Fall gibt sie die verschlüsselungsbezogenen Informationen zurück, die dem Treiber das Verschlüsseln von Parameterwerten ermöglichen. Dieses Verhalten stellt einen hohen Grad an Transparenz für die Clientanwendung sicher. So lange die Anwendung Parameter verwendet, um auf verschlüsselte Spalten ausgerichteten Werte an den Treiber zu übergeben, muss die Anwendung (und der Anwendungsentwickler) nicht wissen, welche Abfragen Zugriff auf verschlüsselte Spalten haben.

Festlegen von Always Encrypted auf Abfrageebene

Sie können Always Encrypted für einzelne Abfragen aktivieren, anstatt es für die Verbindung einzurichten, um beim Abrufen von Verschlüsselungsmetadaten für parametrisierte Abfragen die Auswirkung auf die Leistung zu steuern. Auf diese Weise können Sie sicherstellen, dass „sys.sp_describe_parameter_encryption“ nur für Abfragen aufgerufen wird, bei denen Ihnen bekannt ist, dass sie über Parameter verfügen, die auf verschlüsselte Spalten ausgerichtet sind. Beachten Sie jedoch, dass Sie auf diese Weise die Transparenz der Verschlüsselung reduzieren: Wenn Sie die Verschlüsselungseigenschaften der Datenbankspalten ändern, müssen Sie möglicherweise den Code der Anwendung ändern, um ihn mit den Schemaänderungen auszurichten.

Um das Verhalten von Always Encrypted für einzelne Abfragen zu steuern, müssen Sie einzelne Anweisungsobjekte durch Übergabe einer Enumeration, SQLServerStatementColumnEncryptionSetting, konfigurieren, die festlegt, wie Daten beim Lesen und Schreiben verschlüsselter Spalten für diese spezifische Anweisung gesendet und empfangen werden. Hier sind einige nützliche Richtlinien:

  • Wenn für die meisten Abfragen eine Clientanwendung über eine Datenbankverbindung auf verschlüsselte Spalten zugreift, verwenden Sie die folgenden Richtlinien:

    • Legen Sie das Schlüsselwort für die columnEncryptionSetting-Verbindungszeichenfolge auf Enabled fest.
    • Legen Sie SQLServerStatementColumnEncryptionSetting.Disabled für einzelne Abfragen fest, die nicht auf verschlüsselte Spalten zugreifen. Diese Einstellung deaktiviert den Aufruf von sys.sp_describe_parameter_encryption und die Entschlüsselung von Werten im Resultset.
    • Legen Sie SQLServerStatementColumnEncryptionSetting.ResultSet für einzelne Abfragen fest, die keine Parameter besitzen, für die eine Verschlüsselung erforderlich ist, die aber Daten aus verschlüsselten Spalten abrufen. Durch diese Einstellung werden der Aufruf von sys.sp_describe_parameter_encryption und die Parameterverschlüsselung deaktiviert. Die Abfrage entschlüsselt die Ergebnisse von verschlüsselten Spalten.
  • Wenn für die meisten Abfragen eine Clientanwendung nicht über eine Datenbankverbindung auf verschlüsselte Spalten zugreift, verwenden Sie die folgenden Richtlinien:

    • Legen Sie das Schlüsselwort für die columnEncryptionSetting-Verbindungszeichenfolge auf Disabled fest.
    • Legen Sie SQLServerStatementColumnEncryptionSetting.Enabled für einzelne Abfragen mit Parametern fest, die verschlüsselt werden müssen. Diese Einstellung aktiviert den Aufruf von sys.sp_describe_parameter_encryption und die Entschlüsselung von Abfrageergebnissen, die aus verschlüsselten Spalten abgerufen wurden.
    • Legen Sie SQLServerStatementColumnEncryptionSetting.ResultSet für Abfragen fest, die keine Parameter besitzen, für die eine Verschlüsselung erforderlich ist, die aber Daten aus verschlüsselten Spalten abrufen. Durch diese Einstellung werden der Aufruf von sys.sp_describe_parameter_encryption und die Parameterverschlüsselung deaktiviert. Die Abfrage entschlüsselt die Ergebnisse von verschlüsselten Spalten.

Die SQLServerStatementColumnEncryptionSetting-Einstellungen können nicht dazu verwendet werden, die Verschlüsselung zu umgehen und Zugriff auf Klartextdaten zu erhalten. Weitere Informationen zum Konfigurieren der Spaltenverschlüsselung für eine Anweisung finden Sie unter Always Encrypted – API-Referenz für den JDBC-Treiber.

Im folgenden Beispiel ist Always Encrypted für die Datenbankverbindung deaktiviert. Die von der Anwendung ausgestellte Abfrage weist einen Parameter auf, der die nicht verschlüsselte Spalte „LastName“ anzielt. Die Abfrage ruft Daten aus den Spalten „SSN“ und „BirthDate“ ab, die beide verschlüsselt sind. In diesem Fall ist der Aufruf von sys.sp_describe_parameter_encryption nicht erforderlich, um Verschlüsselungsmetadaten abzurufen. Die Entschlüsselung der Abfrageergebnisse muss jedoch aktiviert sein, damit die Anwendung Klartextwerte aus den beiden verschlüsselten Spalten erhalten kann. Dazu wird die SQLServerStatementColumnEncryptionSetting.ResultSet-Einstellung verwendet.

// 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();
}

Zwischenspeichern von Abfrageparametermetadaten

Um die Anzahl der Roundtrips zur Datenbank zu verringern, kann der Microsoft JDBC-Treiber für SQL Server verschlüsselungsbezogene Informationen für Abfrageparameter zwischenspeichern. Ab Version 11.2.0 werden verschlüsselungsbezogene Informationen für Parameter, die von Aufrufen vom Typ sys.sp_describe_parameter_encryption zurückgegeben werden, vom Treiber zwischengespeichert, wenn der zugeordnete SQL Server-Prozess keine Secure Enclaves verwendet. Für die Zwischenspeicherung mit Verwendung von Secure Enclaves muss der Server die erneute Einrichtung der Enclave-Sitzung in Fällen unterstützen, in denen die Sitzung nicht mehr gültig ist.

Zwischenspeicherung von Spaltenverschlüsselungsschlüsseln

Der Microsoft JDBC-Treiber für SQL Server speichert die Spaltenverschlüsselungsschlüssel im Klartext im Speicher zwischen, um die Anzahl der Aufrufe an einen Spaltenhauptschlüsselspeicher zu verringern. Nachdem der Treiber den Wert des verschlüsselten Spaltenverschlüsselungsschlüssels aus den Metadaten der Datenbank erhalten hat, versucht er zunächst, den Klartextschlüssel für die Spalte zu finden, der dem verschlüsselten Schlüsselwert entspricht. Der Treiber ruft den Keystore, der den Spaltenhauptschlüssel enthält, nur dann auf, wenn er den verschlüsselten Spaltenverschlüsselungsschlüssel im Cache nicht finden kann.

Sie können einen Gültigkeitsdauerwert für die Spaltenverschlüsselungsschlüssel-Einträge im Cache mithilfe der API, setColumnEncryptionKeyCacheTtl(), in der SQLServerConnection-Klasse konfigurieren. Der Standardwert für die Gültigkeitsdauer der Spaltenverschlüsselungsschlüssel-Einträge im Cache beträgt zwei Stunden. Um das Zwischenspeichern zu deaktivieren, verwenden Sie den Wert 0. Verwenden Sie die folgende API, um einen beliebigen Wert für die Gültigkeitsdauer festzulegen:

SQLServerConnection.setColumnEncryptionKeyCacheTtl (int columnEncryptionKeyCacheTTL, TimeUnit unit)

Um beispielsweise für die Gültigkeitsdauer einen Wert von 10 Minuten festzulegen, verwenden Sie Folgendes:

SQLServerConnection.setColumnEncryptionKeyCacheTtl (10, TimeUnit.MINUTES)

Nur TAGE, STUNDEN, MINUTEN oder SEKUNDEN werden als Zeiteinheit unterstützt.

Kopieren verschlüsselter Daten mit SQLServerBulkCopy

Mit SQLServerBulkCopy können Sie Daten, die bereits verschlüsselt sind und in einer Tabelle gespeichert werden, in eine andere Tabelle kopieren, ohne die Daten zu entschlüsseln. Gehen Sie dafür folgendermaßen vor:

  • Stellen Sie sicher, dass die Verschlüsselungskonfiguration der Zieltabelle mit der Konfiguration der Quelltabelle identisch ist. Insbesondere müssen für beide Tabellen dieselben Spalten verschlüsselt sein. Zudem müssen die Spalten mithilfe derselben Verschlüsselungstypen und mit denselben Verschlüsselungsschlüsseln verschlüsselt werden. Wenn eine der Zielspalten anders als die entsprechende Quellspalte verschlüsselt wurde, können Sie die Daten in der Zieltabelle nach dem Kopiervorgang nicht entschlüsseln. Die Daten werden beschädigt.
  • Konfigurieren Sie beide Datenbankverbindungen, für die Quelltabelle und für die Zieltabelle, ohne Always Encrypted zu aktivieren.
  • Legen Sie die allowEncryptedValueModifications-Option fest. Weitere Informationen finden Sie unter Verwenden von Massenkopieren mit dem JDBC Driver.

Hinweis

Gehen Sie beim Angeben von AllowEncryptedValueModifications mit Bedacht vor, da diese Option unter Umständen zu einer Beschädigung der Datenbank führen kann, da der Microsoft JDBC-Treiber für SQL Server nicht überprüft, ob die Daten tatsächlich verschlüsselt oder mit demselben Verschlüsselungstyp, Algorithmus und Schlüssel wie die Zielspalte ordnungsgemäß verschlüsselt wurden.

Weitere Informationen

„Immer verschlüsselt“ (Datenbank-Engine)