All Products
Search
Document Center

Object Storage Service:Client-side encryption

Last Updated:Mar 20, 2026

Client-side encryption encrypts objects locally before they are uploaded to OSS, ensuring that plaintext data never reaches the network or OSS servers. You retain full control of your encryption keys—they never leave your environment.

Important

You are responsible for the integrity and security of your customer master keys (CMKs). If a CMK is incorrectly used or lost due to improper maintenance, you are responsible for all losses and consequences caused by decryption failures. You are also responsible for preserving object metadata—if encrypted metadata is incorrect or lost due to improper maintenance, you are responsible for all losses and consequences resulting from data decryption failures.

Use cases

  • Highly sensitive data: Encrypt personally identifiable information (PII), financial records, or medical data before it leaves your environment, so plaintext never touches the network.

  • Compliance requirements: Meet HIPAA, GDPR, and similar regulations that mandate strict encryption key management. Because CMKs stay with you and are never transmitted, client-side encryption satisfies these key-custody requirements.

  • Full key control: Own the entire encryption process—choose your algorithm and manage your own CMKs—so only authorized parties can decrypt your data.

  • Cross-region data migration: Keep objects encrypted throughout migration, eliminating exposure during transfer.

How it works

Client-side encryption uses envelope encryption: a unique, randomly generated data key encrypts each object, and your CMK wraps (encrypts) that data key. The encrypted data key is stored in the object's metadata on OSS. The CMK itself never leaves the client.

Why envelope encryption? Symmetric algorithms are fast and efficient for encrypting large objects. Asymmetric or KMS-backed algorithms provide better key management and role separation. Envelope encryption combines both: symmetric encryption for object data, and your preferred key management method for the data key.

Upload flow:

  1. The client generates a random data key for the object.

  2. The client encrypts the object with the data key, then encrypts the data key with your CMK.

  3. The client uploads the encrypted object to OSS with the encrypted data key embedded in object metadata.

Download flow:

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

  2. The client uses the CMK to decrypt the data key from the metadata.

  3. The client uses the decrypted data key to decrypt the object.

OSS supports two CMK types:

KMS-managed CMKCustomer-managed CMK
Key generationKMS generates data keys on demandClient generates data keys locally
Key storageKMS holds the CMK; OSS holds the encrypted data keyYou hold the CMK; OSS holds the encrypted data key
Key rotationRotate via KMSRotate manually
Best forTeams that want managed key infrastructureTeams that require complete, self-managed key custody

Limitations

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

  • Encrypted object metadata cannot be modified via the CopyObject API.

  • CMKs and unencrypted data are never sent to OSS. Keep your CMKs secure—if a CMK is lost, all objects encrypted with data keys derived from it are permanently unrecoverable.

Use KMS-managed CMKs

When you use a Key Management Service (KMS)-managed CMK, you specify a CMK ID—KMS handles data key generation and CMK-based decryption automatically.

Upload:

  1. The client sends the CMK ID to KMS and requests a data key.

  2. KMS returns both the plaintext and ciphertext of the data key.

  3. The client encrypts the object with the plaintext data key, then uploads the encrypted object to OSS with the ciphertext data key stored in object metadata. The plaintext data key is discarded immediately after use.

Download:

  1. The client downloads the encrypted object and retrieves the ciphertext data key from the metadata.

  2. The client sends the ciphertext data key and the CMK ID to KMS for decryption.

  3. KMS returns the plaintext data key; the client uses it to decrypt the object.

Note

Each object is encrypted with a unique data key. Rotate or update your CMK periodically. Maintain the mapping between CMK IDs and the objects they encrypted—OSS does not store this relationship.

Use customer-managed CMKs

When you manage your own CMKs, you supply either a symmetric or asymmetric CMK to the encryption client. Data keys are generated locally.

Upload:

  1. Provide the encryption client with your CMK (symmetric or asymmetric).

  2. The client generates a random, one-time symmetric data key for the object.

  3. The client encrypts the object with the data key, then encrypts the data key with your CMK.

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

Download:

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

  2. The client identifies the correct CMK from the metadata, decrypts the data key, and uses it to decrypt the object.

Implement client-side encryption with OSS SDKs

The examples below demonstrate encrypting and decrypting objects using OSS SDKs. The Java, Go, and C++ examples use customer-managed RSA keys. The Python and PHP examples use KMS-managed CMKs. For additional SDK languages, see Overview. A complete Python sample is also available on GitHub.

All examples use an EncryptionClient (or language equivalent) that wraps the standard OSS client. Encryption and decryption happen automatically on PutObject and GetObject calls.

<details> <summary>Python</summary>

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

parser = argparse.ArgumentParser(description="OSS client-side encryption with KMS")
parser.add_argument('--region', required=True, help='Region where the bucket is located')
parser.add_argument('--bucket', required=True, help='Bucket name')
parser.add_argument('--endpoint', help='OSS endpoint (optional)')
parser.add_argument('--key', required=True, help='Object name (key)')
parser.add_argument('--kms_id', required=True, help='KMS CMK ID')


class MasterKmsCipher(oss.crypto.MasterCipher):
    """Wraps KMS encrypt/decrypt operations for use as a 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 = json.dumps(mat_desc) if mat_desc else ''

    def get_wrap_algorithm(self) -> str:
        return 'KMS/ALICLOUD'

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

    def encrypt(self, data: bytes) -> bytes:
        """Encrypt a data key using KMS."""
        request = EncryptRequest()
        request.set_KeyId(self.kms_id)
        request.set_Plaintext(base64.b64encode(data))
        response = self.kms_client.do_action_with_exception(request)
        return base64.b64decode(json.loads(response).get('CiphertextBlob'))

    def decrypt(self, data: bytes) -> bytes:
        """Decrypt a data key using KMS."""
        request = DecryptRequest()
        request.set_CiphertextBlob(base64.b64encode(data))
        response = self.kms_client.do_action_with_exception(request)
        return base64.b64decode(json.loads(response).get('Plaintext'))


def main():
    args = parser.parse_args()

    # Load credentials from environment variables OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()

    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider
    cfg.region = args.region
    if args.endpoint:
        cfg.endpoint = args.endpoint

    client = oss.Client(cfg)

    kms_client = KmsTransferAcsClient(
        ak=credentials_provider._credentials.access_key_id,
        secret=credentials_provider._credentials.access_key_secret,
        region_id=args.region,
    )

    mc = MasterKmsCipher(
        mat_desc={"desc": "your master encrypt key material describe information"},
        kms_client=kms_client,
        kms_id=args.kms_id,
    )

    encryption_client = oss.EncryptionClient(client, mc)

    # Upload: the encryption client encrypts the object before sending it to OSS
    result = encryption_client.put_object(
        oss.PutObjectRequest(bucket=args.bucket, key=args.key, body=b'hello world')
    )
    print(vars(result))

    # Download: the encryption client decrypts the object automatically
    result = encryption_client.get_object(
        oss.GetObjectRequest(bucket=args.bucket, key=args.key)
    )
    print(result.body.read())


if __name__ == "__main__":
    main()

</details>

<details> <summary>Java</summary>

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 {
        // Replace with your actual endpoint, e.g., https://oss-cn-hangzhou.aliyuncs.com
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
        // Load credentials from environment variables OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET
        EnvironmentVariableCredentialsProvider credentialsProvider =
            CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
        String bucketName = "examplebucket";
        String objectName = "exampleobject.txt";
        String content = "Hello OSS!";

        // RSA private key (PKCS#1 PEM format). Generate with: openssl genrsa -out private.pem 2048
        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-----";

        // RSA public key (X.509 PEM format). Generate with: openssl rsa -in private.pem -pubout
        final String PUBLIC_X509_PEM =
            "-----BEGIN PUBLIC KEY-----\n" +
            "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCokfiAVXXf5ImFzKDw+XO/UByW\n" +
            "6mse2QsIgz3ZwBtMNu59fR5zttSx+8fB7vR4CN3bTztrP9A6bjoN0FFnhlQ3vNJC\n" +
            "5MFO1PByrE/MNd5AAfSVba93I6sx8NSk5MzUCA4NJzAUqYOEWGtGBcom6kEF6MnR\n" +
            "1EKib1Id8hpooY5xaQID****\n" +
            "-----END PUBLIC KEY-----";

        RSAPrivateKey privateKey = SimpleRSAEncryptionMaterials.getPrivateKeyFromPemPKCS1(PRIVATE_PKCS1_PEM);
        RSAPublicKey publicKey = SimpleRSAEncryptionMaterials.getPublicKeyFromPemX509(PUBLIC_X509_PEM);
        KeyPair keyPair = new KeyPair(publicKey, privateKey);

        // Assign a description to each CMK. Descriptions cannot be changed after the CMK is set.
        // Store the CMK-to-description mapping on the client—OSS does not persist it.
        // If you leave the description blank, the client cannot identify which CMK to use for decryption.
        Map<String, String> matDesc = new HashMap<String, String>();
        matDesc.put("desc-key", "desc-value");

        SimpleRSAEncryptionMaterials encryptionMaterials =
            new SimpleRSAEncryptionMaterials(keyPair, matDesc);
        // To decrypt objects encrypted with a different CMK, add it to the encryption materials:
        // encryptionMaterials.addKeyPairDescMaterial(<otherKeyPair>, <otherKeyPairMatDesc>);

        // Build the encryption client. Call shutdown() when done to release resources.
        OSSEncryptionClient ossEncryptionClient = new OSSEncryptionClientBuilder()
            .build(endpoint, credentialsProvider, encryptionMaterials);

        try {
            // Upload: the client encrypts the object before sending it to OSS
            ossEncryptionClient.putObject(bucketName, objectName,
                new ByteArrayInputStream(content.getBytes()));

            // Download: the client decrypts the object automatically
            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();

            System.out.println("Uploaded (plaintext): " + content);
            System.out.println("Downloaded (decrypted): " + buffer.toString());
        } catch (OSSException oe) {
            System.out.println("OSS error — " + oe.getErrorMessage()
                + " [" + oe.getErrorCode() + "] RequestId: " + oe.getRequestId());
        } catch (ClientException ce) {
            System.out.println("Client error — " + ce.getMessage());
        } finally {
            if (ossEncryptionClient != null) {
                ossEncryptionClient.shutdown();
            }
        }
    }
}

</details>

<details> <summary>Go</summary>

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() {
    // Load credentials from environment variables OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET
    provider, err := oss.NewEnvironmentVariableCredentialsProvider()
    if err != nil {
        log.Fatalf("Failed to create credentials provider: %v", err)
    }

    // Replace yourEndpoint and yourRegion with your actual values,
    // e.g., https://oss-cn-hangzhou.aliyuncs.com and cn-hangzhou
    clientOptions := []oss.ClientOption{oss.SetCredentialsProvider(&provider)}
    clientOptions = append(clientOptions, oss.Region("yourRegion"))
    clientOptions = append(clientOptions, oss.AuthVersion(oss.AuthV4))
    client, err := oss.New("yourEndpoint", "", "", clientOptions...)
    if err != nil {
        log.Fatalf("Failed to create OSS client: %v", err)
    }

    // Assign a description to each CMK as a JSON string.
    // Store the CMK-to-description mapping on the client—OSS does not persist it.
    materialDesc := map[string]string{
        "desc": "your master encrypt key material describe information",
    }

    // Replace yourRsaPublicKey and yourRsaPrivateKey with your actual RSA key strings
    masterRsaCipher, err := osscrypto.CreateMasterRsa(
        materialDesc, "yourRsaPublicKey", "yourRsaPrivateKey",
    )
    if err != nil {
        log.Fatalf("Failed to create master RSA cipher: %v", err)
    }

    // Use AES-CTR mode for object encryption
    contentProvider := osscrypto.CreateAesCtrCipher(masterRsaCipher)

    // Get the bucket with encryption enabled; use it like a regular bucket
    cryptoBucket, err := osscrypto.GetCryptoBucket(client, "yourBucketName", contentProvider)
    if err != nil {
        log.Fatalf("Failed to get crypto bucket: %v", err)
    }

    // Upload: encryption is applied automatically
    err = cryptoBucket.PutObject("yourObjectName",
        bytes.NewReader([]byte("yourObjectValueByteArray")))
    if err != nil {
        log.Fatalf("Failed to put object: %v", err)
    }

    // Download: decryption is applied automatically
    body, err := cryptoBucket.GetObject("yourObjectName")
    if err != nil {
        log.Fatalf("Failed to get object: %v", err)
    }
    defer body.Close()

    data, err := io.ReadAll(body)
    if err != nil {
        log.Fatalf("Failed to read object data: %v", err)
    }
    log.Printf("Decrypted content: %s", string(data))
}

</details>

<details> <summary>PHP</summary>

<?php
require_once 'vendor/autoload.php';

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

$optsdesc = [
    "region"   => ['help' => 'Region where the bucket is located', 'required' => true],
    "endpoint" => ['help' => 'OSS endpoint (optional)',            'required' => false],
    "bucket"   => ['help' => 'Bucket name',                       'required' => true],
    "key"      => ['help' => 'Object name',                       'required' => true],
];

$longopts = array_map(fn($k) => "$k:", array_keys($optsdesc));
$options = getopt("", $longopts);

foreach ($optsdesc as $key => $value) {
    if ($value['required'] && empty($options[$key])) {
        echo "Error: --$key is required. {$value['help']}\n";
        exit(1);
    }
}

$region = $options["region"];
$bucket = $options["bucket"];
$key    = $options["key"];

// Load credentials from environment variables OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();

/**
 * KMS-backed cipher that implements MasterCipherInterface.
 * Encrypts and decrypts data keys via the dedicated KMS instance.
 */
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->kmsClient  = $kmsClient;
        $this->algorithm  = $algorithm;
        $this->matDesc    = is_array($matDesc)
            ? (json_encode($matDesc) ?: null)
            : (is_string($matDesc) ? $matDesc : null);
    }

    public function encrypt(string $data): string
    {
        $request            = new \AlibabaCloud\Dkms\Gcs\Sdk\Models\AdvanceEncryptRequest();
        $request->algorithm = $this->algorithm;
        $request->keyId     = $this->keyId;
        $request->plaintext = \AlibabaCloud\Tea\Utils\Utils::toBytes($data);
        $response = $this->kmsClient->advanceEncryptWithOptions(
            $request, new \AlibabaCloud\Dkms\Gcs\OpenApi\Util\Models\RuntimeOptions()
        );
        return base64_decode((string)$response->ciphertextBlob);
    }

    public function decrypt(string $data): string
    {
        $request               = new \AlibabaCloud\Dkms\Gcs\Sdk\Models\AdvanceDecryptRequest();
        $request->keyId        = $this->keyId;
        $request->ciphertextBlob = $data;
        $request->algorithm    = $this->algorithm;
        $response = $this->kmsClient->advanceDecryptWithOptions(
            $request, new \AlibabaCloud\Dkms\Gcs\OpenApi\Util\Models\RuntimeOptions()
        );
        return base64_decode((string)$response->plaintext);
    }

    public function getWrapAlgorithm(): string { return "KMS/ALICLOUD"; }

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

function getDkmsGcsSdkClient(): KmsClient
{
    global $clientKeyFile, $password, $dkmsEndpoint;
    $config               = new \AlibabaCloud\Dkms\Gcs\OpenApi\Models\Config();
    $config->protocol     = 'https';
    $config->clientKeyFile = $clientKeyFile;
    $config->password     = $password;
    $config->endpoint     = $dkmsEndpoint;
    $config->caFilePath   = 'path/to/caCert.pem';
    return new \AlibabaCloud\Dkms\Gcs\Sdk\Client($config);
}

// Replace these placeholders with your actual KMS instance details
$clientKeyFile = '<your client key file path>';
$password      = '<your dkms client password>';
$dkmsEndpoint  = '<your dkms instance service address>';
$kmsKeyId      = '<your cmk id>';
$algorithm     = '<your encrypt algorithm>';

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

// Upload: the encryption client encrypts the object before sending it to OSS
$eClient->putObject(new Oss\Models\PutObjectRequest(
    bucket: $bucket,
    key:    $key,
    body:   Oss\Utils::streamFor('hi kms'),
));

// Download: the encryption client decrypts the object automatically
$result = $eClient->getObject(new Oss\Models\GetObjectRequest(
    bucket: $bucket,
    key:    $key,
));

echo "Decrypted content: " . $result->body->getContents();

</details>

<details> <summary>C++</summary>

#include <alibabacloud/oss/OssEncryptionClient.h>
using namespace AlibabaCloud::OSS;

int main(void)
{
    // Replace with your actual endpoint, region, bucket, and object path
    std::string Endpoint = "yourEndpoint";
    std::string Region = "yourRegion";
    std::string BucketName = "examplebucket";
    std::string ObjectName = "exampledir/exampleobject.txt";

    // Replace with your RSA public and private key strings
    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";

    InitializeSdk();

    ClientConfiguration conf;
    conf.signatureVersion = SignatureVersionType::V4;
    // Load credentials from environment variables OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();

    CryptoConfiguration cryptoConf;
    auto materials = std::make_shared<SimpleRSAEncryptionMaterials>(
        RSAPublicKey, RSAPrivateKey, desc);
    OssEncryptionClient encryptionClient(
        Endpoint, credentialsProvider, conf, materials, cryptoConf);
    encryptionClient.SetRegion(Region);

    // Upload: the encryption client encrypts the object automatically
    auto outcome = encryptionClient.PutObject(BucketName, ObjectName, "yourLocalFilename");
    if (!outcome.isSuccess()) {
        std::cout << "PutObject failed — code: " << outcome.error().Code()
                  << ", message: " << outcome.error().Message()
                  << ", requestId: " << outcome.error().RequestId() << std::endl;
        ShutdownSdk();
        return -1;
    }

    ShutdownSdk();
    return 0;
}

</details>

What's next