客户端加密是指将数据发送到OSS之前在用户本地进行加密。

免责声明

  • 使用客户端加密功能时,您需要对主密钥的完整性和正确性负责。因您维护不当导致主密钥用错或丢失,从而导致加密数据无法解密所引起的一切损失和后果均由您自行承担。
  • 在对加密数据进行复制或者迁移时,您需要对加密元信息的完整性和正确性负责。因您维护不当导致加密元信息出错或丢失,从而导致加密数据无法解密所引起的一切损失和后果均由您自行承担。

背景信息

使用客户端加密时,会为每个Object生成一个随机密钥,用该随机密钥对Object的数据进行对称加密。主密钥用于对该随机的对称密钥进行加密,加密后的内容会当作Object的meta信息保存在服务端。解密时先用主密钥将加密后的随机密钥解密出来,再用解密出来的随机密钥解密Object的数据。主密钥只参与客户端本地计算,不会在网络上进行传输或保存在服务端,以保证主密钥的数据安全。

注意
  • 客户端加密支持分片上传超过5 GB的文件。在使用分片方式上传文件时,需要指定上传文件的总大小和分片大小, 除了最后一个分片外,每个分片的大小要一致,且分片大小目前必须是16的整数倍。
  • 调用客户端加密上传文件后,加密元数据会被保护,无法通过CopyObject修改object meta信息。

加密方式

对于主密钥的使用,目前支持如下两种方式:

  • 使用KMS托管用户主密钥

    当使用KMS托管用户主密钥用于客户端数据加密时,需要将KMS用户主密钥ID(即CMK ID)传递给SDK。

  • 使用用户自主管理的主密钥(RSA)

    主密钥信息由用户提供,需要用户将主密钥的公钥、私钥信息当做参数传递给SDK。

使用以上两种加密方式能够有效地避免数据泄漏,保护客户端数据安全。即使数据泄漏,其他人也无法解密得到原始数据。

加密元信息

参数 描述 是否必需
x-oss-meta-client-side-encryption-key 加密后的密钥。 经过主秘钥加密后再经过base64编码的字符串。
x-oss-meta-client-side-encryption-start 随机产生的加密数据的初始值 。经过主秘钥加密后再经过base64编码的字符串。
x-oss-meta-client-side-encryption-cek-alg 数据的加密算法。
x-oss-meta-client-side-encryption-wrap-alg 数据密钥的加密算法。
x-oss-meta-client-side-encryption-matdesc 主秘钥的描述信息。JSON格式。
警告 强烈建议为每个主秘钥都配置描述信息,并保存好主秘钥和描述信息之间的对应关系。否则不支持更换主秘钥进行加密。
x-oss-meta-client-side-encryption-unencrypted-content-length 加密前的数据长度。如未指定content-length则不生成该参数。
x-oss-meta-client-side-encryption-unencrypted-content-md5 加密前数据的MD5。如未指定MD5,则不生成该参数。
x-oss-meta-client-side-encryption-data-size 若加密Multipart文件需要在init_multipart时传入整个大文件的总大小。 是(分片上传)
x-oss-meta-client-side-encryption-part-size 若加密Multipart文件需要在init_multipart时传入分片大小。
说明 目前分片大小必须是16的整数倍。
是(分片上传)

以下提供了如何使用用户自主管理的主秘钥(RSA)方式从内存中上传文件、上传本地文件、断点续传上传、分片上传、下载到本地文件等场景的完整示例代码。

从内存中上传文件

以下代码用于使用主密钥RSA加密内存中上传的文件:

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

int main(void)
{
    /* 初始化OSS账号信息 */
    std::string AccessKeyId = "yourAccessKeyId";
    std::string AccessKeySecret = "yourAccessKeySecret";
    std::string Endpoint = "yourEndpoint";
    std::string BucketName = "yourBucketName";
    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;
    CryptoConfiguration cryptoConf;
    auto materials = std::make_shared<SimpleRSAEncryptionMaterials>(RSAPublicKey, RSAPrivateKey, desc);
    OssEncryptionClient client(Endpoint, AccessKeyId, AccessKeySecret, conf, materials, cryptoConf);
    std::shared_ptr<std::iostream> content = std::make_shared<std::stringstream>();
    *content << "Thank you for using Aliyun 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;
}

上传本地文件

以下代码用于使用主密钥RSA加密本地上传的文件:

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

int main(void)
{
    /* 初始化OSS账号信息 */
    std::string AccessKeyId = "yourAccessKeyId";
    std::string AccessKeySecret = "yourAccessKeySecret";
    std::string Endpoint = "yourEndpoint";
    std::string BucketName = "yourBucketName";
    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;
    CryptoConfiguration cryptoConf;
    auto materials = std::make_shared<SimpleRSAEncryptionMaterials>(RSAPublicKey, RSAPrivateKey, desc);
    OssEncryptionClient client(Endpoint, AccessKeyId, AccessKeySecret, conf, materials, cryptoConf);
    /* 上传文件 */
    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;
}

断点续传上传

以下代码用于使用主密钥RSA加密断点续传上传的文件:

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

int main(void)
{
    /* 初始化OSS账号信息 */
    std::string AccessKeyId = "yourAccessKeyId";
    std::string AccessKeySecret = "yourAccessKeySecret";
    std::string Endpoint = "yourEndpoint";
    std::string BucketName = "yourBucketName";
    std::string ObjectName = "yourObjectName";
    std::string UploadFilePath = "yourUploadfilePath";
    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;
    CryptoConfiguration cryptoConf;
    auto materials = std::make_shared<SimpleRSAEncryptionMaterials>(RSAPublicKey, RSAPrivateKey, desc);
    OssEncryptionClient client(Endpoint, AccessKeyId, AccessKeySecret, conf, materials, cryptoConf);

    /* 断点续传上传 */
    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;
}

分片上传

以下代码用于使用主密钥RSA加密分片上传的文件:

#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)
{
    /* 初始化OSS账号信息 */
    std::string AccessKeyId = "yourAccessKeyId";
    std::string AccessKeySecret = "yourAccessKeySecret";
    std::string Endpoint = "yourEndpoint";
    std::string BucketName = "yourBucketName";
    std::string ObjectName = "yourObjectName";
    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;
    CryptoConfiguration cryptoConf;
    auto materials = std::make_shared<SimpleRSAEncryptionMaterials>(RSAPublicKey, RSAPrivateKey, desc);
    OssEncryptionClient client(Endpoint, AccessKeyId, AccessKeySecret, conf, materials, cryptoConf);

    /* 初始化分片加密上下文 */
    /* 需要16字节对齐 */
    int64_t partSize = 100 * 1024;
    auto fileSize = getFileSize(fileToUpload);
    MultipartUploadCryptoContext cryptoCtx;
    cryptoCtx.setPartSize(partSize);
    cryptoCtx.setDataSize(fileSize);

    /* 初始化分片上传事件 */
    InitiateMultipartUploadRequest initUploadRequest(BucketName, ObjectName);
    auto uploadIdResult = client.InitiateMultipartUpload(initUploadRequest, cryptoCtx);
    auto uploadId = uploadIdResult.result().UploadId();
    PartList partETagList;
    int partCount = static_cast<int> (fileSize / partSize);
    /* 计算分片个数 */
    if (fileSize % partSize != 0)  {
        partCount++;
    }
    /* 对每一个分片进行上传 */
    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;
        }
    }
    /* 完成分片上传 */
    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;
}

下载到本地文件

以下代码用于使用主密钥RSA解密下载到本地的文件:

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

int main(void)
{
    /* 初始化OSS账号信息 */
    std::string AccessKeyId = "yourAccessKeyId";
    std::string AccessKeySecret = "yourAccessKeySecret";
    std::string Endpoint = "yourEndpoint";
    std::string BucketName = "yourBucketName";
    std::string ObjectName = "yourObjectName";
    std::string FileNametoSave = "yourFileName";

    /* 主密钥及描述信息 */
    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;
    CryptoConfiguration cryptoConf;
    auto materials = std::make_shared<SimpleRSAEncryptionMaterials>(RSAPublicKey, RSAPrivateKey, desc);

    /* 如果需要解密不同主秘钥加密的内容, 需要传对应的密钥信息 */
    //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, AccessKeyId, AccessKeySecret, conf, materials, cryptoConf);

    /* 获取文件到本地文件 */
    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 << "GetObjectToFile success" << outcome.result().Metadata().ContentLength() << std::endl;
    }
    else {
        /* 异常处理 */
        std::cout << "GetObjectToFile fail" <<
        ",code:" << outcome.error().Code() <<
        ",message:" << outcome.error().Message() <<
        ",requestId:" << outcome.error().RequestId() << std::endl;
        ShutdownSdk();
        return -1;
    }
    /* 释放网络等资源 */
    ShutdownSdk();
    return 0;
}

下载到本地内存

以下代码用于使用主密钥RSA解密下载到本地内存的文件:

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

int main(void)
{
    /* 初始化OSS账号信息 */
    std::string AccessKeyId = "yourAccessKeyId";
    std::string AccessKeySecret = "yourAccessKeySecret";
    std::string Endpoint = "yourEndpoint";
    std::string BucketName = "yourBucketName";
    std::string ObjectName = "yourObjectName";
    std::string RSAPublicKey = "your rsa public key";
    std::string RSAPrivateKey = "your rsa private key";

    /* 主密钥及描述信息*/
    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;
    CryptoConfiguration cryptoConf;
    auto materials = std::make_shared<SimpleRSAEncryptionMaterials>(RSAPublicKey, RSAPrivateKey, desc);

    /* 如果需要解密不同主秘钥加密的内容, 需要传对应的密钥信息 */
    //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, AccessKeyId, AccessKeySecret, conf, materials, cryptoConf);

    /* 获取文件到本地内存 */
    GetObjectRequest request(BucketName, ObjectName);
    auto outcome = client.GetObject(request);
    if (outcome.isSuccess()) {    
      std::cout << "getObjectToBuffer" << " success, Content-Length:" << outcome.result().Metadata().ContentLength() << std::endl;
        /* 打印下载内容 */
        std::string content;
        *(outcome.result().Content()) >> content;
        std::cout << "getObjectToBuffer" << "content:" << content << std::endl; 
    }
    else {
        /* 异常处理 */
        std::cout << "getObjectToBuffer fail" <<
        ",code:" << outcome.error().Code() <<
        ",message:" << outcome.error().Message() <<
        ",requestId:" << outcome.error().RequestId() << std::endl;
        ShutdownSdk();
        return -1;
    }

    /*释放网络等资源*/
    ShutdownSdk();
    return 0;
}

范围下载

以下代码用于使用主密钥RSA解密范围下载的文件:

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

int main(void)
{
    /* 初始化OSS账号信息 */
    std::string AccessKeyId = "yourAccessKeyId";
    std::string AccessKeySecret = "yourAccessKeySecret";
    std::string Endpoint = "yourEndpoint";
    std::string BucketName = "yourBucketName";
    std::string ObjectName = "yourObectName ";

    /* 主密钥及描述信息*/
    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;
    CryptoConfiguration cryptoConf;
    auto materials = std::make_shared<SimpleRSAEncryptionMaterials>(RSAPublicKey, RSAPrivateKey, desc);

    /* 如果需要解密不同主秘钥加密的内容, 需要传对应的密钥信息 */
    //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, AccessKeyId, AccessKeySecret, conf, materials, cryptoConf);

    /* 设置下载范围 */
    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;
}

断点续传下载

以下代码用于使用主密钥RSA解密断点续传下载的文件:

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

int main(void)
{
    /* 初始化OSS账号信息 */
    std::string AccessKeyId = "yourAccessKeyId";
    std::string AccessKeySecret = "yourAccessKeySecret";
    std::string Endpoint = "yourEndpoint";
    std::string BucketName = "yourBucketName";
    std::string ObjectName = "yourObjectName";
    std::string DownloadFilePath = "yourDownloadFilePath";
    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;
    CryptoConfiguration cryptoConf;
    auto materials = std::make_shared<SimpleRSAEncryptionMaterials>(RSAPublicKey, RSAPrivateKey, desc);

    /* 如果需要解密不同主秘钥加密的内容, 需要传对应的密钥信息 */
    //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, AccessKeyId, AccessKeySecret, conf, materials, cryptoConf);

    /* 断点续传下载 */
    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;
}