All Products
Search
Document Center

ApsaraDB RDS:Kelola otorisasi

Last Updated:Jul 06, 2025

Jika Anda mengaktifkan fitur database selalu rahasia untuk instance ApsaraDB RDS for PostgreSQL, data teks terenkripsi dari pengguna yang berbeda pada instance RDS akan diisolasi secara otomatis. Topik ini menjelaskan cara mengelola izin untuk menerapkan komputasi terintegrasi data multi-pengguna dan memberi otorisasi kepada administrator basis data (DBA) untuk melakukan operasi data berisiko tinggi.

Deskripsi fitur

Dalam database selalu rahasia, data pengguna dienkripsi menggunakan kunci enkripsi data (DEK) milik pengguna. Data teks terenkripsi dari pengguna yang berbeda diisolasi secara otomatis. Jika query gabungan melibatkan data dari beberapa pengguna, pemilik data harus memberikan izin kepada subjek yang ingin menanyakan data untuk mengakses data teks terenkripsi milik pemilik data. Proses ini disebut manajemen otorisasi multi-pengguna.

Dalam database selalu rahasia, Anda dapat menggunakan daftar kontrol perilaku (BCL) untuk memberikan izin akses pada data multi-pengguna. Setelah BCL diterbitkan, subjek dapat melakukan query gabungan tanpa perlu memahami konten data dari penerbit yang memiliki data. Penerbit tidak perlu khawatir tentang kebocoran data dan dapat menerbitkan atau mencabut BCL untuk membatasi perilaku subjek. Ini dengan cepat dan efektif mencegah data digunakan secara tidak terduga.

Untuk memberi otorisasi kepada DBA melakukan operasi berisiko tinggi pada database selalu rahasia, seperti membuat kunci kustom atau mengonversi data antara teks biasa dan teks terenkripsi, Anda dapat menerbitkan BCL. Dengan cara ini, DBA dapat melakukan operasi terkait menggunakan akun. Dalam hal ini, subjek dan penerbit menggunakan akun yang sama.

Prasyarat

  • Fitur database selalu rahasia telah diaktifkan. Untuk informasi lebih lanjut, lihat Aktifkan fitur database selalu rahasia.

    Catatan

    Versi mesin minor dari instance RDS adalah 20230830 atau lebih baru. Untuk informasi lebih lanjut tentang cara memperbarui versi mesin minor dari instance RDS, lihat Perbarui versi mesin minor.

  • Akun dibuat secara terpisah untuk subjek dan penerbit. Untuk informasi lebih lanjut, lihat Buat akun.

  • Data sensitif telah didefinisikan. Untuk informasi lebih lanjut, lihat Tentukan data sensitif.

  • SDK EncDB digunakan oleh subjek dan penerbit untuk masuk secara terpisah ke database yang diperlukan dan membuat data uji dalam database. Untuk informasi lebih lanjut, lihat Gunakan fitur database selalu rahasia dari klien.

Langkah 1: Inisialisasi kunci publik dan privat

    1. Instal OpenSSL.

      Dalam topik ini, OpenSSL, toolkit kriptografi sumber terbuka, digunakan untuk mendapatkan kunci publik dan privat. Sistem operasi Linux dilengkapi dengan OpenSSL. Jika Anda menggunakan sistem operasi Linux, Anda tidak perlu menginstal OpenSSL. Jika Anda menggunakan sistem operasi Windows, Anda harus mengunduh paket perangkat lunak OpenSSL dan menginstal OpenSSL. Untuk informasi lebih lanjut, lihat Win32/Win64 OpenSSL.

      Catatan

      Jika Anda menggunakan instance RDS yang fitur database selalu rahasia (edisi dasar)-nya diaktifkan, Anda harus menggunakan algoritma SM2 untuk menghasilkan kunci publik dan privat. Anda dapat menjalankan perintah openssl ecparam -list_curves untuk memeriksa apakah algoritma SM2 didukung oleh versi OpenSSL saat ini. Instance RDS yang fitur database selalu rahasia (edisi dasar)-nya diaktifkan adalah instance RDS yang menggunakan tipe instans berbasis non-Intel SGX. Jika SM2 termasuk dalam output perintah, algoritma SM2 didukung. Jika SM2 tidak termasuk dalam output perintah, Anda harus meningkatkan OpenSSL ke versi yang mendukung algoritma SM2. Anda dapat meningkatkan OpenSSL ke V1.1.1 atau lebih baru.

    2. Dapatkan kunci publik dan privat subjek dan penerbit berdasarkan tipe instans dari instance RDS yang fitur database selalu rahasia-nya diaktifkan.

      Dua file kunci dihasilkan. File pub_key.pem adalah file kunci publik. File pri_key_pkcs8.pem adalah file kunci privat.

      Edisi diperkuat perangkat keras dari fitur database selalu rahasia (berbasis Intel SGX)

      Instance RDS yang fitur database selalu rahasia (edisi diperkuat perangkat keras)-nya diaktifkan adalah instance RDS yang menggunakan tipe instans berbasis Intel SGX. Untuk informasi lebih lanjut, lihat Tipe Instans untuk Instance Utama ApsaraDB RDS for PostgreSQL.

      Anda dapat melakukan operasi berikut untuk mendapatkan kunci publik dan privat:

      1. Gunakan OpenSSL untuk menghasilkan kunci privat Rivest-Shamir-Adleman (RSA).

        openssl genpkey -algorithm RSA -out pri_key_pkcs8.pem -pkeyopt rsa_keygen_bits:3072
        Catatan

        3072: panjang kunci. Anda dapat mengubah panjang kunci ke nilai lain yang sesuai dengan standar keamanan berdasarkan persyaratan bisnis Anda.

      2. Gunakan OpenSSL untuk menghasilkan kunci publik RSA.

        openssl rsa -in pri_key_pkcs8.pem -pubout -out pub_key.pem

      Edisi dasar dari fitur database selalu rahasia

      Instance RDS yang fitur database selalu rahasia (edisi dasar)-nya diaktifkan adalah instance RDS yang menggunakan tipe instans berbasis non-Intel SGX. Untuk informasi lebih lanjut, lihat Tipe Instans untuk Instance Utama ApsaraDB RDS for PostgreSQL.

      Anda dapat melakukan operasi berikut untuk mendapatkan kunci publik dan privat:

      1. Gunakan OpenSSL untuk menghasilkan kunci privat SM2.

        # Hasilkan kunci privat dalam format Public-Key Cryptography Standards (PKCS)#1.
        openssl ecparam -out ec_param.pem -name SM2 -param_enc explicit -genkey
        # Konversikan kunci privat ke format PKCS#8.
        openssl pkcs8 -topk8 -inform PEM -in ec_param.pem -outform pem -nocrypt -out pri_key_pkcs8.pem
      2. Gunakan OpenSSL untuk menghasilkan kunci publik SM2.

        openssl ec -in ec_param.pem -pubout -out pub_key.pem
  1. Inisialisasi kunci publik dan privat subjek dan penerbit secara terpisah.

    Inisialisasi kunci publik dan privat subjek dan penerbit dalam kode bisnis mereka.

    Catatan

    Baris baru secara otomatis ditambahkan dalam file PEM yang dihasilkan menggunakan OpenSSL. Di editor tertentu, tampilan baris baru mungkin dioptimalkan. Akibatnya, ketika Anda menyalin isi file PEM secara manual, baris baru dihilangkan. Kami sarankan Anda menggunakan kode program untuk membaca isi file PEM untuk memastikan akurasi data.

    // Kunci privat subjek dan penerbit.
    String userPrkPemString = readPemFile("path/to/pri_key_pkcs8.pem");
    // Kunci publik subjek dan penerbit.
    String userPukPemString = readPemFile("path/to/pub_key.pem");
    // Inisialisasi kunci. Kunci hanya perlu diinisialisasi sekali.
    KeyManager km = sdk.getKeyManager();
    km.registerCertificate(userPrkPemString, userPukPemString);

Langkah 2: Berikan izin akses pada data multi-pengguna

  1. Tentukan konten otorisasi.

    String bclBodyJsonString = """{
      "version": 1,
      "serial_num": "a121bac0-5cb3-4463-a2b2-1155ff29f4c8",
      "issuer_pukid": "p81x+WqYb7BR0yP0LK0qiEaxgLDqwuIjfJhgC0mMJcE=",
      "subject_pukid": "qIPPfgTJEEG/9WkjP0E5LLAijZ14h/Qgb2EfmBZCWSo=",
      "validity": {
        "not_after": "20250820111111+0800",
        "not_before": "20240820111111+0800"
      },
      "policies": {
        "issuer_dek_group": [
          {
            "min": 1,
            "max": 100000,
            "groupid": "5bc60759-5b05-45ec-afc1-ffca1229e554"
          }
        ],
        "result_dek": "SUBJECT",
        "subject_dek_group": [
          {
            "min": 1,
            "max": 100000,
            "groupid": "53413af9-f90a-48a9-93b6-49847861b823"
          }
        ],
        "operation": [
          "*"
        ],
        "postproc": "NULL",
        "preproc": "NULL"
      }
    }
      """;

    Deskripsi Parameter

    Parameter

    Nilai contoh

    Deskripsi

    version

    1

    Nomor versi. Atur nilainya menjadi 1.

    serial_num

    "a121bac0-5cb3-4463-a2b2-1155ff29f4c8"

    Nomor seri unik, acak dalam format UUID.

    Catatan

    Anda dapat menggunakan alat pembuatan UUID untuk menghasilkan nomor seri.

    issuer_pukid

    "p81x+WqYb7BR0yP0LK0qiEaxgLDqwuIjfJhgC0mMJcE="

    Ringkasan kunci publik penerbit. Anda dapat menjalankan kode berikut untuk mendapatkan ringkasan kunci publik berdasarkan edisi fitur database selalu rahasia dan file kunci publik pub_key.pem yang dihasilkan:

    • Edisi diperkuat perangkat keras (berbasis Intel SGX)

      openssl sha256 -binary pub_key.pem | openssl base64

    • Edisi dasar

      openssl sm3 -binary pub_key.pem | openssl base64

    subject_pukid

    "qIPPfgTJEEG/9WkjP0E5LLAijZ14h/Qgb2EfmBZCWSo="

    Ringkasan kunci publik subjek. Anda dapat mengonfigurasi parameter ini dengan cara yang sama seperti Anda mengonfigurasi parameter issuer_pukid.

    validity

    { "not_before": "20240820111111+0800", "not_after": "20250820111111+0800"}

    Periode validitas otorisasi. Nilainya harus dalam format waktu GeneralizedTime.

    • "not_before": awal periode validitas.

    • "not_after": akhir periode validitas.

    policies

    issuer_dek_group

    [ { "min": 1, "max": 100000, "groupid": "5bc60759-5b05-45ec-afc1-ffca1229e554" }]

    Grup DEK yang diizinkan oleh penerbit.

    • "groupid": ID grup DEK

    • "min": ID DEK terkecil dalam grup DEK

    • "max": ID DEK terbesar dalam grup DEK

    Catatan

    Dalam grup DEK, nilai ID DEK harus meningkat secara monoton.

    Anda dapat menjalankan kode berikut untuk mendapatkan nilai parameter groupid:

    SELECT encdb_get_cc_entry_by_name(encdb.keyname_generate('<user_name>', '<database_name>', '<schema_name>', '<table_name>', '<column_name>'));

    Catatan
    • <user_name>: nama pengguna penerbit

    • <table_name>: nama tabel yang diizinkan penerbit untuk diakses subjek

    • <column_name>: nama kolom yang diizinkan penerbit untuk diakses subjek

    subject_dek_group

    [ { "min": 1, "max": 100000, "groupid": "53413af9-f90a-48a9-93b6-49847861b823" }]

    Grup DEK yang diizinkan untuk diakses subjek.

    • "groupid": ID grup DEK

    • "min": ID DEK terkecil dalam grup DEK

    • "max": ID DEK terbesar dalam grup DEK

    Catatan

    Dalam grup DEK, nilai ID DEK harus meningkat secara monoton.

    Anda dapat menjalankan kode berikut untuk mendapatkan nilai parameter groupid:

    SELECT encdb_get_cc_entry_by_name(encdb.keyname_generate('<user_name>', '<database_name>', '<schema_name>', '<table_name>', '<column_name>'));

    Catatan
    • <user_name>: nama pengguna subjek

    • <table_name>: nama tabel yang diizinkan untuk diakses subjek.

    • <column_name>: nama kolom yang diizinkan untuk diakses subjek.

    result_dek

    "SUBJECT"

    DEK yang digunakan untuk mengenkripsi hasil komputasi.

    • "SUBJECT": menggunakan DEK subjek dalam komputasi saat ini.

    • "ISSUER": menggunakan DEK penerbit dalam komputasi saat ini.

    • ID DEK: menggunakan DEK dengan ID DEK tertentu.

      Penting

      Jika Anda ingin menggunakan DEK tertentu, atur parameter ini ke ID DEK. Anda dapat langsung menentukan ID DEK tanpa perlu mengapit ID DEK dalam tanda kutip ganda ("").

    operation

    [ "*"]

    Operasi komputasi yang diizinkan.

    • "encrypt": enkripsi

    • "decrypt": dekripsi

    • "cmp": perbandingan

    • "*": semua operasi

    postproc

    "NULL"

    Operasi pra-pemrosesan sebelum komputasi. Atur nilainya menjadi NULL, yang menentukan bahwa tidak ada operasi pra-pemrosesan yang diperlukan.

    preproc

    "NULL"

    Operasi pasca-pemrosesan setelah komputasi. Atur nilainya menjadi NULL, yang menentukan bahwa tidak ada operasi pasca-pemrosesan yang diperlukan.

  2. Berikan izin akses pada data multi-pengguna

    • Penerbit menggunakan kunci publik dan privatnya untuk menerbitkan BCL guna memberi otorisasi kepada subjek untuk mengakses data di kolom yang diizinkan.

      boolean isIssuer = true;
      bclBodyJsonString = km.issueBCL(bclBodyJsonString, userPukPemString, userPrkPemString, isIssuer);
    • Subjek menggunakan kunci publik dan privatnya untuk menerbitkan BCL dan mengakses data yang diizinkan.

      boolean isIssuer = false;
      bclBodyJsonString = km.issueBCL(bclBodyJsonString, userPukPemString, userPrkPemString, isIssuer);

(Opsional) Langkah 3: Cabut otorisasi

Jika penerbit ingin mencabut otorisasi, lakukan operasi berikut:

  1. Tentukan otorisasi yang akan dicabut.

    String brlBodyJsonString = """{
      "version": 1,
      "pukid": "dYJ3Wfj/n0eZbuqgQjv8bnBdPXGyWGOlxE/uMy16NXo=",
      "this_update": "20220819111128+0800",
      "next_update": "20220919111128+0800",
      "revoked": [
        {
          "revocation_date": "20220819111128+0800",
          "serial_num": "a121bac0-5cb3-4463-a2b2-1155ff29f4c8"
        }
      ]
    }
     """;

    Deskripsi Parameter

    Parameter

    Nilai contoh

    Deskripsi

    version

    1

    Nomor versi. Atur nilainya menjadi 1.

    pukid

    "dYJ3Wfj/n0eZbuqgQjv8bnBdPXGyWGOlxE/uMy16NXo="

    Ringkasan kunci publik penerbit. Atur nilainya menjadi ringkasan kunci publik penerbit dalam aplikasi otorisasi BCL. Anda dapat mengonfigurasi parameter ini dengan cara yang sama seperti Anda mengonfigurasi parameter yang dijelaskan dalam Tentukan konten otorisasi.

    this_update

    "20220819111128+0800"

    Saat pembaruan daftar pencabutan. Nilainya harus dalam format waktu GeneralizedTime.

    next_update

    "20220919111128+0800"

    Waktu pembaruan berikutnya dari daftar pencabutan. Nilainya harus dalam format waktu GeneralizedTime.

    revoked

    revocation_date

    "20220819111128+0800"

    Saat otorisasi dicabut. Nilainya harus dalam format waktu GeneralizedTime.

    serial_num

    "a121bac0-5cb3-4463-a2b2-1155ff29f4c8"

    Nomor seri dalam format UUID. Anda harus mengatur parameter ini ke nomor seri yang ditentukan dalam aplikasi otorisasi BCL.

  2. Penerbit menyetujui dan menerbitkan pencabutan.

    brlBodyJsonString = km.revokeBCL(brlBodyJsonString, userPukPemString, userPrkPemString);

Skenario

Skenario 1: Operasi berisiko tinggi: konversi data antara teks biasa dan teks terenkripsi

Jika Anda ingin mengaktifkan fitur database selalu rahasia untuk basis data teks biasa yang sudah ada tanpa perlu memigrasi data, Anda dapat langsung mengonversi data teks biasa di basis data menjadi data teks terenkripsi. Untuk informasi lebih lanjut, lihat Konversi Data antara Teks Biasa dan Teks Terenkripsi. Secara default, pengguna termasuk DBA tidak dapat melakukan konversi data antara teks biasa dan teks terenkripsi pada database selalu rahasia karena operasi ini dianggap sebagai operasi berisiko tinggi.

Jika Anda memahami dan mengakui risiko keamanan data, Anda dapat menerbitkan BCL untuk memberi otorisasi kepada pengguna yang diperlukan untuk secara langsung mengonversi data antara teks biasa dan teks terenkripsi pada database selalu rahasia. Dalam hal ini, subjek dan penerbit menggunakan akun yang sama.

  • Ketika Anda mendefinisikan konten BCL, Anda harus mengatur parameter issuer_pukid ke nilai yang sama dengan parameter subject_pukid dan parameter issuer_dek_group ke nilai yang sama dengan parameter subject_dek_group. Untuk informasi lebih lanjut, lihat Tentukan Konten Otorisasi.

    Tentukan Konten Otorisasi

    String bclBodyJsonString = """{
      "version": 1,
      "serial_num": "fdbed057-7fe5-4c31-97ae-afdd615732fe",
      "issuer_pukid": "p81x+WqYb7BR0yP0LK0qiEaxgLDqwuIjfJhgC0mMJcE=",
      "subject_pukid": "p81x+WqYb7BR0yP0LK0qiEaxgLDqwuIjfJhgC0mMJcE=",
      "validity": {
        "not_after": "20250920111111+0800",
        "not_before": "20240920111111+0800"
      },
      "policies": {
        "issuer_dek_group": [
          {
            "min": 1,
            "max": 100000,
            "groupid": "93165651-609f-47db-91aa-c3a2ab7084c4"
          }
        ],
        "result_dek": "SUBJECT",
        "subject_dek_group": [
          {
            "min": 1,
            "max": 100000,
            "groupid": "93165651-609f-47db-91aa-c3a2ab7084c4"
          }
        ],
        "operation": [
          "*"
        ],
        "postproc": "NULL",
        "preproc": "NULL"
      }
    }
     """;
  • Ketika Anda menginisialisasi kunci publik dan privat, subjek dan penerbit menggunakan kunci publik dan privat yang sama. Kunci publik ditentukan oleh parameter userPukPemString, dan kunci privat ditentukan oleh parameter userPrkPemString. Nilai parameter userPukPemString dan userPrkPemString untuk penerbit sama dengan nilai parameter tersebut untuk subjek. Untuk informasi lebih lanjut, lihat Inisialisasi Kunci Publik dan Privat.

    Terbitkan BCL

    // Subjek dan penerbit menggunakan kunci publik dan privat yang sama untuk menerbitkan BCL.
    bclBodyJsonString = km.issueBCL(bclBodyJsonString, userPukPemString, userPrkPemString, true); 
    bclBodyJsonString = km.issueBCL(bclBodyJsonString, userPukPemString, userPrkPemString, false);

Setelah Anda menerbitkan BCL, Anda dapat melakukan operasi yang diperlukan menggunakan akun yang telah diberi otorisasi. Contoh kode:

Operasi Berisiko Tinggi: Konversi Data antara Teks Biasa dan Teks Terenkripsi

package org.example;

import com.alibaba.encdb.EncdbSDK;
import com.alibaba.encdb.crypto.EncdbSDKBuilder;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class Main {
    public static void main(String[] args) {
        Main main = new Main();
        main.testSelfBcl();
    }

    private String readPemFile(String filename) throws IOException {
        return new String(Files.readAllBytes(Paths.get(filename)));
    }

    public void testSelfBcl() {
        // Ganti nilai parameter dengan informasi sebenarnya tentang instance RDS.
        String hostname = "pgm-****.pg.rds.aliyuncs.com";
        String port = "5432";
        String dbname = "testdb";
        String username = "testdbuser";
        String password = "****";
        String mek = "00112233445566778899aabbccddeeff";  // Ini adalah nilai contoh. Kami sarankan Anda menggunakan kunci enkripsi utama (MEK) yang lebih kompleks.
        String prikeyFilename = "D:\\test\\Issuer\\pri_key_pkcs8.pem"; // Ini adalah nilai contoh. Anda harus mengganti nilainya dengan jalur file kunci.
        String pubkeyFilename = "D:\\test\\Issuer\\pub_key.pem"; // Ini adalah nilai contoh. Anda harus mengganti nilainya dengan jalur file kunci.
        try (Connection conn = DriverManager.getConnection("jdbc:postgresql://" + hostname + ":" + port + "/" + dbname + "?binaryTransfer=true", username, password)) {
            System.out.println("koneksi berhasil");
            String tblname = "tbl_alter_" + conn.getMetaData().getUserName();
            // Buat tabel.
            conn.createStatement().executeUpdate("DROP TABLE IF EXISTS " + tblname);
            conn.createStatement().executeUpdate("CREATE TABLE " + tblname + " (id int)");
            // Tulis data teks biasa ke dalam tabel.
            try (PreparedStatement stmt = conn.prepareStatement("INSERT INTO " + tblname + " VALUES(?)")) {
                stmt.setInt(1, 231);
                stmt.executeUpdate();
            }
            // Opsional. Bersihkan catatan BCL historis.
            conn.createStatement().executeUpdate("DELETE FROM encdb.encdb_internal_bcl_table");
            conn.createStatement().executeUpdate("DELETE FROM encdb.encdb_internal_brl_map_table");
            conn.createStatement().executeUpdate("DELETE FROM encdb.encdb_internal_brl_table");
            // Inisialisasi SDK dan distribusikan MEK.
            EncdbSDK sdk = EncdbSDKBuilder.newInstance()
                    .setDbConnection(conn)
                    .setMek(mek)
                    .build();
            System.out.println("inisialisasi berhasil");
            // Baca konten kunci publik dan privat dari file kunci.
            String userPrkPemString = readPemFile(prikeyFilename);
            String userPukPemString = readPemFile(pubkeyFilename);
            // Daftarkan kunci publik dan privat.
            sdk.getKeyManager().registerCertificate(userPrkPemString, userPukPemString);
            System.out.println("registrasi sertifikat berhasil");

            // Gunakan DEK tertentu untuk mengonversi kolom teks biasa menjadi kolom teks terenkripsi. Misalnya, Anda dapat menggunakan encdb.dek_generate untuk menghasilkan DEK.
            // DEK default digunakan. Anda dapat menggunakan encdb.keyname_generate untuk menghasilkan keyname yang diperlukan.
            String keyname = "'|" + username + "|" + dbname + "|'";
            String alterStmString = "ALTER TABLE " + tblname + " ALTER COLUMN id SET DATA TYPE enc_int4 USING encdb.enc_int4_encrypt(id, " + keyname + ")";

            // Perubahan teks biasa gagal sebelum otorisasi.
            try {
                conn.createStatement().executeUpdate(alterStmString);
            } catch (SQLException exception) {
                if (exception.getMessage().contains("fa030000") || exception.getMessage().contains("fa020000")) {
                    System.out.println("mengubah kolom menjadi enc_int4 gagal tanpa otorisasi");
                } else {
                    throw exception; // Lempar ulang pengecualian lainnya.
                }
            }
            // Lakukan otorisasi multi-pengguna.
            String bclBodyJsonString = """
                    {
                        "version": 1,
                        "serial_num": "fdbed057-7fe5-4c31-97ae-afdd615732fe",
                        "issuer_pukid": "p81x+WqYb7BR0yP0LK0qiEaxgLDqwuIjfJhgC0mMJcE=",
                        "subject_pukid": "p81x+WqYb7BR0yP0LK0qiEaxgLDqwuIjfJhgC0mMJcE=",
                        "validity": {
                            "not_after": "20250920111111+0800",
                            "not_before": "20240920111111+0800"
                        },
                        "policies": {
                            "issuer_dek_group": [
                                {
                                    "min": 1,
                                    "max": 100000,
                                    "groupid": "93165651-609f-47db-91aa-c3a2ab7084c4"
                                }
                            ],
                            "result_dek": "SUBJECT",
                            "subject_dek_group": [
                                {
                                    "min": 1,
                                    "max": 100000,
                                    "groupid": "93165651-609f-47db-91aa-c3a2ab7084c4"
                                }
                            ],
                            "operation": [
                                "*"
                            ],
                            "postproc": "NULL",
                            "preproc": "NULL"
                        }
                    }
                    """;
            // Terbitkan otorisasi.
            {
                bclBodyJsonString = sdk.getKeyManager().issueBCL(bclBodyJsonString, userPukPemString, userPrkPemString, true);
                bclBodyJsonString = sdk.getKeyManager().issueBCL(bclBodyJsonString, userPukPemString, userPrkPemString, false);
                System.out.println("penerbitan bcl berhasil");
            }
            // Perubahan teks biasa berhasil setelah otorisasi.
            conn.createStatement().executeUpdate(alterStmString);
            System.out.println("mengubah kolom menjadi enc_int4 berhasil setelah diberi otorisasi");
        } catch (SQLException |
                 IOException e) {
            e.printStackTrace(); // Cetak pengecualian.
        }
    }
}

Skenario 2: Query gabungan data multi-pengguna

Sebagai contoh, perusahaan platform data mendapatkan izin yang diperlukan untuk menggunakan platform data untuk mengumpulkan data pengguna dan menghasilkan profil pengguna. Kemudian, perusahaan platform data memberi otorisasi kepada pihak ketiga, seperti perusahaan asuransi, untuk menggunakan data tersebut untuk integrasi dan komputasi data multipihak. Ini membantu menerapkan pemasaran bersama. Untuk informasi lebih lanjut, lihat Integrasi dan Komputasi Data Multipihak.

Kode berikut memberikan contoh cara melakukan query gabungan.

Query Gabungan Data Multi-Pengguna

package org.example;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import com.alibaba.encdb.Cryptor;
import com.alibaba.encdb.EncdbSDK;
import com.alibaba.encdb.common.Constants.EncAlgo;
import com.alibaba.encdb.crypto.EncdbSDKBuilder;

public class BlcTest {
    private String readPemFile(String filename) throws IOException {
        return new String(Files.readAllBytes(Paths.get(filename)));
    }

    private void prepareData(Connection conn, EncdbSDK sdk) throws SQLException {
        int id = 1;
        String name = "name";
        int price = 1234;
        float miles = 12.34f;
        String secret = "aliyun";

        String tblname = "tbl_" + conn.getMetaData().getUserName();
        // Buat tabel.
        conn.createStatement().executeUpdate("DROP TABLE IF EXISTS " + tblname);
        conn.createStatement().executeUpdate("CREATE TABLE " + tblname
                + " (id INTEGER, name VARCHAR, price enc_int4, miles enc_float4, secret enc_text, PRIMARY KEY (id))");

        // Tulis data.
        PreparedStatement stmt = conn.prepareStatement(
                "INSERT INTO " + tblname + " (id, name, price, miles, secret) VALUES(?,?,?,?,?)");
        stmt.setInt(1, id);
        stmt.setString(2, name);
        // Enkripsi dan tulis data.
        Cryptor cryptor = sdk.getCryptor();
        stmt.setBytes(3, cryptor.encrypt(tblname, "price", price));
        stmt.setBytes(4, cryptor.encrypt(tblname, "miles", miles));
        stmt.setBytes(5, cryptor.encrypt(tblname, "secret", secret));
        stmt.execute();
    }

    private void validateResult(ResultSet rs, EncdbSDK sdk) throws SQLException {
        int id = 1;
        String name = "name";
        int price = 1234;
        float miles = 12.34f;
        String secret = "aliyun";

        Cryptor cryptor = sdk.getCryptor();
        while (rs.next()) {
            int idRs = rs.getInt(1);
            Assertions.assertEquals(id, idRs);
            String nameRs = rs.getString(2);
            Assertions.assertEquals(name, nameRs);
            int priceRs = cryptor.decryptInt(rs.getBytes(3));
            Assertions.assertEquals(price, priceRs);
            float milesRs = cryptor.decryptFloat(rs.getBytes(4));
            Assertions.assertEquals(miles, milesRs, 0.000001f);
            String secretRs = cryptor.decryptString(rs.getBytes(5));
            Assertions.assertEquals(secret, secretRs);
        }
    }

    private void simpleQuery(Connection conn, EncdbSDK sdk) throws SQLException {
        String tblname = "tbl_" + conn.getMetaData().getUserName();
        String sqlCmd = "SELECT * FROM " + tblname + " WHERE  price > ?";
        PreparedStatement stmt = conn.prepareStatement(sqlCmd);
        // Enkripsi konten query.
        Cryptor cryptor = sdk.getCryptor();
        stmt.setBytes(1, cryptor.encrypt(tblname, "price", 100));
        ResultSet rs = stmt.executeQuery();

        validateResult(rs, sdk);
    }

    private void subjectJoinQuery(Connection subjectConn, EncdbSDK subjectSdk, Connection issuerConn)
            throws SQLException {
        String issuerTblname = "tbl_" + issuerConn.getMetaData().getUserName();
        String subjectTblname = "tbl_" + subjectConn.getMetaData().getUserName();

        // Mulai query JOIN sebagai subjek untuk menanyakan data dalam tabel penerbit.
        String sqlCmd = "SELECT subject.id as id, subject.name as name, subject.price as price, subject.miles as miles, subject.secret as secret "
                + "FROM " + issuerTblname + " issuer, " + subjectTblname + " subject "
                + "WHERE issuer.price = subject.price and subject.price > ?";

        Connection conn = subjectConn;
        EncdbSDK sdk = subjectSdk;
        PreparedStatement stmt = conn.prepareStatement(sqlCmd);
        // Enkripsi konten query.
        Cryptor cryptor = sdk.getCryptor();
        stmt.setBytes(1, cryptor.encrypt(subjectTblname, "price", 100));
        ResultSet rs = stmt.executeQuery();

        validateResult(rs, sdk);
    }

    @Test
    public void testBcl() throws SQLException, IOException {
        // Konfigurasikan parameter hostname, port, dan dbname berdasarkan informasi tentang instance RDS.
        String hostname = "pgm-****.pg.rds.aliyuncs.com";
        String port = "5432";
        String dbname = "testdb";

        // Informasi tentang penerbit.
        Connection issuerConn;
        EncdbSDK issuerSdk;
        String issuerPriKeyPemString;
        String issuerPubKeyPemString;
        // Informasi tentang subjek.
        Connection subjectConn;
        EncdbSDK subjectSdk;
        String subjectPriKeyPemString;
        String subjectPubKeyPemString;

        // Inisialisasi koneksi basis data dan lengkapi distribusi MEK. Untuk informasi lebih lanjut, lihat EncDB SDK.
        {
            // Inisialisasi informasi penerbit.
            {
                // Konfigurasikan parameter username dan password berdasarkan informasi tentang instance RDS.
                String username = "testdbuser";
                String password = "****";
                String mek = "00112233445566778899aabbccddeeff"; // Ini adalah nilai contoh. Kami sarankan Anda menggunakan MEK yang lebih kompleks.

                // Buat koneksi basis data.
                String dbUrl = String.format("jdbc:postgresql://%s:%s/%s?binaryTransfer=true", hostname, port, dbname);
                issuerConn = DriverManager.getConnection(dbUrl, username, password);
                System.out.println("koneksi penerbit berhasil");

                // Inisialisasi SDK dan distribusikan MEK. Untuk informasi lebih lanjut, lihat EncDB SDK.
                issuerSdk = EncdbSDKBuilder.newInstance()
                        .setDbConnection(issuerConn)
                        .setMek(mek)
                        .setEncAlgo(EncAlgo.SM4_128_CBC)
                        .build();
                System.out.println("inisialisasi penerbit berhasil");
            }
            // Inisialisasi informasi subjek.
            {
                // Konfigurasikan parameter username dan password berdasarkan informasi tentang instance RDS.
                String username = "testdbuser02";
                String password = "****";
                String mek = "ffeeddccbbaa99887766554433221100"; // Ini adalah nilai contoh. Kami sarankan Anda menggunakan MEK yang lebih kompleks.

                // Buat koneksi basis data.
                String dbUrl = String.format("jdbc:postgresql://%s:%s/%s?binaryTransfer=true", hostname, port, dbname);
                subjectConn = DriverManager.getConnection(dbUrl, username, password);
                System.out.println("koneksi subjek berhasil");

                // Inisialisasi SDK dan distribusikan MEK. Untuk informasi lebih lanjut, lihat EncDB SDK.
                subjectSdk = EncdbSDKBuilder.newInstance()
                        .setDbConnection(subjectConn)
                        .setMek(mek)
                        .setEncAlgo(EncAlgo.SM4_128_CBC)
                        .build();
                System.out.println("inisialisasi subjek berhasil");
            }

            // Masukkan data uji dan verifikasi bahwa pemilik data dapat mengakses dan melihat data teks biasa setelah dekripsi.
            {
                // Tulis data sebagai penerbit dan verifikasi data.
                {
                    Connection tmpConnection = issuerConn;
                    EncdbSDK tmpSdk = issuerSdk;

                    // Persiapkan data uji sebagai penerbit dan enkripsi bidang tertentu.
                    prepareData(tmpConnection, tmpSdk);
                    System.out.println("data penerbit siap");

                    // Tanyakan data sebagai penerbit untuk memeriksa apakah penerbit dapat mengakses dan melihat data teks biasa setelah dekripsi.
                    Assertions.assertDoesNotThrow(() -> {
                        simpleQuery(tmpConnection, tmpSdk);
                    });
                    System.out.println("penerbit berhasil menanyakan data miliknya sendiri");
                }
                // Tulis data sebagai subjek dan verifikasi data.
                {
                    Connection tmpConnection = subjectConn;
                    EncdbSDK tmpSdk = subjectSdk;

                    // Persiapkan data uji sebagai subjek dan enkripsi bidang tertentu.
                    prepareData(tmpConnection, tmpSdk);
                    System.out.println("data subjek siap");

                    // Tanyakan data sebagai subjek untuk memeriksa apakah subjek dapat mengakses dan melihat data teks biasa setelah dekripsi.
                    Assertions.assertDoesNotThrow(() -> {
                        simpleQuery(tmpConnection, tmpSdk);
                    });
                    System.out.println("subjek berhasil menanyakan data miliknya sendiri");
                }
                // Berikan izin akses pada tabel data. Konten data dalam tabel tidak dapat diakses.
                {
                    String tblname = "tbl_" + issuerConn.getMetaData().getUserName();
                    issuerConn.createStatement().execute(
                            "GRANT SELECT ON TABLE " + tblname + " TO " + subjectConn.getMetaData().getUserName());

                    // Bersihkan catatan BCL historis.
                    subjectConn.createStatement().execute("DELETE FROM encdb.encdb_internal_bcl_table");
                    subjectConn.createStatement().execute("DELETE FROM encdb.encdb_internal_brl_map_table");
                    subjectConn.createStatement().execute("DELETE FROM encdb.encdb_internal_brl_table");
                }
            }
        }

        // Daftarkan kunci publik dan privat subjek dan penerbit. Anda hanya perlu melakukan operasi ini sekali selama inisialisasi.
        {
            // Daftarkan kunci publik dan privat sebagai penerbit.
            {
                // Baca konten kunci publik dan privat dari file kunci.
                String prikeyFilename = "D:\\test\\Issuer\\pri_key_pkcs8.pem"; // Ini adalah nilai contoh. Anda harus mengganti nilainya dengan jalur file kunci.
                String pubkeyFilename = "D:\\test\\Issuer\\pub_key.pem"; // Ini adalah nilai contoh. Anda harus mengganti nilainya dengan jalur file kunci.
                issuerPriKeyPemString = readPemFile(prikeyFilename);
                issuerPubKeyPemString = readPemFile(pubkeyFilename);
                // Daftarkan kunci publik dan privat.
                issuerSdk.getKeyManager().registerCertificate(issuerPriKeyPemString, issuerPubKeyPemString);
                System.out.println("registrasi sertifikat penerbit berhasil");

            }

            // Daftarkan kunci publik dan privat sebagai subjek.
            {
                // Baca konten kunci publik dan privat dari file kunci.
                String prikeyFilename = "D:\\test\\Subject\\pri_key_pkcs8.pem"; // Ini adalah nilai contoh. Anda harus mengganti nilainya dengan jalur file kunci.
                String pubkeyFilename = "D:\\test\\Subject\\pub_key.pem"; // Ini adalah nilai contoh. Anda harus mengganti nilainya dengan jalur file kunci.
                subjectPriKeyPemString = readPemFile(prikeyFilename);
                subjectPubKeyPemString = readPemFile(pubkeyFilename);
                // Daftarkan kunci publik dan privat.
                subjectSdk.getKeyManager().registerCertificate(subjectPriKeyPemString, subjectPubKeyPemString);
                System.out.println("registrasi sertifikat subjek berhasil");
            }
        }

        // Sebelum otorisasi data multi-pengguna, subjek mencoba mengakses data yang ditulis oleh penerbit. Pesan kesalahan yang menunjukkan bahwa akses ditolak akan ditampilkan.
        {
            Connection tmpConnection = subjectConn;
            EncdbSDK tmpSdk = subjectSdk;
            Connection tmpIssuerConn = issuerConn;
            SQLException exception = Assertions.assertThrows(SQLException.class, () -> {
                subjectJoinQuery(tmpConnection, tmpSdk, tmpIssuerConn);
            });
            Assertions.assertEquals("ERROR: encdb_get_hash_from_bytea: errno:fa030000", exception.getMessage());
            System.out.println("query data penerbit oleh subjek gagal tanpa otorisasi");
        }

        // Lakukan otorisasi data multi-pengguna. Pastikan konten otorisasi benar.
        {
            // Otorisasi subjek untuk mengakses DEK kolom yang ditentukan oleh penerbit.
            // DEK penerbit ditentukan oleh issuer_dek_group. DEK subjek ditentukan oleh subject_dek_group.
            String bclBodyJsonString = """
                    {
                        "version": 1,
                        "serial_num": "a121bac0-5cb3-4463-a2b2-1155ff29f4c8",
                        "issuer_pukid": "p81x+WqYb7BR0yP0LK0qiEaxgLDqwuIjfJhgC0mMJcE=",
                        "subject_pukid": "qIPPfgTJEEG/9WkjP0E5LLAijZ14h/Qgb2EfmBZCWSo=",
                        "validity": {
                            "not_after": "20250820111111+0800",
                            "not_before": "20240820111111+0800"
                        },
                        "policies": {
                            "issuer_dek_group": [
                                {
                                    "min": 1,
                                    "max": 100000,
                                    "groupid": "e4a92b05-f64d-4665-aadd-cd1336d0c0cc"
                                },
                                {
                                    "min": 1,
                                    "max": 100000,
                                    "groupid": "1010b43b-50da-4473-81ac-ce84657eb4f9"
                                },
                                {
                                    "min": 1,
                                    "max": 100000,
                                    "groupid": "b99e70cf-c6b0-444f-a404-81a721e38734"
                                }
                            ],
                            "result_dek": "SUBJECT",
                            "subject_dek_group": [
                                {
                                    "min": 1,
                                    "max": 100000,
                                    "groupid": "d1cbe5a6-49f0-42e0-ba07-572e2a5e2f5f"
                                },
                                {
                                    "min": 1,
                                    "max": 100000,
                                    "groupid": "9c772fa3-4712-4034-9447-0f1e9e18fbeb"
                                },
                                {
                                    "min": 1,
                                    "max": 100000,
                                    "groupid": "e2541c31-891e-4f8d-8389-09c5631f1e66"
                                }
                            ],
                            "operation": [
                                "*"
                            ],
                            "postproc": "NULL",
                            "preproc": "NULL"
                        }
                    }
                    """;

            // Urutan langkah-langkah berikut dapat diubah: Penerbit secara aktif melakukan otorisasi. Subjek meminta otorisasi.
            // Penerbit menerbitkan BCL untuk mengizinkan subjek mengakses data yang dienkripsi menggunakan DEK yang ditentukan dalam BCL.
            {
                bclBodyJsonString = issuerSdk.getKeyManager().issueBCL(bclBodyJsonString, issuerPubKeyPemString,
                        issuerPriKeyPemString, true);
                System.out.println("penerbit berhasil menerbitkan bcl");
            }
            // Subjek menerbitkan permintaan untuk mengakses data yang dienkripsi menggunakan DEK yang ditentukan dalam BCL.
            {
                bclBodyJsonString = subjectSdk.getKeyManager().issueBCL(bclBodyJsonString, subjectPubKeyPemString,
                        subjectPriKeyPemString, false);
                System.out.println("subjek berhasil menerbitkan bcl");
            }
        }

        // Setelah otorisasi data multi-pengguna selesai, subjek mencoba mengakses data yang ditulis oleh penerbit. Subjek berhasil mengakses dan melihat konten yang didekripsi.
        {
            Connection tmpConnection = subjectConn;
            EncdbSDK tmpSdk = subjectSdk;
            Connection tmpIssuerConn = issuerConn;
            Assertions.assertDoesNotThrow(() -> {
                subjectJoinQuery(tmpConnection, tmpSdk, tmpIssuerConn);
            });
            System.out.println("subjek berhasil menanyakan data penerbit setelah diberi otorisasi");
        }
    }
}

FAQ

  • Apa yang harus saya lakukan jika pesan kesalahan ERROR: permission denied for table <table name> muncul?

    Pesan kesalahan menunjukkan bahwa Anda tidak memiliki izin untuk mengakses tabel. Untuk menyelesaikan kesalahan, pemilik tabel atau administrator perlu menjalankan pernyataan GRANT SELECT ON <table> TO <user>; untuk memberikan izin akses.

  • Bagaimana cara memverifikasi bahwa otorisasi berhasil?

    Setelah otorisasi selesai, Anda dapat menanyakan data penerbit menggunakan akun subjek. Jika query berhasil, otorisasi berhasil. Jika query gagal, kode kesalahan fa030000 atau fa020000 dikembalikan. Kode kesalahan fa030000 menunjukkan bahwa tidak ada catatan otorisasi yang ditemukan. Kode kesalahan fa020000 menunjukkan bahwa akses ditolak. Jika Anda ingin memeriksa apakah otorisasi untuk operasi berisiko tinggi berhasil, Anda dapat melakukan operasi berisiko tinggi. Jika operasi berisiko tinggi berhasil, otorisasi berhasil.

  • Bagaimana cara memberikan izin akses pada DEK beberapa kolom?

    Jika subjek perlu mengakses beberapa kolom milik penerbit, kami sarankan Anda menambahkan DEK dari kolom-kolom tersebut dalam satu BCL untuk otorisasi. Anda dapat menambahkan DEK ke parameter issuer_dek_group dan subject_dek_group.

    Jika Anda ingin memberikan izin akses pada DEK individual, Anda harus menambahkan DEK tersebut dalam BCL yang berbeda dan memberikan nomor seri yang berbeda untuk setiap BCL. Jika Anda menggunakan nomor seri yang sama untuk BCL yang berbeda, konten BCL akan ditimpa. Dalam hal ini, kesalahan mungkin terjadi selama komputasi data tertentu.