Gunakan Always Encrypted dengan driver JDBC

Unduh driver JDBC

Halaman ini menyediakan informasi tentang cara mengembangkan aplikasi Java untuk digunakan Always Encrypted dengan Microsoft JDBC Driver 6.0 (atau lebih tinggi) untuk SQL Server.

Always Encrypted memungkinkan klien untuk mengenkripsi data sensitif dan tidak pernah mengungkapkan data atau kunci enkripsi untuk SQL Server atau Azure SQL Database. Driver yang diaktifkan Always Encrypted, seperti Microsoft JDBC Driver 6.0 (atau lebih tinggi) untuk SQL Server, mencapai perilaku ini dengan mengenkripsi dan mendekripsi data sensitif secara transparan dalam aplikasi klien. Driver mencari tahu parameter kueri mana yang sesuai dengan kolom database Always Encrypted, dan mengenkripsi nilai parameter tersebut sebelum mengirimkannya ke database. Demikian pula, driver secara transparan mendekripsi data yang diambil dari kolom database terenkripsi dalam hasil kueri. Untuk informasi selengkapnya, lihat Always Encrypted (Mesin Database) dan referensi API Always Encrypted untuk driver JDBC.

Prasyarat

  • Pastikan Microsoft JDBC Driver 6.0 (atau lebih tinggi) untuk SQL Server diinstal pada komputer pengembangan Anda.
  • Unduh dan instal File Kebijakan Yurisdiksi Kekuatan Tanpa Batas Ekstensi Kriptografi Java (JCE). Pastikan untuk membaca Readme yang disertakan dalam file zip untuk instruksi penginstalan, dan detail yang relevan tentang kemungkinan masalah ekspor atau impor.

Bekerja dengan penyimpanan kunci master kolom

Untuk mengenkripsi atau mendekripsi data untuk kolom terenkripsi, SQL Server mempertahankan kunci enkripsi kolom. Kunci enkripsi kolom disimpan dalam formulir terenkripsi dalam metadata database. Setiap kunci enkripsi kolom memiliki kunci master kolom terkait yang digunakan untuk mengenkripsi kunci enkripsi kolom.

Metadata database tidak berisi kunci master kolom. Kunci tersebut hanya dipegang oleh klien. Namun metadata database memang berisi informasi tentang di mana kunci master kolom disimpan relatif terhadap klien. Misalnya, metadata database mungkin mengatakan keystore yang menyimpan kunci master kolom adalah Windows Certificate Store, dan sertifikat tertentu yang digunakan untuk mengenkripsi dan mendekripsi terletak di jalur tertentu dalam Windows Certificate Store.

Jika klien memiliki akses ke sertifikat tersebut di Windows Certificate Store, klien dapat memperoleh sertifikat. Sertifikat kemudian dapat digunakan untuk mendekripsi kunci enkripsi kolom. Kemudian kunci enkripsi tersebut dapat digunakan untuk mendekripsi atau mengenkripsi data untuk kolom terenkripsi yang menggunakan kunci enkripsi kolom tersebut.

Driver Microsoft JDBC untuk SQL Server berkomunikasi dengan keystore yang menggunakan penyedia penyimpanan kunci master kolom, yang merupakan instans kelas yang berasal dari SQLServerColumnEncryptionKeyStoreProvider.

Menggunakan penyedia penyimpanan kunci master kolom bawaan

Driver Microsoft JDBC untuk SQL Server dilengkapi dengan penyedia penyimpanan kunci master kolom bawaan berikut. Beberapa penyedia ini telah terdaftar sebelumnya dengan nama penyedia tertentu (digunakan untuk mencari penyedia) dan beberapa memerlukan kredensial tambahan atau pendaftaran eksplisit.

Kelas Deskripsi Nama penyedia (pencarian) Apakah sudah terdaftar sebelumnya? Platform
SQLServerColumnEncryptionAzureKeyVaultProvider Penyedia untuk keystore untuk Key Vault Azure. AZURE_KEY_VAULT Tidak sebelum driver JDBC versi 7.4.1, tetapi ya pada driver JDBC versi 7.4.1. Windows, Linux, macOS
SQLServerColumnEncryptionCertificateStoreProvider Penyedia untuk Windows Certificate Store. MSSQL_CERTIFICATE_STORE Ya Windows
SQLServerColumnEncryptionJavaKeyStoreProvider Penyedia untuk keystore Java. MSSQL_JAVA_KEYSTORE Ya Windows, Linux, macOS

Untuk penyedia keystore yang telah terdaftar sebelumnya, Anda tidak memerlukan perubahan kode aplikasi apa pun untuk menggunakan penyedia ini tetapi perhatikan item berikut:

  • Anda harus memastikan nama penyedia yang dikonfigurasi dalam metadata kunci master kolom sudah benar dan jalur kunci master kolom mengikuti format jalur kunci yang valid untuk penyedia tertentu. Disarankan agar Anda mengonfigurasi kunci dengan alat seperti SQL Server Management Studio, yang secara otomatis menghasilkan nama penyedia dan jalur kunci yang valid untuk mengeluarkan CREATE COLUMN MASTER KEY pernyataan (Transact-SQL).
  • Pastikan aplikasi Anda dapat mengakses kunci di keystore. Tugas ini mungkin melibatkan pemberian akses aplikasi Anda ke kunci dan/atau keystore. Bergantung pada keystore, ini mungkin melibatkan langkah-langkah konfigurasi khusus keystore lainnya. Misalnya, untuk menggunakan SQLServerColumnEncryptionJavaKeyStoreProvider, Anda harus menyediakan lokasi dan kata sandi keystore di properti koneksi.

Semua penyedia keystore ini dijelaskan secara lebih rinci di bagian berikut. Anda hanya perlu mengimplementasikan satu penyedia keystore untuk menggunakan Always Encrypted.

Menggunakan penyedia Azure Key Vault

Azure Key Vault adalah opsi yang nyaman untuk menyimpan dan mengelola kunci master kolom untuk Always Encrypted (terutama jika aplikasi Anda dihosting di Azure). Driver Microsoft JDBC untuk SQL Server menyertakan penyedia bawaan, , SQLServerColumnEncryptionAzureKeyVaultProvideruntuk aplikasi yang memiliki kunci yang disimpan di Azure Key Vault. Nama penyedia ini AZURE_KEY_VAULT.

Catatan

Penyedia Azure Key Vault bawaan driver JDBC mendukung Vault dan HSM Terkelola di Azure Key Vault.

Untuk menggunakan penyedia penyimpanan Azure Key Vault, pengembang aplikasi harus membuat brankas dan kunci di Azure Key Vault dan membuat pendaftaran Aplikasi di Azure Active Directory. Aplikasi terdaftar harus diberikan izin Get, Decrypt, Encrypt, Unwrap Key, Wrap Key, dan Verify dalam Kebijakan akses yang ditentukan untuk brankas kunci yang dibuat untuk digunakan dengan Always Encrypted. Untuk informasi selengkapnya tentang cara menyiapkan brankas kunci dan membuat kunci master kolom, lihat Azure Key Vault—Langkah demi Langkah dan Membuat Kunci Master Kolom di Azure Key Vault.

Untuk penyedia Azure Key Vault, driver JDBC memvalidasi jalur kunci master kolom terhadap daftar titik akhir tepercaya. Pada versi 8.2.2, daftar ini dapat dikonfigurasi: buat mssql-jdbc.properties file di direktori kerja aplikasi, atur AKVTrustedEndpoints properti ke daftar yang dibatasi titik koma. Jika nilai dimulai dengan titik koma, nilai akan memperluas daftar default. Jika tidak, itu menggantikan daftar default.

Titik akhir default yang tepercaya adalah:

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

Untuk contoh di halaman ini, jika Anda telah membuat kunci master kolom berbasis Azure Key Vault dan kunci enkripsi kolom dengan SQL Server Management Studio, skrip T-SQL untuk membuatnya kembali mungkin terlihat mirip dengan contoh ini dengan KEY_PATH dan ENCRYPTED_VALUE spesifiknya sendiri:

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

Aplikasi yang menggunakan driver JDBC dapat menggunakan Key Vault Azure. Sintaks atau pernyataan untuk penggunaan Azure Key Vault ini berubah per driver JDBC versi 7.4.1.

Driver JDBC 7.4.1 atau yang lebih baru

Bagian ini melibatkan driver JDBC versi 7.4.1 atau yang lebih baru.

Aplikasi klien yang menggunakan driver JDBC dapat mengonfigurasi untuk menggunakan Azure Key Vault dengan menyebutkan keyVaultProviderClientId=<ClientId>;keyVaultProviderClientKey=<ClientKey> dalam string koneksi JDBC.

Berikut adalah contoh yang menyediakan informasi konfigurasi ini dalam string koneksi JDBC.

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

Driver JDBC secara otomatis membuat instans SQLServerColumnEncryptionAzureKeyVaultProvider objek ketika kredensial ini ada di antara properti koneksi.

Penting

Properti keyVaultProviderClientId koneksi dan keyVaultProviderClientKey telah tidak digunakan lagi pada v8.4.1. Pengguna didorong untuk menggunakan keyStoreAuthentication, , KeyStorePrincipalIddan KeyStoreSecret sebagai gantinya.

Versi driver JDBC sebelum 7.4.1

Bagian ini melibatkan versi driver JDBC sebelum 7.4.1.

Aplikasi klien yang menggunakan driver JDBC harus membuat instans SQLServerColumnEncryptionAzureKeyVaultProvider objek, lalu mendaftarkan objek dengan driver.

SQLServerColumnEncryptionAzureKeyVaultProvider akvProvider = new SQLServerColumnEncryptionAzureKeyVaultProvider(clientID, clientKey);

clientID adalah ID Aplikasi pendaftaran Aplikasi dalam instans Azure Active Directory. clientKeyadalah Kata Sandi Kunci yang terdaftar di bawah Aplikasi tersebut, yang menyediakan akses API ke Key Vault Azure.

Setelah aplikasi membuat instans SQLServerColumnEncryptionAzureKeyVaultProvider, aplikasi harus mendaftarkan instans dengan driver dengan SQLServerConnection.registerColumnEncryptionKeyStoreProviders() metode . Sangat disarankan agar instans terdaftar menggunakan nama pencarian default, AZURE_KEY_VAULT, yang dapat diperoleh oleh SQLServerColumnEncryptionAzureKeyVaultProvider.getName() API. Nama default memungkinkan Anda menggunakan alat seperti SQL Server Management Studio atau PowerShell untuk menyediakan dan mengelola kunci Always Encrypted (alat menggunakan nama default untuk menghasilkan objek metadata ke kunci master kolom). Contoh berikut menunjukkan mendaftarkan penyedia Azure Key Vault. Untuk informasi selengkapnya tentang metode iniSQLServerConnection.registerColumnEncryptionKeyStoreProviders(), lihat Referensi API Always Encrypted untuk Driver JDBC.

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

Penting

Jika Anda menggunakan penyedia keystore Azure Key Vault, implementasi azure Key Vault driver JDBC memiliki dependensi pada pustaka ini (dari GitHub) yang harus disertakan dengan aplikasi Anda:

azure-sdk-for-java

pustaka microsoft-authentication-for-java

Untuk contoh cara menyertakan dependensi ini dalam proyek Maven, lihat Mengunduh Dependensi MSAL4J dan AKV dengan Apache Maven

Menggunakan autentikasi Azure Key Vault dengan Identitas Terkelola

Pada Driver JDBC 8.4.1, driver menambahkan dukungan untuk mengautentikasi ke Azure Key Vaults dengan Identitas Terkelola.

Anda dapat menggunakan Identitas Terkelola untuk mengautentikasi ke Azure Key Vault jika aplikasi dihosting di Azure. Ini menghilangkan kebutuhan untuk memberikan dan mengekspos info masuk apa pun dalam kode.

Properti koneksi untuk autentikasi Key Vault dengan Identitas Terkelola

Untuk JDBC Driver 8.4.1 dan yang lebih baru, driver memperkenalkan properti koneksi berikut:

ConnectionProperty Kemungkinan Pasangan Nilai 1 Kemungkinan Pasangan Nilai 2 Kemungkinan Pasangan Nilai 3
keyStoreAuthentication KeyVaultClientSecret KeyVaultManagedIdentity JavaKeyStorePassword
keyStorePrincipalId <ID Klien Aplikasi Azure AD> <AZURE AD ID> objek Aplikasi (opsional) n/a
keyStoreSecret <Rahasia Klien Aplikasi Azure AD> n/a <rahasia/kata sandi untuk Java Key Store>

Contoh berikut menunjukkan bagaimana properti koneksi digunakan dalam string koneksi.

Menggunakan Identitas Terkelola untuk mengautentikasi ke AKV

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

Gunakan Identitas Terkelola dan ID utama untuk mengautentikasi ke AKV

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

Menggunakan clientId dan clientSecret untuk autentikasi ke AKV

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

Pengguna didorong untuk menggunakan properti koneksi ini untuk menentukan jenis autentikasi yang digunakan untuk Key Stores alih-alih SQLServerColumnEncryptionAzureKeyVaultProvider API.

Properti keyVaultProviderClientId koneksi yang sebelumnya ditambahkan dan keyVaultProviderClientKey tidak digunakan lagi dan digantikan oleh properti koneksi yang dijelaskan di atas.

Untuk informasi tentang cara mengonfigurasi Identitas Terkelola, lihat Mengonfigurasi identitas terkelola untuk sumber daya Azure pada VM menggunakan portal Azure.

Menggunakan penyedia Windows Certificate Store

SQLServerColumnEncryptionCertificateStoreProvider dapat digunakan untuk menyimpan kunci master kolom di Windows Certificate Store. Gunakan wizard Always Encrypted SQL Server Management Studio (SSMS) atau alat lain yang didukung untuk membuat kunci master kolom dan definisi kunci enkripsi kolom dalam database. Wizard yang sama dapat digunakan untuk menghasilkan sertifikat yang ditandatangani sendiri di Windows Certificate Store yang dapat digunakan sebagai kunci master kolom untuk data Always Encrypted. Untuk informasi selengkapnya tentang kunci master kolom dan sintaks T-SQL kunci enkripsi kolom, lihat CREATE COLUMN MASTER KEY dan CREATE COLUMN ENCRYPTION KEY masing-masing.

Nama MSSQL_CERTIFICATE_STORE dan dapat dikueri SQLServerColumnEncryptionCertificateStoreProvider oleh API getName() dari objek penyedia. Ini secara otomatis didaftarkan oleh driver dan dapat digunakan dengan mulus tanpa perubahan aplikasi apa pun.

Untuk contoh di halaman ini, jika Anda telah membuat kunci master kolom berbasis Penyimpanan Sertifikat Windows dan kunci enkripsi kolom dengan SQL Server Management Studio, skrip T-SQL untuk membuatnya kembali mungkin terlihat mirip dengan contoh ini dengan KEY_PATH dan ENCRYPTED_VALUE spesifiknya sendiri:

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

Penting

Sementara penyedia keystore lainnya dalam artikel ini tersedia di semua platform yang didukung oleh driver, SQLServerColumnEncryptionCertificateStoreProvider implementasi driver JDBC hanya tersedia pada sistem operasi Windows. Ini memiliki dependensi pada mssql-jdbc_auth-<version>-<arch>.dll yang tersedia dalam paket driver. Untuk menggunakan penyedia ini, salin file ke mssql-jdbc_auth-<version>-<arch>.dll direktori pada jalur sistem Windows pada komputer tempat driver JDBC diinstal. Atau Anda dapat mengatur properti sistem java.library.path untuk menentukan direktori mssql-jdbc_auth-<version>-<arch>.dll. Jika Anda menjalankan Java Virtual Machine (JVM) 32-bit, gunakan mssql-jdbc_auth-<version>-x86.dll file di folder x86, bahkan jika sistem operasi adalah versi x64. Jika Anda menjalankan JVM 64-bit pada prosesor x64, gunakan mssql-jdbc_auth-<version>-x64.dll file di folder x64. Misalnya, jika Anda menggunakan JVM 32-bit dan driver JDBC diinstal di direktori default, Anda dapat menentukan lokasi DLL dengan argumen komputer virtual (VM) berikut saat aplikasi Java dimulai: -Djava.library.path=C:\Microsoft JDBC Driver <version> for SQL Server\sqljdbc_<version>\enu\auth\x86

Menggunakan penyedia Java Key Store

Driver JDBC dilengkapi dengan implementasi penyedia keystore bawaan untuk Java Key Store. keyStoreAuthentication Jika properti string koneksi ada dalam string koneksi dan diatur ke JavaKeyStorePassword, driver secara otomatis membuat instans dan mendaftarkan penyedia untuk Java Key Store. Nama penyedia Java Key Store MSSQL_JAVA_KEYSTORE. Nama ini juga dapat dikueri oleh SQLServerColumnEncryptionJavaKeyStoreProvider.getName() API.

Ada tiga properti string koneksi yang memungkinkan aplikasi klien menentukan kredensial yang perlu diautentikasi driver ke Java Key Store. Driver menginisialisasi penyedia berdasarkan nilai ketiga properti ini dalam string koneksi.

keyStoreAuthentication: Mengidentifikasi Java Key Store yang akan digunakan. Dengan Microsoft JDBC Driver 6.0 dan yang lebih tinggi untuk SQL Server, Anda dapat mengautentikasi ke Java Key Store hanya melalui properti ini. Untuk Java Key Store, nilai untuk properti ini harus JavaKeyStorePassword.

keyStoreLocation: Jalur ke file Java Key Store yang menyimpan kunci master kolom. Jalur ini mencakup nama file keystore.

keyStoreSecret: Rahasia/kata sandi yang akan digunakan untuk keystore dan kunci. Untuk menggunakan Java Key Store, keystore dan kata sandi kunci harus sama.

Berikut adalah contoh penyediaan kredensial ini dalam string koneksi:

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

Anda juga bisa mendapatkan atau mengatur pengaturan ini dengan SQLServerDataSource objek . Untuk informasi selengkapnya, lihat Always Encrypted Referensi API untuk Driver JDBC.

Driver JDBC secara otomatis membuat instans SQLServerColumnEncryptionJavaKeyStoreProvider kapan kredensial ini ada di properti koneksi.

Membuat kunci master kolom untuk Java Key Store

SQLServerColumnEncryptionJavaKeyStoreProvider dapat digunakan dengan jenis keystore JKS atau PKCS12. Untuk membuat atau mengimpor kunci yang akan digunakan dengan penyedia ini, gunakan utilitas keytool Java. Kunci harus memiliki kata sandi yang sama dengan keystore itu sendiri. Berikut adalah contoh cara membuat kunci umum dan kunci privat terkait dengan keytool utilitas:

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

Perintah ini membuat kunci publik dan membungkusnya dalam sertifikat X.509 yang ditandatangani sendiri, yang disimpan di keystore keystore.jks bersama dengan kunci privat terkait. Entri di keystore ini diidentifikasi oleh alias AlwaysEncryptedKey.

Berikut adalah contoh yang sama dengan jenis penyimpanan PKCS12:

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

Jika keystore berjenis PKCS12, utilitas keytool tidak meminta kata sandi kunci dan kata sandi kunci perlu disediakan dengan -keypass opsi karena SQLServerColumnEncryptionJavaKeyStoreProvider mengharuskan keystore dan kunci memiliki kata sandi yang sama.

Anda juga dapat mengekspor sertifikat dari penyimpanan Sertifikat Windows dalam format .pfx dan menggunakannya dengan SQLServerColumnEncryptionJavaKeyStoreProvider. Sertifikat yang diekspor juga dapat diimpor ke Java Key Store sebagai jenis keystore JKS.

Setelah Anda membuat entri keytool, buat metadata kunci master kolom di database, yang memerlukan nama penyedia keystore dan jalur kunci. Untuk informasi selengkapnya tentang cara membuat data meta kunci master kolom, lihat MEMBUAT KUNCI MASTER KOLOM. Untuk SQLServerColumnEncryptionJavaKeyStoreProvider, jalur kunci hanyalah alias kunci dan nama SQLServerColumnEncryptionJavaKeyStoreProvider adalah MSSQL_JAVA_KEYSTORE. Anda juga dapat mengkueri nama ini dengan getName() API SQLServerColumnEncryptionJavaKeyStoreProvider publik kelas .

Sintaks T-SQL untuk membuat kunci master kolom adalah:

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

Untuk 'AlwaysEncryptedKey' yang dibuat di atas, definisi kunci master kolom adalah:

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

Catatan

Fungsionalitas Studio manajemen SQL Server bawaan tidak dapat membuat definisi kunci master kolom untuk Java Key Store. Perintah T-SQL harus digunakan secara terprogram.

Membuat kunci enkripsi kolom untuk Java Key Store

SQL Server Management Studio atau alat lain tidak dapat digunakan untuk membuat kunci enkripsi kolom menggunakan kunci master kolom di Java Key Store. Aplikasi klien harus membuat kunci enkripsi kolom secara terprogram dengan SQLServerColumnEncryptionJavaKeyStoreProvider kelas . Untuk informasi selengkapnya, lihat Menggunakan penyedia penyimpanan kunci master kolom untuk provisi kunci terprogram.

Menerapkan penyedia penyimpanan kunci master kolom kustom

Jika Anda ingin menyimpan kunci master kolom di keystore yang tidak didukung oleh penyedia yang ada, Anda dapat menerapkan penyedia kustom dengan memperluas SQLServerColumnEncryptionKeyStoreProvider Kelas dan mendaftarkan penyedia dengan salah satu metode berikut:

  • SQLServerConnection.registerColumnEncryptionKeyStoreProviders
  • SQLServerConnection.registerColumnEncryptionKeyStoreProvidersOnConnection (Ditambahkan dalam JDBC versi 10.2)
  • SQLServerStatement.registerColumnEncryptionKeyStoreProvidersOnStatement (Ditambahkan dalam JDBC versi 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
    }
}

Daftarkan penyedia dengan SQLServerConnection.registerColumnEncryptionKeyStoreProviders:

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

Prioritas cache kunci enkripsi kolom

Bagian ini berlaku untuk driver JDBC versi 10.2 dan yang lebih tinggi.

Kunci enkripsi kolom (CEK) yang didekripsi oleh penyedia penyimpanan kunci kustom yang terdaftar pada koneksi atau instans pernyataan tidak akan di-cache oleh Driver Microsoft JDBC untuk SQL Server. Penyedia penyimpanan kunci kustom harus menerapkan mekanisme penembolokan CEK mereka sendiri.

Pada versi 10.2, SQLServerColumnEncryptionAzureKeyVaultProvider memiliki implementasi penembolokan CEK sendiri. Ketika terdaftar pada koneksi atau instans pernyataan, CEK yang didekripsi oleh instans SQLServerColumnEncryptionAzureKeyVaultProvider akan dihapus ketika instans tersebut keluar dari cakupan:

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

Catatan

Penembolokan CEK yang diterapkan oleh penyedia penyimpanan kunci kustom akan dinonaktifkan oleh driver jika instans penyedia penyimpanan kunci terdaftar di driver secara global dengan SQLServerConnection.registerColumnEncryptionKeyStoreProviders metode . Implementasi penembolokan CEK apa pun harus mereferensikan nilai durasi time-to-live sebelum penembolokan CEK dan tidak menyimpannya dalam cache jika nilainya nol. Ini akan menghindari penembolokan duplikat dan kemungkinan kebingungan pengguna ketika mereka mencoba mengonfigurasi penembolokan kunci. Nilai time-to-live untuk cache dapat diatur dengan SQLServerColumnEncryptionKeyStoreProvider.setColumnEncryptionCacheTtl metode .

Mendaftarkan penyedia penyimpanan kunci master kolom kustom

Bagian ini berlaku untuk driver JDBC versi 10.2 dan yang lebih tinggi.

Penyedia penyimpanan kunci master kustom dapat didaftarkan ke driver pada tiga lapisan yang berbeda. Prioritas dari tiga pendaftaran adalah sebagai berikut:

  • Pendaftaran per pernyataan diperiksa jika tidak kosong.
  • Jika pendaftaran per pernyataan kosong, pendaftaran per koneksi diperiksa jika tidak kosong.
  • Jika pendaftaran per koneksi kosong, pendaftaran global akan diperiksa.

Setelah penyedia penyimpanan kunci ditemukan pada tingkat pendaftaran, driver TIDAK akan kembali ke pendaftaran lain untuk mencari penyedia. Jika penyedia terdaftar tetapi penyedia yang tepat tidak ditemukan pada tingkat, pengecualian dilemparkan yang hanya berisi penyedia terdaftar dalam pendaftaran yang diperiksa.

Penyedia penyimpanan kunci master kolom bawaan yang tersedia untuk Penyimpanan Sertifikat Windows telah didaftarkan sebelumnya. Penyedia Microsoft Java Keystore dan penyedia Azure Key Vault Keystore dapat secara implisit didaftarkan sebelumnya dengan instans koneksi jika kredensial disediakan terlebih dahulu.

Tiga tingkat pendaftaran mendukung skenario yang berbeda saat mengkueri data terenkripsi. Metode yang sesuai dapat digunakan untuk memastikan bahwa pengguna aplikasi dapat mengakses data teks biasa. Akses ke data yang tidak terenkripsi hanya terjadi jika mereka dapat menyediakan kunci master kolom yang diperlukan, dengan mengautentikasi terhadap penyimpanan kunci yang berisi kunci master kolom.

Aplikasi yang berbagi SQLServerConnection instans antara beberapa pengguna mungkin ingin menggunakan SQLServerStatement.registerColumnEncryptionKeyStoreProvidersOnStatement. Setiap pengguna harus mendaftarkan penyedia penyimpanan kunci pada SQLServerStatement instans sebelum menjalankan kueri untuk mengakses kolom terenkripsi. Jika penyedia penyimpanan kunci dapat mengakses kunci master kolom yang diperlukan di penyimpanan kunci yang menggunakan kredensial yang diberikan pengguna, kueri akan berhasil.

Aplikasi yang membuat SQLServerConnection instans untuk setiap pengguna mungkin ingin menggunakan SQLServerConnection.registerColumnEncryptionKeyStoreProvidersOnConnection. Penyedia penyimpanan kunci yang terdaftar dengan metode ini dapat digunakan oleh koneksi untuk setiap kueri yang mengakses data terenkripsi.

Penyedia penyimpanan kunci yang terdaftar SQLServerConnection.registerColumnEncryptionKeyStoreProviders akan menggunakan identitas yang diberikan oleh aplikasi saat mengautentikasi terhadap penyimpanan kunci.

Contoh berikut menunjukkan prioritas penyedia penyimpanan kunci master kolom kustom yang terdaftar pada instans koneksi:

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

Contoh berikut menunjukkan prioritas penyedia penyimpanan kunci master kolom kustom yang terdaftar pada instans pernyataan:

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

Menggunakan penyedia penyimpanan kunci master kolom untuk provisi kunci terprogram

Untuk mengakses kolom terenkripsi, Driver Microsoft JDBC untuk SQL Server secara transparan menemukan dan memanggil penyedia penyimpanan kunci master kolom yang tepat untuk mendekripsi kunci enkripsi kolom. Biasanya, kode aplikasi normal Anda tidak langsung memanggil penyedia penyimpanan kunci master kolom. Namun, Anda dapat membuat instans dan memanggil penyedia secara terprogram untuk menyediakan dan mengelola kunci Always Encrypted. Langkah ini dapat dilakukan untuk menghasilkan kunci enkripsi kolom terenkripsi dan mendekripsi kunci enkripsi kolom sebagai rotasi kunci master kolom bagian, misalnya. Untuk informasi selengkapnya, lihat Gambaran Umum Manajemen Kunci untuk Always Encrypted.

Jika Anda menggunakan penyedia keystore kustom, menerapkan alat manajemen kunci Anda sendiri mungkin diperlukan. Untuk menggunakan kunci yang disimpan di Windows Certificate Store atau di Azure Key Vault, Anda bisa menggunakan alat yang sudah ada, seperti SQL Server Management Studio atau PowerShell, untuk mengelola dan menyediakan kunci. Untuk menggunakan kunci yang disimpan di Java Key Store, Anda perlu menyediakan kunci secara terprogram. Contoh berikut menggambarkan cara menggunakan SQLServerColumnEncryptionJavaKeyStoreProvider kelas untuk mengenkripsi kunci dengan kunci yang disimpan di 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 = "********".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();
    }
}

Mengaktifkan Always Encrypted untuk kueri aplikasi

Cara termampu untuk mengaktifkan enkripsi parameter dan dekripsi hasil kueri kolom terenkripsi adalah dengan mengatur nilai columnEncryptionSetting kata kunci string koneksi ke Enabled.

String koneksi berikut adalah contoh mengaktifkan Always Encrypted di driver JDBC:

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

Kode berikut adalah contoh yang setara untuk menggunakan SQLServerDataSource objek :

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 juga dapat diaktifkan untuk kueri individual. Untuk informasi selengkapnya, lihat Mengontrol dampak performa Always Encrypted. Mengaktifkan Always Encrypted tidak cukup untuk enkripsi atau dekripsi agar berhasil. Anda juga perlu memastikan:

  • Aplikasi ini memiliki VIEW ANY COLUMN MASTER KEY DEFINITION izin database dan VIEW ANY COLUMN ENCRYPTION KEY DEFINITION , yang diperlukan untuk mengakses metadata tentang kunci Always Encrypted dalam database. Untuk detailnya, lihat Izin di Always Encrypted (Mesin Database).
  • Aplikasi dapat mengakses kunci master kolom yang melindungi kunci enkripsi kolom, yang mengenkripsi kolom database yang dikueri. Untuk menggunakan penyedia Java Key Store, Anda perlu memberikan kredensial tambahan dalam string koneksi. Untuk informasi selengkapnya, lihat Menggunakan penyedia Java Key Store.

Mengonfigurasi bagaimana nilai java.sql.Time dikirim ke server

Properti sendTimeAsDatetime koneksi digunakan untuk mengonfigurasi bagaimana nilai java.sql.Time dikirim ke server. Saat diatur ke false, nilai waktu dikirim sebagai jenis waktu SQL Server. Saat diatur ke true, nilai waktu dikirim sebagai jenis tanggalwaktu. Jika kolom waktu dienkripsi, sendTimeAsDatetime properti harus salah, karena kolom terenkripsi tidak mendukung konversi dari waktu ke tanggalwaktu. Perhatikan juga bahwa properti ini secara default benar, jadi untuk menggunakan kolom waktu terenkripsi, atur ke false. Jika tidak, driver akan melemparkan pengecualian. Dimulai dengan driver versi 6.0, SQLServerConnection kelas memiliki dua metode untuk mengonfigurasi nilai properti ini secara terprogram:

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

Untuk informasi selengkapnya tentang properti ini, lihat Mengonfigurasi Cara Nilai java.sql.Time Dikirim ke Server.

Mengonfigurasi bagaimana nilai String dikirim ke server

Properti sendStringParametersAsUnicode koneksi digunakan untuk mengonfigurasi bagaimana nilai String dikirim ke SQL Server. Jika diatur ke true, parameter String dikirim ke server dalam format Unicode. Jika diatur ke false, parameter String dikirim dalam format non-Unicode, seperti ASCII atau MBCS, bukan Unicode. Nilai default untuk properti ini adalah true. Saat Always Encrypted diaktifkan dan char//varcharvarchar(max) kolom dienkripsi, nilai sendStringParametersAsUnicode harus diatur ke false. Jika properti ini diatur ke true, driver akan memberikan pengecualian saat mendekripsi data dari kolom terenkripsi char//varcharvarchar(max) yang memiliki karakter Unicode. Untuk informasi selengkapnya tentang properti ini, lihat Mengatur Properti Koneksi.

Penting

Jika sendStringParametersAsUnicode diatur ke true dan data unicode dimasukkan ke dalam kolom yang char/varchar dienkripsi dengan Always Encrypted, kehilangan data dapat terjadi tanpa kesalahan yang dilaporkan. Kehilangan data hanya dapat dideteksi ketika mencoba mendekripsi data setelah membacanya kembali dari server. Kesalahan seperti 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'. mungkin hasilnya.

Penting untuk menggunakan jenis data kolom yang benar dan menentukan jenis data yang benar untuk parameter saat menyisipkan data terenkripsi. Jika data unicode diharapkan, gunakan nchar/nvarchar kolom dan setNString() metode. Server tidak dapat melakukan konversi data implisit dan memiliki kemampuan terbatas untuk mendeteksi kesalahan data saat Always Encrypted diaktifkan.

Mengambil dan memodifikasi data dalam kolom terenkripsi

Setelah mengaktifkan Always Encrypted untuk kueri aplikasi, Anda dapat menggunakan API JDBC standar untuk mengambil atau memodifikasi data di kolom database terenkripsi. Jika aplikasi Anda memiliki izin database yang diperlukan dan dapat mengakses kunci master kolom, driver akan mengenkripsi parameter kueri apa pun yang menargetkan kolom terenkripsi, dan mendekripsi data yang diambil dari kolom terenkripsi.

Jika Always Encrypted tidak diaktifkan, kueri dengan parameter yang menargetkan kolom terenkripsi akan gagal. Kueri masih dapat mengambil data dari kolom terenkripsi selama kueri tidak memiliki parameter yang menargetkan kolom terenkripsi. Namun, driver tidak akan mencoba mendekripsi nilai apa pun yang diambil dari kolom terenkripsi dan aplikasi akan menerima data terenkripsi biner (sebagai array byte).

Tabel berikut ini meringkas perilaku kueri tergantung pada apakah Always Encrypted diaktifkan atau tidak:

Karakteristik kueri Always Encrypted diaktifkan dan aplikasi dapat mengakses kunci dan metadata kunci Always Encrypted diaktifkan dan aplikasi tidak dapat mengakses kunci atau metadata kunci Always Encrypted dinonaktifkan
Kueri dengan parameter yang menargetkan kolom terenkripsi. Nilai parameter dienkripsi secara transparan. Kesalahan Kesalahan
Kueri mengambil data dari kolom terenkripsi tanpa parameter yang menargetkan kolom terenkripsi. Hasil dari kolom terenkripsi didekripsi secara transparan. Aplikasi menerima nilai teks biasa dari jenis data JDBC yang sesuai dengan jenis SQL Server yang dikonfigurasi untuk kolom terenkripsi. Kesalahan Hasil dari kolom terenkripsi tidak didekripsi. Aplikasi menerima nilai terenkripsi sebagai array byte (byte[]).

Menyisipkan dan mengambil contoh data terenkripsi

Contoh berikut mengilustrasikan pengambilan dan modifikasi data dalam kolom terenkripsi. Contoh mengasumsikan tabel target dengan skema berikut dan kolom SSN dan BirthDate terenkripsi. Jika Anda telah mengonfigurasi Kunci Master Kolom bernama "MyCMK" dan Kunci Enkripsi Kolom bernama "MyCEK" (seperti yang dijelaskan di bagian penyedia keystore sebelumnya), Anda dapat membuat tabel dengan skrip ini:

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

Untuk setiap contoh kode Java, Anda harus menyisipkan kode khusus keystore di lokasi yang disebutkan.

Untuk menggunakan penyedia keystore 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;";

Untuk menggunakan penyedia keystore Windows Certificate Store:

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

Untuk menggunakan penyedia 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>";

Menyisipkan contoh data

Contoh ini menyisipkan baris ke dalam tabel Pasien. Perhatikan item berikut:

  • Tidak ada yang khusus untuk enkripsi dalam kode sampel. Driver Microsoft JDBC untuk SQL Server secara otomatis mendeteksi dan mengenkripsi parameter yang menargetkan kolom terenkripsi. Perilaku ini membuat enkripsi transparan terhadap aplikasi.
  • Nilai yang disisipkan ke dalam kolom database, termasuk kolom terenkripsi, diteruskan sebagai parameter dengan SQLServerPreparedStatement. Meskipun parameter bersifat opsional saat mengirim nilai ke kolom yang tidak dienkripsi (meskipun, parameter sangat disarankan karena membantu mencegah injeksi SQL), diperlukan untuk nilai yang menargetkan kolom terenkripsi. Jika nilai yang dimasukkan ke dalam kolom terenkripsi diteruskan sebagai harfiah yang disematkan dalam pernyataan kueri, kueri akan gagal karena driver tidak akan dapat menentukan nilai dalam kolom terenkripsi target dan tidak akan mengenkripsi nilai. Akibatnya, server akan menolaknya sebagai tidak kompatibel dengan kolom terenkripsi.
  • Semua nilai yang dicetak oleh program akan berada dalam teks biasa, karena Driver Microsoft JDBC untuk SQL Server akan secara transparan mendekripsi data yang diambil dari kolom terenkripsi.
  • Jika Anda melakukan pencarian dengan klausa WHERE, nilai yang digunakan dalam klausa WHERE perlu diteruskan sebagai parameter sehingga driver dapat mengenkripsinya secara transparan sebelum mengirimkannya ke database. Dalam contoh berikut, SSN diteruskan sebagai parameter tetapi LastName diteruskan sebagai harfiah karena LastName tidak dienkripsi.
  • Metode setter yang digunakan untuk parameter yang menargetkan kolom SSN adalah setString(), yang memetakan ke char/varchar jenis data SQL Server. Jika untuk parameter ini, metode setter yang digunakan adalah setNString(), yang memetakan ke ncharnvarchar/, kueri akan gagal, karena Always Encrypted tidak mendukung konversi dari nilai terenkripsi/nvarcharncharke nilai terenkripsi.char/varchar
// <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();
}

Mengambil contoh data teks biasa

Contoh berikut menunjukkan pemfilteran data berdasarkan nilai terenkripsi dan mengambil data teks biasa dari kolom terenkripsi. Perhatikan item berikut:

  • Nilai yang digunakan dalam klausa WHERE untuk memfilter pada kolom SSN perlu diteruskan sebagai parameter sehingga Driver Microsoft JDBC untuk SQL Server dapat mengenkripsinya secara transparan sebelum mengirimkannya ke database.
  • Semua nilai yang dicetak oleh program akan berada dalam teks biasa, karena Driver Microsoft JDBC untuk SQL Server akan secara transparan mendekripsi data yang diambil dari kolom SSN dan BirthDate.

Catatan

jika kolom dienkripsi dengan enkripsi deterministik, kueri dapat melakukan perbandingan kesetaraan pada kolom tersebut. Untuk informasi selengkapnya, lihat enkripsi deterministik.

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

Mengambil contoh data terenkripsi

Jika Always Encrypted tidak diaktifkan, kueri masih dapat mengambil data dari kolom terenkripsi, selama kueri tidak memiliki parameter yang menargetkan kolom terenkripsi.

Contoh berikut mengilustrasikan pengambilan data terenkripsi biner dari kolom terenkripsi. Perhatikan item berikut:

  • Karena Always Encrypted tidak diaktifkan dalam string koneksi, kueri akan mengembalikan nilai terenkripsi SSN dan BirthDate sebagai array byte (program mengonversi nilai menjadi string).
  • Kueri yang mengambil data dari kolom terenkripsi dengan Always Encrypted dinonaktifkan dapat memiliki parameter, selama tidak ada parameter yang menargetkan kolom terenkripsi. Kueri berikut memfilter menurut LastName, yang tidak dienkripsi dalam database. Jika kueri difilter oleh SSN atau BirthDate, kueri akan gagal.
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();
}

Menghindari masalah umum saat mengkueri kolom terenkripsi

Bagian ini menjelaskan kategori kesalahan umum saat mengkueri kolom terenkripsi dari aplikasi Java dan beberapa panduan tentang cara menghindarinya.

Kesalahan konversi tipe data yang tidak didukung

Always Encrypted mendukung beberapa konversi untuk jenis data terenkripsi. Lihat Always Encrypted (Mesin Database) untuk daftar terperinci konversi jenis yang didukung. Inilah yang dapat Anda lakukan untuk menghindari kesalahan konversi jenis data. Pastikan bahwa:

  • Anda menggunakan metode setter yang tepat saat meneruskan nilai untuk parameter yang menargetkan kolom terenkripsi. Pastikan bahwa jenis data SQL Server parameter sama persis dengan jenis kolom target atau konversi jenis data SQL Server parameter ke jenis target kolom didukung. Metode API telah ditambahkan ke SQLServerPreparedStatementkelas , SQLServerCallableStatement, dan SQLServerResultSet untuk meneruskan parameter yang sesuai dengan jenis data SQL Server tertentu. Untuk daftar lengkap API baru, lihat Referensi API Always Encrypted untuk Driver JDBC. Berikut ini adalah beberapa contoh penyesuaian yang mungkin diperlukan saat menggunakan Always Encrypted:
    • Anda dapat menggunakan setTimestamp() metode untuk meneruskan parameter ke kolom datetime2 atau datetime yang tidak dienkripsi. Tetapi ketika kolom dienkripsi, Anda harus menggunakan metode yang tepat yang mewakili jenis kolom dalam database. Gunakan setTimestamp() untuk meneruskan nilai ke kolom datetime2 terenkripsi dan gunakan setDateTime() untuk meneruskan nilai ke kolom tanggalwaktu terenkripsi.
    • Anda dapat menggunakan setBinary() metode untuk meneruskan parameter ke kolom atau binary yang tidak dienkripsivarbinary(max). Driver default ke BINARY jenis data untuk setBinary() parameter dan server dapat secara implisit mengonversi data untuk disisipkan varbinary(max) ke dalam kolom. Tetapi ketika varbinary(max) kolom dienkripsi, Anda harus menentukan jenis yang lebih tepat untuk data parameter. Contoh: preparedStatement.setObject(1, binaryData, java.sql.JDBCType.LONGVARBINARY)
  • presisi dan skala parameter yang menargetkan kolom dari jenis data SQL Server desimal dan numerik sama dengan presisi dan skala yang dikonfigurasi untuk kolom target. Metode API telah ditambahkan ke SQLServerPreparedStatementkelas , SQLServerCallableStatement, dan SQLServerResultSet untuk menerima presisi dan skala bersama dengan nilai data untuk parameter/kolom yang mewakili jenis data desimal dan numerik. Lihat Always Encrypted Referensi API untuk Driver JDBC untuk daftar lengkap API baru/kelebihan beban.
  • presisi/skala detik pecahan parameter yang menargetkan kolom datetime2, , datetimeoffsetatau waktu SQL Server jenis data tidak lebih besar dari presisi/skala detik pecahan untuk kolom target dalam kueri yang memodifikasi nilai kolom target. Metode API telah ditambahkan ke SQLServerPreparedStatementkelas , SQLServerCallableStatement, dan SQLServerResultSet untuk menerima presisi/skala detik pecahan bersama dengan nilai data untuk parameter yang mewakili jenis data ini. Untuk daftar lengkap API baru/kelebihan beban, lihat Referensi API Always Encrypted untuk Driver JDBC.

Kesalahan karena properti koneksi yang salah

Bagian ini menjelaskan cara mengonfigurasi pengaturan koneksi dengan benar untuk menggunakan data Always Encrypted. Karena jenis data terenkripsi mendukung konversi terbatas, sendTimeAsDatetime pengaturan koneksi dan sendStringParametersAsUnicode memerlukan konfigurasi yang tepat untuk menggunakan kolom terenkripsi. Pastikan bahwa:

Kesalahan karena meneruskan teks biasa alih-alih nilai terenkripsi

Nilai apa pun yang menargetkan kolom terenkripsi perlu dienkripsi di dalam aplikasi. Upaya untuk menyisipkan/memodifikasi atau memfilter berdasarkan nilai teks biasa pada kolom terenkripsi akan mengakibatkan kesalahan yang mirip dengan yang ini:

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'

Untuk mencegah kesalahan tersebut, pastikan:

  • Always Encrypted diaktifkan untuk kueri aplikasi yang menargetkan kolom terenkripsi (untuk string koneksi atau untuk kueri tertentu).
  • Anda menggunakan pernyataan dan parameter yang disiapkan untuk mengirim kolom terenkripsi penargetan data. Contoh berikut menunjukkan kueri yang salah memfilter menurut harfiah/konstanta pada kolom terenkripsi (SSN), alih-alih meneruskan harfiah di dalamnya sebagai parameter. Kueri ini akan gagal:
ResultSet rs = connection.createStatement().executeQuery("SELECT * FROM Customers WHERE SSN='795-73-9838'");

Paksa enkripsi pada parameter input

Fitur Force Encryption memberlakukan enkripsi parameter dengan Always Encrypted. Jika enkripsi paksa digunakan dan SQL Server memberi tahu driver bahwa parameter tidak perlu dienkripsi, kueri yang menggunakan parameter akan gagal. Properti ini memberikan perlindungan tambahan terhadap serangan keamanan yang melibatkan SQL Server yang disusupi yang memberikan metadata enkripsi yang salah kepada klien, yang dapat menyebabkan pengungkapan data. Metode set* di SQLServerPreparedStatement kelas dan SQLServerCallableStatement dan update* metode di SQLServerResultSet kelas kelebihan beban untuk menerima argumen boolean untuk menentukan pengaturan enkripsi paksa. Jika nilai argumen ini salah, driver tidak akan memaksa enkripsi pada parameter. Jika enkripsi paksa diatur ke true, parameter kueri hanya dikirim jika kolom tujuan dienkripsi dan Always Encrypted diaktifkan pada koneksi atau pada pernyataan. Properti ini memberikan lapisan keamanan ekstra, memastikan bahwa driver tidak keliru mengirim data ke SQL Server sebagai teks biasa ketika diharapkan untuk dienkripsi.

Untuk informasi selengkapnya tentang SQLServerPreparedStatement metode dan SQLServerCallableStatement yang kelebihan beban dengan pengaturan enkripsi paksa, lihat Always Encrypted Referensi API untuk Driver JDBC

Mengontrol dampak performa Always Encrypted

Karena Always Encrypted adalah teknologi enkripsi sisi klien, sebagian besar overhead performa diamati di sisi klien, bukan dalam database. Terlepas dari biaya operasi enkripsi dan dekripsi, sumber overhead performa lainnya di sisi klien adalah:

  • Menambahkan perjalanan pulang pergi ke database untuk mengambil metadata untuk parameter kueri.
  • Memanggil ke penyimpanan kunci master kolom untuk mengakses kunci master kolom.

Bagian ini menjelaskan pengoptimalan performa bawaan di Driver Microsoft JDBC untuk SQL Server dan bagaimana Anda dapat mengontrol dampak dari dua faktor di atas pada performa.

Mengontrol perjalanan pulang pergi untuk mengambil metadata untuk parameter kueri

Jika Always Encrypted diaktifkan untuk koneksi, secara default driver akan memanggil sys.sp_describe_parameter_encryption untuk setiap kueri berparameter, meneruskan pernyataan kueri (tanpa nilai parameter apa pun) ke database. sys.sp_describe_parameter_encryption menganalisis pernyataan kueri untuk mengetahui apakah ada parameter yang perlu dienkripsi, dan jika demikian, untuk masing-masing parameter, ia mengembalikan informasi terkait enkripsi yang memungkinkan driver mengenkripsi nilai parameter. Perilaku ini memastikan tingkat transparansi yang tinggi terhadap aplikasi klien. Selama aplikasi menggunakan parameter untuk meneruskan nilai yang menargetkan kolom terenkripsi ke driver, aplikasi (dan pengembang aplikasi) tidak perlu mengetahui kueri mana yang mengakses kolom terenkripsi.

Mengatur Always Encrypted di tingkat kueri

Untuk mengontrol dampak performa pengambilan metadata enkripsi untuk kueri berparameter, Anda dapat mengaktifkan Always Encrypted untuk kueri individual alih-alih menyiapkannya untuk koneksi. Dengan cara ini Anda dapat memastikan bahwa sys.sp_describe_parameter_encryption hanya dipanggil untuk kueri yang Anda ketahui memiliki parameter yang menargetkan kolom terenkripsi. Namun, perhatikan bahwa dengan melakukannya Anda mengurangi transparansi enkripsi: jika Anda mengubah properti enkripsi kolom database, Anda mungkin perlu mengubah kode aplikasi Anda untuk menyelaraskannya dengan perubahan skema.

Untuk mengontrol perilaku Always Encrypted kueri individual, Anda perlu mengonfigurasi objek pernyataan individual dengan meneruskan Enum, SQLServerStatementColumnEncryptionSetting, yang menentukan bagaimana data akan dikirim dan diterima saat membaca dan menulis kolom terenkripsi untuk pernyataan tertentu tersebut. Berikut adalah beberapa panduan yang berguna:

  • Jika sebagian besar kueri yang dikirim aplikasi klien melalui kolom terenkripsi akses koneksi database, gunakan panduan berikut:

    • Atur columnEncryptionSetting kata kunci string koneksi ke Enabled.
    • Atur SQLServerStatementColumnEncryptionSetting.Disabled untuk kueri individual yang tidak mengakses kolom terenkripsi apa pun. Pengaturan ini akan menonaktifkan panggilan sys.sp_describe_parameter_encryption dan mendekripsi nilai apa pun dalam tataan hasil.
    • Atur SQLServerStatementColumnEncryptionSetting.ResultSet untuk kueri individual yang tidak memiliki parameter apa pun yang memerlukan enkripsi tetapi mengambil data dari kolom terenkripsi. Pengaturan ini akan menonaktifkan panggilan sys.sp_describe_parameter_encryption dan enkripsi parameter. Kueri akan mendekripsi hasil dari kolom enkripsi.
  • Jika sebagian besar kueri yang dikirim aplikasi klien melalui koneksi database tidak mengakses kolom terenkripsi, gunakan panduan berikut:

    • Atur columnEncryptionSetting kata kunci string koneksi ke Disabled.
    • Atur SQLServerStatementColumnEncryptionSetting.Enabled untuk kueri individual yang memiliki parameter apa pun yang perlu dienkripsi. Pengaturan ini akan memungkinkan panggilan sys.sp_describe_parameter_encryption dan dekripsi hasil kueri apa pun yang diambil dari kolom terenkripsi.
    • Atur SQLServerStatementColumnEncryptionSetting.ResultSet untuk kueri yang tidak memiliki parameter apa pun yang memerlukan enkripsi tetapi mengambil data dari kolom terenkripsi. Pengaturan ini akan menonaktifkan panggilan sys.sp_describe_parameter_encryption dan enkripsi parameter. Kueri akan mendekripsi hasil dari kolom enkripsi.

Pengaturan SQLServerStatementColumnEncryptionSetting tidak dapat digunakan untuk melewati enkripsi dan mendapatkan akses ke data teks biasa. Untuk informasi selengkapnya tentang cara mengonfigurasi enkripsi kolom pada pernyataan, lihat Always Encrypted Referensi API untuk Driver JDBC.

Dalam contoh berikut, Always Encrypted dinonaktifkan untuk koneksi database. Kueri masalah aplikasi memiliki parameter yang menargetkan kolom LastName yang tidak dienkripsi. Kueri mengambil data dari kolom SSN dan BirthDate yang keduanya dienkripsi. Dalam kasus seperti itu, panggilan sys.sp_describe_parameter_encryption untuk mengambil metadata enkripsi tidak diperlukan. Namun, dekripsi hasil kueri perlu diaktifkan sehingga aplikasi dapat menerima nilai teks biasa dari dua kolom terenkripsi. Pengaturan SQLServerStatementColumnEncryptionSetting.ResultSet digunakan untuk memastikan bahwa.

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

Penembolokan metadata parameter kueri

Untuk mengurangi jumlah perjalanan pulang pergi ke database, Driver Microsoft JDBC untuk SQL Server dapat menyimpan informasi terkait enkripsi untuk parameter kueri. Pada versi 11.2.0, informasi terkait enkripsi untuk parameter yang dikembalikan dari panggilan sys.sp_describe_parameter_encryption akan di-cache oleh driver jika proses SQL Server terkait tidak menggunakan enklave aman. Untuk penembolokan dengan penggunaan enklave aman, server harus mendukung pembentukan ulang sesi enklave jika sesi tidak lagi valid.

Penembolokan kunci enkripsi kolom

Untuk mengurangi jumlah panggilan ke penyimpanan kunci master kolom untuk mendekripsi kunci enkripsi kolom, Driver Microsoft JDBC untuk SQL Server menyimpan kunci enkripsi kolom teks biasa dalam memori. Setelah driver menerima nilai kunci enkripsi kolom terenkripsi dari metadata database, driver pertama-tama mencoba menemukan kunci enkripsi kolom teks biasa yang sesuai dengan nilai kunci terenkripsi. Driver memanggil keystore yang berisi kunci master kolom hanya jika tidak dapat menemukan nilai kunci enkripsi kolom terenkripsi di cache.

Anda dapat mengonfigurasi nilai time-to-live untuk entri kunci enkripsi kolom di cache dengan API, setColumnEncryptionKeyCacheTtl(), di SQLServerConnection kelas . Nilai time-to-live default untuk entri kunci enkripsi kolom dalam cache adalah dua jam. Untuk menonaktifkan penembolokan, gunakan nilai 0. Untuk mengatur nilai time-to-live, gunakan API berikut:

SQLServerConnection.setColumnEncryptionKeyCacheTtl (int columnEncryptionKeyCacheTTL, TimeUnit unit)

Misalnya, untuk mengatur nilai time-to-live 10 menit, gunakan:

SQLServerConnection.setColumnEncryptionKeyCacheTtl (10, TimeUnit.MINUTES)

Hanya DAYS, HOURS, MINUTES, atau SECONDS yang didukung sebagai unit waktu.

Menyalin data terenkripsi dengan SQLServerBulkCopy

Dengan SQLServerBulkCopy, Anda dapat menyalin data yang sudah dienkripsi dan disimpan dalam satu tabel ke tabel lain tanpa mendekripsi data. Untuk melakukannya:

  • Pastikan konfigurasi enkripsi tabel target identik dengan konfigurasi tabel sumber. Secara khusus, kedua tabel harus memiliki kolom yang sama yang dienkripsi dan kolom harus dienkripsi menggunakan jenis enkripsi yang sama dan kunci enkripsi yang sama. Jika ada kolom target yang dienkripsi secara berbeda dari kolom sumber terkait, Anda tidak akan dapat mendekripsi data dalam tabel target setelah operasi salin. Data akan rusak.
  • Konfigurasikan kedua koneksi database ke tabel sumber dan ke tabel target tanpa Always Encrypted diaktifkan.
  • Atur allowEncryptedValueModifications opsi . Untuk informasi selengkapnya, lihat Menggunakan salinan massal dengan driver JDBC.

Catatan

Berhati-hatilah saat menentukan AllowEncryptedValueModifications sebagai opsi ini dapat menyebabkan kerusakan database karena Driver Microsoft JDBC untuk SQL Server tidak memeriksa apakah data memang dienkripsi atau apakah dienkripsi dengan benar dengan jenis enkripsi, algoritma, dan kunci yang sama dengan kolom target.

Lihat juga

Always Encrypted (Mesin Database)