In an always-confidential database cluster, each user's data is encrypted with their own data encryption key (DEK) and automatically isolated from other users. To enable multi-party data integration and joint computation, issue a behavior control list (BCL) to grant an applicant access to another user's ciphertext data.
Prerequisites
Before you begin, ensure that you have:
The always-confidential database feature enabled and configured. See Enable the always-confidential database feature, Define sensitive data, and Use the always-confidential database feature from a client
A PolarDB for PostgreSQL (Compatible with Oracle) cluster at minor version 2.0.14.14.0 or later
The certificates and private keys of all participants (both data owner and applicant)
How BCL authorization works
BCL authorization involves two parties:
| Party | Role | Certificate field |
|---|---|---|
| Data owner (issuer) | Owns the encrypted data. Reviews and approves or rejects access requests. | issuer_pukid |
| Applicant (subject) | Requests access to the data owner's ciphertext for joint computation. | subject_pukid |
After a BCL is issued, the applicant can query the data owner's ciphertext within the scope defined by the BCL. Only the final computation result is accessible — intermediate data remains unavailable.
Use case
An advertising platform wants to identify users under 30 from a data platform's encrypted user profile table. The data platform owns the DEK and the applicant (the advertising platform) holds its own DEK. Without authorization, the applicant can only see ciphertext and cannot use the data.
To proceed:
The applicant submits a BCL authorization request specifying which DEK groups and operations are needed.
The data platform reviews and signs the BCL to approve access.
After approval, the advertising platform can query the user profile data within the authorized scope.
Grant BCL authorization
Step 1: Initialize certificates
Both parties must register their certificates in the application code before calling any BCL APIs. The following code registers the participant's certificate with the KeyManager. Key registration is required only once per session.
// The private key of the participant's certificate.
String userPriPemString = "*****";
// The public key of the participant's certificate.
String userPukPemString = "*****";
// Initialize the KeyManager and register the certificate.
KeyManager km = sdk.getKeyManager();
km.registerCertificate(userPriPemString, userPukPemString);Step 2: Collect required parameter values
Before defining the BCL JSON body, collect the following values. Both the applicant and data owner need these values.
Certificate thumbprints (`issuer_pukid` and `subject_pukid`)
A certificate thumbprint is the SHA-256 hash of the certificate content, encoded in Base64:
Compute the SHA-256 hash of the full certificate content, including the
-----BEGIN CERTIFICATE-----and-----END CERTIFICATE-----markers. You can specify a different hash algorithm if needed.Base64-encode the hash.
Use the resulting string as the thumbprint value.
DEK group values (`groupid`, `min`, `max`)
Run the following SQL statement to retrieve the groupid and DEK ID (dekid) for a given encrypted column:
SELECT encdb_get_cc_entry_by_name(<keyname>);Set <keyname> to the name of the column on which the applicant needs access. The result contains the groupid and the DEK IDs that define the range (min and max).
Step 3: Define the BCL JSON body
Construct the BCL JSON body with the values collected in Step 2. The issueBCL method uses this JSON body for both the applicant's request and the data owner's approval.
String bclBodyJsonString = """
{
"version": 1,
"serial_num": "a121bac0-5cb3-4463-a2b2-1155ff29f4c8",
"issuer_pukid": "dYJ3Wfj/n0eZbuqgQjv8bnBdPXGyWGOlxE/uMy16NXo=",
"subject_pukid": "+Gg4vXehPt9BWlCPdR83wKDn6E1b/XddNhKQ5mVbKVQ=",
"validity": {
"not_before": "20220820111111+0800",
"not_after": "20230820111111+0800"
},
"policies": {
"issuer_dek_group": [
{ "min": 1, "max": 100000, "groupid": "b6785611-0c49-4f13-87a9-13f151de9b4d" }
],
"subject_dek_group": [
{ "min": 1, "max": 100000, "groupid": "5e19cfa7-c001-4e44-acab-2e5de4b97dcb" }
],
"result_dek": "SUBJECT",
"operation": ["*"],
"preproc": "NULL",
"postproc": "NULL"
}
}
""";The following table describes each parameter.
| Parameter | Example | Description |
|---|---|---|
version | 1 | Version number. Set to 1. |
serial_num | "a121bac0-5cb3-4463-a2b2-1155ff29f4c8" | A unique, random serial number in universally unique identifier (UUID) format. |
issuer_pukid | "dYJ3Wfj/..." | Certificate thumbprint of the data owner. See Step 2 for how to compute this value. |
subject_pukid | "+Gg4vXehPt9..." | Certificate thumbprint of the applicant. Computed the same way as issuer_pukid. |
validity.not_before | "20220820111111+0800" | Start of the authorization validity period, in GeneralizedTime format. |
validity.not_after | "20230820111111+0800" | End of the authorization validity period, in GeneralizedTime format. |
policies.issuer_dek_group[].groupid | "b6785611-..." | ID of the DEK group owned by the data owner. To get this value, run SELECT encdb_get_cc_entry_by_name(<keyname>). |
policies.issuer_dek_group[].min | 1 | Smallest DEK ID in the data owner's group. |
policies.issuer_dek_group[].max | 100000 | Largest DEK ID in the data owner's group. |
policies.subject_dek_group[].groupid | "5e19cfa7-..." | ID of the DEK group owned by the applicant. To get this value, run SELECT encdb_get_cc_entry_by_name(<keyname>). |
policies.subject_dek_group[].min | 1 | Smallest DEK ID in the applicant's group. |
policies.subject_dek_group[].max | 100000 | Largest DEK ID in the applicant's group. |
policies.result_dek | "SUBJECT" | DEK used to encrypt the computation result. Valid values: "SUBJECT" (applicant's DEK), "ISSUER" (data owner's DEK), or a specific DEK ID (specified without enclosing the value in double quotation marks). |
policies.operation | ["*"] | Allowed computation operations. Valid values: "encrypt", "decrypt", "cmp" (comparison), or "*" (all operations). |
policies.preproc | "NULL" | Pre-processing operation before computation. Set to NULL if none is required. |
policies.postproc | "NULL" | Post-processing operation after computation. Set to NULL if none is required. |
Step 4: Applicant — submit the authorization request
The applicant calls issueBCL with isIssuer set to false. This signs the BCL JSON body with the applicant's private key and submits the authorization request to the data owner.
boolean isIssuer = false;
bclBodyJsonString = km.issueBCL(bclBodyJsonString, userPukPemString, userPriPemString, isIssuer);Step 5: Data owner — approve and issue the BCL
The data owner reviews the BCL JSON body and, if approved, calls issueBCL with isIssuer set to true. This signs the BCL with the data owner's private key and issues the authorization.
boolean isIssuer = true;
bclBodyJsonString = km.issueBCL(bclBodyJsonString, userPukPemString, userPriPemString, isIssuer);After this step, the applicant can query the data owner's ciphertext within the scope defined by the BCL.
Revoke BCL authorization
Revoke a BCL when the authorization period ends, the collaboration terminates, or a security incident requires immediate access removal. Revocation is performed by the data owner.
Step 1: Define the revocation list
Construct the behavior revocation list (BRL) JSON body. The serial_num value must match the serial number in the BCL you want to revoke.
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"
}
]
}
""";The following table describes each parameter.
| Parameter | Example | Description |
|---|---|---|
version | 1 | Version number. Set to 1. |
pukid | "dYJ3Wfj/..." | Certificate thumbprint of the data owner. Must match the issuer_pukid in the original BCL. |
this_update | "20220819111128+0800" | The point in time at which the revocation list is updated, in GeneralizedTime format. |
next_update | "20220919111128+0800" | Timestamp of the next scheduled revocation list update, in GeneralizedTime format. |
revoked[].revocation_date | "20220819111128+0800" | Timestamp when the specific BCL authorization is revoked, in GeneralizedTime format. |
revoked[].serial_num | "a121bac0-..." | Serial number of the BCL to revoke. Must match the serial_num in the original BCL authorization. |
Step 2: Data owner — issue the revocation
The data owner calls revokeBCL to sign and submit the BRL. After this call, the applicant loses access to the data owner's ciphertext.
brlBodyJsonString = km.revokeBCL(brlBodyJsonString, userPukPemString, userPriPemString);