Hyperf方案 密钥管理服务(KMS)

张开发
2026/4/21 3:22:15 15 分钟阅读

分享文章

Hyperf方案 密钥管理服务(KMS)
先搞懂 KMS 是什么8)KMS密钥管理服务专门管加密用的钥匙问题你用密钥加密数据但密钥存哪 存数据库万一数据库泄露密钥和数据一起完蛋 存代码更危险 解决密钥交给专业的 KMS 管你的代码只存加密后的密钥 信封加密最核心概念 明文数据 ↓ 用 DEK数据密钥加密 加密数据 ← 存数据库 DEK数据密钥 ↓ 用 KMS 里的 CMK主密钥加密 加密的DEK ← 也存数据库 CMK 永远不离开 KMS你只能让 KMS 帮你加解密---三种方案选一个 ┌─────────────────┬─────────────┬────────────┐ │ 方案 │ 适合场景 │ 成本 │ ├─────────────────┼─────────────┼────────────┤ │ AWS KMS │ 已在用AWS │ 按调用收费 │ ├─────────────────┼─────────────┼────────────┤ │ HashiCorp Vault │ 自建/私有云 │ 免费自托管 │ ├─────────────────┼─────────────┼────────────┤ │ 本地软KMS │ 开发/小项目 │ 免费 │ └─────────────────┴─────────────┴────────────┘---安装 # AWS KMS composer require aws/aws-sdk-php # HashiCorp Vault composer require mittwald/vault-php # 加密基础库三种方案都用 composer require defuse/php-encryption---目录结构 app/Service/Kms/KmsInterface.php # 统一接口 AwsKmsService.php # AWS实现 VaultKmsService.php # Vault实现 LocalKmsService.php # 本地实现开发用 EncryptionService.php # 信封加密核心 Middleware/FieldEncryptMiddleware.php # 自动加解密敏感字段 config/autoload/kms.php---1.配置// config/autoload/kms.phpreturn[// 选择驱动aws / vault / localdriverenv(KMS_DRIVER,local),aws[regionenv(AWS_REGION,ap-northeast-1),key_idenv(AWS_KMS_KEY_ID),// CMK的ARN或别名access_keyenv(AWS_ACCESS_KEY_ID),secret_keyenv(AWS_SECRET_ACCESS_KEY),],vault[urlenv(VAULT_URL,http://127.0.0.1:8200),tokenenv(VAULT_TOKEN),key_nameenv(VAULT_KEY_NAME,my-app-key),// Transit引擎的密钥名],local[// 本地主密钥生产环境绝对不能用这个master_keyenv(LOCAL_MASTER_KEY,base64:.base64_encode(random_bytes(32))),],];---2.统一接口// app/Service/Kms/KmsInterface.php?php namespace App\Service\Kms;interfaceKmsInterface{// 生成一个数据密钥返回[明文DEK, 加密后的DEK]publicfunctiongenerateDataKey():array;// 用主密钥解密DEK拿回明文DEKpublicfunctiondecryptDataKey(string $encryptedDek):string;// 轮换主密钥定期换密钥publicfunctionrotateKey():void;}---3.AWS KMS 实现// app/Service/Kms/AwsKmsService.php?php namespace App\Service\Kms;use Aws\Kms\KmsClient;classAwsKmsServiceimplementsKmsInterface{private KmsClient $client;private string $keyId;publicfunction__construct(){$cfgconfig(kms.aws);$this-keyId$cfg[key_id];$this-clientnewKmsClient([region$cfg[region],versionlatest,credentials[key$cfg[access_key],secret$cfg[secret_key],],]);}publicfunctiongenerateDataKey():array{// AWS直接帮你生成DEK同时返回明文和加密版本$result$this-client-generateDataKey([KeyId$this-keyId,KeySpecAES_256,]);return[plaintextbase64_encode($result[Plaintext]),// 用完立即丢弃encrypted_dekbase64_encode($result[CiphertextBlob]),// 存数据库];}publicfunctiondecryptDataKey(string $encryptedDek):string{$result$this-client-decrypt([CiphertextBlobbase64_decode($encryptedDek),]);returnbase64_encode($result[Plaintext]);}publicfunctionrotateKey():void{// AWS KMS 支持自动轮换也可手动触发$this-client-enableKeyRotation([KeyId$this-keyId]);}}---4.HashiCorp Vault 实现// app/Service/Kms/VaultKmsService.php?php namespace App\Service\Kms;use GuzzleHttp\Client;classVaultKmsServiceimplementsKmsInterface{private Client $http;private string $baseUrl;private string $keyName;publicfunction__construct(){$cfgconfig(kms.vault);$this-keyName$cfg[key_name];$this-baseUrlrtrim($cfg[url],/)./v1/transit;$this-httpnewClient([headers[X-Vault-Token$cfg[token]],timeout3,]);}publicfunctiongenerateDataKey():array{// 本地生成随机DEK用Vault加密它$plainDekrandom_bytes(32);$resp$this-http-post({$this-baseUrl}/encrypt/{$this-keyName},[json[plaintextbase64_encode($plainDek)],]);$datajson_decode($resp-getBody(),true);return[plaintextbase64_encode($plainDek),encrypted_dek$data[data][ciphertext],// vault:v1:xxxxx];}publicfunctiondecryptDataKey(string $encryptedDek):string{$resp$this-http-post({$this-baseUrl}/decrypt/{$this-keyName},[json[ciphertext$encryptedDek],]);$datajson_decode($resp-getBody(),true);return$data[data][plaintext];// 已经是base64}publicfunctionrotateKey():void{// Vault Transit 引擎一键轮换$this-http-post({$this-baseUrl}/keys/{$this-keyName}/rotate);}}---5.本地软KMS开发/测试用// app/Service/Kms/LocalKmsService.php?php namespace App\Service\Kms;classLocalKmsServiceimplementsKmsInterface{private string $masterKey;publicfunction__construct(){$keyconfig(kms.local.master_key);// 支持 base64: 前缀$this-masterKeystr_starts_with($key,base64:)?base64_decode(substr($key,7)):$key;}publicfunctiongenerateDataKey():array{$plainDekrandom_bytes(32);$ivrandom_bytes(16);// 用主密钥加密DEK$encryptedopenssl_encrypt($plainDek,AES-256-CBC,$this-masterKey,OPENSSL_RAW_DATA,$iv);$encryptedDekbase64_encode($iv.$encrypted);return[plaintextbase64_encode($plainDek),encrypted_dek$encryptedDek,];}publicfunctiondecryptDataKey(string $encryptedDek):string{$rawbase64_decode($encryptedDek);$ivsubstr($raw,0,16);$encsubstr($raw,16);$plainDekopenssl_decrypt($enc,AES-256-CBC,$this-masterKey,OPENSSL_RAW_DATA,$iv);returnbase64_encode($plainDek);}publicfunctionrotateKey():void{// 本地模式不支持轮换仅开发用thrownew\RuntimeException(本地KMS不支持密钥轮换请使用AWS或Vault);}}---6.信封加密服务核心业务层// app/Service/EncryptionService.php?php namespace App\Service;use App\Service\Kms\KmsInterface;use App\Service\Kms\{AwsKmsService,VaultKmsService,LocalKmsService};classEncryptionService{private KmsInterface $kms;publicfunction__construct(){$this-kmsmatch(config(kms.driver)){awsmake(AwsKmsService::class),vaultmake(VaultKmsService::class),defaultmake(LocalKmsService::class),};}/** * 加密数据信封加密 * 返回可以直接存数据库的字符串 */publicfunctionencrypt(string $plaintext):string{// 1. 让KMS生成一个DEK[plaintext$dekPlain,encrypted_dek$dekEnc]$this-kms-generateDataKey();// 2. 用DEK加密实际数据$dekbase64_decode($dekPlain);$ivrandom_bytes(16);$encopenssl_encrypt($plaintext,AES-256-CBC,$dek,OPENSSL_RAW_DATA,$iv);// 3. 明文DEK用完立即销毁PHP里置空$dekstr_repeat(\0,strlen($dek));unset($dek);// 4. 把加密数据 加密DEK打包存储returnjson_encode([v1,// 版本号方便以后升级dek$dekEnc,// 加密后的DEKivbase64_encode($iv),encbase64_encode($enc),]);}/** * 解密数据 */publicfunctiondecrypt(string $ciphertext):string{$datajson_decode($ciphertext,true);// 1. 让KMS解密DEK拿回明文DEK$dekPlain$this-kms-decryptDataKey($data[dek]);$dekbase64_decode($dekPlain);// 2. 用DEK解密实际数据$ivbase64_decode($data[iv]);$encbase64_decode($data[enc]);$plaintextopenssl_decrypt($enc,AES-256-CBC,$dek,OPENSSL_RAW_DATA,$iv);// 3. 明文DEK用完销毁$dekstr_repeat(\0,strlen($dek));unset($dek);return$plaintext;}/** * 加密数组里的指定字段批量处理 */publicfunctionencryptFields(array $data,array $fields):array{foreach($fields as $field){if(isset($data[$field])){$data[$field]$this-encrypt((string)$data[$field]);}}return$data;}publicfunctiondecryptFields(array $data,array $fields):array{foreach($fields as $field){if(isset($data[$field])){$data[$field]$this-decrypt($data[$field]);}}return$data;}/** * 密钥轮换定期执行 */publicfunctionrotateKey():void{$this-kms-rotateKey();}}---7.实际使用示例// app/Controller/UserController.php?php namespace App\Controller;use App\Service\EncryptionService;use Hyperf\HttpServer\Annotation\{Controller,PostMapping,GetMapping};use Hyperf\HttpServer\Contract\RequestInterface;#[Controller(prefix:/api/user)]classUserController{// 需要加密的敏感字段private array $sensitiveFields[id_card,phone,bank_card];publicfunction__construct(private EncryptionService $enc){}#[PostMapping(path:/create)]publicfunctioncreate(RequestInterface $request){$data$request-all();// 存库前加密敏感字段$encrypted$this-enc-encryptFields($data,$this-sensitiveFields);// $encrypted[id_card] 现在是加密后的字符串安全存库// DB::table(users)-insert($encrypted);return[code200,msg创建成功];}#[GetMapping(path:/{id})]publicfunctionshow(int $id){// $user DB::table(users)-find($id);// 从库里取出后解密// $user $this-enc-decryptFields($user, $this-sensitiveFields);return[code200,data用户信息];}}---8.定时密钥轮换命令// app/Cron/KeyRotationCron.php?php namespace App\Cron;use App\Service\EncryptionService;use Hyperf\Crontab\Annotation\Crontab;// 每90天轮换一次密钥#[Crontab(rule:0 2 1 */3 *,name:KeyRotation,memo:季度密钥轮换)]classKeyRotationCron{publicfunction__construct(private EncryptionService $enc){}publicfunctionexecute():void{$this-enc-rotateKey();// 记录轮换日志logger()-info(密钥轮换完成,[timedate(Y-m-d H:i:s)]);}}---整体流程图 写入数据库 用户提交{id_card:110101...}↓ EncryptionService::encrypt()↓ KMS生成DEK随机256位密钥 ↓ DEK加密id_card → 密文 CMK加密DEK → 加密DEK ↓ 存库:{id_card:{v:1,dek:xxx,iv:xxx,enc:xxx}}明文DEK内存销毁 读取数据库 取出加密字段 ↓ EncryptionService::decrypt()↓ KMS用CMK解密DEK → 明文DEK ↓ 明文DEK解密密文 → 原始数据 明文DEK内存销毁 ↓ 返回{id_card:110101...}---三种方案对比 ┌──────────┬────────────┬────────────┬──────────┐ │ │ AWS KMS │ Vault │ 本地 │ ├──────────┼────────────┼────────────┼──────────┤ │ 适合 │ 云上生产 │ 私有化部署 │ 开发测试 │ ├──────────┼────────────┼────────────┼──────────┤ │ 密钥安全 │ AWS托管 │ 自己管 │ 不安全 │ ├──────────┼────────────┼────────────┼──────────┤ │ 轮换 │ 自动 │ 一键 │ 不支持 │ ├──────────┼────────────┼────────────┼──────────┤ │ 审计 │ CloudTrail │ Vault审计 │ 无 │ ├──────────┼────────────┼────────────┼──────────┤ │ 费用 │ 按调用 │ 免费 │ 免费 │ └──────────┴────────────┴────────────┴──────────┘.env 里切换 KMS_DRIVERaws/vault/local代码不用改。

更多文章