All Products
Search
Document Center

Object Storage Service:Client-side encryption

Last Updated:Jul 01, 2025

If client-side encryption is enabled, objects are locally encrypted before they are uploaded to Object Storage Service (OSS). This way, data security during data transmission and storage is enhanced.

Disclaimer

  • When you use client-side encryption, you must ensure the integrity and validity of the customer master key (CMK). If the CMK is incorrectly used or lost due to improper maintenance, you are responsible for all losses and consequences caused by decryption failures.

  • When you copy or migrate encrypted data, you are responsible for the integrity and validity of object metadata. If the encrypted metadata is incorrect or lost due to improper maintenance, you are responsible for all losses and consequences resulting from data decryption failures.

Scenarios

  • Highly sensitive data: For data that contains extremely sensitive information, such as personally identifiable information (PII), financial transaction records, medical and health data, users can encrypt the data before the data leaves the local environment to ensure that the original data is effectively protected during transmission.

  • Compliance requirements: Specific regulations, such as HIPAA and GDPR, require strict data encryption management. Client-side encryption meets these compliance requirements because CMKs are managed by users and not passed through the network.

  • Strong permission control: An enterprise or a developer wants to have full control over the encryption process, including selecting encryption algorithms and managing CMKs. Client-side encryption ensures that only legally authorized users can decrypt and access data.

  • Security of cross-region data migration: Client-side encryption helps maintain data in the encrypted state before and after cross-region data migration. This enhances the security of data transmission over the Internet.

Background information

In client-side encryption, a random data key is generated for each object to perform symmetric encryption on the object. The client uses a CMK to encrypt the random data key. The encrypted data key is uploaded as part of the object metadata and stored in the OSS server. When an encrypted object is downloaded, the client uses the CMK to decrypt the random data key, and then uses the decrypted data key to decrypt the object. To ensure data security, the CMK is used only on the client and is not transmitted over the network or stored on the server.

Important
  • Client-side encryption supports multipart upload for objects that are greater than 5 GB in size. When you use multipart upload to upload an object, you must specify the total object size and the part size. The size of each part, except for the last part, must be the same and must be an integer multiple of 16.

  • If you upload an object encrypted on a local client, the encrypted metadata of the object is protected and cannot be modified by calling CopyObject.

You can use two types of CMKs for client-side encryption:

For the complete sample code, visit GitHub.

Use KMS-managed CMKs

If you use a Key Management Service (KMS)-managed CMK for client-side encryption, you need to only specify a CMK ID when you upload an object. You do not need to provide the encryption client with a data key. The following figure shows the encryption process.

image
  • Encrypt and upload an object

    1. Obtain a data key.

      The client uses the specified CMK ID to request a data key from KMS to encrypt an object. KMS generates a random key and returns the plaintext and ciphertext of the data key.

    2. Encrypt the object and upload the encrypted object to OSS.

      The client uses the plaintext of the data key to encrypt the object on the client side and upload the encrypted object and ciphertext of the data key to OSS.

  • Download and decrypt an object

    1. Download an object.

      The client downloads an encrypted object and the ciphertext of the data key from OSS.

    2. Decrypt the object.

      The client sends the ciphertext of the data key and the corresponding CMK ID to KMS. KMS uses the CMK sent by the client to decrypt the encrypted object and returns the plaintext of the data key to the client.

Note
  • The client uses a unique data key to encrypt each object to upload.

  • To ensure data security, we recommend that you periodically rotate or update the CMK.

  • You must maintain the mapping relationship between the CMK IDs and the encrypted objects.

Use customer-managed CMKs

To use customer-managed CMKs for client-side encryption, you must manually generate and manage CMKs. You must upload a symmetric CMK or an asymmetric CMK to the encryption client for object encryption. The following figure shows the encryption process.

image
  • Encrypt and upload an object

    1. You provide the client with a symmetric CMK or an asymmetric CMK.

    2. The client uses the CMK to generate a one-time symmetric data key that is used only to encrypt the object to upload. The client generates a random and unique data key for each object that you want to upload.

    3. The client uses the data key to encrypt the object that you want to upload and uses the CMK to encrypt the data key.

    4. The client uploads the encrypted object to OSS together with the encrypted data key included in the metadata of the object.

  • Download and decrypt an object

    1. The client downloads an encrypted object and its metadata from OSS.

    2. The client uses the corresponding CMK to decrypt the encrypted data key based on the key material indicated by the metadata, and then uses the decrypted data key to decrypt the object.

Important
  • The client does not send CMKs and unencrypted data to OSS. You must keep your CMKs secure. If a CMK is lost, the objects that are encrypted by using the data keys generated from this CMK cannot be decrypted.

  • Data keys are randomly generated by the client.

Use OSS SDKs

The following sample code provides examples on how to configure client-side encryption by using OSS SDKs for common programming languages. For more information about how to configure client-side encryption by using OSS SDKs for other programming languages, see Overview.

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.crypto.SimpleRSAEncryptionMaterials;
import com.aliyun.oss.model.OSSObject;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.Map;

public class Demo {
    public static void main(String[] args) throws Throwable {
        // In this example, the endpoint of the China (Hangzhou) region is used. Specify your actual endpoint. 
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
        // Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured. 
        EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
        // Specify the name of the bucket. Example: examplebucket. 
        String bucketName = "examplebucket";
        // Specify the full path of the object. Example: exampleobject.txt. Do not include the bucket name in the full path. 
        String objectName = "exampleobject.txt";
        String content = "Hello OSS!";

        // Specify your RSA private key string. You can generate the string by using OpenSSL. The following lines show a sample RSA private key string. 
        final String PRIVATE_PKCS1_PEM =
                "-----BEGIN RSA PRIVATE KEY-----\n" +
                "MIICWwIBAAKBgQCokfiAVXXf5ImFzKDw+XO/UByW6mse2QsIgz3ZwBtMNu59fR5z\n" +
                "ttSx+8fB7vR4CN3bTztrP9A6bjoN0FFnhlQ3vNJC5MFO1PByrE/MNd5AAfSVba93\n" +
                "I6sx8NSk5MzUCA4NJzAUqYOEWGtGBcom6kEF6MmR1EKib1Id8hpooY5xaQIDAQAB\n" +
                "AoGAOPUZgkNeEMinrw31U3b2JS5sepG6oDG2CKpPu8OtdZMaAkzEfVTJiVoJpP2Y\n" +
                "nPZiADhFW3e0ZAnak9BPsSsySRaSNmR465cG9tbqpXFKh9Rp/sCPo4Jq2n65yood\n" +
                "JBrnGr6/xhYvNa14sQ6xjjfSgRNBSXD1XXNF4kALwgZyCAECQQDV7t4bTx9FbEs5\n" +
                "36nAxPsPM6aACXaOkv6d9LXI7A0J8Zf42FeBV6RK0q7QG5iNNd1WJHSXIITUizVF\n" +
                "6aX5NnvFAkEAybeXNOwUvYtkgxF4s28s6gn11c5HZw4/a8vZm2tXXK/QfTQrJVXp\n" +
                "VwxmSr0FAajWAlcYN/fGkX1pWA041CKFVQJAG08ozzekeEpAuByTIOaEXgZr5MBQ\n" +
                "gBbHpgZNBl8Lsw9CJSQI15wGfv6yDiLXsH8FyC9TKs+d5Tv4Cvquk0efOQJAd9OC\n" +
                "lCKFs48hdyaiz9yEDsc57PdrvRFepVdj/gpGzD14mVerJbOiOF6aSV19ot27u4on\n" +
                "Td/3aifYs0CveHzFPQJAWb4LCDwqLctfzziG7/S7Z74gyq5qZF4FUElOAZkz123E\n" +
                "yZvADwuz/4aK0od0lX9c4Jp7Mo5vQ4TvdoBnPuGo****\n" +
                "-----END RSA PRIVATE KEY-----";
        // Specify your RSA public key string. You can generate the string by using OpenSSL. The following lines show a sample RSA public key string. 
        final String PUBLIC_X509_PEM =
                "-----BEGIN PUBLIC KEY-----\n" +
                "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCokfiAVXXf5ImFzKDw+XO/UByW\n" +
                "6mse2QsIgz3ZwBtMNu59fR5zttSx+8fB7vR4CN3bTztrP9A6bjoN0FFnhlQ3vNJC\n" +
                "5MFO1PByrE/MNd5AAfSVba93I6sx8NSk5MzUCA4NJzAUqYOEWGtGBcom6kEF6MnR\n" +
                "1EKib1Id8hpooY5xaQID****\n" +
                "-----END PUBLIC KEY-----";

        // Create an RSA key pair. 
        RSAPrivateKey privateKey = SimpleRSAEncryptionMaterials.getPrivateKeyFromPemPKCS1(PRIVATE_PKCS1_PEM);
        RSAPublicKey publicKey = SimpleRSAEncryptionMaterials.getPublicKeyFromPemX509(PUBLIC_X509_PEM);
        KeyPair keyPair = new KeyPair(publicKey, privateKey);

        // Specify the description of the CMK. The description cannot be modified after it is specified. You can specify only one description for a CMK. 
        // If all objects that you want to encrypt share a CMK, you can leave the description of the CMK empty. In this case, the CMK cannot be replaced. 
        // If you leave the description of the CMK empty, the client cannot determine which CMK to use for decryption. 
        // We recommend that you specify a description for each CMK and store the mapping relationship between the CMK and the description on the client. The server does not store the mapping relationship. 
        Map<String, String> matDesc = new HashMap<String, String>();
        matDesc.put("desc-key", "desc-value");

        // Create RSA encryption materials. 
        SimpleRSAEncryptionMaterials encryptionMaterials = new SimpleRSAEncryptionMaterials(keyPair, matDesc);
        // To download and decrypt objects encrypted by using other CMKs, add these CMKs and their descriptions to the encryption materials. 
        // encryptionMaterials.addKeyPairDescMaterial(<otherKeyPair>, <otherKeyPairMatDesc>);

        // Create a client for client-side encryption.
        // Call the shutdown method to release associated resources when the client is no longer in use. 
        OSSEncryptionClient ossEncryptionClient = new OSSEncryptionClientBuilder().
                build(endpoint, credentialsProvider, encryptionMaterials);

        try {
            // Encrypt and upload the object. 
            ossEncryptionClient.putObject(bucketName, objectName, new ByteArrayInputStream(content.getBytes()));

            // Download the object. The object is automatically decrypted. 
            OSSObject ossObject = ossEncryptionClient.getObject(bucketName, objectName);
            BufferedReader reader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()));
            StringBuffer buffer = new StringBuffer();
            String line;
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
            reader.close();

            // Check whether the decrypted content is the same as the uploaded object in plaintext. 
            System.out.println("Put plain text: " + content);
            System.out.println("Get and decrypted text: " + buffer.toString());
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossEncryptionClient != null) {
                ossEncryptionClient.shutdown();
            }
        }
    }
}
import argparse
import base64
import json
from aliyunsdkkms.request.v20160120.DecryptRequest import DecryptRequest
from aliyunsdkkms.request.v20160120.EncryptRequest import EncryptRequest
from alibabacloud_dkms_transfer.kms_transfer_acs_client import KmsTransferAcsClient
from typing import Optional, Dict
import alibabacloud_oss_v2 as oss


# Parse the command line parameters
parser = argparse.ArgumentParser(description="encryption kms sample")

# Add the --region command line parameter, which specifies the region where the bucket is located. This parameter is required.
parser.add_argument('--region', help='The region in which the bucket is located.', required=True)

# Add the --bucket command line parameter, which specifies the name of the bucket. This parameter is required.
parser.add_argument('--bucket', help='The name of the bucket.', required=True)

# Add the --endpoint command line parameter, which specifies the domain name used by other services to access OSS. This parameter is optional.
parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')

# Add the --key command line parameter, which specifies the name of the object (file path). This parameter is required.
parser.add_argument('--key', help='The name of the object.', required=True)

# Add the --kms_id command line parameter, which specifies the user's CMK (Customer Master Key) ID. This parameter is required.
parser.add_argument('--kms_id', help='The id of the your CMK ID.', required=True)


# Custom master key encryptor class, inheriting from oss.crypto.MasterCipher
class MasterKmsCipher(oss.crypto.MasterCipher):

    def __init__(
        self,
        mat_desc: Optional[Dict] = None,
        kms_client: Optional[KmsTransferAcsClient] = None,
        kms_id: Optional[str] = None,
    ):
        self.kms_client = kms_client
        self.kms_id = kms_id
        self._mat_desc = None

        # If a description of the master key is provided, serialize it to a JSON string
        if mat_desc is not None and len(mat_desc.items()) > 0:
            self._mat_desc = json.dumps(mat_desc)

    def get_wrap_algorithm(self) -> str:
        # Return the encryption algorithm name, fixed as 'KMS/ALICLOUD'
        return 'KMS/ALICLOUD'

    def get_mat_desc(self) -> str:
        return self._mat_desc or ''

    def encrypt(self, data: bytes) -> bytes:
        """
        Encrypt data using the KMS service
        :param data: Original data to be encrypted (in bytes format)
        :return: Encrypted data (in bytes format)
        """
        # Encode the original data in Base64 format
        base64_crypto = base64.b64encode(data)

        # Construct an encryption request object
        request = EncryptRequest()
        request.set_KeyId(self.kms_id)  # Set the CMK ID
        request.set_Plaintext(base64_crypto)  # Set the Base64 data to be encrypted

        # Call the KMS client to perform the encryption operation and get the response
        response = self.kms_client.do_action_with_exception(request)

        # Parse the encrypted data field in the response and decode it to bytes format
        return base64.b64decode(json.loads(response).get('CiphertextBlob'))

    def decrypt(self, data: bytes) -> bytes:
        """
        Decrypt data using the KMS service
        :param data: Encrypted data (in bytes format)
        :return: Decrypted original data (in bytes format)
        """
        # Encode the encrypted data in Base64 format
        base64_crypto = base64.b64encode(data)

        # Construct a decryption request object
        request = DecryptRequest()
        request.set_CiphertextBlob(base64_crypto)  # Set the encrypted data

        # Call the KMS client to perform the decryption operation and get the response
        response = self.kms_client.do_action_with_exception(request)

        # Parse the plaintext field in the response and decode it to bytes format
        return base64.b64decode(json.loads(response).get('Plaintext'))


def main():
    # Parse the command line arguments
    args = parser.parse_args()

    # Load credential information (AccessKeyId and AccessKeySecret) from environment variables
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()

    # Load the default configuration of the SDK
    cfg = oss.config.load_default()

    # Specify the credential provider
    cfg.credentials_provider = credentials_provider

    # Set the region where the bucket is located
    cfg.region = args.region

    # If the user provides a custom endpoint, set it in the configuration
    if args.endpoint is not None:
        cfg.endpoint = args.endpoint

    # Initialize the OSS client with the configuration object
    client = oss.Client(cfg)

    # Initialize the KMS client for interacting with the KMS service
    kms_client = KmsTransferAcsClient(
        ak=credentials_provider._credentials.access_key_id,  # Get AccessKeyId from the credential provider
        secret=credentials_provider._credentials.access_key_secret,  # Get AccessKeySecret from the credential provider
        region_id=args.region  # Specify region information
    )

    # Initialize the master key encryptor (MasterKmsCipher) for encryption and decryption operations
    mc = MasterKmsCipher(
        mat_desc={"desc": "your master encrypt key material describe information"},  # Master key description information
        kms_client=kms_client,  # KMS client instance
        kms_id=args.kms_id  # User's CMK ID
    )

    # Create an encryption client
    encryption_client = oss.EncryptionClient(client, mc)

    # Define the data to upload
    data = b'hello world'

    # Call the put_object method of the encryption client to upload the encrypted object
    result = encryption_client.put_object(
        oss.PutObjectRequest(
            bucket=args.bucket,  # Specify the name of the target bucket
            key=args.key,        # Specify the name of the object (file path)
            body=data,           # Specify the data to upload
        )
    )

    # Print the result of uploading the encrypted object
    print(vars(result))

    # Call the get_object method of the encryption client to get the content of the encrypted object
    result = encryption_client.get_object(
        oss.GetObjectRequest(
            bucket=args.bucket,  # Specify the name of the target bucket
            key=args.key,        # Specify the name of the object (file path)
        )
    )

    # Print the result of getting the encrypted object
    print(vars(result))

    # Print the decrypted object content
    print(result.body.read())


if __name__ == "__main__":
    # Program entry, call the main function to execute the logic
    main()
package main

import (
	"bytes"
	"io"
	"log"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	osscrypto "github.com/aliyun/aliyun-oss-go-sdk/oss/crypto"
)

func main() {
	// Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured. 
	provider, err := oss.NewEnvironmentVariableCredentialsProvider()
	if err != nil {
		log.Fatalf("Error creating credentials provider: %v", err)
	}

	// Create an OSSClient instance. 
	// Specify the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. Specify your actual endpoint. 
	// Specify the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the region to cn-hangzhou. Specify the actual region.
	clientOptions := []oss.ClientOption{oss.SetCredentialsProvider(&provider)}
	clientOptions = append(clientOptions, oss.Region("yourRegion"))
	// Specify the version of the signature algorithm.
	clientOptions = append(clientOptions, oss.AuthVersion(oss.AuthV4))
	client, err := oss.New("yourEndpoint", "", "", clientOptions...)
	if err != nil {
		log.Fatalf("Error creating OSS client: %v", err)
	}

	// Specify a description for the CMK. The description cannot be modified after being specified. You can specify only one description for a CMK. 
	// If all objects share a CMK, you can leave the description of the CMK empty. In this case, the CMK cannot be replaced. 
	// If the description of the CMK is left empty, the client cannot determine which CMK to use for decryption. 
	// We recommend that you configure a description in the form of a JSON string for each CMK and store the mapping relationship between the CMK and the description on the client. The server does not store the mapping relationship. 

	// Obtain the mapping relationship based on the CMK description that is a JSON string. 
	materialDesc := map[string]string{
		"desc": "your master encrypt key material describe information",
	}

	// Create a CMK object based on the description of the CMK. 
	// Set yourRsaPublicKey to the public key of the CMK that you manage, and set yourRsaPrivateKey to the private key of the CMK that you manage. 
	masterRsaCipher, err := osscrypto.CreateMasterRsa(materialDesc, "yourRsaPublicKey", "yourRsaPrivateKey")
	if err != nil {
		log.Fatalf("Error creating master RSA cipher: %v", err)
	}

	// Create an encryption operation based on the CMK object and perform encryption in AES CTR mode. 
	contentProvider := osscrypto.CreateAesCtrCipher(masterRsaCipher)

	// Obtain an existing bucket to be used for client-side encryption. 
	// The bucket for which client-side encryption is configured is used in the same manner in which a common bucket is used.
	cryptoBucket, err := osscrypto.GetCryptoBucket(client, "yourBucketName", contentProvider)
	if err != nil {
		log.Fatalf("Error getting crypto bucket: %v", err)
	}

	// Encryption is automatically performed when you call the PutObject operation. 
	err = cryptoBucket.PutObject("yourObjectName", bytes.NewReader([]byte("yourObjectValueByteArrary")))
	if err != nil {
		log.Fatalf("Error putting object: %v", err)
	}

	// Decryption is automatically performed when you call the GetObject operation. 
	body, err := cryptoBucket.GetObject("yourObjectName")
	if err != nil {
		log.Fatalf("Error getting object: %v", err)
	}
	defer body.Close()

	data, err := io.ReadAll(body)
	if err != nil {
		log.Fatalf("Error reading object data: %v", err)
	}
	log.Printf("Data: %s", string(data))
}
#include <alibabacloud/oss/OssEncryptionClient.h>
using namespace AlibabaCloud::OSS;

int main(void)
{
    /* Initialize information about the account that is used to access OSS. */
    
    /* Specify the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. */
    std::string Endpoint = "yourEndpoint";
    /* Specify the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the region to cn-hangzhou. */
    std::string Region = "yourRegion";
    /* Specify the name of the bucket. Example: examplebucket. */
    std::string BucketName = "examplebucket";
    /* Specify the full path of the object. Do not include the bucket name in the full path. Example: exampledir/exampleobject.txt. */
    std::string ObjectName = "exampledir/exampleobject.txt";

    /* Specify the CMK and the description. */
    std::string RSAPublicKey = "your rsa public key";
    std::string RSAPrivateKey = "your rsa private key";
    std::map<std::string, std::string> desc;
    desc["comment"] = "your comment";

    /* Initialize resources, such as network resources. */
    InitializeSdk();
    
    ClientConfiguration conf;
    conf.signatureVersion = SignatureVersionType::V4;
    /* Obtain access credential from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured. */
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
    OssClient client(Endpoint, credentialsProvider, conf);
    client.SetRegion(Region);
  
    CryptoConfiguration cryptoConf;
    auto materials = std::make_shared<SimpleRSAEncryptionMaterials>(RSAPublicKey, RSAPrivateKey, desc);
    OssEncryptionClient client(Endpoint, credentialsProvider, conf, materials, cryptoConf);
    /* Upload the object. */
    auto outcome = client.PutObject(BucketName, ObjectName, "yourLocalFilename");
    if (!outcome.isSuccess()) {
        /* Handle exceptions. */
        std::cout << "PutObject fail" <<
        ",code:" << outcome.error().Code() <<
        ",message:" << outcome.error().Message() <<
        ",requestId:" << outcome.error().RequestId() << std::endl;
        return -1;
    }
    /* Release resources, such as network resources. */
    ShutdownSdk();
    return 0;
}
<?php
// Introduce autoload files to load dependent libraries.
require_once 'vendor/autoload.php';

use AlibabaCloud\Dkms\Gcs\Sdk\Client as KmsClient;
use AlibabaCloud\Oss\V2 as Oss;

// Specify descriptions for command line parameters.
$optsdesc = [
    "region" => ['help' => The region in which the bucket is located.', 'required' => True], // (Required) Specify the region in which the bucket is located.
    "endpoint" => ['help' => The domain names that other services can use to access OSS.', 'required' => False], // (Optional) Specify the endpoint that can be used by other services to access OSS.
    "bucket" => ['help' => The name of the bucket, 'required' => True], // (Required) Specify the name of the bucket.
    "key" => ['help' => The name of the object, 'required' => True], // (Required) Specify the name of the object.
];

// Generate a long options list to parse the command line parameters.
$longopts = \array_map(function ($key) {
    return "$key:"; // Add a colon (:) to the end of each parameter to indicate that a value is required.
}, array_keys($optsdesc));

// Parse the command line parameters.
$options = getopt("", $longopts);

// Check whether the required parameters are configured.
foreach ($optsdesc as $key => $value) {
    if ($value['required'] === True && empty($options[$key])) {
        $help = $value['help'];
        echo "Error: the following arguments are required: --$key, $help"; // Indicate that the required parameters are not configured.
        exit(1);
    }
}

// Obtain the values of the command line parameters.
$region = $options["region"]; // The region in which the bucket is located.
$bucket = $options["bucket"]; // The name of the bucket.
$key = $options["key"]; // The name of the object.

// Use environment variables to load the AccessKey ID and AccessKey secret.
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();

// Specify the KMS encryption and decryption class to call the MasterCipherInterface operation.
class KmsCipher implements Oss\Crypto\MasterCipherInterface
{
    private $matDesc;
    private ?KmsClient $kmsClient;
    private ?string $keyId;
    private ?string $algorithm;

    public function __construct(
        $matDesc = null,
        ?string $keyId = null,
        ?KmsClient $kmsClient = null,
        ?string $algorithm = null
    )
    {
        $this->keyId = $keyId;
        $this->matDesc = null;
        if (\is_array($matDesc)) {
            $val = json_encode($matDesc);
            if ($val !== false) {
                $this->matDesc = $val;
            }
        } else if (is_string($matDesc)) {
            $this->matDesc = $matDesc;
        }

        $this->kmsClient = $kmsClient;
        $this->algorithm = $algorithm;
    }

    // Specify the encryption method.
    public function encrypt(string $data): string
    {
        $encryptRequest = new \AlibabaCloud\Dkms\Gcs\Sdk\Models\AdvanceEncryptRequest();
        $encryptRequest->algorithm = $this->algorithm;
        $encryptRequest->keyId = $this->keyId;
        $encryptRequest->plaintext = \AlibabaCloud\Tea\Utils\Utils::toBytes($data);
        $runtimeOptions = new \AlibabaCloud\Dkms\Gcs\OpenApi\Util\Models\RuntimeOptions();
        $encryptResponse = $this->kmsClient->advanceEncryptWithOptions($encryptRequest, $runtimeOptions);
        return base64_decode((string)$encryptResponse->ciphertextBlob);
    }

    // Specify the decryption method.
    public function decrypt(string $data): string
    {
        $decryptRequest = new \AlibabaCloud\Dkms\Gcs\Sdk\Models\AdvanceDecryptRequest();
        $decryptRequest->keyId = $this->keyId;
        $decryptRequest->ciphertextBlob = $data;
        $decryptRequest->algorithm = $this->algorithm;
        $runtimeOptions = new \AlibabaCloud\Dkms\Gcs\OpenApi\Util\Models\RuntimeOptions();
        $decryptResponse = $this->kmsClient->advanceDecryptWithOptions($decryptRequest, $runtimeOptions);
        return base64_decode((string)$decryptResponse->plaintext);
    }

    // Obtain the wrapping algorithm.
    public function getWrapAlgorithm(): string
    {
        return "KMS/ALICLOUD";
    }

    public function getMatDesc(): string
    {
        return $this->matDesc;
    }
}

/**
 * Create a dedicated KMS SDK client.
 * @return KmsClient
 */
function getDkmsGcsSdkClient()
{
    global $clientKeyFile, $password, $endpoint;
    // Create configurations for the dedicated KMS SDK client.
    $config = new \AlibabaCloud\Dkms\Gcs\OpenApi\Models\Config();
    $config->protocol = 'https';
    $config->clientKeyFile = $clientKeyFile;
    $config->password = $password;
    $config->endpoint = $endpoint;
    // Verify the certificate.
    $config->caFilePath = 'path/to/caCert.pem';
    // Create a KMS SDK client.
    return new \AlibabaCloud\Dkms\Gcs\Sdk\Client($config);
}

// Specify the path of the file that stores the client key of the KMS instance.
$clientKeyFile = '<your client key file path>';
// Specify the password when you create the client key file.
$password = '<your dkms client passowrd>';
// Specify the endpoint of the KMS instance.
$endpoint = '<your dkms instance service address>';
// Specify the ID of the key that you created in KMS.
$kmsKeyId = '<your cmk id>';
// Specify the encryption and decryption algorithms.
$algorithm = '<your encrypt algorithm>';

// Specify the dedicated KMS SDK client.
$kmsClient = getDkmsGcsSdkClient();

$cfg = Oss\Config::loadDefault();
$cfg->setCredentialsProvider($credentialsProvider);
$cfg->setRegion($region);
if (isset($options["endpoint"])) {
    $cfg->setEndpoint($options["endpoint"]);
}

$client = new Oss\Client($cfg);
$materialDesc = ['desc' => 'your kms encrypt key material describe information'];
$masterKmsCipher = new KmsCipher($materialDesc, $kmsKeyId, $kmsClient, $algorithm);
$eClient = new \AlibabaCloud\Oss\V2\EncryptionClient($client, $masterKmsCipher);

$eClient->putObject(new Oss\Models\PutObjectRequest(
    bucket: $bucket,
    key: $key,
    body: Oss\Utils::streamFor('hi kms')
));

$result = $eClient->getObject(new Oss\Models\GetObjectRequest(
    bucket: $bucket,
    key: $key,
));

$data = $result->body->getContents();
echo "get object data: " . $data;