Usare Always Encrypted con il driver JDBC

Scaricare il driver JDBC

Questa pagina fornisce informazioni su come sviluppare applicazioni Java per usare Always Encrypted con Microsoft JDBC Driver 6.0 (o versioni successive) per SQL Server.

Always Encrypted consente ai client di eseguire la crittografia dei dati sensibili senza mai rivelare i dati o le chiavi di crittografia a SQL Server o al database SQL di Azure. Di tutto ciò si occupa un driver abilitato per Always Encrypted, come Microsoft JDBC Driver 6.0 (o versione successiva) per SQL Server, che esegue in modo trasparente la crittografia e la decrittografia dei dati sensibili nell'applicazione client. Il driver determina i parametri di query corrispondenti alle colonne del database Always Encrypted ed esegue la crittografia dei valori di tali parametri prima di passarli al database. Analogamente, il driver esegue in modo trasparente la decrittografia dei dati, recuperati dalle colonne di database crittografate nei risultati delle query. Per altre informazioni, vedere Always Encrypted (motore di database) e Informazioni di riferimento sull'API Always Encrypted per il driver JDBC.

Prerequisiti

  • Assicurarsi che nel computer di sviluppo sia installato Microsoft JDBC Driver 6.0 (o versione successiva) per SQL Server.
  • Scaricare e installare Java Cryptography Extension (JCE) Unlimited Strenght Jurisdiction Policy Files. Leggere il file Readme incluso nel file ZIP per istruzioni sull'installazione e informazioni dettagliate sui possibili problemi di importazione o esportazione.

Usare archivi chiavi master di colonna

Per crittografare o decrittografare i dati delle colonne crittografate, SQL Server gestisce chiavi di crittografia di colonna. Le chiavi di crittografia di colonna vengono archiviate in forma crittografata nei metadati del database. Ogni chiave di crittografia di colonna ha una chiave master corrispondente che viene usata per crittografare la chiave di crittografia della colonna.

I metadati del database non contengono le chiavi master della colonna. Queste chiavi vengono usate solo dal client. Tuttavia i metadati del database contengono informazioni sulla posizione di archiviazione delle chiavi master della colonna rispetto al client. I metadati del database possono ad esempio indicare che l'archivio chiavi contenente una chiave master di colonna è l'archivio certificati Windows e che il certificato specifico usato per le operazioni di crittografia e decrittografia si trova in un percorso specifico all'interno dell'archivio certificati Windows.

Se il client ha accesso a tale certificato nell'archivio certificati di Windows, può ottenere il certificato. Il certificato può quindi essere usato per decrittografare la chiave di crittografia della colonna. A sua volta la chiave di crittografia può essere usata per decrittografare o crittografare i dati delle colonne crittografate che usano la chiave di crittografia della colonna.

Microsoft JDBC Driver per SQL Server comunica con un archivio chiavi che usa un provider di archivi chiavi master di colonna, ovvero un'istanza di una classe derivata da SQLServerColumnEncryptionKeyStoreProvider.

Usare provider di archivi chiavi master di colonna predefiniti

Microsoft JDBC Driver per SQL Server include i provider dell'archivio chiavi master delle colonne predefiniti seguenti. Alcuni di questi provider sono preregistrati con nomi di provider specifici (usati per cercare il provider) e altri richiedono credenziali aggiuntive o una registrazione esplicita.

Classe Descrizione Nome del provider (ricerca) È preregistrato? Piattaforma
SQLServerColumnEncryptionAzureKeyVaultProvider Provider per un archivio chiavi per Azure Key Vault. AZURE_KEY_VAULT No prima della versione del driver JDBC 7.4.1, ma a partire dal driver JDBC versione 7.4.1. Windows, Linux, macOS
SQLServerColumnEncryptionCertificateStoreProvider Un provider per l'archivio certificati Windows. MSSQL_CERTIFICATE_STORE Finestre
SQLServerColumnEncryptionJavaKeyStoreProvider Provider per l'archivio chiavi Java. MSSQL_JAVA_KEYSTORE Windows, Linux, macOS

Per usare i provider di archivi chiavi preregistrati non è necessario apportare alcuna modifica del codice dell'applicazione, ma tenere presenti gli aspetti seguenti:

  • È necessario assicurarsi che il nome del provider configurato nei metadati della chiave master di colonna sia corretto e che il percorso della chiave master di colonna sia conforme al formato del percorso della chiave valido per un determinato provider. È consigliabile configurare le chiavi usando strumenti come SQL Server Management Studio, che genera automaticamente nomi di provider e percorsi di chiave validi per l'esecuzione dell'istruzione CREATE COLUMN MASTER KEY (Transact-SQL).
  • Verificare che l'applicazione possa accedere alla chiave nell'archivio chiavi. Per questa attività può essere necessario concedere all'applicazione l'accesso alla chiave e/o all'archivio chiavi. A seconda dell'archivio chiavi, possono essere necessari altri passaggi di configurazione specifici dell'archivio. Per usare, ad esempio, SQLServerColumnEncryptionJavaKeyStoreProvider, è necessario specificare il percorso e la password dell'archivio chiavi nelle proprietà di connessione.

Tutti questi provider dell'archivio chiavi sono descritti in modo più dettagliato nelle sezioni seguenti. È sufficiente implementare un solo provider dell'archivio chiavi per usare Always Encrypted.

Usare il provider di Azure Key Vault

Azure Key Vault rappresenta una scelta valida per archiviare e gestire le chiavi master delle colonne per Always Encrypted, soprattutto se l'applicazione è ospitata in Azure. Microsoft JDBC driver per SQL Server include un provider predefinito, SQLServerColumnEncryptionAzureKeyVaultProvider, per le applicazioni che hanno chiavi archiviate in Azure Key Vault. Il nome di questo provider è AZURE_KEY_VAULT.

Nota

Il provider di Azure Key Vault integrato nel driver JDBC supporta insiemi di credenziali e moduli HSM gestiti in Azure Key Vault.

Per usare il provider dell'archivio di Azure Key Vault, uno sviluppatore di applicazioni deve creare l'insieme di credenziali e le chiavi in Azure Key Vault, nonché creare una registrazione dell'app in Microsoft Entra ID (precedentemente noto come Azure Active Directory). All'applicazione registrata devono essere concesse le autorizzazioni Ottieni, Decrittografa, Crittografa, Annulla il wrapping della chiave, Esegui il wrapping della chiave e Verifica nei criteri di accesso definiti per l'insieme di credenziali delle chiavi creato per l'uso con Always Encrypted. Per altre informazioni su come configurare l'insieme di credenziali delle chiavi e creare una chiave master di colonna, vedere Azure Key Vault - Istruzioni dettagliate e Creazione di chiavi master di colonna in Azure Key Vault.

Per il provider di Azure Key Vault, il driver JDBC convalida il percorso della chiave master di colonna rispetto all'elenco di endpoint attendibili. A partire dalla versione 8.2.2, questo elenco è configurabile: creare un file mssql-jdbc.properties nella directory di lavoro dell'applicazione e impostare la proprietà AKVTrustedEndpoints su un elenco con valori delimitati da punti e virgola. Se il valore inizia con un punto e virgola, estende l'elenco predefinito. In caso contrario, sostituisce l'elenco predefinito.

Gli endpoint attendibili predefiniti sono i seguenti:

  • *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+)

Per gli esempi in questa pagina, se sono state create una chiave master di colonna basata su Azure Key Vault e una chiave di crittografia di colonna usando SQL Server Management Studio, lo script T-SQL per ricrearle può essere simile a questo esempio con valori di KEY_PATH e ENCRYPTED_VALUE specifici:

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

Un'applicazione che usa il driver JDBC può usare Azure Key Vault. La sintassi o le istruzioni per questo uso di Azure Key Vault sono cambiate a partire dalla versione 7.4.1 del driver JDBC.

Driver JDBC 7.4.1 o versione successiva

Questa sezione riguarda il driver JDBC versione 7.4.1 o successiva.

Un'applicazione client che usa il driver JDBC può essere configurata per l'uso di Azure Key Vault citando keyVaultProviderClientId=<ClientId>;keyVaultProviderClientKey=<ClientKey> nella stringa di connessione JDBC.

L'esempio seguente fornisce le informazioni di configurazione in una stringa di connessione JDBC.

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

Il driver JDBC crea automaticamente un'istanza di un oggetto SQLServerColumnEncryptionAzureKeyVaultProvider quando queste credenziali sono presenti nelle proprietà di connessione.

Importante

Le proprietà di connessione keyVaultProviderClientId e keyVaultProviderClientKey sono state deprecate a partire dalla versione 8.4.1. Gli utenti sono invitati a usare invece keyStoreAuthentication, KeyStorePrincipalId e KeyStoreSecret.

Versioni del driver JDBC precedenti alla 7.4.1

Questa sezione riguarda le versioni del driver JDBC precedenti alla 7.4.1.

Un'applicazione client che usa il driver JDBC deve creare un'istanza di un oggetto SQLServerColumnEncryptionAzureKeyVaultProvider e quindi registrare l'oggetto con il driver.

SQLServerColumnEncryptionAzureKeyVaultProvider akvProvider = new SQLServerColumnEncryptionAzureKeyVaultProvider(clientID, clientKey);

clientID è l'ID applicazione di una registrazione di app in un tenant di Microsoft Entra. clientKey è una password chiave registrata in tale applicazione, che garantisce all'API l'accesso ad Azure Key Vault.

Dopo aver creato un'istanza di SQLServerColumnEncryptionAzureKeyVaultProvider, l'applicazione deve registrare l'istanza con il driver usando il metodo SQLServerConnection.registerColumnEncryptionKeyStoreProviders(). È consigliabile registrare l'istanza usando il nome di ricerca predefinito, AZURE_KEY_VAULT, che è possibile ottenere tramite l'API SQLServerColumnEncryptionAzureKeyVaultProvider.getName(). L'uso del nome predefinito consente di usare strumenti come SQL Server Management Studio o PowerShell per il provisioning e la gestione delle chiavi Always Encrypted (gli strumenti usano il nome predefinito per generare l'oggetto metadati per la chiave master di colonna). L'esempio seguente illustra la registrazione del provider Azure Key Vault. Per altre informazioni sul metodo SQLServerConnection.registerColumnEncryptionKeyStoreProviders(), vedere Informazioni di riferimento sull'API Always Encrypted per il driver JDBC.

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

Importante

Se si usa il provider dell'archivio chiavi Azure Key Vault, l'implementazione Azure Key Vault del driver JDBC presenta dipendenze dalle librerie seguenti (di GitHub) che devono essere incluse nell'applicazione:

azure-sdk-for-java

microsoft-authentication-library-for-java libraries

Per un esempio di come includere queste dipendenze in un progetto Maven, vedere Download MSAL4J And AKV Dependencies with Apache Maven (Scaricare le dipendenze MSAL4J e AKV con Apache Maven)

Usare l'autenticazione di Azure Key Vault con le identità gestite

A partire dal driver JDBC 8.4.1 è stato aggiunto il supporto per l'autenticazione in Azure Key Vault tramite identità gestite.

È possibile usare le identità gestite per eseguire l'autenticazione in Azure Key Vault se l'applicazione è ospitata in Azure. Ciò elimina la necessità di fornire ed esporre le credenziali nel codice.

Proprietà di connessione per l'autenticazione di Azure Key Vault con le identità gestite

Per il driver JDBC 8.4.1 e versioni successive, sono state introdotte le seguenti proprietà di connessione:

Proprietà di connessione Possibile associazione di valori 1 Possibile associazione di valori 2 Possibile associazione di valori 3
keyStoreAuthentication KeyVaultClientSecret KeyVaultManagedIdentity JavaKeyStorePassword
keyStorePrincipalId <ID client dell'applicazione Microsoft Entra> <ID oggetto applicativo Microsoft Entra> (facoltativo) n/d
keyStoreSecret <Segreto client dell'applicazione Microsoft Entra> n/d <Segreto/password per l'archivio chiavi Java>

Negli esempi seguenti viene illustrato come usare le proprietà di connessione in una stringa di connessione.

Usare un'identità gestita per l'autenticazione in Azure Key Vault

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

Usare un'identità gestita e l'ID entità di sicurezza per l'autenticazione in Azure Key Vault

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

Usare clientId e clientSecret per l'autenticazione in Azure Key Vault

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

È consigliabile usare queste proprietà di connessione al posto dell'API SQLServerColumnEncryptionAzureKeyVaultProvider per specificare il tipo di autenticazione usato per gli archivi chiavi.

Le proprietà di connessione keyVaultProviderClientId e keyVaultProviderClientKey aggiunte in precedenza sono deprecate e sono state sostituite dalle proprietà di connessione descritte in precedenza.

Per informazioni su come configurare le identità gestite, vedere Configurare identità gestite per risorse di Azure in una macchina virtuale tramite il portale di Azure.

Usare il provider dell'archivio certificati Windows

È possibile usare SQLServerColumnEncryptionCertificateStoreProvider per archiviare chiavi master di colonna nell'archivio certificati Windows. Usare la procedura guidata Always Encrypted di SQL Server Management Studio (SSMS) o altri strumenti supportati per creare le definizioni delle chiavi master della colonna e della chiave di crittografia della colonna nel database. È possibile usare la stessa procedura guidata per generare un certificato autofirmato nell'archivio certificati di Windows, che può essere usato come chiave master della colonna per i dati Always Encrypted. Per altre informazioni sulla sintassi T-SQL della chiave master della colonna e della chiave di crittografia della colonna, vedere le istruzioni CREATE COLUMN MASTER KEY e CREATE COLUMN ENCRYPTION KEY rispettivamente.

Il nome di SQLServerColumnEncryptionCertificateStoreProvider è MSSQL_CERTIFICATE_STORE ed è possibile eseguire query per il nome tramite l'API getName() API dell'oggetto provider. Viene registrato automaticamente dal driver e può essere usato senza alcuna modifica dell'applicazione.

Per gli esempi in questa pagina, se sono state create una chiave master di colonna basata sull'archivio certificati Windows e una chiave di crittografia di colonna usando SQL Server Management Studio, lo script T-SQL per ricrearle può essere simile a questo esempio con valori di KEY_PATH e ENCRYPTED_VALUE specifici:

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

Importante

Mentre gli altri provider di archivi chiavi in questo articolo sono disponibili su tutte le piattaforme supportate dal driver, l'implementazione di SQLServerColumnEncryptionCertificateStoreProvider del driver JDBC è disponibile solo nei sistemi operativi Windows. Ha una dipendenza da mssql-jdbc_auth-<version>-<arch>.dll, disponibile nel pacchetto del driver. Per usare questo provider, copiare il file mssql-jdbc_auth-<version>-<arch>.dll in una directory nel percorso di sistema di Windows nel computer in cui è installato il driver JDBC. In alternativa è possibile impostare la proprietà di sistema java.library.path in modo da specificare la directory di mssql-jdbc_auth-<version>-<arch>.dll. Se si esegue Java Virtual Machine (JVM) a 32 bit, usare il file mssql-jdbc_auth-<version>-x86.dll nella cartella x86, anche se la versione del sistema operativo è x64. Se si esegue JVM a 64 bit in un processore x64, usare i file mssql-jdbc_auth-<version>-x64.dll nella cartella x64. Se, ad esempio, si usa JVM a 32 bit e il driver JDBC è installato nella directory predefinita, è possibile specificare il percorso della DLL tramite l'argomento della macchina virtuale seguente quando l'applicazione Java viene avviata: -Djava.library.path=C:\Microsoft JDBC Driver <version> for SQL Server\sqljdbc_<version>\enu\auth\x86

Usare il provider dell'archivio chiavi Java

Il driver JDBC è disponibile in un’implementazione predefinita del provider dell’archivio chiavi per l’archivio chiavi Java. Se la proprietà della stringa di connessione keyStoreAuthentication è presente nella stringa di connessione ed è impostata su JavaKeyStorePassword, il driver crea automaticamente un'istanza del provider e ne esegue la registrazione per l'archivio chiavi Java. Il nome del provider dell'archivio chiavi Java è MSSQL_JAVA_KEYSTORE. È anche possibile eseguire query per questo nome tramite l'API SQLServerColumnEncryptionJavaKeyStoreProvider.getName().

Sono disponibili tre proprietà della stringa di connessione che consentono a un'applicazione client di specificare le credenziali necessarie al driver per l'autenticazione nell'archivio chiavi Java. Il driver Inizializza il provider in base ai valori di queste tre proprietà nella stringa di connessione.

keyStoreAuthentication: Identifica l'archivio chiavi Java da usare. Con Microsoft JDBC Driver 6.0 e versioni successive per SQL Server è possibile eseguire l'autenticazione all'archivio chiavi Java solo tramite questa proprietà. Per l'archivio chiavi Java il valore di questa proprietà deve essere JavaKeyStorePassword.

keyStoreLocation: Percorso del file dell'archivio chiavi Java in cui è archiviata la chiave master di colonna. Il percorso include il nome file dell'archivio chiavi.

keyStoreSecret: Segreto/password da usare per l'archivio chiavi e la chiave. Per usare l'archivio chiavi Java, la password dell'archivio chiavi e la password della chiave devono essere uguali.

L'esempio seguente illustra come specificare queste credenziali nella stringa di connessione:

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

È anche possibile ottenere o definire queste impostazioni usando l'oggetto SQLServerDataSource. Per altre informazioni, vedere Informazioni di riferimento sull'API Always Encrypted per il driver JDBC.

Il driver JDBC crea automaticamente un'istanza di SQLServerColumnEncryptionJavaKeyStoreProvider quando queste credenziali sono presenti nelle proprietà di connessione.

Creazione di una chiave master della colonna (CMK) per l'archivio chiavi Java

È possibile usare SQLServerColumnEncryptionJavaKeyStoreProvider con i tipi di archivio chiavi JKS o PKCS12. Per creare o importare una chiave da usare con questo provider, usare l'utilità keytool Java. La chiave deve avere la stessa password dell'archivio chiavi. L'esempio seguente illustra come creare una chiave pubblica e la chiave privata associata usando l'utilità keytool:

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

Questo comando crea una chiave pubblica e ne esegue il wrapping in un certificato autofirmato X.509, archiviato nell'archivio chiavi keystore.jks insieme alla chiave privata associata. Questa voce nell'archivio chiavi è identificata dall'alias AlwaysEncryptedKey.

L'esempio seguente dello stesso comando usa un tipo di archivio PKCS12:

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

Se l'archivio chiavi è di tipo PKCS12, l'utilità keytool non richiede una password della chiave e tale password deve essere specificata con l'opzione -keypass, perché SQLServerColumnEncryptionJavaKeyStoreProvider richiede che l'archivio chiavi e la chiave abbiano la stessa password.

È inoltre possibile esportare un certificato dall'archivio certificati di Windows in formato pfx e usarlo con SQLServerColumnEncryptionJavaKeyStoreProvider. Il certificato esportato può anche essere importato nell'archivio chiavi Java come tipo di archivio chiavi JKS.

Dopo aver creato la voce keytool, creare i metadati della chiave master di colonna nel database, che richiede il nome del provider dell'archivio chiavi e il percorso della chiave. Per altre informazioni su come creare i metadati della chiave master della colonna, vedere l'istruzione CREATE COLUMN MASTER KEY. Per SQLServerColumnEncryptionJavaKeyStoreProvider il percorso della chiave è solo l'alias della chiave e il nome di SQLServerColumnEncryptionJavaKeyStoreProvider è MSSQL_JAVA_KEYSTORE. È anche possibile eseguire una query per questo nome usando l'API pubblica getName() della classe SQLServerColumnEncryptionJavaKeyStoreProvider.

La sintassi T-SQL per creare la chiave master di colonna è la seguente:

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

Per 'AlwaysEncryptedKey' creato in precedenza, la definizione della chiave master della colonna sarà:

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

Nota

La funzionalità incorporata di SQL Server Management Studio non è in grado di creare definizioni chiave master della colonna per l'archivio chiavi Java. È necessario usare i comandi T-SQL a livello di codice.

Creare una chiave di crittografia di colonna per l'archivio chiavi Java

Non è possibile usare SQL Server Management Studio o altri strumenti per creare chiavi di crittografia della colonna usando le chiavi master della colonna nell'archivio chiavi Java. L'applicazione client deve creare la chiave di crittografia di colonna a livello di codice usando la classe SQLServerColumnEncryptionJavaKeyStoreProvider. Per altre informazioni, vedere Usare i provider di archivi chiavi master di colonna per il provisioning di chiavi a livello di codice.

Implementazione di un provider personalizzato di archivio delle chiavi master delle colonne

Se si vogliono archiviare chiavi master di colonna in un archivio chiavi non supportato da un provider esistente, è possibile implementare un provider personalizzato estendendo la classe SQLServerColumnEncryptionKeyStoreProvider e registrando il provider con uno dei metodi seguenti:

  • SQLServerConnection.registerColumnEncryptionKeyStoreProviders
  • SQLServerConnection.registerColumnEncryptionKeyStoreProvidersOnConnection (aggiunto in JDBC versione 10.2)
  • SQLServerStatement.registerColumnEncryptionKeyStoreProvidersOnStatement (aggiunto in JDBC versione 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
    }
}

Registrare il provider con SQLServerConnection.registerColumnEncryptionKeyStoreProviders:

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

Precedenza della cache della chiave di crittografia della colonna

Questa sezione si applica al driver JDBC versione 10.2 e successive.

Le chiavi di crittografia di colonna decrittografate da provider di archivi chiavi personalizzati registrati in un'istanza di connessione o di istruzione non verranno memorizzate nella cache da Microsoft JDBC Driver per SQL Server. I provider di archivi chiavi personalizzati devono implementare il proprio meccanismo di memorizzazione nella cache delle chiavi di crittografia di colonna.

A partire dalla versione 10.2, SQLServerColumnEncryptionAzureKeyVaultProvider ha un'implementazione personalizzata per la memorizzazione nella cache delle chiavi di crittografia di colonna. Se registrate in un'istanza di connessione o di istruzione, le chiavi di crittografia di colonna decrittografate da un'istanza di SQLServerColumnEncryptionAzureKeyVaultProvider verranno cancellate quando l'istanza non rientra più nell'ambito:

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

Nota

La memorizzazione nella cache delle chiavi di crittografia di colonna implementata da provider di archivi chiavi personalizzati verrà disabilitata dal driver se l'istanza del provider dell'archivio chiavi viene registrata nel driver a livello globale usando il metodo SQLServerConnection.registerColumnEncryptionKeyStoreProviders. Qualsiasi implementazione della memorizzazione nella cache delle chiavi di crittografia di colonna deve fare riferimento al valore della durata (TTL) prima di memorizzare nella cache una chiave di crittografia di colonna e non deve eseguire la memorizzazione nella cache se il valore è zero. In questo modo, si evitano casi di memorizzazione nella cache duplicati e la possibile confusione degli utenti quando tentano di configurare la memorizzazione nella cache delle chiavi. Il valore di durata (TTL) per la cache può essere impostato con il metodo SQLServerColumnEncryptionKeyStoreProvider.setColumnEncryptionCacheTtl.

Registrare un provider di archivi chiavi master di colonna personalizzato

Questa sezione si applica al driver JDBC versione 10.2 e successive.

I provider di archivi chiavi master personalizzati possono essere registrati con il driver a tre livelli diversi. Di seguito viene indicata la precedenza delle tre registrazioni:

  • Viene controllata la registrazione per istruzione, se non è vuota.
  • Se la registrazione per istruzione è vuota, viene controllata la registrazione per connessione, se non è vuota.
  • Se la registrazione per connessione è vuota, viene controllata la registrazione globale.

Dopo aver individuato un provider dell'archivio chiavi a livello di registrazione, il driver NON eseguirà il fallback alle altre registrazioni per cercare un provider. Se i provider sono registrati, ma il provider appropriato non viene trovato a un determinato livello, viene generata un'eccezione contenente solo i provider registrati nella registrazione controllata.

Il provider dell'archivio chiavi master di colonna predefinito disponibile per l'archivio certificati Windows è preregistrato. Il provider dell'archivio chiavi di Microsoft Java e il provider dell'archivio chiavi di Azure Key Vault possono essere preregistrati in modo implicito con un'istanza di connessione se le credenziali vengono fornite in anticipo.

I tre livelli di registrazione supportano scenari diversi quando si eseguono query su dati crittografati. È possibile usare il metodo appropriato per fare in modo che un utente di un'applicazione possa accedere ai dati in formato testo non crittografato. L'accesso ai dati non crittografati avviene solo se l'utente è in grado di fornire la chiave master di colonna richiesta, eseguendo l'autenticazione nell'archivio chiavi che contiene la chiave master di colonna.

Le applicazioni che condividono un'istanza di SQLServerConnection tra più utenti possono usare SQLServerStatement.registerColumnEncryptionKeyStoreProvidersOnStatement. Ogni utente deve registrare un provider di archivi chiavi in un'istanza di SQLServerStatement prima di eseguire una query per accedere a una colonna crittografata. Se il provider di archivi chiavi è in grado di accedere alla chiave master di colonna richiesta nell'archivio chiavi che usa le credenziali specificate dell'utente, la query riuscirà.

Le applicazioni che creano un'istanza di SQLServerConnection per ogni utente possono usare SQLServerConnection.registerColumnEncryptionKeyStoreProvidersOnConnection. I provider di archivi chiavi registrati con questo metodo possono essere usati dalla connessione per qualsiasi query che accede ai dati crittografati.

I provider di archivi chiavi registrati con SQLServerConnection.registerColumnEncryptionKeyStoreProviders useranno l'identità specificata dall'applicazione durante l'autenticazione nell'archivio chiavi.

L'esempio seguente mostra la precedenza dei provider di archivi chiavi master di colonna personalizzati registrati in un'istanza di connessione:

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

L'esempio seguente mostra la precedenza dei provider di archivi chiavi master di colonna personalizzati registrati in un'istanza di istruzione:

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

Usare i provider di archivi chiavi master di colonna per il provisioning delle chiavi a livello di codice

Per accedere alle colonne crittografate, Microsoft JDBC Driver per SQL Server trova in modo trasparente e chiama il provider di archivi chiavi master di colonna appropriato per decrittografare le chiavi di crittografia delle colonne. In genere, il codice dell'applicazione normale non chiama direttamente i provider di archivio delle chiavi master delle colonne. È tuttavia possibile creare un'istanza e chiamare un provider a livello di codice per il provisioning e la gestione delle chiavi Always Encrypted. Questo passaggio può essere eseguito per generare una chiave di crittografia della colonna crittografata e decrittografare una chiave di crittografia della colonna, ad esempio nell'ambito della rotazione della chiave master della colonna. Per altre informazioni, vedere Panoramica della gestione delle chiavi per Always Encrypted.

Se si usa un provider dell'archivio chiavi personalizzato, potrebbe essere necessario implementare gli strumenti di gestione delle chiavi. Per usare chiavi archiviate nell'archivio certificati Windows o in Azure Key Vault, è possibile usare strumenti esistenti, ad esempio SQL Server Management Studio o PowerShell, per gestire le chiavi ed effettuarne il provisioning. Per usare chiavi archiviate nell'archivio chiavi Java, è necessario effettuare il provisioning delle chiavi a livello di codice. L'esempio seguente illustra come usare la classe SQLServerColumnEncryptionJavaKeyStoreProvider per crittografare la chiave con una chiave archiviata nell'archivio chiavi Java.

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

Abilitazione di Always Encrypted per le query dell'applicazione

Il modo più semplice per abilitare la crittografia dei parametri e la decrittografia dei risultati delle query sulle colonne crittografate consiste nell'impostare il valore della parola chiave della stringa di connessione columnEncryptionSetting su Enabled.

Di seguito è riportato un esempio di stringa di connessione che abilita Always Encrypted nel driver JDBC:

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

Il codice seguente è un esempio equivalente che usa l'oggetto 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 può anche essere abilitato per le singole query. Per altre informazioni, vedere Controllo dell'impatto di Always Encrypted sulle prestazioni. Perché la crittografia o la decrittografia abbiano esito positivo, non è sufficiente l'abilitazione di Always Encrypted. È necessario anche assicurarsi che:

  • L'applicazione dispone delle autorizzazioni del database VIEW ANY COLUMN MASTER KEY DEFINITION e VIEW ANY COLUMN ENCRYPTION KEY DEFINITION, necessarie per accedere ai metadati sulle chiavi Always Encrypted nel database. Per informazioni dettagliate, vedere Autorizzazioni in Always Encrypted (motore di database).
  • L'applicazione può accedere alla chiave master della colonna che protegge le chiavi di crittografia di colonna, le quali crittografano le colonne di database sottoposte a query. Per usare il provider dell'archivio chiavi Java è necessario fornire credenziali aggiuntive nella stringa di connessione. Per altre informazioni, vedere Usare il provider dell'archivio chiavi Java.

Configurazione della modalità di invio dei valori java.sql.Time al server

La proprietà di connessione sendTimeAsDatetime viene usata per configurare la modalità di invio del valore java.sql.Time al server. Se è impostata su false, il valore dell'ora viene inviato come tipo time di SQL Server. Se è impostata su true, il valore dell'ora viene inviato come tipo datetime. Se una colonna time è crittografata, la proprietà sendTimeAsDatetime deve essere false, perché le colonne crittografate non supportano la conversione da time a datetime. Si noti anche che questa proprietà è true per impostazione predefinita, quindi per usare le colonne time crittografate è necessario impostarla su false. In caso contrario il driver genererà un'eccezione. A partire dalla versione 6.0 del driver, la classe SQLServerConnection ha due metodi per configurare il valore di questa proprietà a livello di codice:

  • public void setSendTimeAsDatetime(boolean sendTimeAsDateTimeValue)
  • public boolean getSendTimeAsDatetime()

Per altre informazioni su questa proprietà, vedere Configurazione della modalità di invio dei valori java.sql.Time al server.

Configurazione della modalità di invio dei valori String al server

La proprietà di connessione sendStringParametersAsUnicode viene usata per configurare la modalità di invio dei valori String a SQL Server. Se è impostata su True, i parametri String vengono inviati al server in formato Unicode. Se è impostata su false, i parametri String vengono inviati in formato non Unicode, ad esempio come ASCII o MBCS anziché come Unicode. Il valore predefinito di questa proprietà è True. Quando la funzionalità Always Encrypted è abilitata e una colonna char/varchar/varchar(max) è crittografata, il valore di sendStringParametersAsUnicode deve essere impostato su false. Se questa proprietà è impostata su true, il driver genera un'eccezione durante la decrittografia dei dati da una colonna char/varchar/varchar(max) crittografata che include caratteri Unicode. Per altre informazioni su questa proprietà, vedere Impostazione delle proprietà di connessione.

Importante

Se il valore di sendStringParametersAsUnicode è impostato su true e i dati Unicode vengono inseriti in una colonna char/varchar crittografata con Always Encrypted, può verificarsi una perdita di dati senza che venga segnalato un errore. La perdita di dati può essere rilevata solo quando si cerca di decrittografare i dati dopo la lettura dal server. Può venire generato un errore simile al seguente: 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'..

È importante usare tipi di dati di colonna corretti e specificare il tipo di dati appropriato per i parametri quando si inseriscono dati crittografati. Se sono previsti dati Unicode, usare colonne nchar/nvarchar e metodi setNString(). Il server non può eseguire conversioni di dati implicite e ha una capacità limitata di rilevare gli errori di dati quando è abilitata la funzionalità Always Encrypted.

Recupero e modifica dei dati nelle colonne crittografate

Dopo aver abilitato Always Encrypted per le query dell'applicazione, è possibile usare le API JDBC standard per recuperare o modificare i dati nelle colonne di database crittografate. Se l'applicazione ha le autorizzazioni necessarie per il database e può accedere alla chiave master di colonna, il driver crittograferà tutti i parametri di query che puntano a colonne crittografate e decrittograferà i dati recuperati dalle colonne crittografate.

Se Always Encrypted non è abilitato, le query con parametri destinati alle colonne crittografate avranno esito negativo. Le query possono comunque recuperare i dati dalle colonne crittografate, a condizione che non presentino parametri destinati alle colonne crittografate. Il driver non cercherà tuttavia di decrittografare i valori recuperati dalle colonne crittografate e l'applicazione riceverà dati crittografati binari, sotto forma di matrici di byte.

La tabella seguente riepiloga il comportamento delle query, a seconda che la funzionalità Always Encrypted sia abilitata o meno:

Caratteristica della query Always Encrypted è abilitato e l'applicazione può accedere alle chiavi e ai metadati delle chiavi Always Encrypted è abilitato e l'applicazione non può accedere alle chiavi o ai metadati delle chiavi Always Encrypted è disabilitato
Query con parametri destinati alle colonne crittografate. I valori dei parametri vengono crittografati in modo trasparente. Error Error
Query che recuperano dati dalle colonne crittografate, senza parametri destinati alle colonne crittografate. I risultati delle colonne vengono decrittografati in modo trasparente. L'applicazione riceve valori di testo non crittografato dei tipi di dati JDBC corrispondenti ai tipi di SQL Server configurati per le colonne crittografate. Error I risultati delle colonne crittografate non vengono decrittografati. L'applicazione riceve i valori crittografati come matrici di byte (byte[]).

Esempi di inserimento e recupero di dati crittografati

Gli esempi seguenti illustrano il recupero e la modifica dei dati nelle colonne crittografate. Gli esempi presuppongono la presenza della tabella di destinazione con lo schema seguente e le colonne crittografate SSN e BirthDate. Se sono state configurate una chiave master di colonna denominata "MyCMK" e una chiave di crittografia di colonna denominata "MyCEK", come descritto nelle sezioni precedenti per i provider di archivi chiavi, è possibile creare la tabella usando questo script:

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

Per ogni esempio di codice Java è necessario inserire il codice specifico dell'archivio chiavi nel percorso indicato.

Per usare un provider dell'archivio chiavi di 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;";

Per usare un provider dell'archivio chiavi dell'archivio certificati Windows:

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

Per usare un provider dell'archivio chiavi di Java:

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

Esempio di inserimento dei dati

Questo esempio illustra come inserire una riga nella tabella Patients. Si noti quanto segue:

  • Il codice di esempio non contiene alcun elemento specifico per la crittografia. Il provider di dati Microsoft JDBC Driver per SQL Server rileva automaticamente e crittografa i parametri destinati alle colonne crittografate. In questo modo la crittografia diventa trasparente per l'applicazione.
  • I valori inseriti nelle colonne di database, incluse quelle crittografate, vengono passati come parametri con SQLServerPreparedStatement. I parametri sono facoltativi quando si inviano valori a colonne non crittografate (sebbene siano comunque consigliati per prevenire attacchi SQL injection), mentre sono obbligatori per i valori destinati a colonne crittografate. Se i valori inseriti nelle colonne crittografate sono stati passati come valori letterali incorporati nell'istruzione della query, la query ha esito negativo, perché il driver non riesce a determinare i valori delle colonne di destinazione crittografate e quindi non crittograferà i valori. Di conseguenza, il server li rifiuterà come incompatibili con le colonne crittografate.
  • Tutti i valori stampati dal programma saranno in testo non crittografato perché Microsoft JDBC Driver per SQL Server possa decrittografare in modo trasparente i dati recuperati dalle colonne crittografate.
  • Se si esegue una ricerca con una clausola WHERE, il valore usato nella clausola WHERE deve essere passato come parametro, così che il driver possa crittografarlo in modo trasparente prima di inviarlo al database. Nell'esempio seguente SSN viene passato come parametro, ma LastName viene passato come valore letterale, perché LastName non è crittografato.
  • Il metodo setter usato per il parametro destinato alla colonna SSN è setString(), che esegue il mapping al tipo di dati char/varchar di SQL Server. Se per questo parametro viene usato il metodo setter setNString(), che esegue il mapping a nchar/nvarchar, la query ha esito negativo in quanto Always Encrypted non supporta le conversioni da valori nchar/nvarchar crittografati a valori char/varchar crittografati.
// <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();
}

Esempio di recupero di dati di testo non crittografato

L'esempio seguente illustra come filtrare i dati in base ai valori crittografati e recuperare i dati in testo non crittografato da colonne crittografate. Si noti quanto segue:

  • Il valore usato nella clausola WHERE per filtrare la colonna SSN deve essere passato come parametro, così che Microsoft JDBC Driver per SQL Server possa crittografarlo in modo trasparente prima di inviarlo al database.
  • Tutti i valori stampati dal programma saranno in testo non crittografato perché Microsoft JDBC Driver per SQL Server possa decrittografare in modo trasparente i dati recuperati dalle colonne SSN e BirthDate.

Nota

Se le colonne sono crittografate tramite crittografia deterministica, le query possono eseguire confronti di uguaglianza su di esse. Per altre informazioni, vedere Crittografia deterministica.

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

Esempio di recupero di dati crittografati

Se Always Encrypted non è abilitato, una query può comunque recuperare dati dalle colonne crittografate, a condizione che non presenti parametri destinati alle colonne crittografate.

L'esempio seguente illustra come recuperare dati crittografati binari da colonne crittografate. Si noti quanto segue:

  • Considerato che Always Encrypted non è abilitato nella stringa di connessione, la query restituirà i valori crittografati di SSN e BirthDate come matrici di byte. Il programma converte i valori in stringhe.
  • Una query che recupera dati dalle colonne crittografate con Always Encrypted disabilitato può avere parametri, a condizione che nessuno dei parametri sia destinato a una colonna crittografata. La query seguente filtra in base alla colonna LastName, che non è crittografata nel database. Se la query avesse filtrato per SSN o data di nascita, avrebbe avuto esito negativo.
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();
}

Come evitare i problemi comuni quando si eseguono query su colonne crittografate

Questa sezione descrive categorie di errori comuni che si verificano quando si eseguono query su colonne crittografate da applicazioni Java e contiene alcune linee guida su come correggere tali errori.

Errori di conversione dei tipi di dati non supportati

Always Encrypted supporta alcune conversioni per i tipi di dati crittografati. Per l'elenco dettagliato delle conversioni dei tipi supportati, vedere Always Encrypted (motore di database). Ecco cosa fare per evitare errori di conversione dei tipi di dati. Assicurati che:

  • Vengano usati i metodi setter appropriati quando si passano valori per i parametri destinati alle colonne crittografate. Verificare che il tipo di dati SQL Server del parametro corrisponda esattamente al tipo della colonna di destinazione oppure che sia supportata una conversione del tipo di dati di SQL Server del parametro al tipo di destinazione della colonna. Sono stati aggiunti metodi API alle classi SQLServerPreparedStatement, SQLServerCallableStatement e SQLServerResultSet per il passaggio di parametri corrispondenti a tipi di dati di SQL Server specifici. Per un elenco completo delle nuove API, vedere Informazioni di riferimento sull'API Always Encrypted per il driver JDBC. Se non si rispettano le definizioni dei tipi di dati, è probabile che si verifichino errori di conflitto del tipo di operando. Di seguito sono riportati alcuni esempi di rettifiche che possono essere necessarie quando si usa Always Encrypted:

    • È possibile usare il metodo setTimestamp() per passare un parametro a una colonna datetime o datetime2 non crittografata. Quando tuttavia una colonna è crittografata, è necessario usare il metodo esatto che rappresenta il tipo della colonna nel database. Usare setTimestamp() per passare valori a una colonna datetime2 crittografata e setDateTime() per passare valori a una colonna datetime crittografata.
    • È possibile usare il metodo setBinary() per passare un parametro a una colonna varbinary(max) o binary non crittografata. Per impostazione predefinita, il driver usa il tipo di dati BINARY per i parametri setBinary() e il server può convertire in modo implicito i dati da inserire in una colonna varbinary(max). Quando tuttavia una colonna varbinary(max) è crittografata, è necessario specificare un tipo più esatto per i dati dei parametri. Esempio: preparedStatement.setObject(1, binaryData, java.sql.JDBCType.LONGVARBINARY)
  • La precisione e la scala dei parametri destinati alle colonne dei tipi di dati di SQL Server decimali e numerici sono uguali a quelle configurate per la colonna di destinazione. Sono stati aggiunti metodi API alle classi SQLServerPreparedStatement, SQLServerCallableStatement e SQLServerResultSet che accettano precisione e scala insieme ai valori dei dati per parametri/colonne che rappresentano i tipi di dati decimale e numerico. Per un elenco completo delle API nuove/con overload, vedere Informazioni di riferimento sull'API Always Encrypted per il driver JDBC.

    • Ad esempio, quando si usa BigDecimal di Java come tipo di parametro destinato a una colonna decimale specificata nel database, è necessario fornire la precisione e la scala al metodo setBigDecimal() o al metodo setValue(). Se non si specifica la precisione e la scala corrette, è possibile che si verifichi un errore simile al seguente:
    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')
    
  • La scala/precisione in frazioni di secondo dei parametri destinati alle colonne con tipi di dati di SQL Server datetime2, datetimeoffset o time non sia maggiore della scala/precisione in frazioni di secondo per la colonna di destinazione nelle query che modificano i valori di tale colonna. Sono stati aggiunti metodi API alle classi SQLServerPreparedStatement, SQLServerCallableStatement e SQLServerResultSet che accettano scala/precisione in frazioni di secondo insieme ai valori dei dati per i parametri che rappresentano questi tipi di dati. Per un elenco completo delle API nuove/con overload, vedere Informazioni di riferimento sull'API Always Encrypted per il driver JDBC.

Errori causati da proprietà di connessione non corrette

Questa sezione descrive come configurare correttamente le impostazioni di connessione per usare i dati Always Encrypted. Poiché i tipi di dati crittografati supportano conversioni limitate, le impostazioni di connessione sendTimeAsDatetime e sendStringParametersAsUnicode richiedono una configurazione appropriata per usare le colonne crittografate. Assicurati che:

Errori causati dal passaggio di testo non crittografato anziché di valori crittografati

Qualsiasi valore destinato a una colonna crittografata deve essere crittografato all'interno dell'applicazione. Il tentativo di inserire, modificare o filtrare in base a un valore di testo non crittografato in una colonna crittografata genererà un errore simile al seguente:

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'

Per evitare tali errori, verificare che:

  • Always Encrypted sia abilitato per le query dell'applicazione destinate alle colonne crittografate (per la stringa di connessione o per una query specifica).
  • Per inviare i dati destinati alle colonne crittografate vengano usate istruzioni e parametri preparati. L'esempio seguente illustra una query che filtra in modo errato in base a un valore letterale o costante in una colonna crittografata (SSN), anziché passare il valore letterale all'interno sotto forma di parametro. Questa query avrà esito negativo:
ResultSet rs = connection.createStatement().executeQuery("SELECT * FROM Customers WHERE SSN='795-73-9838'");

Forzare la crittografia sui parametri di input

La funzionalità Forza crittografia applica la crittografia di un parametro con Always Encrypted. Se questa funzionalità viene usata e SQL Server indica al driver che non è necessario crittografare il parametro, la query che usa il parametro avrà esito negativo. Questa proprietà fornisce protezione aggiuntiva contro attacchi alla sicurezza in cui un'istanza di SQL Server compromessa fornisce al client metadati di crittografia non corretti, causando la divulgazione dei dati. I metodi set* nelle classi SQLServerPreparedStatement e SQLServerCallableStatement e i metodi update* nella classe SQLServerResultSet sono sottoposti a overload per accettare un argomento booleano che consenta di specificare l'impostazione Forza crittografia. Se il valore di questo argomento è false, il driver non forza la crittografia sui parametri. Se Forza crittografia è true, il parametro di query viene inviato solo se la colonna di destinazione è crittografata e Always Encrypted è abilitato per la connessione o per l'istruzione. L'uso di questa proprietà offre un livello di sicurezza aggiuntivo, facendo in modo che il driver non invii erroneamente i dati a SQL Server in formato testo non crittografato quando è invece previsto che sia crittografato.

Per altre informazioni sui metodi SQLServerPreparedStatement e SQLServerCallableStatement sottoposti a overload con l'impostazione Forza crittografia, vedere Informazioni di riferimento sull'API Always Encrypted per il driver JDBC

Controllo dell'impatto di Always Encrypted sulle prestazioni

Considerato che Always Encrypted è una tecnologia di crittografia lato client, la maggior parte del sovraccarico delle prestazioni si verifica sul lato client, non nel database. A parte il costo delle operazioni di crittografia e decrittografia, le altre origini di sovraccarico delle prestazioni sul lato client sono le seguenti:

  • Aggiunta di round trip al database per recuperare metadati per i parametri di query.
  • Chiamate a un archivio delle chiavi master delle colonne per accedere alla chiave master di una colonna.

Questa sezione descrive le ottimizzazioni delle prestazioni predefinite in Microsoft JDBC Driver per SQL Server e come controllare l'impatto sulle prestazioni esercitato dai due fattori descritti.

Controllo dei round trip per recuperare i metadati per i parametri di query

Se la funzionalità Always Encrypted è abilitata per una connessione, per impostazione predefinita il driver chiama sys.sp_describe_parameter_encryption per ogni query con parametri, passando l'istruzione di query (senza i valori dei parametri) al database. sys.sp_describe_parameter_encryption analizza l'istruzione di query per verificare se sono presenti parametri da crittografare e, in tal caso, restituisce per ognuno le informazioni relative alla crittografia che consentono al driver di crittografare i valori dei parametri. Il comportamento descritto garantisce all'applicazione client un elevato livello di trasparenza. A condizione che i valori destinati alle colonne crittografate vengano passati al driver tramite parametri. non è necessario che l'applicazione (e lo sviluppatore dell'applicazione) sappia quali query accedono alle colonne crittografate.

Impostazione di Always Encrypted a livello di query

Per controllare l'impatto sulle prestazioni del recupero dei metadati di crittografia per le query con parametri, è possibile abilitare Always Encrypted per singole query, anziché l'impostare la funzionalità per la connessione. In questo modo, è possibile assicurarsi che sys.sp_describe_parameter_encryption venga richiamato solo per le query con parametri destinati a colonne crittografate. Si noti, tuttavia, che in tal modo si riduce la trasparenza della crittografia. Se si modificano le proprietà di crittografia delle colonne di database, potrebbe essere necessario modificare il codice dell'applicazione per allinearlo alle modifiche dello schema.

Per controllare il comportamento Always Encrypted delle singole query è necessario configurare singoli oggetti istruzione passando un'enumerazione SQLServerStatementColumnEncryptionSetting, che indica come verranno inviati e ricevuti i dati durante la lettura e la scrittura di colonne crittografate per l'istruzione specifica. Di seguito sono riportate alcune linee guida utili:

  • Se la maggior parte delle query che un'applicazione client invia alla connessione del database accede alle colonne crittografate, seguire queste linee guida:

    • Impostare la parola chiave della stringa di connessione columnEncryptionSetting su Enabled.
    • Impostare SQLServerStatementColumnEncryptionSetting.Disabled per singole query che non accedono ad alcuna colonna crittografata. Questa impostazione disabilita sia la chiamata di sys.sp_describe_parameter_encryption sia la decrittografia dei valori nel set di risultati.
    • Impostare SQLServerStatementColumnEncryptionSetting.ResultSet per singole query che non includono parametri che richiedono la crittografia, ma recuperano dati da colonne crittografate. Questa impostazione disabilita la chiamata di sys.sp_describe_parameter_encryption e la crittografia dei parametri. La query eseguirà la decrittografia dei risultati provenienti dalle colonne crittografate.
  • Se la maggior parte delle query che un'applicazione client invia alla connessione del database non accede alle colonne crittografate, seguire queste linee guida:

    • Impostare la parola chiave della stringa di connessione columnEncryptionSetting su Disabled.
    • Impostare SQLServerStatementColumnEncryptionSetting.Enabled per le singole query che presentano parametri che devono essere crittografati. Questa impostazione abilita sia la chiamata di sys.sp_describe_parameter_encryption sia la decrittografia dei risultati di query recuperati da colonne crittografate.
    • Impostare SQLServerStatementColumnEncryptionSetting.ResultSet per le query che non includono parametri che richiedono la crittografia, ma recuperano dati da colonne crittografate. Questa impostazione disabilita la chiamata di sys.sp_describe_parameter_encryption e la crittografia dei parametri. La query eseguirà la decrittografia dei risultati provenienti dalle colonne crittografate.

Non è possibile usare le impostazioni SQLServerStatementColumnEncryptionSetting per ignorare la crittografia e ottenere l'accesso ai dati in formato testo non crittografato. Per altre informazioni sulla configurazione della crittografia di colonna in un'istruzione, vedere Informazioni di riferimento sull'API Always Encrypted per il driver JDBC.

Nell'esempio seguente la funzionalità Always Encrypted è disabilitata per la connessione di database. La query eseguita dall'applicazione ha un parametro la cui destinazione è la colonna LastName non crittografata. La query recupera i dati dalle colonne SSN e BirthDate, che sono entrambe crittografate. In questo caso, la chiamata di sys.sp_describe_parameter_encryption per recuperare i metadati di crittografia non è necessaria. È tuttavia necessario abilitare la decrittografia dei risultati della query in modo che l'applicazione possa ricevere i valori di testo non crittografato dalle due colonne crittografate. A tale scopo, usare l'impostazione SQLServerStatementColumnEncryptionSetting.ResultSet.

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

Memorizzazione nella cache dei metadati dei parametri di query

Per ridurre il numero di round trip al database, Microsoft JDBC Driver per SQL Server può memorizzare nella cache le informazioni relative alla crittografia per i parametri di query. A partire dalla versione 11.2.0, le informazioni relative alla crittografia per i parametri restituiti dalle chiamate di sys.sp_describe_parameter_encryption vengono memorizzate nella cache dal driver se il processo di SQL Server associato non usa enclave sicure. Per la memorizzazione nella cache con l'uso di enclave sicure, il server deve consentire di ristabilire la sessione dell'enclave nei casi in cui la sessione non è più valida.

Memorizzazione nella cache delle chiavi di crittografia della colonna

Per ridurre il numero di chiamate a un archivio chiavi master della colonna per decrittografare le chiavi di crittografia della colonna, Microsoft JDBC Driver per SQL Server memorizza nella cache le chiavi di crittografia della colonna di testo non crittografato. Dopo aver ricevuto il valore della chiave di crittografia di colonna crittografata dai metadati del database, il driver prova prima di tutto a trovare la chiave di crittografia di colonna in formato testo non crittografato corrispondente al valore della chiave crittografata. Il driver chiama l'archivio chiavi contenente la chiave master di colonna solo se non trova nella cache il valore della chiave di crittografia di colonna crittografata.

È possibile configurare un valore di durata (TTL) per le voci della chiave di crittografia di colonna nella cache usando l'API setColumnEncryptionKeyCacheTtl() nella classe SQLServerConnection. Il valore di durata (TTL) predefinito per le voci della chiave di crittografia della colonna nella cache è pari a due ore. Per disattivare la memorizzazione nella cache, usare un valore pari a 0. Per impostare un valore di durata (TTL) usare l'API seguente:

SQLServerConnection.setColumnEncryptionKeyCacheTtl (int columnEncryptionKeyCacheTTL, TimeUnit unit)

Ad esempio, per impostare un valore di durata (TTL) di 10 minuti, usare:

SQLServerConnection.setColumnEncryptionKeyCacheTtl (10, TimeUnit.MINUTES)

Le unità di tempo supportate sono DAYS (Giorni), HOURS (Ore), MINUTES (Minuti) o SECONDS (Secondi).

Copia di dati crittografati tramite SQLServerBulkCopy

Con SQLServerBulkCopy è possibile copiare dati, che sono già crittografati e archiviati in una tabella, in un'altra tabella, senza eseguire la decrittografia dei dati. A tale scopo, eseguire queste operazioni:

  • Assicurarsi che la configurazione di crittografia della tabella di destinazione sia identica alla configurazione della tabella di origine. In particolare, le due le tabelle devono avere le stesse colonne crittografate e le colonne devono essere crittografate usando gli stessi tipi di crittografia e le stesse chiavi di crittografia. Se una delle colonne di destinazione è crittografata in modo diverso dalla relativa colonna di origine corrispondente, non sarà possibile decrittografare i dati nella tabella di destinazione dopo l'operazione di copia. I dati risulteranno danneggiati.
  • Configurare le due connessioni di database alla tabella di origine e la tabella di destinazione, senza abilitare Always Encrypted.
  • Impostare l'opzione allowEncryptedValueModifications. Per altre informazioni, vedere Uso della copia bulk con il driver JDBC.

Nota

Prestare attenzione quando si specifica AllowEncryptedValueModifications poiché questa opzione può causare il danneggiamento del database in quanto Microsoft JDBC Driver per SQL Server non controlla se i dati sono effettivamente crittografati o se sono crittografati correttamente usando tipo di crittografia, algoritmo e chiave corrispondenti a quelli della colonna di destinazione.

Vedi anche

Always Encrypted (Motore di database)