OSS客户端加密是在数据上传至OSS之前,由用户在本地对数据进行加密处理,确保只有密钥持有者才能解密数据,增强数据在传输和存储过程中的安全性。
注意事项
本文示例代码以华东1(杭州)的地域ID
cn-hangzhou
为例,默认使用外网Endpoint,如果您希望通过与OSS同地域的其他阿里云产品访问OSS,请使用内网Endpoint。关于OSS支持的Region与Endpoint的对应关系,请参见OSS地域和访问域名。使用客户端加密功能时,您需要对主密钥的完整性和正确性负责。
在对加密数据进行复制或者迁移时,您需要对加密元数据的完整性和正确性负责。
使用RSA主密钥
使用主密钥RSA简单上传和下载Object
使用主密钥RSA简单上传和下载Object示例代码如下:
<?php
// 引入自动加载文件 加载依赖库
require_once __DIR__ . '/../vendor/autoload.php';
use AlibabaCloud\Oss\V2 as Oss;
// 定义RSA公钥 用于加密操作
const RSA_PUBLIC_KEY = <<<BBB
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCokfiAVXXf5ImFzKDw+XO/UByW
6mse2QsIgz3ZwBtMNu59fR5zttSx+8fB7vR4CN3bTztrP9A6bjoN0FFnhlQ3vNJC
5MFO1PByrE/MNd5AAfSVba93I6sx8NSk5MzUCA4NJzAUqYOEWGtGBcom6kEF6MmR
1EKib1Id8hpooY5xaQIDAQAB
-----END PUBLIC KEY-----
BBB;
// 定义RSA私钥 用于解密操作
const RSA_PRIVATE_KEY = <<<BBB
-----BEGIN PRIVATE KEY-----
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKiR+IBVdd/kiYXM
oPD5c79QHJbqax7ZCwiDPdnAG0w27n19HnO21LH7x8Hu9HgI3dtPO2s/0DpuOg3Q
UWeGVDe80kLkwU7U8HKsT8w13kAB9JVtr3cjqzHw1KTkzNQIDg0nMBSpg4RYa0YF
yibqQQXoyZHUQqJvUh3yGmihjnFpAgMBAAECgYA49RmCQ14QyKevDfVTdvYlLmx6
kbqgMbYIqk+7w611kxoCTMR9VMmJWgmk/Zic9mIAOEVbd7RkCdqT0E+xKzJJFpI2
ZHjrlwb21uqlcUqH1Gn+wI+jgmrafrnKih0kGucavr/GFi81rXixDrGON9KBE0FJ
cPVdc0XiQAvCBnIIAQJBANXu3htPH0VsSznfqcDE+w8zpoAJdo6S/p30tcjsDQnx
l/jYV4FXpErSrtAbmI013VYkdJcghNSLNUXppfk2e8UCQQDJt5c07BS9i2SDEXiz
byzqCfXVzkdnDj9ry9mba1dcr9B9NCslVelXDGZKvQUBqNYCVxg398aRfWlYDTjU
IoVVAkAbTyjPN6R4SkC4HJMg5oReBmvkwFCAFsemBk0GXwuzD0IlJAjXnAZ+/rIO
ItewfwXIL1Mqz53lO/gK+q6TR585AkB304KUIoWzjyF3JqLP3IQOxzns92u9EV6l
V2P+CkbMPXiZV6sls6I4XppJXX2i3bu7iidN3/dqJ9izQK94fMU9AkBZvgsIPCot
y1/POIbv9LtnviDKrmpkXgVQSU4BmTPvXwTJm8APC7P/horSh3SVf1zgmnsyjm9D
hO92gGc+4ajL
-----END PRIVATE KEY-----
BBB;
// 定义命令行参数描述
$optsdesc = [
"region" => ['help' => 'The region in which the bucket is located', 'required' => True], // 区域是必填项 存储空间所在的区域
"endpoint" => ['help' => 'The domain names that other services can use to access OSS', 'required' => False], // 终端节点是可选项 其他服务可以用来访问OSS的域名
"bucket" => ['help' => 'The name of the bucket', 'required' => True], // 存储空间名称是必填项
"key" => ['help' => 'The name of the object', 'required' => True], // 对象名称是必填项
];
// 生成长选项列表 用于解析命令行参数
$longopts = \array_map(function ($key) {
return "$key:"; // 每个参数后面加冒号 表示需要值
}, array_keys($optsdesc));
// 解析命令行参数
$options = getopt("", $longopts);
// 检查必填参数是否缺失
foreach ($optsdesc as $key => $value) {
if ($value['required'] === True && empty($options[$key])) {
$help = $value['help'];
echo "Error: the following arguments are required: --$key, $help"; // 提示用户缺少必填参数
exit(1);
}
}
// 获取命令行参数值
$region = $options["region"]; // 存储空间所在区域
$bucket = $options["bucket"]; // 存储空间名称
$key = $options["key"]; // 对象名称
// 使用环境变量加载凭证信息 AccessKeyId 和 AccessKeySecret
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();
// 使用SDK的默认配置
$cfg = Oss\Config::loadDefault();
// 设置凭证提供者
$cfg->setCredentialsProvider($credentialsProvider);
// 设置区域
$cfg->setRegion($region);
// 如果提供了终端节点 则设置终端节点
if (isset($options["endpoint"])) {
$cfg->setEndpoint($options["endpoint"]);
}
// 创建OSS客户端实例
$client = new Oss\Client($cfg);
// 创建主密钥加密器 使用RSA公钥和私钥进行加密解密
$masterCipher = new Oss\Crypto\MasterRsaCipher(
publicKey: RSA_PUBLIC_KEY, // RSA公钥
privateKey: RSA_PRIVATE_KEY, // RSA私钥
matDesc: ['tag' => 'value'] // 加密附加标签
);
// 创建加密客户端实例
$eclient = new Oss\EncryptionClient(client: $client, masterCipher: $masterCipher);
// 创建简单上传对象的请求对象
$putObjRequest = new Oss\Models\PutObjectRequest(bucket: $bucket, key: $key);
// 调用putObject方法上传对象
$putObjResult = $eclient->putObject(request: $putObjRequest);
// 打印返回结果
printf(
'put object status code:' . $putObjResult->statusCode . PHP_EOL . // HTTP响应状态码
'request id:' . $putObjResult->requestId . PHP_EOL // 请求的唯一标识
);
使用主密钥RSA分片上传Object
使用主密钥RSA分片上传Object示例代码如下:
<?php
// 引入自动加载文件 加载依赖库
require_once __DIR__ . '/../vendor/autoload.php';
use AlibabaCloud\Oss\V2 as Oss;
// 定义RSA公钥 用于加密操作
const RSA_PUBLIC_KEY = <<<BBB
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCokfiAVXXf5ImFzKDw+XO/UByW
6mse2QsIgz3ZwBtMNu59fR5zttSx+8fB7vR4CN3bTztrP9A6bjoN0FFnhlQ3vNJC
5MFO1PByrE/MNd5AAfSVba93I6sx8NSk5MzUCA4NJzAUqYOEWGtGBcom6kEF6MmR
1EKib1Id8hpooY5xaQIDAQAB
-----END PUBLIC KEY-----
BBB;
// 定义RSA私钥 用于解密操作
const RSA_PRIVATE_KEY = <<<BBB
-----BEGIN PRIVATE KEY-----
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKiR+IBVdd/kiYXM
oPD5c79QHJbqax7ZCwiDPdnAG0w27n19HnO21LH7x8Hu9HgI3dtPO2s/0DpuOg3Q
UWeGVDe80kLkwU7U8HKsT8w13kAB9JVtr3cjqzHw1KTkzNQIDg0nMBSpg4RYa0YF
yibqQQXoyZHUQqJvUh3yGmihjnFpAgMBAAECgYA49RmCQ14QyKevDfVTdvYlLmx6
kbqgMbYIqk+7w611kxoCTMR9VMmJWgmk/Zic9mIAOEVbd7RkCdqT0E+xKzJJFpI2
ZHjrlwb21uqlcUqH1Gn+wI+jgmrafrnKih0kGucavr/GFi81rXixDrGON9KBE0FJ
cPVdc0XiQAvCBnIIAQJBANXu3htPH0VsSznfqcDE+w8zpoAJdo6S/p30tcjsDQnx
l/jYV4FXpErSrtAbmI013VYkdJcghNSLNUXppfk2e8UCQQDJt5c07BS9i2SDEXiz
byzqCfXVzkdnDj9ry9mba1dcr9B9NCslVelXDGZKvQUBqNYCVxg398aRfWlYDTjU
IoVVAkAbTyjPN6R4SkC4HJMg5oReBmvkwFCAFsemBk0GXwuzD0IlJAjXnAZ+/rIO
ItewfwXIL1Mqz53lO/gK+q6TR585AkB304KUIoWzjyF3JqLP3IQOxzns92u9EV6l
V2P+CkbMPXiZV6sls6I4XppJXX2i3bu7iidN3/dqJ9izQK94fMU9AkBZvgsIPCot
y1/POIbv9LtnviDKrmpkXgVQSU4BmTPvXwTJm8APC7P/horSh3SVf1zgmnsyjm9D
hO92gGc+4ajL
-----END PRIVATE KEY-----
BBB;
// 定义命令行参数描述
$optsdesc = [
"region" => ['help' => 'The region in which the bucket is located', 'required' => True], // 区域是必填项 存储空间所在的区域
"endpoint" => ['help' => 'The domain names that other services can use to access OSS', 'required' => False], // 终端节点是可选项 其他服务可以用来访问OSS的域名
"bucket" => ['help' => 'The name of the bucket', 'required' => True], // 存储空间名称是必填项
"key" => ['help' => 'The name of the object', 'required' => True], // 对象名称是必填项
];
// 生成长选项列表 用于解析命令行参数
$longopts = \array_map(function ($key) {
return "$key:"; // 每个参数后面加冒号 表示需要值
}, array_keys($optsdesc));
// 解析命令行参数
$options = getopt("", $longopts);
// 检查必填参数是否缺失
foreach ($optsdesc as $key => $value) {
if ($value['required'] === True && empty($options[$key])) {
$help = $value['help'];
echo "Error: the following arguments are required: --$key, $help"; // 提示用户缺少必填参数
exit(1);
}
}
// 获取命令行参数值
$region = $options["region"]; // 存储空间所在区域
$bucket = $options["bucket"]; // 存储空间名称
$key = $options["key"]; // 对象名称
// 使用环境变量加载凭证信息 AccessKeyId 和 AccessKeySecret
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();
// 使用SDK的默认配置
$cfg = Oss\Config::loadDefault();
// 设置凭证提供者
$cfg->setCredentialsProvider($credentialsProvider);
// 设置区域
$cfg->setRegion($region);
// 如果提供了终端节点 则设置终端节点
if (isset($options["endpoint"])) {
$cfg->setEndpoint($options["endpoint"]);
}
// 创建OSS客户端实例
$client = new Oss\Client($cfg);
// 创建主密钥加密器 使用RSA公钥和私钥进行加密解密
$masterCipher = new Oss\Crypto\MasterRsaCipher(
RSA_PUBLIC_KEY, // RSA公钥
RSA_PRIVATE_KEY, // RSA私钥
['tag' => 'value'] // 加密附加标签
);
// 创建加密客户端实例
$eclient = new Oss\EncryptionClient($client, $masterCipher);
// 初始化分片上传请求 设置分片大小和数据总大小
$initRequest = new Oss\Models\InitiateMultipartUploadRequest(bucket: $bucket, key: $key);
$initRequest->cseDataSize = 500 * 1024; // 数据总大小为500KB
$initRequest->csePartSize = 200 * 1024; // 每个分片大小为200KB
// 调用initiateMultipartUpload方法初始化分片上传
$initResult = $eclient->initiateMultipartUpload(request: $initRequest);
// 创建上传分片请求对象
$uploadPartRequest = new Oss\Models\UploadPartRequest(bucket: $bucket, key: $key);
// 定义临时文件名和分片大小
$bigFileName = "upload.tmp"; // 临时文件名
$partSize = 200 * 1024; // 每个分片大小为200KB
// 生成一个500KB的临时文件用于测试
generateFile($bigFileName, 500 * 1024);
// 打开临时文件准备读取
$file = fopen(filename: $bigFileName, mode: 'r');
// 用于存储分片信息的数组
$parts = array();
// 如果文件成功打开 则逐片读取并上传
if ($file) {
$i = 1; // 分片编号从1开始
while (!feof(stream: $file)) {
// 读取一个分片的数据
$chunk = fread(stream: $file, length: $partSize);
// 创建上传分片请求对象 并设置分片编号和上传ID
$uploadPartRequest = new Oss\Models\UploadPartRequest(
bucket: $bucket,
key: $key,
partNumber: $i,
uploadId: $initResult->uploadId,
contentLength: null,
contentMd5: null,
trafficLimit: null,
requestPayer: null,
body: Oss\Utils::streamFor(resource: $chunk) // 将分片数据转换为流
);
// 设置加密上下文
$uploadPartRequest->encryptionMultipartContext = $initResult->encryptionMultipartContext;
// 调用uploadPart方法上传分片
$partResult = $eclient->uploadPart($uploadPartRequest);
// 创建分片信息对象 并添加到分片数组中
$part = new Oss\Models\UploadPart(
partNumber: $i,
etag: $partResult->etag, // 获取分片的ETag
);
array_push(array: $parts, values: $part);
$i++; // 分片编号递增
}
fclose(stream: $file); // 关闭文件
}
// 调用completeMultipartUpload方法完成分片上传
$comResult = $eclient->completeMultipartUpload(
request: new Oss\Models\CompleteMultipartUploadRequest(
bucket: $bucket,
key: $key,
uploadId: $initResult->uploadId,
acl: null,
completeMultipartUpload: new Oss\Models\CompleteMultipartUpload(
parts: $parts
),
)
);
// 删除临时文件
unlink($bigFileName);
// 打印返回结果
printf(
'complete multipart upload status code:' . $comResult->statusCode . PHP_EOL . // HTTP响应状态码
'complete multipart upload request id:' . $comResult->requestId . PHP_EOL . // 请求的唯一标识
'complete multipart upload result:' . var_export($comResult, true) // 完成分片上传的结果
);
// 生成指定大小的文件 用于测试分片上传
function generateFile($filename, $size)
{
// 如果文件已存在且大小符合要求 则直接返回
if (
file_exists($filename) &&
$size == sprintf('%u', filesize($filename))
) {
return;
}
$part_size = 32; // 每次写入的块大小为32字节
$fp = fopen($filename, "w"); // 打开文件以写入模式
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; // 字符集
$charactersLength = strlen($characters); // 字符集长度
// 如果文件成功打开 则循环写入数据直到达到指定大小
if ($fp) {
while ($size > 0) {
// 计算本次写入的大小
if ($size < $part_size) {
$write_size = $size;
} else {
$write_size = $part_size;
}
$size -= $write_size; // 更新剩余需要写入的大小
// 随机选择一个字符并重复生成内容
$a = $characters[rand(0, $charactersLength - 1)];
$content = str_repeat($a, $write_size);
// 将内容写入文件
$flag = fwrite($fp, $content);
if (!$flag) {
break; // 如果写入失败 则退出循环
}
}
}
fclose($fp); // 关闭文件
}
使用自定义主密钥
使用自定义主密钥简单上传和下载Object
SDK提供了RSA默认实现, 当这个方式不满足用户的需求时,用户可以自己实现主密钥的加解密行为。以下示例代码以阿里云KMS 3.0为例,演示如何自定义主密钥加解密进行简单上传和下载Object。
<?php
// 引入自动加载文件 加载依赖库
require_once 'vendor/autoload.php';
use AlibabaCloud\Dkms\Gcs\Sdk\Client as KmsClient;
use AlibabaCloud\Oss\V2 as Oss;
// 定义命令行参数描述
$optsdesc = [
"region" => ['help' => 'The region in which the bucket is located.', 'required' => True], // 区域是必填项 存储空间所在的区域
"endpoint" => ['help' => 'The domain names that other services can use to access OSS.', 'required' => False], // 终端节点是可选项 其他服务可以用来访问OSS的域名
"bucket" => ['help' => 'The name of the bucket', 'required' => True], // 存储空间名称是必填项
"key" => ['help' => 'The name of the object', 'required' => True], // 对象名称是必填项
];
// 生成长选项列表 用于解析命令行参数
$longopts = \array_map(function ($key) {
return "$key:"; // 每个参数后面加冒号 表示需要值
}, array_keys($optsdesc));
// 解析命令行参数
$options = getopt("", $longopts);
// 检查必填参数是否缺失
foreach ($optsdesc as $key => $value) {
if ($value['required'] === True && empty($options[$key])) {
$help = $value['help'];
echo "Error: the following arguments are required: --$key, $help"; // 提示用户缺少必填参数
exit(1);
}
}
// 获取命令行参数值
$region = $options["region"]; // 存储空间所在区域
$bucket = $options["bucket"]; // 存储空间名称
$key = $options["key"]; // 对象名称
// 使用环境变量加载凭证信息 AccessKeyId 和 AccessKeySecret
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();
// 自定义KMS加密解密类 实现MasterCipherInterface接口
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;
}
// 加密方法
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);
}
// 解密方法
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);
}
// 获取包装算法
public function getWrapAlgorithm(): string
{
return "KMS/ALICLOUD";
}
public function getMatDesc(): string
{
return $this->matDesc;
}
}
/**
* 构建专属KMS SDK Client对象
* @return KmsClient
*/
function getDkmsGcsSdkClient()
{
global $clientKeyFile, $password, $endpoint;
// 构建专属KMS SDK Client配置
$config = new \AlibabaCloud\Dkms\Gcs\OpenApi\Models\Config();
$config->protocol = 'https';
$config->clientKeyFile = $clientKeyFile;
$config->password = $password;
$config->endpoint = $endpoint;
// 验证服务端证书
$config->caFilePath = 'path/to/caCert.pem';
// 构建专属KMS SDK Client对象
return new \AlibabaCloud\Dkms\Gcs\Sdk\Client($config);
}
// 填写您在KMS应用管理获取的ClientKey文件路径
$clientKeyFile = '<your client key file path>';
// 填写您在KMS应用管理创建ClientKey时输入的加密口令
$password = '<your dkms client passowrd>';
// 填写您的专属KMS实例服务地址
$endpoint = '<your dkms instance service address>';
// 填写您在KMS创建的主密钥Id
$kmsKeyId = '<your cmk id>';
// 加解密算法
$algorithm = '<your encrypt algorithm>';
// 专属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;
相关文档
该文章对您有帮助吗?
- 本页导读 (1)
- 注意事项
- 使用RSA主密钥
- 使用主密钥RSA简单上传和下载Object
- 使用主密钥RSA分片上传Object
- 使用自定义主密钥
- 使用自定义主密钥简单上传和下载Object
- 相关文档