Client-side encryption (C++ SDK)

Updated at:

OSS client-side encryption encrypts data locally before it is uploaded to OSS. Only key holders can decrypt the data, which protects it during transmission and storage.

Disclaimer

  • You are responsible for the integrity and validity of the customer master key (CMK). If the CMK is lost or used incorrectly due to improper maintenance, you bear all losses and consequences from decryption failures.

  • When copying or migrating encrypted data, you are responsible for the integrity and validity of the object metadata. If encrypted metadata is lost or corrupted due to improper maintenance, you bear all losses and consequences from decryption failures.

Use cases

  • Sensitive data protection: Encrypt personally identifiable information (PII), financial records, or medical data before it leaves your environment. Even if the data is intercepted in transit, it remains unreadable.

  • Regulatory compliance: Regulations such as the Health Insurance Portability and Accountability Act (HIPAA) and the General Data Protection Regulation (GDPR) require strict encryption controls for data stored on third-party platforms. Client-side encryption satisfies these requirements because you manage the keys — they are never transmitted over the network or controlled by the cloud provider.

  • Full encryption control: Select your own encryption algorithm, and manage and rotate keys independently. Only authorized users can decrypt and access the data.

  • Secure cross-region migration: Data stays encrypted throughout the migration, protecting it while it traverses the internet.

Usage notes

  • The examples in this topic use the public endpoint for the China (Hangzhou) region. To access OSS from other Alibaba Cloud services in the same region, use an internal endpoint. For more information, see Regions and endpoints.

  • The examples create an OssEncryptionClient instance directly. For alternative configurations — such as using a custom domain or authenticating with Security Token Service (STS) credentials — see Create an OSSClient instance.

  • Client-side encryption encrypts only object content, not object metadata.

How it works

For each object, the SDK generates a random data key and uses it to encrypt the object with symmetric encryption. The CMK then encrypts the data key, and the encrypted data key is stored as object metadata in OSS.

When downloading an encrypted object, the SDK uses the CMK to decrypt the data key, then uses the data key to decrypt the object. The CMK is used only on the client and is never transmitted over the network or stored in OSS.

Important

Client-side encryption supports multipart upload for objects larger than 5 GB. When using multipart upload, you must specify both the total object size and the part size upfront. All parts except the last must be the same size and must be a multiple of 16 bytes. This is required because AES uses a 128-bit (16-byte) block size — part sizes that are not 16-byte aligned cannot be encrypted correctly.

After uploading an encrypted object, its encryption metadata is protected and cannot be modified via the CopyObject API.

Encryption methods

The SDK supports two types of CMKs:

  • RSA-based CMKs managed by you: Provide your RSA public key and private key as parameters when initializing the encryption client.

  • KMS-managed CMKs: Provide the CMK ID from Key Management Service (KMS) when initializing the encryption client.

Even if encrypted data is exposed, it cannot be decrypted without the corresponding CMK.

Encryption metadata

Each encrypted object carries the following metadata headers:

ParameterDescriptionRequired
x-oss-meta-client-side-encryption-keyThe encrypted data key, Base64-encoded after encryption by the CMKYes
x-oss-meta-client-side-encryption-startThe randomly generated initialization vector for data encryption, Base64-encoded after encryption by the CMKYes
x-oss-meta-client-side-encryption-cek-algThe data encryption algorithmYes
x-oss-meta-client-side-encryption-wrap-algThe key wrap algorithm used to encrypt the data keyYes
x-oss-meta-client-side-encryption-matdescThe CMK description, in JSON format.
Warning

Assign a unique description to each CMK and maintain the mapping between CMKs and their descriptions. Without this mapping, you cannot rotate to a different CMK.

No
x-oss-meta-client-side-encryption-unencrypted-content-lengthThe plaintext data length before encryption. Not generated if Content-Length is not set.No
x-oss-meta-client-side-encryption-unencrypted-content-md5The MD5 hash of the plaintext. Not generated if no MD5 is specified.No
x-oss-meta-client-side-encryption-data-sizeThe total object size, required during InitiateMultipartUpload for encrypted multipart uploadsYes (multipart upload only)
x-oss-meta-client-side-encryption-part-sizeThe part size, required during InitiateMultipartUpload. Must be a multiple of 16 bytes.Yes (multipart upload only)

Code examples

All examples below use a user-managed RSA CMK and follow the same initialization pattern: create a SimpleRSAEncryptionMaterials instance with your key pair and description, then construct an OssEncryptionClient with a CryptoConfiguration. Credentials are read from environment variables (OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET).

Upload from memory

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

int main(void)
{
    /* Replace with the endpoint for your bucket's region.
       Example for China (Hangzhou): https://oss-cn-hangzhou.aliyuncs.com */
    std::string Endpoint = "yourEndpoint";
    /* Replace with your region ID. Example: cn-hangzhou */
    std::string Region = "yourRegion";
    /* Replace with your bucket name. Example: examplebucket */
    std::string BucketName = "examplebucket";
    /* Replace with the full object path (excluding the bucket name).
       Example: exampledir/exampleobject.txt */
    std::string ObjectName = "exampledir/exampleobject.txt";

    /* RSA key pair and 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";

    InitializeSdk();

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

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

    /* Prepare in-memory content */
    std::shared_ptr<std::iostream> content = std::make_shared<std::stringstream>();
    *content << "Thank you for using Alibaba Cloud Object Storage Service!";
    PutObjectRequest request(BucketName, ObjectName, content);

    auto outcome = client.PutObject(request);
    if (!outcome.isSuccess()) {
        std::cout << "PutObject fail"
            << ", code:" << outcome.error().Code()
            << ", message:" << outcome.error().Message()
            << ", requestId:" << outcome.error().RequestId() << std::endl;
        ShutdownSdk();
        return -1;
    }

    ShutdownSdk();
    return 0;
}

Upload a local file

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

int main(void)
{
    std::string Endpoint = "yourEndpoint";
    std::string Region = "yourRegion";
    std::string BucketName = "examplebucket";
    std::string ObjectName = "exampledir/exampleobject.txt";

    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;
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();

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

    /* Specify the local file path to upload */
    auto outcome = client.PutObject(BucketName, ObjectName, "yourLocalFilename");
    if (!outcome.isSuccess()) {
        std::cout << "PutObject fail"
            << ", code:" << outcome.error().Code()
            << ", message:" << outcome.error().Message()
            << ", requestId:" << outcome.error().RequestId() << std::endl;
        ShutdownSdk();
        return -1;
    }

    ShutdownSdk();
    return 0;
}

Resumable upload

The SDK saves upload progress to a checkpoint file. If the upload is interrupted, it resumes from the last checkpoint.

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

int main(void)
{
    std::string Endpoint = "yourEndpoint";
    std::string Region = "yourRegion";
    std::string BucketName = "examplebucket";
    std::string ObjectName = "exampledir/exampleobject.txt";
    /* Local file to upload. Example: D:\\localpath\\examplefile.txt */
    std::string UploadFilePath = "D:\\localpath\\examplefile.txt";
    /* Checkpoint file path. Defaults to the same directory as the upload file if not specified.
       The file is deleted automatically after the upload completes. */
    std::string CheckpointFilePath = "yourCheckpointFilepath";

    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;
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();

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

    UploadObjectRequest request(BucketName, ObjectName, UploadFilePath, CheckpointFilePath);
    auto outcome = client.ResumableUploadObject(request);
    if (!outcome.isSuccess()) {
        std::cout << "ResumableUploadObject fail"
            << ", code:" << outcome.error().Code()
            << ", message:" << outcome.error().Message()
            << ", requestId:" << outcome.error().RequestId() << std::endl;
        ShutdownSdk();
        return -1;
    }

    ShutdownSdk();
    return 0;
}

Multipart upload

For objects larger than 5 GB, use multipart upload. Specify the total file size and part size in MultipartUploadCryptoContext before calling InitiateMultipartUpload — the encryption context requires both values upfront. Part sizes must be a multiple of 16 bytes (AES block size requirement).

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

static int64_t getFileSize(const std::string& file)
{
    std::fstream f(file, std::ios::in | std::ios::binary);
    f.seekg(0, f.end);
    int64_t size = f.tellg();
    f.close();
    return size;
}

int main(void)
{
    std::string Endpoint = "yourEndpoint";
    std::string Region = "yourRegion";
    std::string BucketName = "examplebucket";
    std::string ObjectName = "exampledir/exampleobject.txt";
    std::string fileToUpload = "yourLocalFilename";

    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;
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();

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

    /* Set part size (must be 16-byte aligned for AES encryption) and total file size */
    int64_t partSize = 100 * 1024;
    auto fileSize = getFileSize(fileToUpload);
    MultipartUploadCryptoContext cryptoCtx;
    cryptoCtx.setPartSize(partSize);
    cryptoCtx.setDataSize(fileSize);

    /* Initiate the multipart upload */
    InitiateMultipartUploadRequest initUploadRequest(BucketName, ObjectName);
    auto uploadIdResult = client.InitiateMultipartUpload(initUploadRequest, cryptoCtx);
    auto uploadId = uploadIdResult.result().UploadId();

    /* Calculate the number of parts */
    PartList partETagList;
    int partCount = static_cast<int>(fileSize / partSize);
    if (fileSize % partSize != 0) {
        partCount++;
    }

    /* Upload each part */
    for (int i = 1; i <= partCount; i++) {
        auto skipBytes = partSize * (i - 1);
        auto size = (partSize < fileSize - skipBytes) ? partSize : (fileSize - skipBytes);
        std::shared_ptr<std::iostream> content = std::make_shared<std::fstream>(fileToUpload, std::ios::in | std::ios::binary);
        content->seekg(skipBytes, std::ios::beg);
        UploadPartRequest uploadPartRequest(BucketName, ObjectName, content);
        uploadPartRequest.setContentLength(size);
        uploadPartRequest.setUploadId(uploadId);
        uploadPartRequest.setPartNumber(i);
        auto uploadPartOutcome = client.UploadPart(uploadPartRequest, cryptoCtx);
        if (uploadPartOutcome.isSuccess()) {
            Part part(i, uploadPartOutcome.result().ETag());
            partETagList.push_back(part);
        } else {
            std::cout << "UploadPart fail"
                << ", code:" << uploadPartOutcome.error().Code()
                << ", message:" << uploadPartOutcome.error().Message()
                << ", requestId:" << uploadPartOutcome.error().RequestId() << std::endl;
        }
    }

    /* Complete the multipart upload */
    CompleteMultipartUploadRequest request(BucketName, ObjectName);
    request.setUploadId(uploadId);
    request.setPartList(partETagList);
    auto outcome = client.CompleteMultipartUpload(request, cryptoCtx);
    if (!outcome.isSuccess()) {
        std::cout << "CompleteMultipartUpload fail"
            << ", code:" << outcome.error().Code()
            << ", message:" << outcome.error().Message()
            << ", requestId:" << outcome.error().RequestId() << std::endl;
        ShutdownSdk();
        return -1;
    }

    ShutdownSdk();
    return 0;
}

Download to a local file

To decrypt content that was encrypted with a different CMK, call addEncryptionMaterial with the corresponding key pair before constructing OssEncryptionClient.

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

int main(void)
{
    std::string Endpoint = "yourEndpoint";
    std::string Region = "yourRegion";
    std::string BucketName = "examplebucket";
    std::string ObjectName = "exampledir/exampleobject.txt";
    /* Download destination. If the file exists, it is overwritten; if not, it is created.
       Defaults to the sample program's directory if no path is specified. */
    std::string FileNametoSave = "D:\\localpath\\examplefile.txt";

    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;
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();

    CryptoConfiguration cryptoConf;
    auto materials = std::make_shared<SimpleRSAEncryptionMaterials>(RSAPublicKey, RSAPrivateKey, desc);

    /* To support objects encrypted with a different CMK, add its key pair here:
    std::string RSAPublicKey2 = "your rsa public key";
    std::string RSAPrivateKey2 = "your rsa private key";
    std::map<std::string, std::string> desc2;
    desc2["comment"] = "your comment";
    materials->addEncryptionMaterial(RSAPublicKey2, RSAPrivateKey2, desc2); */

    OssEncryptionClient client(Endpoint, credentialsProvider, conf, materials, cryptoConf);
    client.SetRegion(Region);

    GetObjectRequest request(BucketName, ObjectName);
    request.setResponseStreamFactory([=]() {
        return std::make_shared<std::fstream>(FileNametoSave,
            std::ios_base::out | std::ios_base::in | std::ios_base::trunc | std::ios_base::binary);
    });
    auto outcome = client.GetObject(request);
    if (outcome.isSuccess()) {
        std::cout << "GetObject success, Content-Length: " << outcome.result().Metadata().ContentLength() << std::endl;
    } else {
        std::cout << "GetObject fail"
            << ", code:" << outcome.error().Code()
            << ", message:" << outcome.error().Message()
            << ", requestId:" << outcome.error().RequestId() << std::endl;
        ShutdownSdk();
        return -1;
    }

    ShutdownSdk();
    return 0;
}

Download to memory

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

int main(void)
{
    std::string Endpoint = "yourEndpoint";
    std::string Region = "yourRegion";
    std::string BucketName = "examplebucket";
    std::string ObjectName = "yourObjectName";

    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;
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();

    CryptoConfiguration cryptoConf;
    auto materials = std::make_shared<SimpleRSAEncryptionMaterials>(RSAPublicKey, RSAPrivateKey, desc);

    /* To support objects encrypted with a different CMK, add its key pair here:
    std::string RSAPublicKey2 = "your rsa public key";
    std::string RSAPrivateKey2 = "your rsa private key";
    std::map<std::string, std::string> desc2;
    desc2["comment"] = "your comment";
    materials->addEncryptionMaterial(RSAPublicKey2, RSAPrivateKey2, desc2); */

    OssEncryptionClient client(Endpoint, credentialsProvider, conf, materials, cryptoConf);
    client.SetRegion(Region);

    GetObjectRequest request(BucketName, ObjectName);
    auto outcome = client.GetObject(request);
    if (outcome.isSuccess()) {
        std::cout << "GetObject success, Content-Length: " << outcome.result().Metadata().ContentLength() << std::endl;
        std::string content;
        *(outcome.result().Content()) >> content;
        std::cout << "Content: " << content << std::endl;
    } else {
        std::cout << "GetObject fail"
            << ", code:" << outcome.error().Code()
            << ", message:" << outcome.error().Message()
            << ", requestId:" << outcome.error().RequestId() << std::endl;
        ShutdownSdk();
        return -1;
    }

    ShutdownSdk();
    return 0;
}

Range download

Range download retrieves and decrypts a byte range of an encrypted object.

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

int main(void)
{
    std::string Endpoint = "yourEndpoint";
    std::string Region = "yourRegion";
    std::string BucketName = "examplebucket";
    std::string ObjectName = "yourObjectName";

    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;
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();

    CryptoConfiguration cryptoConf;
    auto materials = std::make_shared<SimpleRSAEncryptionMaterials>(RSAPublicKey, RSAPrivateKey, desc);

    /* To support objects encrypted with a different CMK, add its key pair here:
    std::string RSAPublicKey2 = "your rsa public key";
    std::string RSAPrivateKey2 = "your rsa private key";
    std::map<std::string, std::string> desc2;
    desc2["comment"] = "your comment";
    materials->addEncryptionMaterial(RSAPublicKey2, RSAPrivateKey2, desc2); */

    OssEncryptionClient client(Endpoint, credentialsProvider, conf, materials, cryptoConf);
    client.SetRegion(Region);

    /* Download bytes 0–1 (inclusive) */
    GetObjectRequest request(BucketName, ObjectName);
    request.setRange(0, 1);
    auto outcome = client.GetObject(request);
    if (!outcome.isSuccess()) {
        std::cout << "GetObject fail"
            << ", code:" << outcome.error().Code()
            << ", message:" << outcome.error().Message()
            << ", requestId:" << outcome.error().RequestId() << std::endl;
        ShutdownSdk();
        return -1;
    }

    ShutdownSdk();
    return 0;
}

Resumable download

The SDK saves download progress to a checkpoint file. If the download is interrupted, it resumes from the last checkpoint.

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

int main(void)
{
    std::string Endpoint = "yourEndpoint";
    std::string Region = "yourRegion";
    std::string BucketName = "examplebucket";
    std::string ObjectName = "exampledir/exampleobject.txt";
    /* Download destination. If the file exists, it is overwritten; if not, it is created. */
    std::string DownloadFilePath = "D:\\localpath\\examplefile.txt";
    /* Checkpoint file path. Generated when a download is interrupted; deleted when the download completes. */
    std::string CheckpointFilePath = "D:\\localpath\\examplefile.txt.dcp";

    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;
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();

    CryptoConfiguration cryptoConf;
    auto materials = std::make_shared<SimpleRSAEncryptionMaterials>(RSAPublicKey, RSAPrivateKey, desc);

    /* To support objects encrypted with a different CMK, add its key pair here:
    std::string RSAPublicKey2 = "your rsa public key";
    std::string RSAPrivateKey2 = "your rsa private key";
    std::map<std::string, std::string> desc2;
    desc2["comment"] = "your comment";
    materials->addEncryptionMaterial(RSAPublicKey2, RSAPrivateKey2, desc2); */

    OssEncryptionClient client(Endpoint, credentialsProvider, conf, materials, cryptoConf);
    client.SetRegion(Region);

    DownloadObjectRequest request(BucketName, ObjectName, DownloadFilePath, CheckpointFilePath);
    auto outcome = client.ResumableDownloadObject(request);
    if (!outcome.isSuccess()) {
        std::cout << "ResumableDownloadObject fail"
            << ", code:" << outcome.error().Code()
            << ", message:" << outcome.error().Message()
            << ", requestId:" << outcome.error().RequestId() << std::endl;
        ShutdownSdk();
        return -1;
    }

    ShutdownSdk();
    return 0;
}