Partilhar via


Usar Always Encrypted com o driver JDBC

Baixar driver JDBC

Esta página fornece informações sobre como desenvolver aplicativos Java para usar Always Encrypted com o Microsoft JDBC Driver 6.0 (ou superior) para SQL Server.

O Always Encrypted permite que os clientes criptografem dados confidenciais e nunca revelem os dados ou as chaves de criptografia para o SQL Server ou o Banco de Dados SQL do Azure. Um driver habilitado para Always Encrypted, como o Microsoft JDBC Driver 6.0 (ou superior) para SQL Server, alcança esse comportamento criptografando e descriptografando dados confidenciais de forma transparente no aplicativo cliente. O driver descobre quais parâmetros de consulta correspondem às colunas do banco de dados Always Encrypted e criptografa os valores desses parâmetros antes de enviá-los para o banco de dados. Da mesma forma, o driver recupera os dados de colunas de banco de dados criptografadas e os descriptografa transparentemente nos resultados da consulta. Para obter mais informações, consulte Always Encrypted (Mecanismo de Banco de Dados) e a referência da API Always Encrypted para o driver JDBC .

Pré-requisitos

  • Verifique se o Microsoft JDBC Driver 6.0 (ou superior) para SQL Server está instalado em sua máquina de desenvolvimento.
  • Transfira e instale os Ficheiros de Política de Jurisdição de Força Ilimitada da Extensão de Criptografia Java (JCE). Certifique-se de ler o Leiame incluído no arquivo zip para obter instruções de instalação e detalhes relevantes sobre possíveis problemas de exportação ou importação.
    • Para mssql-jdbc-X.X.X.jre7.jar ou sqljdbc41.jar, os ficheiros de política podem ser baixados de Ficheiros de Política de Jurisdição de Força Ilimitada da Extensão de Criptografia Java (JCE) 7: Download
    • Para mssql-jdbc-X.X.X.jre8.jar ou sqljdbc42.jar, pode descarregar os Ficheiros de Política de Jurisdição de Força Ilimitada da Extensão de Criptografia Java (JCE) 8 a partir de Download
    • Para qualquer JRE versão 9 ou superior (por exemplo, mssql-jdbc-X.X.X.jre9.jar), nenhum arquivo de política precisa ser baixado. A política de jurisdição em Java 9, e superior, tem como padrão a criptografia de força ilimitada.

Trabalhar com repositórios para chaves mestras de coluna

Para criptografar ou descriptografar dados para colunas criptografadas, o SQL Server mantém chaves de criptografia de coluna. As chaves de criptografia de coluna são armazenadas de forma criptografada nos metadados do banco de dados. Cada chave de criptografia de coluna tem uma chave mestra de coluna correspondente que é usada para criptografar a chave de criptografia de coluna.

Os metadados do banco de dados não contêm as chaves mestras da coluna. Essas chaves são mantidas apenas pelo cliente. No entanto, os metadados do banco de dados contêm informações sobre onde as chaves mestras de coluna são armazenadas relativamente ao cliente. Por exemplo, os metadados do banco de dados podem dizer que o armazenamento de chaves que contém uma chave mestra de coluna é o Repositório de Certificados do Windows e o certificado específico usado para criptografar e descriptografar está localizado em um caminho específico dentro do Repositório de Certificados do Windows.

Se o cliente tiver acesso a esse certificado no Repositório de Certificados do Windows, ele poderá obtê-lo. O certificado pode então ser usado para desencriptar a chave de encriptação da coluna. Em seguida, essa chave de criptografia pode ser usada para descriptografar ou criptografar dados para colunas criptografadas que usam essa chave de criptografia de colunas.

O Driver JDBC da Microsoft para SQL Server comunica-se com um keystore que utiliza um provedor de armazenamento de chave mestre de coluna, o qual é uma instância de uma classe derivada de SQLServerColumnEncryptionKeyStoreProvider.

Usar provedores de armazenamento de chaves mestras de coluna integrados

O Microsoft JDBC Driver para SQL Server vem com os seguintes provedores internos de armazenamento de chaves mestras de coluna. Alguns desses provedores são pré-registrados com os nomes de provedores específicos (usados para procurar o provedor) e alguns exigem credenciais extras ou registro explícito.

Classe Descrição Nome do provedor (consulta) Está pré-registado? Plataforma
SQLServerColumnEncryptionAzureKeyVaultProvider Um fornecedor de um keystore para o Cofre de Chaves do Azure. AZURE_KEY_VAULT Não antes da versão 7.4.1 do driver JDBC, mas sim a partir da versão 7.4.1 do driver JDBC. Windows, Linux, macOS
SQLServerColumnEncryptionCertificateStoreProvider Um provedor para o Armazenamento de Certificados do Windows. MSSQL_CERTIFICATE_STORE Sim Windows
SQLServerColumnEncryptionJavaKeyStoreProvider Um provedor para o armazenamento de chaves Java. MSSQL_JAVA_KEYSTORE Sim Windows, Linux, macOS

Para os provedores de keystore pré-registrados, você não precisa de nenhuma alteração no código do aplicativo para usar esses provedores, mas observe os seguintes itens:

  • Verifique se o nome do provedor configurado nos metadados da chave mestra da coluna está correto e se o caminho da chave mestra da coluna segue o formato do caminho da chave que é válido para um determinado provedor. É recomendável configurar as chaves com ferramentas como o SQL Server Management Studio, que gera automaticamente os nomes de provedor válidos e os caminhos de chave para emitir a instrução CREATE COLUMN MASTER KEY (Transact-SQL).
  • Certifique-se de que seu aplicativo possa acessar a chave no armazenamento de chaves. Esta tarefa pode envolver a concessão de acesso da sua aplicação à chave e/ou ao repositório de chaves. Dependendo do keystore, isso pode envolver etapas adicionais de configuração específicas. Por exemplo, para usar o SQLServerColumnEncryptionJavaKeyStoreProvider, deve-se fornecer a localização e a palavra-passe do repositório de chaves nas propriedades de conexão.

Todos esses provedores de keystore são descritos com mais detalhes nas seções a seguir. Você só precisa implementar um provedor keystore para usar Always Encrypted.

Usar o provedor do Azure Key Vault

O Azure Key Vault é uma opção conveniente para armazenar e gerenciar chaves mestras de coluna para Always Encrypted (especialmente se seu aplicativo estiver hospedado no Azure). O Microsoft JDBC Driver para SQL Server inclui um provedor interno, SQLServerColumnEncryptionAzureKeyVaultProvider, para aplicativos que têm chaves armazenadas no Cofre de Chaves do Azure. O nome deste provedor é AZURE_KEY_VAULT.

Observação

O provedor do Azure Key Vault integrado ao driver JDBC dá suporte a Vaults e HSMs gerenciados no Azure Key Vault.

Para usar o provedor de armazenamento do Azure Key Vault, um desenvolvedor de aplicações deve criar o cofre e as chaves no Azure Key Vault e criar um registo de aplicação em Microsoft Entra ID (anteriormente Azure Active Directory). A aplicação registada deve receber as permissões Get, Decrypt, Encrypt, Unwrap Key, Wrap Key e Verify nas políticas de acesso definidas para o cofre de chaves criado para uso com Always Encrypted. Para obter mais informações sobre como configurar o cofre de chaves e criar uma chave mestra de coluna, consulte Azure Key Vault—Step by Step e Criando chaves mestras de coluna no Azure Key Vault.

Para o provedor do Azure Key Vault, o driver JDBC valida o caminho da chave mestre da coluna em relação à lista de pontos de extremidade confiáveis. A partir da versão 8.2.2, essa lista é configurável: crie um arquivo de mssql-jdbc.properties no diretório de trabalho do aplicativo, defina a propriedade AKVTrustedEndpoints como uma lista delimitada por ponto-e-vírgula. Se o valor começar com um ponto-e-vírgula, ele estenderá a lista padrão. Caso contrário, ele substitui a lista padrão.

Os pontos de extremidade padrão e confiáveis são:

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

Para os exemplos nesta página, se você criou uma chave mestra de coluna e uma chave de criptografia de coluna baseadas no Cofre de Chaves do Azure com o SQL Server Management Studio, o script T-SQL para recriá-las pode ser semelhante a este exemplo com seu próprio KEY_PATH e ENCRYPTED_VALUEespecíficos:

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

Um aplicativo que usa o driver JDBC pode usar o Cofre da Chave do Azure. A sintaxe ou instruções para esse uso do Cofre de Chaves do Azure foi alterada a partir da versão 7.4.1 do driver JDBC.

Driver JDBC 7.4.1 ou posterior

Esta seção envolve o driver JDBC versão 7.4.1 ou posterior.

Um aplicativo cliente que usa o driver JDBC pode configurar para usar o Azure Key Vault mencionando keyVaultProviderClientId=<ClientId>;keyVaultProviderClientKey=<ClientKey> na cadeia de conexão JDBC.

Este é um exemplo que fornece essas informações de configuração em uma cadeia de conexão JDBC.

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

O driver JDBC instancia automaticamente um objeto SQLServerColumnEncryptionAzureKeyVaultProvider quando essas credenciais estão presentes entre as propriedades de conexão.

Importante

As propriedades de conexão keyVaultProviderClientId e keyVaultProviderClientKey foram preteridas a partir da v8.4.1. Os utilizadores são encorajados a usar keyStoreAuthentication, KeyStorePrincipalIde KeyStoreSecret em vez disso.

Versões do driver JDBC anteriores à 7.4.1

Esta seção envolve versões de driver JDBC anteriores à 7.4.1.

Um aplicativo cliente que usa o driver JDBC deve instanciar um objeto SQLServerColumnEncryptionAzureKeyVaultProvider e, em seguida, registrar o objeto com o driver.

SQLServerColumnEncryptionAzureKeyVaultProvider akvProvider = new SQLServerColumnEncryptionAzureKeyVaultProvider(clientID, clientKey);

clientID é o ID da Aplicação de um registo de Aplicação num tenant do Microsoft Entra. clientKey é uma Senha de Chave registrada nesse Aplicativo, que fornece acesso à API para o Cofre de Chaves do Azure.

Depois que o aplicativo cria uma instância de SQLServerColumnEncryptionAzureKeyVaultProvider, o aplicativo deve registrar a instância com o driver com o método SQLServerConnection.registerColumnEncryptionKeyStoreProviders(). É altamente recomendável que a instância seja registrada usando o nome de pesquisa padrão, AZURE_KEY_VAULT, que pode ser obtido pela API SQLServerColumnEncryptionAzureKeyVaultProvider.getName(). O nome padrão permite que você use ferramentas como o SQL Server Management Studio ou o PowerShell para provisionar e gerenciar chaves Always Encrypted (as ferramentas usam o nome padrão para gerar o objeto de metadados para a chave mestra da coluna). O exemplo a seguir mostra o registro do provedor do Azure Key Vault. Para obter mais informações sobre o método SQLServerConnection.registerColumnEncryptionKeyStoreProviders(), consulte Always Encrypted API Reference for the JDBC Driver.

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

Importante

Se você usar o provedor keystore do Azure Key Vault, a implementação do Azure Key Vault do driver JDBC terá dependências nessas bibliotecas (do GitHub) que devem ser incluídas no seu aplicativo:

azure-sdk-for-java

bibliotecas microsoft-authentication-library-for-java

Para obter um exemplo de como incluir essas dependências em um projeto Maven, consulte Descarregar dependências MSAL4J e AKV com Apache Maven

Usar a autenticação do Azure Key Vault com Identidades Geridas

A partir do 8.4.1 do JDBC Driver8.4.1, o driver adicionou suporte para autenticação nos Cofres de Chaves do Azure com Identidades Gerenciadas.

Você pode usar de Identidades Gerenciadas para autenticar no Cofre da Chave do Azure se o aplicativo estiver hospedado no Azure. Isso elimina a necessidade de fornecer e expor quaisquer credenciais no código.

Propriedades de conexão para autenticação do Cofre da Chave com Identidades Gerenciadas

Para o JDBC Driver 8.4.1 e posterior, o driver introduziu as seguintes propriedades de conexão:

PropriedadeDeConexão Possível Emparelhamento de Valor 1 Emparelhamento Possível de Valores 2 Pareamento de Valores Possíveis 3
autenticação do keyStore KeyVaultClientSecret KeyVaultManagedIdentity JavaKeyStorePassword
keyStorePrincipalId <ID do Cliente de Aplicativo Microsoft Entra> <ID de objeto da aplicação Microsoft Entra> (opcional) n/a
keyStoreSecret < > Segredo do Cliente de Aplicativo Microsoft Entra n/a <segredo/senha para o Java Key Store>

Os exemplos a seguir mostram como as propriedades de conexão são usadas em uma cadeia de conexão.

Usar a Identidade Gerenciada para autenticar no AKV

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

Use a Identidade Gerenciada e o ID principal para autenticar no AKV

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

Utilize clientId e clientSecret para realizar a autenticação no AKV.

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

Os usuários são incentivados a usar essas propriedades de conexão para especificar o tipo de autenticação usado para os Armazenamentos de Chaves em vez da API SQLServerColumnEncryptionAzureKeyVaultProvider.

As propriedades de conexão keyVaultProviderClientId e keyVaultProviderClientKey adicionadas anteriormente são preteridas e substituídas pelas propriedades de conexão descritas anteriormente.

Para obter informações sobre como configurar identidades gerenciadas, consulte Configurar identidades gerenciadas para recursos do Azure em uma VM usando o portal do Azure.

Usar o provedor do Armazenamento de Certificados do Windows

O SQLServerColumnEncryptionCertificateStoreProvider pode ser usado para armazenar chaves mestras de coluna no Armazém de Certificados do Windows. Use o assistente Always Encrypted do SQL Server Management Studio (SSMS) ou outras ferramentas com suporte para criar as definições de chave mestra de coluna e chave de criptografia de coluna no banco de dados. O mesmo assistente pode ser usado para gerar um certificado autoassinado no Repositório de Certificados do Windows que pode ser usado como uma chave mestra de coluna para os dados Sempre Criptografados. Para obter mais informações sobre a sintaxe T-SQL da chave mestra de coluna e da chave de criptografia de coluna, consulte CREATE COLUMN MASTER KEY e CREATE COLUMN ENCRYPTION KEY RESPECTIVAMENTE.

O nome do SQLServerColumnEncryptionCertificateStoreProvider é MSSQL_CERTIFICATE_STORE e pode ser consultado pela API getName() do objeto provedor. É registado automaticamente pelo condutor e pode ser utilizado sem qualquer alteração na aplicação.

Para os exemplos nesta página, se você criou uma chave mestra de coluna baseada no Repositório de Certificados do Windows e uma chave de criptografia de coluna com o SQL Server Management Studio, o script T-SQL para recriá-los pode ser semelhante a este exemplo com seus próprios KEY_PATH e ENCRYPTED_VALUEespecíficos:

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

Enquanto os outros provedores de armazenamento de chaves neste artigo estão disponíveis em todas as plataformas suportadas pelo driver, a implementação SQLServerColumnEncryptionCertificateStoreProvider do driver JDBC está disponível apenas em sistemas operacionais Windows. Ele tem uma dependência do mssql-jdbc_auth-<version>-<arch>.dll que está disponível no pacote de driver. Para usar esse provedor, copie o arquivo mssql-jdbc_auth-<version>-<arch>.dll para um diretório no caminho do sistema Windows no computador onde o driver JDBC está instalado. Como alternativa, você pode definir a propriedade do sistema java.library.path para especificar o diretório do mssql-jdbc_auth-<version>-<arch>.dll. Se você estiver executando uma JVM (Java Virtual Machine) de 32 bits, use o arquivo mssql-jdbc_auth-<version>-x86.dll na pasta x86, mesmo que o sistema operacional seja a versão x64. Se você estiver executando uma JVM de 64 bits em um processador x64, use o arquivo mssql-jdbc_auth-<version>-x64.dll na pasta x64. Por exemplo, se você usar a JVM de 32 bits e o driver JDBC estiver instalado no diretório padrão, poderá especificar o local da DLL com o seguinte argumento de máquina virtual (VM) quando o aplicativo Java for iniciado: -Djava.library.path=C:\Microsoft JDBC Driver <version> for SQL Server\sqljdbc_<version>\enu\auth\x86

Usar o provedor Java Key Store

O driver JDBC vem com uma implementação de provedor de repositório de chaves integrada para o repositório de chaves do Java. Se a propriedade keyStoreAuthentication estiver presente na string de conexão e esteja configurada como JavaKeyStorePassword, o driver automaticamente instanciará e registrará o provedor para o Java Key Store. O nome do provedor Java Key Store é MSSQL_JAVA_KEYSTORE. Esse nome também pode ser consultado pela API SQLServerColumnEncryptionJavaKeyStoreProvider.getName().

Há três propriedades de cadeia de conexão que permitem que um aplicativo cliente especifique as credenciais de que o driver precisa para se autenticar no Java Key Store. O driver inicializa o provedor com base nos valores dessas três propriedades na cadeia de conexão.

keyStoreAuthentication: Identifica o armazenamento de chaves Java a ser usado. Com o Microsoft JDBC Driver 6.0 e superior para SQL Server, você pode autenticar no Java Key Store somente por meio dessa propriedade. Para o Java Key Store, o valor dessa propriedade deve ser JavaKeyStorePassword.

keyStoreLocation: O caminho para o arquivo Java Key Store que armazena a chave mestra da coluna. O caminho inclui o nome do arquivo keystore.

keyStoreSecret: A senha/palavra-passe a utilizar para o keystore e a chave. Para usar o Java Key Store, o keystore e a senha da chave devem ser os mesmos.

Veja um exemplo de fornecimento dessas credenciais na cadeia de conexão:

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

Você também pode obter ou definir essas configurações com o objeto SQLServerDataSource. Para obter mais informações, consulte Referência de API sempre criptografada para o driver JDBC.

O driver JDBC instancia automaticamente o SQLServerColumnEncryptionJavaKeyStoreProvider quando essas credenciais estão presentes nas propriedades de conexão.

Criando uma chave mestra de coluna para o Java Key Store

O SQLServerColumnEncryptionJavaKeyStoreProvider pode ser usado com os tipos de keystore JKS ou PKCS12. Para criar ou importar uma chave para usar com este provedor, use o utilitário Java keytool. A chave deve ter a mesma senha que o próprio armazenamento de chaves. Aqui está um exemplo de como criar uma chave pública e sua chave privada associada com o utilitário keytool. Substitua <password> por uma senha válida.

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

Este comando cria uma chave pública e a encapsula em um certificado autoassinado X.509, que é armazenado no keystore.jks keystore junto com sua chave privada associada. Esta entrada no keystore é identificada pelo alias AlwaysEncryptedKey.

Aqui está um exemplo do mesmo com um tipo de armazenamento PKCS12. Substitua <password> por uma senha válida.

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

Se o keystore for do tipo PKCS12, o utilitário keytool não solicitará uma senha para a chave, sendo necessário fornecer a senha da chave com a opção -keypass, pois a SQLServerColumnEncryptionJavaKeyStoreProvider exige que o keystore e a chave tenham a mesma senha.

Você também pode exportar um certificado do repositório de certificados do Windows no formato .pfx e usá-lo com o SQLServerColumnEncryptionJavaKeyStoreProvider. O certificado exportado também pode ser importado para o Java Key Store como um tipo de keystore JKS.

Depois de criar a entrada Keytool, crie os metadados da chave mestra da coluna no banco de dados, que precisam do nome do provedor keystore e do caminho da chave. Para obter mais informações sobre como criar metadados de chave mestra de coluna, consulte CREATE COLUMN MASTER KEY. Para SQLServerColumnEncryptionJavaKeyStoreProvider, o caminho-chave é o alias da chave e SQLServerColumnEncryptionJavaKeyStoreProvider tem o nome MSSQL_JAVA_KEYSTORE. Você também pode consultar esse nome com a API pública getName() da classe SQLServerColumnEncryptionJavaKeyStoreProvider.

A sintaxe T-SQL para criar a chave mestra da coluna é:

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

Para o 'AlwaysEncryptedKey' criado anteriormente, a definição da chave mestra da coluna seria:

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

Observação

A funcionalidade interna do SQL Server Management Studio não pode criar definições de chave mestra de coluna para o Java Key Store. Os comandos T-SQL devem ser usados programaticamente.

Criar uma chave de criptografia de coluna para o Java Key Store

O SQL Server Management Studio ou qualquer outra ferramenta não pode ser usado para criar chaves de criptografia de coluna usando chaves mestras de coluna no Java Key Store. O aplicativo cliente deve criar a chave de criptografia de coluna programaticamente com a classe SQLServerColumnEncryptionJavaKeyStoreProvider. Para obter mais informações, consulte Usar provedores de armazenamento de chaves mestras de coluna para aprovisionamento programático de chaves.

Implementando um provedor de armazenamento de chaves mestras de coluna personalizado

Se desejar armazenar chaves mestras de coluna em um armazenamento de chaves que não seja suportado por um provedor existente, você poderá implementar um provedor personalizado estendendo a Classe SQLServerColumnEncryptionKeyStoreProvider e registrando o provedor com um dos seguintes métodos:

  • SQLServerConnection.registerColumnEncryptionKeyStoreProviders
  • SQLServerConnection.registerColumnEncryptionKeyStoreProvidersOnConnection (Adicionado no JDBC versão 10.2)
  • SQLServerStatement.registerColumnEncryptionKeyStoreProvidersOnStatement (Adicionado no JDBC versão 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
    }
}

Registe o fornecedor com SQLServerConnection.registerColumnEncryptionKeyStoreProviders:

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

Precedência do cache da chave de criptografia de coluna

Esta seção se aplica ao driver JDBC versão 10.2 e superior.

As chaves de criptografia de coluna (CEK) descriptografadas por fornecedores de armazenamento de chaves personalizados registados numa instância de ligação ou declaração não serão mantidas em cache pelo Microsoft JDBC Driver for SQL Server. Os provedores de armazenamento de chaves personalizadas devem implementar seu próprio mecanismo de cache CEK.

A partir da versão 10.2, o SQLServerColumnEncryptionAzureKeyVaultProvider tem sua própria implementação de cache CEK. Quando registados numa instância de conexão ou declaração, os CEKs decifrados por uma instância de SQLServerColumnEncryptionAzureKeyVaultProvider serão eliminados quando essa instância sair do escopo.

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

Observação

O cache CEK implementado por provedores de armazenamento de chaves personalizados será desabilitado pelo driver se a instância do provedor de armazenamento de chaves estiver registrada no driver globalmente com o método SQLServerConnection.registerColumnEncryptionKeyStoreProviders. Qualquer implementação de cache CEK deve fazer referência ao valor de duração de tempo de vida antes de armazenar em cache um CEK e não armazená-lo em cache se o valor for zero. Isso evitará o cache duplicado e a possível confusão do usuário quando ele estiver tentando configurar o cache de chaves. O valor de tempo de vida para o cache pode ser definido com o método SQLServerColumnEncryptionKeyStoreProvider.setColumnEncryptionCacheTtl.

Registrar um provedor de armazenamento de chaves mestras de coluna personalizada

Esta seção se aplica ao driver JDBC versão 10.2 e superior.

Os provedores de armazenamento de chaves mestras personalizadas podem ser registrados com o driver em três camadas diferentes. A precedência dos três registos é a seguinte:

  • O registo por cada declaração é verificado se não estiver vazio.
  • Se o registro por declaração estiver vazio, o registro por conexão será verificado se não estiver vazio.
  • Se o registro por conexão estiver vazio, o registro global será verificado.

Uma vez que qualquer provedor de armazenamento de chaves é encontrado em um nível de registro, o driver NÃO recorrer aos outros registros para procurar um provedor. Se os provedores estiverem registrados, mas o provedor adequado não for encontrado em um nível, será lançada uma exceção que contém apenas os provedores registrados no registro que foi verificado.

O provedor integrado de armazenamento de chaves mestras de coluna que está disponível para o Repositório de Certificados do Windows está pré-registrado. O provedor Microsoft Java Keystore e o provedor Azure Key Vault Keystore podem ser implicitamente pré-registrados com uma instância de conexão se as credenciais forem fornecidas antecipadamente.

Os três níveis de registro oferecem suporte a diferentes cenários ao consultar dados criptografados. O método apropriado pode ser usado para garantir que um usuário de um aplicativo possa acessar os dados de texto sem formatação. O acesso aos dados não encriptados só acontece se eles puderem fornecer a chave mestra de coluna necessária, autenticando-se no repositório de chaves que contém a chave mestra de coluna.

Os aplicativos que compartilham uma instância SQLServerConnection entre vários usuários podem querer usar SQLServerStatement.registerColumnEncryptionKeyStoreProvidersOnStatement. Cada usuário deve registrar um provedor de armazenamento de chaves em uma instância SQLServerStatement antes de executar uma consulta para acessar uma coluna criptografada. Se o provedor de armazenamento de chaves conseguir aceder à chave mestra da coluna necessária no armazenamento de chaves que utiliza as credenciais fornecidas pelo utilizador, a consulta será bem-sucedida.

Os aplicativos que criam uma instância SQLServerConnection para cada usuário podem querer usar SQLServerConnection.registerColumnEncryptionKeyStoreProvidersOnConnection. Os provedores de armazenamento de chaves registrados com esse método podem ser usados pela conexão para qualquer consulta que acesse dados criptografados.

Os provedores de armazenamento de chaves registados no SQLServerConnection.registerColumnEncryptionKeyStoreProviders usarão a identidade fornecida pela aplicação ao autenticar-se no armazenamento de chaves.

O exemplo a seguir mostra a precedência dos provedores de armazenamento de chaves mestras de coluna personalizada registrados em uma instância de conexão:

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

O exemplo a seguir mostra a prioridade dos fornecedores de armazenamento de chaves mestras de coluna personalizadas registados numa instância de comando:

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

Usar provedores de armazenamento de chaves mestre de coluna para provisionamento de chaves programáticas

Para aceder a colunas cifradas, o Microsoft JDBC Driver para SQL Server localiza e chama de forma transparente o fornecedor de armazenamento de chaves mestras de coluna correto para decifrar chaves de cifragem de coluna. Normalmente, o código normal do aplicativo não chama diretamente os provedores de armazenamento de chaves mestras de coluna. No entanto, você pode instanciar e chamar um provedor programaticamente para provisionar e gerenciar chaves Always Encrypted. Esta etapa pode ser feita para gerar uma chave de criptografia de coluna encriptada e descriptografar uma chave de criptografia de coluna, como parte da rotação da chave mestra de coluna, por exemplo. Para obter mais informações, consulte Visão geral do gerenciamento de chaves paraAlways Encrypted .

Se você usar um provedor de armazenamento de chaves personalizado, a implementação de suas próprias ferramentas de gerenciamento de chaves pode ser necessária. Para usar chaves armazenadas no Repositório de Certificados do Windows ou no Cofre de Chaves do Azure, você pode usar ferramentas existentes, como o SQL Server Management Studio ou o PowerShell, para gerenciar e provisionar chaves. Para usar chaves armazenadas no Java Key Store, você precisa provisionar chaves programaticamente. O exemplo a seguir ilustra como usar a classe SQLServerColumnEncryptionJavaKeyStoreProvider para criptografar a chave com uma chave armazenada no Java Key Store.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionJavaKeyStoreProvider;
import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionKeyStoreProvider;
import com.microsoft.sqlserver.jdbc.SQLServerException;

/**
 * This program demonstrates how to create a column encryption key programmatically for the Java Key Store.
 */
public class AlwaysEncrypted {
    // Alias of the key stored in the keystore.
    private static String keyAlias = "<provide key alias>";

    // Name by which the column master key will be known in the database.
    private static String columnMasterKeyName = "MyCMK";

    // Name by which the column encryption key will be known in the database.
    private static String columnEncryptionKey = "MyCEK";

    // The location of the keystore.
    private static String keyStoreLocation = "C:\\Dev\\Always Encrypted\\keystore.jks";

    // The password of the keystore and the key.
    private static char[] keyStoreSecret = "<password>".toCharArray();

    /**
     * Name of the encryption algorithm used to encrypt the value of the column encryption key. The algorithm for the system providers must be
     * RSA_OAEP.
     */
    private static String algorithm = "RSA_OAEP";

    public static void main(String[] args) {
        String connectionUrl = "jdbc:sqlserver://<server>:<port>;encrypt=true;databaseName=<databaseName>;user=<user>;password=<password>;columnEncryptionSetting=Enabled;";

        try (Connection connection = DriverManager.getConnection(connectionUrl);
                Statement statement = connection.createStatement();) {

            // Instantiate the Java Key Store provider.
            SQLServerColumnEncryptionKeyStoreProvider storeProvider = new SQLServerColumnEncryptionJavaKeyStoreProvider(keyStoreLocation,
                    keyStoreSecret);

            byte[] encryptedCEK = getEncryptedCEK(storeProvider);

            /**
             * Create column encryption key For more details on the syntax, see:
             * https://learn.microsoft.com/sql/t-sql/statements/create-column-encryption-key-transact-sql Encrypted column encryption key first needs
             * to be converted into varbinary_literal from bytes, for which byteArrayToHex() is used.
             */
            String createCEKSQL = "CREATE COLUMN ENCRYPTION KEY "
                    + columnEncryptionKey
                    + " WITH VALUES ( "
                    + " COLUMN_MASTER_KEY = "
                    + columnMasterKeyName
                    + " , ALGORITHM =  '"
                    + algorithm
                    + "' , ENCRYPTED_VALUE =  0x"
                    + byteArrayToHex(encryptedCEK)
                    + " ) ";
            statement.executeUpdate(createCEKSQL);
            System.out.println("Column encryption key created with name : " + columnEncryptionKey);
        }
        // Handle any errors that may have occurred.
        catch (SQLException e) {
            e.printStackTrace();
        }
    }

    private static byte[] getEncryptedCEK(SQLServerColumnEncryptionKeyStoreProvider storeProvider) throws SQLServerException {
        String plainTextKey = "You need to give your plain text";

        // plainTextKey has to be 32 bytes with current algorithm supported
        byte[] plainCEK = plainTextKey.getBytes();

        // This will give us encrypted column encryption key in bytes
        byte[] encryptedCEK = storeProvider.encryptColumnEncryptionKey(keyAlias, algorithm, plainCEK);

        return encryptedCEK;
    }

    public static String byteArrayToHex(byte[] a) {
        StringBuilder sb = new StringBuilder(a.length * 2);
        for (byte b : a)
            sb.append(String.format("%02x", b).toUpperCase());
        return sb.toString();
    }
}

Habilitando o Always Encrypted para consultas de aplicativos

A maneira mais fácil de ativar a criptografia de parâmetros e a descriptografia de resultados de consulta de colunas criptografadas é definindo o valor da palavra-chave da cadeia de conexão columnEncryptionSetting para Enabled.

A cadeia de conexão a seguir é um exemplo de ativação do Always Encrypted no driver JDBC:

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

O código a seguir é um exemplo equivalente para usar o objeto 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();

O Always Encrypted também pode ser ativado para consultas individuais. Para obter mais informações, consulte Controlando o impacto no desempenho do Always Encrypted. Ativar o Always Encrypted não é suficiente para que a encriptação ou desencriptação seja bem-sucedida. Você também precisa ter certeza:

  • O aplicativo tem as permissões de banco de dados VIEW ANY COLUMN MASTER KEY DEFINITION e VIEW ANY COLUMN ENCRYPTION KEY DEFINITION, necessárias para acessar os metadados sobre chaves Always Encrypted no banco de dados. Para obter detalhes, consulte Permissões em Always Encrypted (Mecanismo de Banco de Dados).
  • O aplicativo pode acessar a chave mestra de coluna que protege as chaves de criptografia de coluna, que criptografam as colunas de banco de dados consultadas. Para usar o provedor Java Key Store, você precisa fornecer credenciais extras na cadeia de conexão. Para obter mais informações, consulte Utilize o provedor Java Key Store.

Configurando como os valores java.sql.Time são enviados ao servidor

A propriedade de conexão sendTimeAsDatetime é usada para configurar como o valor java.sql.Time é enviado ao servidor. Quando definido como false, o valor temporal é enviado como um tipo de hora do SQL Server. Quando definido como true, o valor de hora é enviado como um tipo datetime. Se uma coluna de hora estiver criptografada, a propriedade sendTimeAsDatetime deverá ser false, pois as colunas criptografadas não suportam a conversão de hora para data/hora. Observe também que essa propriedade é, por padrão, true, portanto, para usar colunas de tempo criptografadas, defina-a como false. Caso contrário, o controlador lançará uma exceção. A partir da versão 6.0 do driver, a classe SQLServerConnection tem dois métodos para configurar o valor dessa propriedade programaticamente:

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

Para obter mais informações sobre essa propriedade, consulte Configurando como os valores java.sql.Time são enviados para o servidor.

Configurando como os valores de String são enviados para o servidor

A propriedade de conexão sendStringParametersAsUnicode é usada para configurar como os valores String são enviados para o SQL Server. Se definido como true, os parâmetros String são enviados para o servidor no formato Unicode. Se definido como false, os parâmetros String são enviados no formato não-Unicode, como ASCII ou MBCS, em vez de Unicode. O valor padrão para essa propriedade é true. Quando Always Encrypted está habilitado e uma coluna char/varchar/varchar(max) é criptografada, o valor de sendStringParametersAsUnicode deve ser definido como false. Se essa propriedade for definida como true, o driver lançará uma exceção ao descriptografar dados de uma coluna char/varchar/varchar(max) criptografada que tenha caracteres Unicode. Para obter mais informações sobre essa propriedade, consulte Definindo as propriedades de conexão.

Importante

Se sendStringParametersAsUnicode estiver definido como true e os dados unicode forem inseridos em uma coluna char/varchar criptografada com Always Encrypted, a perda de dados poderá ocorrer sem que um erro seja relatado. A perda de dados só pode ser detetada ao tentar descriptografar os dados depois de lê-los de volta do servidor. Um erro como 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'. pode ser o resultado.

É importante usar tipos de dados de coluna corretos e especificar o tipo de dados correto para parâmetros ao inserir dados criptografados. Quando se esperam dados unicode, utilize as colunas nchar/nvarchar e os métodos setNString(). O servidor não pode executar conversões de dados implícitas e tem capacidade limitada de detetar erros de dados quando o Always Encrypted está ativado.

Recuperando e modificando dados em colunas criptografadas

Depois de habilitar o Always Encrypted para consultas de aplicativos, você pode usar APIs JDBC padrão para recuperar ou modificar dados em colunas de banco de dados criptografadas. Se seu aplicativo tiver as permissões de banco de dados necessárias e puder acessar a chave mestra de coluna, o driver criptografará todos os parâmetros de consulta destinados a colunas criptografadas e descriptografará os dados recuperados de colunas criptografadas.

Se Sempre Criptografado não estiver habilitado, as consultas com parâmetros direcionados a colunas criptografadas falharão. As consultas ainda podem recuperar dados de colunas criptografadas, desde que a consulta não tenha parâmetros direcionados a colunas criptografadas. No entanto, o driver não tentará descriptografar quaisquer valores que são recuperados de colunas criptografadas e o aplicativo receberá dados criptografados binários (como matrizes de bytes).

A tabela a seguir resume o comportamento das consultas, dependendo se o Always Encrypted está habilitado ou não:

Característica da consulta Always Encrypted está ativado e o aplicativo pode acessar as chaves e metadados de chave O Always Encrypted está ativado e a aplicação não consegue aceder às chaves ou metadados da chave Sempre Criptografado está desativado
Consultas com parâmetros direcionados a colunas criptografadas. Os valores dos parâmetros são criptografados de forma transparente. Erro Erro
Consultas que recuperam dados de colunas criptografadas sem usar parâmetros vistos como direcionados a colunas criptografadas. Os resultados das colunas encriptadas são desencriptados de forma transparente. O aplicativo recebe valores de texto sem formatação dos tipos de dados JDBC correspondentes aos tipos do SQL Server configurados para as colunas criptografadas. Erro Os resultados de colunas encriptadas não são desencriptados. O aplicativo recebe valores criptografados como matrizes de bytes (byte[]).

Inserindo e recuperando exemplos de dados criptografados

Os exemplos a seguir ilustram a recuperação e modificação de dados em colunas criptografadas. Os exemplos assumem a tabela de destino com o esquema a seguir e colunas criptografadas SSN e BirthDate. Se você configurou uma Chave Mestra de Coluna chamada "MyCMK" e uma Chave de Criptografia de Coluna chamada "MyCEK" (conforme descrito nas seções de provedores de armazenamento de chaves anteriores), poderá criar a tabela com este 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

Para cada exemplo de código Java, você precisará inserir código específico do keystore no local indicado.

Para usar um fornecedor de armazenamento de chaves do 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;";

Para usar um provedor de armazenamento de chaves do Windows Certificate Store:

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

Para usar um provedor de keystore Java Key Store:

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

Exemplo de inserção de dados

Este exemplo insere uma linha na tabela Pacientes. Observe os seguintes itens:

  • Não há nada específico para criptografia no código de exemplo. O Microsoft JDBC Driver para SQL Server deteta e criptografa automaticamente os parâmetros que visam colunas criptografadas. Esse comportamento torna a criptografia transparente para o aplicativo.
  • Os valores inseridos nas colunas do banco de dados, incluindo as colunas criptografadas, são passados como parâmetros com SQLServerPreparedStatement. Embora os parâmetros sejam opcionais ao enviar valores para colunas não criptografadas (embora seja altamente recomendado porque ajuda a evitar a injeção de SQL), eles são necessários para valores destinados a colunas criptografadas. Se os valores inseridos nas colunas criptografadas fossem passados como literais incorporados na instrução de consulta, a consulta falharia porque o driver não seria capaz de determinar os valores nas colunas criptografadas de destino e não criptografaria os valores. Como resultado, o servidor os rejeitaria como incompatíveis com as colunas criptografadas.
  • Todos os valores impressos pelo programa serão em texto simples, pois o Microsoft JDBC Driver para SQL Server descriptografará de forma transparente os dados recuperados das colunas criptografadas.
  • Se você estiver fazendo uma pesquisa com uma cláusula WHERE, o valor usado na cláusula WHERE precisa ser passado como um parâmetro para que o driver possa criptografá-lo de forma transparente antes de enviá-lo para o banco de dados. No exemplo a seguir, o SSN é passado como um parâmetro, mas o LastName é passado como um literal, pois LastName não é criptografado.
  • O método setter usado para o parâmetro destinado à coluna SSN é setString(), que mapeia para o tipo de dados char/varchar do SQL Server. Se para esse parâmetro, o método setter usado fosse setNString(), que mapeia para nchar/nvarchar, a consulta falharia, pois Always Encrypted não suporta conversões de valores de nchar/nvarchar criptografados para valores de char/varchar criptografados.
// <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();
}

Exemplo de recuperação de dados de texto simples

O exemplo a seguir demonstra como filtrar dados com base em valores criptografados e recuperar dados de texto em claro de colunas criptografadas. Observe os seguintes itens:

  • O valor usado na cláusula WHERE para filtrar na coluna SSN precisa ser passado como um parâmetro para que o Microsoft JDBC Driver for SQL Server possa criptografá-lo de forma transparente antes de enviá-lo para o banco de dados.
  • Todos os valores impressos pelo programa estarão em texto sem formatação, pois o Microsoft JDBC Driver para SQL Server descriptografará de forma transparente os dados recuperados das colunas SSN e BirthDate.

Observação

Se as colunas forem criptografadas com criptografia determinística, as consultas poderão realizar comparações de igualdade nelas. Para obter mais informações, consulte criptografia determinística.

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

Exemplo de recuperação de dados criptografados

Se Sempre Criptografado não estiver habilitado, uma consulta ainda poderá recuperar dados de colunas criptografadas, desde que a consulta não tenha parâmetros direcionados a colunas criptografadas.

O exemplo a seguir ilustra a recuperação de dados binários criptografados de colunas criptografadas. Observe os seguintes itens:

  • Como Always Encrypted não está habilitado na cadeia de conexão, a consulta retornará valores criptografados de SSN e BirthDate como matrizes de bytes (o programa converte os valores em strings).
  • Uma consulta que recupera dados de colunas criptografadas com Always Encrypted desabilitado pode ter parâmetros, desde que nenhum dos parâmetros tenha como alvo uma coluna criptografada. A consulta a seguir filtra por LastName, que não é criptografado no banco de dados. Se a consulta fosse filtrada por NIF ou data de nascimento, a consulta falharia.
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();
}

Evitando problemas comuns ao consultar colunas criptografadas

Esta seção descreve categorias comuns de erros ao consultar colunas criptografadas de aplicativos Java e algumas diretrizes sobre como evitá-los.

Erros de conversão de tipo de dados não suportados

O Always Encrypted suporta poucas conversões para tipos de dados criptografados. Consulte Always Encrypted (Motor de Base de Dados) para a lista detalhada das conversões de tipos suportadas. Veja o que você pode fazer para evitar erros de conversão de tipo de dados. Certifique-se de que:

  • Você usa os métodos setter adequados ao passar valores para parâmetros que visam colunas criptografadas. Verifique se o tipo de dados do SQL Server do parâmetro é exatamente o mesmo que o tipo da coluna de destino ou se há suporte para uma conversão do tipo de dados do SQL Server do parâmetro para o tipo de destino da coluna. Os métodos de API foram adicionados às classes SQLServerPreparedStatement, SQLServerCallableStatemente SQLServerResultSet para passar parâmetros correspondentes a tipos de dados específicos do SQL Server. Para obter uma lista completa de novas APIs, consulte Referência de API sempre criptografada para o driver JDBC. A não conformidade com as definições de tipo de dados provavelmente resultará em erros de conflito de tipo de operando. A seguir estão alguns exemplos de ajustes que podem ser necessários ao usar o Always Encrypted:

    • Você pode usar o método setTimestamp() para passar um parâmetro para uma coluna datetime2 ou datetime não criptografada. Mas quando uma coluna é criptografada, você tem que usar o método exato que representa o tipo da coluna no banco de dados. Use setTimestamp() para atribuir valores a uma coluna datetime2 criptografada e use setDateTime() para atribuir valores a uma coluna datetime criptografada.
    • Você pode usar o método setBinary() para passar um parâmetro para uma coluna varbinary(max) ou binary não criptografada. O driver assume o tipo de dados BINARY para os parâmetros setBinary() e o servidor pode converter implicitamente os dados para inserir em uma coluna do tipo varbinary(max). Mas quando uma coluna varbinary(max) é criptografada, você precisa especificar um tipo mais exato para os dados do parâmetro. Exemplo: preparedStatement.setObject(1, binaryData, java.sql.JDBCType.LONGVARBINARY)
  • a precisão e a escala dos parâmetros que visam colunas dos tipos de dados decimais e numéricos do SQL Server são as mesmas que a precisão e a escala configuradas para a coluna de destino. Os métodos de API foram adicionados às classes SQLServerPreparedStatement, SQLServerCallableStatemente SQLServerResultSet para aceitar precisão e dimensionamento, juntamente com valores de dados para parâmetros/colunas que representam tipos de dados decimais e numéricos. Consulte a Referência de API Sempre Criptografada para o Driver JDBC para obter uma lista completa de APIs novas/sobrecarregadas.

    • Por exemplo, quando estiveres a usar o BigDecimal do Java como o tipo de parâmetro destinado a uma coluna decimal especificada no banco de dados, precisas fornecer a precisão e a escala para o método setBigDecimal() ou setValue(). A falha na especificação da precisão e escala corretas pode resultar em um erro como o seguinte:
    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')
    
  • a precisão/escala de segundos fracionários dos parâmetros que têm como alvo colunas dos tipos de dados datetime2, datetimeoffsetou tipo de dados de hora do SQL Server não é maior do que a precisão/escala de segundos fracionários da coluna de destino em consultas que modificam valores da coluna de destino. Os métodos de API foram adicionados às classes SQLServerPreparedStatement, SQLServerCallableStatemente SQLServerResultSet para aceitar precisão/escala de segundos fracionários, juntamente com valores de dados para parâmetros que representam esses tipos de dados. Para obter uma lista completa de APIs novas/sobrecarregadas, consulte Referência de API sempre criptografada para o driver JDBC.

Erros devido a propriedades de conexão incorretas

Esta seção descreve como configurar as configurações de conexão corretamente para usar dados Always Encrypted. Como os tipos de dados criptografados suportam conversões limitadas, as configurações de conexão sendTimeAsDatetime e sendStringParametersAsUnicode precisam de configuração adequada para usar colunas criptografadas. Certifique-se de que:

Erros devido à passagem de texto sem formatação em vez de valores criptografados

Qualquer valor direcionado a uma coluna criptografada precisa ser criptografado dentro do aplicativo. Uma tentativa de inserir/modificar ou filtrar por um valor de texto simples em uma coluna criptografada resultará em um erro semelhante a este:

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'

Para evitar tais erros, certifique-se de:

  • Always Encrypted está habilitado para consultas de aplicativos direcionadas a colunas criptografadas (para a cadeia de conexão ou para uma consulta específica).
  • Você usa instruções preparadas e parâmetros para enviar dados para colunas criptografadas. O exemplo a seguir mostra uma consulta que filtra incorretamente por um literal/constante numa coluna criptografada (SSN), em vez de passar o literal como um parâmetro. Esta consulta falhará:
ResultSet rs = connection.createStatement().executeQuery("SELECT * FROM Customers WHERE SSN='795-73-9838'");

Forçar a criptografia nos parâmetros de entrada

O recurso Force Encryption impõe a criptografia de um parâmetro com Always Encrypted. Se for usada encriptação forçada e o SQL Server informar ao motorista que o parâmetro não precisa ser criptografado, a consulta que utiliza o parâmetro não será bem-sucedida. Essa propriedade fornece proteção adicional contra ataques de segurança que envolvem um SQL Server comprometido fornecendo metadados de criptografia incorretos ao cliente, o que pode levar à divulgação de dados. Os métodos set* nas classes SQLServerPreparedStatement e SQLServerCallableStatement e os métodos update* na classe SQLServerResultSet estão sobrecarregados para aceitar um argumento booleano para especificar a configuração de criptografia de força. Se o valor deste argumento for falso, o driver não forçará a criptografia nos parâmetros. Se a opção de forçar criptografia estiver definida como verdadeira, o parâmetro de consulta só será enviado se a coluna de destino estiver criptografada e a funcionalidade Always Encrypted estiver ativada na ligação ou na instrução. Essa propriedade fornece uma camada extra de segurança, garantindo que o driver não envie dados por engano para o SQL Server como texto sem formatação quando se espera que ele seja criptografado.

Para obter mais informações sobre os métodos SQLServerPreparedStatement e SQLServerCallableStatement que estão sobrecarregados com o parâmetro de criptografia forçada, consulte a documentação sobre Always Encrypted API Reference para o driver JDBC

Controlando o impacto no desempenho do Always Encrypted

Como o Always Encrypted é uma tecnologia de criptografia do lado do cliente, a maior parte da sobrecarga de desempenho é observada no lado do cliente, não no banco de dados. Além do custo das operações de encriptação e desencriptação, outras fontes de despesas gerais de desempenho no lado do cliente são:

  • Adicionadas viagens de ida e volta ao banco de dados para recuperar metadados para parâmetros de consulta.
  • Chamadas a um armazenamento de chave mestra de coluna para aceder a uma chave mestra de coluna.

Esta seção descreve as otimizações de desempenho internas no Microsoft JDBC Driver para SQL Server e como você pode controlar o impacto dos dois fatores mencionados anteriormente no desempenho.

Controlando viagens de ida e volta para recuperar metadados para parâmetros de consulta

Se Always Encrypted estiver habilitado para uma conexão, por padrão, o driver chamará sys.sp_describe_parameter_encryption para cada consulta parametrizada, passando a instrução query (sem valores de parâmetro) para o banco de dados. sys.sp_describe_parameter_encryption analisa a instrução de consulta para descobrir se algum parâmetro precisa ser criptografado e, em caso afirmativo, para cada um retorna as informações relacionadas à criptografia que permitem ao driver criptografar valores de parâmetro. Esse comportamento garante um alto nível de transparência para o aplicativo cliente. Contanto que o aplicativo use parâmetros para passar valores que visam colunas criptografadas para o driver, o aplicativo (e o desenvolvedor do aplicativo) não precisa saber quais consultas acessam colunas criptografadas.

Configuração do Always Encrypted ao nível de consulta

Para controlar o impacto no desempenho da recuperação de metadados de criptografia para consultas parametrizadas, você pode habilitar o Always Encrypted para consultas individuais em vez de configurá-lo para a conexão. Dessa forma, você pode garantir que sys.sp_describe_parameter_encryption seja invocado apenas para consultas que você sabe que têm parâmetros direcionados a colunas criptografadas. No entanto, observe que, ao fazer isso, você reduz a transparência da criptografia: se você alterar as propriedades de criptografia das colunas do banco de dados, talvez seja necessário alterar o código do seu aplicativo para alinhá-lo com as alterações de esquema.

Para controlar o comportamento Always Encrypted de consultas individuais, você precisa configurar objetos de instrução individuais passando um Enum, SQLServerStatementColumnEncryptionSetting, que especifica como os dados serão enviados e recebidos ao ler e gravar colunas criptografadas para essa instrução específica. Aqui estão algumas diretrizes úteis:

  • Se a maioria das consultas que um aplicativo cliente enviar por uma conexão de banco de dados acessar colunas criptografadas, use estas diretrizes:

    • Defina a palavra-chave columnEncryptionSetting da string de conexão para Enabled.
    • Defina SQLServerStatementColumnEncryptionSetting.Disabled para consultas individuais que não acessam colunas criptografadas. Esta configuração desativará a chamada de sys.sp_describe_parameter_encryption e descriptografar quaisquer valores no conjunto de resultados.
    • Defina SQLServerStatementColumnEncryptionSetting.ResultSet para consultas individuais que não tenham parâmetros que exijam criptografia, mas recuperem dados de colunas criptografadas. Essa configuração irá desativar a chamada sys.sp_describe_parameter_encryption e a criptografia de parâmetros. A consulta desencriptará os resultados das colunas de encriptação.
  • Se a maioria das consultas que um aplicativo cliente envia por uma conexão de banco de dados não acessar colunas criptografadas, use estas diretrizes:

    • Defina o parâmetro palavra-chave da cadeia de conexão columnEncryptionSetting como Disabled.
    • Defina SQLServerStatementColumnEncryptionSetting.Enabled para consultas individuais que tenham quaisquer parâmetros que precisem ser criptografados. Essa configuração permitirá chamar sys.sp_describe_parameter_encryption e descriptografar quaisquer resultados de consulta recuperados de colunas criptografadas.
    • Defina SQLServerStatementColumnEncryptionSetting.ResultSet para consultas que não tenham parâmetros que exijam criptografia, mas recuperem dados de colunas criptografadas. Essa configuração desativará a chamada sys.sp_describe_parameter_encryption e a criptografia de parâmetros. A consulta desencriptará os resultados das colunas de encriptação.

As configurações de SQLServerStatementColumnEncryptionSetting não podem ser usadas para ignorar a criptografia e obter acesso a dados de texto sem formatação. Para obter mais informações sobre como configurar a criptografia de coluna em uma instrução, consulte Always Encrypted API Reference for the JDBC Driver.

No exemplo a seguir, Always Encrypted está desabilitado para a conexão de banco de dados. A consulta que o aplicativo emite tem um parâmetro direcionado à coluna LastName que não está criptografada. A consulta recupera dados das colunas SSN e BirthDate criptografadas. Nesse caso, não é necessário chamar sys.sp_describe_parameter_encryption para recuperar metadados de criptografia. No entanto, a descriptografia dos resultados da consulta precisa ser ativada para que a aplicação possa receber valores de texto simples das duas colunas criptografadas. A configuração SQLServerStatementColumnEncryptionSetting.ResultSet é usada para garantir isso.

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

Cache de metadados de parâmetros de consulta

Para reduzir o número de viagens de ida e volta ao banco de dados, o Microsoft JDBC Driver para SQL Server pode armazenar em cache informações relacionadas à criptografia para parâmetros de consulta. A partir da versão 11.2.0, as informações relacionadas à criptografia para parâmetros retornados de chamadas sys.sp_describe_parameter_encryption serão armazenadas em cache pelo driver se o processo associado do SQL Server não usar enclaves seguros. Para armazenamento em cache com o uso de enclaves seguros, o servidor deve suportar o restabelecimento da sessão de enclave nos casos em que a sessão não é mais válida.

Cache de chave de criptografia de coluna

Para reduzir o número de chamadas a um armazenamento de chave mestra de coluna para descriptografar chaves de criptografia de coluna, o Microsoft JDBC Driver para SQL Server armazena em cache na memória as chaves de criptografia de coluna em texto simples. Depois que o driver recebe o valor da chave de encriptação de coluna encriptada dos metadados do banco de dados, ele primeiro tenta localizar a chave de encriptação de coluna em texto claro correspondente ao valor da chave encriptada. O driver chama o keystore que contém a chave mestra da coluna somente se não conseguir encontrar o valor da chave de criptografia de coluna criptografada no cache.

Você pode configurar um valor de tempo de vida para as entradas de chave de criptografia de coluna no cache com a API, setColumnEncryptionKeyCacheTtl(), na classe SQLServerConnection. O valor padrão de tempo de vida útil para as entradas da chave de criptografia de coluna no cache é de duas horas. Para desativar o cache, use um valor de 0. Para definir qualquer valor de tempo de vida, use a seguinte API:

SQLServerConnection.setColumnEncryptionKeyCacheTtl (int columnEncryptionKeyCacheTTL, TimeUnit unit)

Por exemplo, para definir um valor de tempo de vida útil de 10 minutos, use:

SQLServerConnection.setColumnEncryptionKeyCacheTtl (10, TimeUnit.MINUTES)

Apenas DIAS, HORAS, MINUTOS ou SEGUNDOS são suportados como unidade de tempo.

Copiando dados criptografados com SQLServerBulkCopy

Com SQLServerBulkCopy, você pode copiar dados que já estão criptografados e armazenados em uma tabela para outra tabela sem descriptografar os dados. Para fazer isso:

  • Verifique se a configuração de criptografia da tabela de destino é idêntica à configuração da tabela de origem. Em particular, ambas as tabelas devem ter as mesmas colunas criptografadas e as colunas devem ser criptografadas usando os mesmos tipos de criptografia e as mesmas chaves de criptografia. Se qualquer coluna de destino for criptografada de forma diferente da coluna de origem correspondente, você não poderá descriptografar os dados na tabela de destino após a operação de cópia. Os dados serão corrompidos.
  • Configure as duas conexões de bases de dados com a tabela de origem e com a tabela de destino sem "Always Encrypted" ativado.
  • Defina a opção allowEncryptedValueModifications. Para obter mais informações, consulte Usando cópia em massa com o driver JDBC.

Observação

Tenha cuidado ao especificar AllowEncryptedValueModifications pois essa opção pode levar ao corrompimento do banco de dados porque o Microsoft JDBC Driver para SQL Server não verifica se os dados estão realmente criptografados ou se estão corretamente criptografados com o mesmo tipo de criptografia, algoritmo e chave que a coluna de destino.

Ver também

Sempre criptografado (Mecanismo de Banco de Dados)