CloudHSM是AWS的云上硬件安全模块服务,由于比较复杂的加密相关专业性,学习入门、测试门槛较高。本文系统性介绍如何创建CloudHSM集群,并使用Java语言(JCE Provider SDK)访问CloudHSM完成密钥生成、加密、解密任务,同时还会提供密钥导出Wrap/Unwrap的样例代码。同时,本文会探讨CloudHSM的SDK选择、加密算法选择、以及海量IoT设备场景的加密体系设计。
一、背景
在AWS云上,有KMS(Key Management Service)和CloudHSM(Cloud Hardware Security Module)两种服务,都提供加密/解密能力。两者有区别也有联系。CloudHSM是AWS托管的云上硬件安全模块,提供单租户的FIPS 140-2 Level 3验证HSM,支持密钥存储、加密、解密、签名等功能,用户可完全控制密钥生命周期和加密操作。KMS是托管的密钥管理服务,主要关注密钥管理和加密服务的API化,通过简化的API让用户无需自己管理底层加密基础设施,加密计算在AWS管理的FIPS验证HSM中执行。KMS默认使用AWS管理的多租户HSM,也可通过CloudHSM key stores使用CloudHSM作为自定义密钥存储。这两个服务都可以独立使用。
KMS和CloudHSM的选择决策主要基于合规要求、控制需求和集成复杂度:
选择KMS的场景:
- 不需要单租户加密机的合规要求
- 希望与AWS服务深度集成和简化密钥管理
- 成本敏感且对密钥控制要求不高的应用
- 此时仅使用KMS服务即可满足需求
选择CloudHSM的场景:
- 有强制使用专用加密机的合规要求(如FIPS 140-2 Level 3)
- 需要完全控制密钥和加密操作
- 需要特定的加密算法或密钥类型
- 此时可仅使用CloudHSM并自行封装加解密功能
KMS + CloudHSM组合的场景:
- 既需要KMS的API便利性,又需要CloudHSM的合规性和控制力
- 通过KMS的CloudHSM key stores功能,结合两者优势
- 适合需要满足严格合规要求但希望简化开发的场景
本文介绍仅使用CloudHSM的场景,创建CloudHSM集群开始介绍。
二、CloudHSM集群的创建和初始化
1、创建CloudHSM集群
首先登录到AWS控制台,选择要部署CloudHSM的区域。然后进入CloudHSM服务界面,点击创建集群按钮。如下截图。

在创建集群位置,选择VPC,选择本区域的所有可用区的至少2个子网。如下截图。

在设备机型选择中,当前仅提供唯一的机型hsm2m.medium,选择加密规范是FIPS规范,选择网络类型是IPv4,选择创建新的集群。如下截图。

在备份数据保存多少天位置,默认是90天。点击下一步继续。如下截图。

创建集群完成。刚创建好的集群是未初始化状态,集群里可用的CloudHSM加密机数量是0,要发起初始化。

2、初始化创建第一台CloudHSM主机并获取证书签名申请(CSR)
在上一步点击了初始化之后,向导第一步是询问在哪个可用区创建第一台CloudHSM。这里可任意选择。如下截图。

集群内开始自动创建第一台CloudHSM加密机,向导进入第二步。此时可以操作下载签署证书申请。如下截图。

下载完成后,下一步会提示上传签署好的证书,这里因为创建CloudHSM加密机还没有完成,因此直接点下一步跳过就可以了。如下截图。

接下来等待5-10分钟,等加密机创建好后,会在页面下方显示出来IP地址,此时就可以签署证书并上传了。如下截图。

3、创建私钥
本文假设一个全新的实验环境,当前没有现成的CA,因此使用OpenSSL库自己创建一个CA,并导入到CloudHSM。
找一台Linux机器,可以是开发者本机,也可以是AWS云上EC2。在安装有OpenSSL库(且最新版本、避免安全隐患)的Linux/MacOS环境下,执行如下命令:
openssl genrsa -aes256 -out customerCA.key 4096
输入自定义的证书密码,然后在本机的当前目录下获得了customerCA.key文件。
4、使用私钥生成CA
openssl req -new -x509 -days 3652 -key customerCA.key -out customerCA.crt
接着输入上一步定义的私钥的密码,然后逐项回答证书信息,即可获得customerCA.crt文件。
Enter pass phrase for customerCA.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:BEIJING
Locality Name (eg, city) []:BJ
Organization Name (eg, company) [Internet Widgits Pty Ltd]:PCMAN
Organizational Unit Name (eg, section) []:TECH
Common Name (e.g. server FQDN or YOUR name) []:bitipcman.com
Email Address []:do-not-reply@bitipcman.com
完成后在当前目录下,生成了customerCA.crt文件。
5、签署证书
在上一步创建好CloudHSM的初始化界面,下载了CSR证书签署申请文件,将这个文件放到OpenSSL环境上,执行如下命令:
openssl x509 -req -days 3652 -in <cluster ID>_ClusterCsr.csr \
-CA customerCA.crt \
-CAkey customerCA.key \
-CAcreateserial \
-out <cluster ID>_CustomerHsmCertificate.crt
由此在当前目录下即可获得<cluster ID>_CustomerHsmCertificate.crt文件和customerCA.srl两个文件。后续要使用<cluster ID>_CustomerHsmCertificate.crt文件。
6、上传到CloudHSM完成初始化
进入到CloudHSM的集群界面,点击初始化,然后跳过下载CSR签名申请的步骤,直接到上传证书的步骤。如下截图。

上传完毕,可以看到集群进入了初始化的过程。如下截图。

等待一段时间后,CloudHSM状态是已经初始化完成(Initialized),下一步是激活(如黄色提示)。如下截图。

初始化完毕后,还要复制出来CloudHSM的IP地址(在页面下方的ENI IPv4 Address),下一步CLI连接将会使用。
三、使用CloudHSM CLI激活集群、用户管理
本章节介绍使用CloudHSM的CLI激活CloudHSM、设置管理员密码、新建加密用户、创建第一个密钥
1、创建云上EC2用于CLI的安装
CloudHSM本身被设计为不能从互联网访问,必须从云上VPC网络的内网访问。在没有打通办公室开发者网络和VPC内网专线的情况下,需要在云上创建一个EC2虚拟机作为一台跳板机来操作CloudHSM加密机。
创建EC2机型和配置如下:
- Ubuntu 24.04 LTS
- 机型c7i-flex.large
- 存储类型为gp3,容量20GB
- 网络选择可以访问外网的子网
- 设置自动获取Public IP Address
- 安全组仅对特定IP地址授权TCP协议22端口访问,不要对全体网络
0.0.0.0/0开放。例如如果在美国us-west-2 Oregen 俄勒冈区域,那么可以在安全组内添加一条规则,允许来源地址范围是pl-047d464325e7bf465访问本机的TCP协议22端口。由此您将可以使用EC2控制台上的EC2 Instance Connect功能连接到SSH。如下截图。
上一步填写的允许pl-047d464325e7bf465访问本机的TCP协议22端口的规则,即可使用EC2网页界面的SSH功能。在界面上点击Connect连接按钮。如下截图。

在连接方式选择第一项EC2 Instance Connect,即可登录到网页版SSH。如下截图。

登录成功,可以继续安装CloudHSM CLI。如下截图。

2、为CloudHSM配置安全组规则以允许CLI访问
在发起CloudHSM CLI连接之前,还需要放行CloudHSM的安全规则组的入站请求。操作方法是,先找到当前作为管理机/跳板机的EC2的安全组,复制下来安全组的ID,然后再找到CloudHSM用的安全规则组,添加一条放行规则。
首先查看上一步创建的EC2使用的安全组的名称。进入EC2服务控制台,选中刚才创建的EC2,切换到下方的Security标签页,从页面下方可看到当前再使用的安全规则组。点击复制按钮将ID复制到剪贴板。如下截图。

切换到CloudHSM服务控制台,点击Security Groups,然后点击Security Group,找到cloudhsm的安全规则组跳转到编辑页面。如下截图。

在编辑安全规则组页面中,点击下方标签页Inbound Rules,点击编辑按钮。如下截图。

点击Add Inbound Rule,添加一条规则,设置协议类型是Custom TCP,端口范围输入2223-2225,来源地址Source位置将上一步复制的运行CloudHSM CLI的EC2的安全规则组名称粘贴进去。备注位置可任意输入友好提示信息。最后点击保存规则按钮。如下截图。

由此安装CloudHSM CLI的EC2就获得了网络权限可以操作CloudHSM了。
3、安装CloudHSM CLI
在上一步创建好的EC2上,执行如下命令。
wget https://s3.amazonaws.com/cloudhsmv2-software/CloudHsmClient/Jammy/cloudhsm-cli_latest_u22.04_amd64.deb
sudo apt install ./cloudhsm-cli_latest_u22.04_amd64.deb
安装完毕后,生成配置文件。注意这里的IP地址要替换为上一章节在CloudHSM控制台内看到的集群内的IP地址。
sudo cp customerCA.crt /opt/cloudhsm/etc/customerCA.crt
sudo /opt/cloudhsm/bin/configure-cli -a 172.31.24.131
4、CLI登录、设置初始管理员密码并激活CloudHSM
激活之前,将上一步上传到CloudHSM的证书也复制到执行CloudHSM CLI的EC2的/opt/cloudhsm/etc/customerCA.crt位置。然后填写IP地址进行初始化。最后启动CLI客户端。执行如下命令:
sudo cp customerCA.crt /opt/cloudhsm/etc/customerCA.crt
sudo /opt/cloudhsm/bin/configure-cli -a 172.31.24.131
/opt/cloudhsm/bin/cloudhsm-cli interactive
由此可看到CLI登陆成功。然后输入cluster activate命令激活CloudHSM,输入新的管理员密码后,激活成功。返回信息如下:
aws-cloudhsm > cluster activate
Enter password:
Confirm password:
{
"error_code": 0,
"data": "Cluster activation successful"
}
aws-cloudhsm >
现在执行user list命令,即可看到当前CloudHSM上配置的用户,并可看到Admin用户是已经激活的状态。
aws-cloudhsm > user list
{
"error_code": 0,
"data": {
"users": [
{
"username": "admin",
"role": "admin",
"locked": "false",
"mfa": [],
"quorum": [],
"cluster-coverage": "full"
},
{
"username": "app_user",
"role": "internal(APPLIANCE_USER)",
"locked": "false",
"mfa": [],
"quorum": [],
"cluster-coverage": "full"
}
]
}
}
aws-cloudhsm >
注意这里除了admin外还有一个app_user,它的全称是Appliance User (AU),它是用于在多台CloudHSM之间进行数据同步的,他已经被配置为最小的权限,AWS无法用它来查看您的密钥,也无法用它做加解密,因此无须担心。只有配置为Crypto user (CU)类型的用户才可以做加解密。
设置完毕新密码后,执行quit即可退出登陆。
5、以管理员身份登陆、并新增用户
由于日常的密钥生成、加密、解密操作,必须创建新用户,不允许使用Admin进行。因此这里必须新建第一个CloudHSM用户、并设置其密码,用于SDK的访问连接。
再次登陆到CloudHSM时需要使用登陆命令:
/opt/cloudhsm/bin/cloudhsm-cli interactive
login --username admin --role admin
输入密码后,即可完成登陆。然后开始配置新的用户。CloudHSM的用户体系主要包含三级:
- Admin:仅用于初始化、备份、容灾、导入导出等;
- Crypto User (CU):日常加密、解密、Wrap导出/Un-wrap导入等;
- Appliance User (AU):设备同步用(用户多机集群同步后台账户,用户API无法使用)
具体用户权限,可参考附件中的文档。
在刚才登陆成功命令行下,继续执行如下命令创建新用户:
user create --username user01 --role crypto-user
然后输入本用户的密码。即可完成设置。再次运行user list命令即可看到返回新添加用户生效。
aws-cloudhsm > user list
{
"error_code": 0,
"data": {
"users": [
{
"username": "admin",
"role": "admin",
"locked": "false",
"mfa": [],
"quorum": [],
"cluster-coverage": "full"
},
{
"username": "user01",
"role": "crypto-user",
"locked": "false",
"mfa": [],
"quorum": [],
"cluster-coverage": "full"
},
{
"username": "app_user",
"role": "internal(APPLIANCE_USER)",
"locked": "false",
"mfa": [],
"quorum": [],
"cluster-coverage": "full"
}
]
}
}
aws-cloudhsm >
输入quit,退出CloudHSM CLI。
6、使用CloudHSM CLI创建第一个密钥
上一步操作完毕后,执行quit退出。重新启动CloudHSM CLI。执行如下命令:
/opt/cloudhsm/bin/cloudhsm-cli interactive
以普通user01身份登陆。执行如下命令:
login --username user01 --role crypto-user
输入user01的密码,登陆成功。执行如下命令生成一个密钥:
key generate-symmetric aes \
--key-length-bytes 32 \
--label "my-aes-256-key" \
--attributes extractable=false encrypt=true decrypt=true
返回如下结果表示创建成功:
{
"error_code": 0,
"data": {
"key": {
"key-reference": "0x0000000000003e54",
"key-info": {
"key-owners": [
{
"username": "user01",
"key-coverage": "full"
}
],
"shared-users": [],
"key-quorum-values": {
"manage-key-quorum-value": 0,
"use-key-quorum-value": 0
},
"cluster-coverage": "full"
},
"attributes": {
"key-type": "aes",
"label": "my-aes-256-key",
"id": "0x",
"check-value": "0x5e0a86",
"class": "secret-key",
"encrypt": true,
"decrypt": true,
"token": true,
"always-sensitive": true,
"derive": false,
"destroyable": true,
"extractable": false,
"local": true,
"modifiable": true,
"never-extractable": true,
"private": true,
"sensitive": true,
"sign": true,
"trusted": false,
"unwrap": false,
"verify": true,
"wrap": false,
"wrap-with-trusted": false,
"key-length-bytes": 32
}
}
}
}
这里要记录下key-reference参数的值0x0000000000003e54,稍后在使用Java代码调用中,将会使用这个密钥进行操作。
四、使用Java语言JCE Provider SDK调用CloudHSM实现密钥创建、加密、解密的例子
1、开发环境准备及JCE Provider SDK安装
本文以在AWS云端使用EC2虚拟机的Ubuntu 22.04操作系统为例进行开发,且开发环境和运行环境都在云上,这样网络默认可连通,避免调试过程中的网络访问造成调试困难。
下载Amazon版本的OpenJDK,Amazon Corretto 17。
wget -O - https://apt.corretto.aws/corretto.key | sudo gpg --dearmor -o /usr/share/keyrings/corretto-keyring.gpg && \
echo "deb [signed-by=/usr/share/keyrings/corretto-keyring.gpg] https://apt.corretto.aws stable main" | sudo tee /etc/apt/sources.list.d/corretto.list
sudo apt-get update; sudo apt-get install -y maven java-17-amazon-corretto-jdk
JDK安装完毕。CloudHSM的SDK有版本3和版本5两个系列,版本3在2025年退役,本文使用最新的版本5系列。
以Ubuntu 22.04系统x86_64架构处理器为例,从官网下载最新版本的CloudHSM的JCE Provider SDK:
wget https://s3.amazonaws.com/cloudhsmv2-software/CloudHsmClient/Jammy/cloudhsm-jce_latest_u22.04_amd64.deb
sudo apt install ./cloudhsm-jce_latest_u22.04_amd64.deb
替换如下命令中的CloudHSM IP为集群内的节点IP完成配置:
sudo /opt/cloudhsm/bin/configure-jce -a 172.31.24.131
确认如下三个文件正常:(其中JCE版本号可能有所不同)
ls -l /opt/cloudhsm/java/cloudhsm-jce-5.16.2.jar
ls -l /opt/cloudhsm/bin/configure-jce
ls -l /opt/cloudhsm/bin/jce_info
仔细检查以上文件名和路径,确保返回结果正确。由此CloudHSM SDK开发环境准备完毕。下面开始准备Java代码和编译。
2、获取Java样例代码并编译构建Jar包
Java代码和pom.xml文件可参考Github上的示例。执行如下命令下载代码:
git clone git@github.com:aobao32/cloudhsm-101.git
cd cloudhsm-101/
在编译环节,为了运行环境无须安装CloudHSM JCE Provider SDK,因此会编译far-jar,将所有库打包到一个jar包。因此需要将cloudhsm的jce的jar包集成到mvn本地库。
mvn install:install-file \
-Dfile=/opt/cloudhsm/java/cloudhsm-jce-5.16.2.jar \
-DgroupId=com.amazonaws \
-DartifactId=cloudhsm-jce \
-Dversion=5.16.2 \
-Dpackaging=jar
执行编译。注意本例中的密钥ID、要加密的明文、要解密的密文是hardcode方式写在样例代码中的,如果改动的话要重新编译代码。
mvn clean package
在本例的pom.xml中,使用了shade打包方式,因此打包后的jar包体积会达到12MB,即包含了JCE Provider的Jar。打包时候会把如下几个不同类型、不同功能的测试每个方法都独立打一个jar包,方便学习和参考。执行ls -lh target/*.jar命令可看到如下返回结果。
-rw-r--r-- 1 ubuntu ubuntu 12M Dec 29 06:12 target/cloudhsm-decryption-1.0-SNAPSHOT.jar
-rw-r--r-- 1 ubuntu ubuntu 30K Dec 29 06:12 target/cloudhsm-demo-1.0-SNAPSHOT.jar
-rw-r--r-- 1 ubuntu ubuntu 12M Dec 29 06:12 target/cloudhsm-encryption-1.0-SNAPSHOT.jar
-rw-r--r-- 1 ubuntu ubuntu 12M Dec 29 06:12 target/cloudhsm-keyderivation-1.0-SNAPSHOT.jar
-rw-r--r-- 1 ubuntu ubuntu 12M Dec 29 06:12 target/cloudhsm-keygen-1.0-SNAPSHOT.jar
-rw-r--r-- 1 ubuntu ubuntu 12M Dec 29 06:12 target/cloudhsm-sessionkey-decrypt-1.0-SNAPSHOT.jar
-rw-r--r-- 1 ubuntu ubuntu 12M Dec 29 06:12 target/cloudhsm-sessionkey-encrypt-1.0-SNAPSHOT.jar
-rw-r--r-- 1 ubuntu ubuntu 12M Dec 29 06:12 target/wrap-demo-step-1-generate-master-key-1.0-SNAPSHOT.jar
-rw-r--r-- 1 ubuntu ubuntu 12M Dec 29 06:12 target/wrap-demo-step-2-generate-data-key-and-wrap-1.0-SNAPSHOT.jar
-rw-r--r-- 1 ubuntu ubuntu 12M Dec 29 06:12 target/wrap-demo-step-3-unwrap-data-key-and-encryptd-1.0-SNAPSHOT.jar
以上就构建好了创建密钥、加密、解密等几个包。如果您对AWS VPC网络不了解,那么在开发者本机调试可能遇到网络连通性问题。因此建议在开始学习时候以上编译代码的开发环境和运行环境都在AWS云上的EC2虚拟机内,这样避免网络调试的困难。下面转向运行环境。
3、在CloudHSM内创建一个AES256算法的密钥作为Master key主密钥
本章节代码见本文开头Github的:src/main/java/com/example/cloudhsm/CloudHSMKeyGenerator.java。
如果您此时CloudHSM集群是单节点的测试集群,还要额外使用--disable-key-availability-check命令。否则后续会报告Cannot perform the requested key operation as the key must be available on at least 2 HSMs。设置单节点运行禁用可用性检查:
sudo /opt/cloudhsm/bin/configure-jce --disable-key-availability-check
运行前,通过环境变量加载CloudHSM的用户user01的用户名和密码:
export HSM_USER=user01
export HSM_PASSWORD=1qazxsw2
运行Java代码:
java -jar target/cloudhsm-keygen-1.0-SNAPSHOT.jar
运行后返回结果如下:
使用用户 user01 连接到CloudHSM...
AES256 密钥创建成功!
密钥标签: MyAES256Key
密钥算法: AES
可看到密钥创建成功。
为了验证密钥创建成功,可通过CloudHSM CLI登陆,查看刚创建的密钥是否存在。
现在使用cloudhsm-cli登陆去确认密钥创建成功。如果您的集群是单节点,还要额外增加一条--disable-key-availability-check命令。否则后续查询时候回报告Cannot perform the requested key operation as the key must be available on at least 2 HSMs。接下来执行如下命令登陆:
sudo /opt/cloudhsm/bin/configure-cli --disable-key-availability-check
/opt/cloudhsm/bin/cloudhsm-cli interactive
login --username user01 --role crypto-user
key list --verbose
由此即可显示详细密钥属性:
ws-cloudhsm > key list --verbose
{
"error_code": 0,
"data": {
"matched_keys": [
{
"key-reference": "0x0000000000002a77",
"key-info": {
"key-owners": [
{
"username": "user01",
"key-coverage": "full"
}
],
"shared-users": [],
"key-quorum-values": {
"manage-key-quorum-value": 0,
"use-key-quorum-value": 0
},
"cluster-coverage": "full"
},
"attributes": {
"key-type": "aes",
"label": "MyAES256Key",
"id": "0x",
"check-value": "0x63f13c",
"class": "secret-key",
"encrypt": true,
"decrypt": true,
"token": true,
"always-sensitive": true,
"derive": false,
"destroyable": true,
"extractable": false,
"local": true,
"modifiable": true,
"never-extractable": true,
"private": true,
"sensitive": true,
"sign": true,
"trusted": false,
"unwrap": true,
"verify": true,
"wrap": true,
"wrap-with-trusted": false,
"key-length-bytes": 32
}
}
],
"total_key_count": 1,
"returned_key_count": 1
}
}
如果要删除某个key,可以使用如下命令:
key delete --filter key-reference=0x0000000000000e74
即可删除密钥。
4、使用AES-256-GCM算法用主密钥对文本执行加密、解密
本章节代码见本文开头Github的:src/main/java/com/example/cloudhsm/CloudHSMEncryption.java和CloudHSMDecryption.java。
上文已经在CloudHSM内创建了密钥,现在对一串文本进行加密。加密时候的方法是在CloudHSM加密机内部执行,密钥明文不离开加密机。
如果修改了代码中的密钥标识符,那么要重新构建包。如果之前已经通过环境变量设置了密钥,那么这一步可以不用设置。
mvn clean package
export HSM_USER=user01
export HSM_PASSWORD=1qazxsw2
java -jar target/cloudhsm-encryption-1.0-SNAPSHOT.jar
返回结果如下:
使用用户 user01 连接到CloudHSM...
=== CloudHSM 加密信息 ===
密钥类型: AES-256
密钥标签: MyAES256Key
加密算法: AES-256-GCM
原文: Hello CloudHSM! This is a test message.
密文 (Base64): HRGNyxEjx5IUq6Ztr4+b/U4y0BeB/tcBBgWOe00wlgYpg7W+87pGw/sYsyVD4AqXhDsueQWpnk38jRITUildD2q2JA==
加密成功!
接下来进行解密测试,将以上密文代入到解密的代码中,再执行命令如下。如果之前已经通过环境变量设置了密钥,那么这一步可以不用设置。
mvn clean package
export HSM_USER=user01
export HSM_PASSWORD=1qazxsw2
java -jar target/cloudhsm-decryption-1.0-SNAPSHOT.jar
执行结果如下:
使用用户 user01 连接到CloudHSM...
=== CloudHSM 解密信息 ===
密钥类型: AES-256
密钥标签: MyAES256Key
解密算法: AES-256-GCM
密文 (Base64): HRGNyxEjx5IUq6Ztr4+b/U4y0BeB/tcBBgWOe00wlgYpg7W+87pGw/sYsyVD4AqXhDsueQWpnk38jRITUildD2q2JA==
明文: Hello CloudHSM! This is a test message.
解密成功!
5、使用KDF算法生成派生密钥并明文返回给客户端
本章节代码见本文开头Github的:src/main/java/com/example/cloudhsm/CloudHSMKeyDerivation.java。
在IoT场景中,所有设备使用唯一的主密钥进行加密可能不够安全,每个设备都分配一个独立的密钥这样管理成本又很高,且在数十万设备时候会超出CloudHSM集群存储的密钥数量上限。此时的办法可将设备的唯一标识、网卡MAC地址作为参数,使用密钥派生算法(KDF)生成新的派生密钥作为本设备的唯一密钥。
主要过程是:
- (1)应用程序将设备唯一标识符(设备ID和MAC地址)传入CloudHSM;
- (2)CloudHSM内部使用主密钥Master Key和设备标识符,通过HKDF-SHA384算法进行密钥派生;
- (3)派生出的设备密钥以明文形式返回给应用程序;
- (4)应用程序获得32字节的设备专用密钥,可用于后续的加解密操作。
流程如下:
┌─────────────┐
│ 主密钥(HSM) │ AES-256 (MyAES256Key)
└──────┬──────┘
│ HKDF-SHA384
│ (设备ID + MAC)
▼
┌─────────────┐
│ 派生密钥 │ 32字节明文密钥
│ (返回应用) │ 返回给应用程序
└─────────────┘
本文的例子使用HKDF-SHA384算法,由此可保证每个设备都有独一无二的密钥,且本密钥可随时由主密钥和设备唯一信息推导生成。在本例中,我们直接将派生密钥(即针对每个IoT设备的设备密钥)以明文方式返回给应用程序进行后续处理。
这种方式的优点是管理灵活,CloudHSM只需要进行密钥派生操作,无需承担大量的加解密计算负载,适合数十万IoT设备的高并发场景。但缺点是派生密钥会暴露给应用程序,安全性相对较低。
在确保前文整体maven构建成功的情况下,在target目录内已经有现成的jar包,可直接运行。
mvn clean package
export HSM_USER=user01
export HSM_PASSWORD=1qazxsw2
java -jar target/cloudhsm-keyderivation-1.0-SNAPSHOT.jar
返回结果如下:
使用用户: user01 连接到CloudHSM...
=== CloudHSM 密钥派生信息 ===
主密钥类型: AES-256
主密钥标签: MyAES256Key
派生算法: HKDF-SHA384
设备ID: DEVICE123456
设备MAC: AA:BB:CC:DD:EE:FF
派生的设备密钥 (32字节):
Hex: 83a813f5a70f3867ab27c5e3d8715026730af7b42ed23e7baa6d7e2e0bc45f9e
Base64: g6gT9acPOGerJ8Xj2HFQJnMK97Qu0j57qm1+LgvEX54=
密钥派生成功!
由此看到派生密钥成功生成并以明文形式返回给应用程序。应用程序可以使用这个32字节的设备专用密钥进行后续的加解密操作。
6、在CloudHSM内以Session Key方式进行密钥派生并加密、解密
本章节代码见本文开头Github的:src/main/java/com/example/cloudhsm/CloudHSMSessionKeyEncrypt.java和CloudHSMSessionKeyDecrypt.java。
上一个例子,派生密钥直接用明文方式返回应用客户端,这样的好处是管理灵活,同时几十万IoT设备需要大量数据时候,CloudHSM加密机只需要生成派生密钥,而无需直接承担加解密过程的开销,避免了CloudHSM加密机的密钥数、并发连接被打爆。但由此加解密安全性不足,未能完全让密钥在CloudHSM内部完成加密。因此如果设备较多,但是每秒QPS并发在相对不高(例如几百QPS)的情况下,一种更加安全的做法是全程在CloudHSM完成加密解密。
主要过程是:
- (1)用主密钥Master Key对设备唯一标识符(如设备ID和MAC地址)进行AES-CMAC KDF派生算法,在CloudHSM内部计算获得派生后的设备密钥;
- (2)设备密钥以Session Key临时密钥的方式保存在CloudHSM中,此时会占用CloudHSM的密钥存储槽位一个,但是占用时长仅限本Session;
- (3)应用端传入要加密的数据,在CloudHSM内部使用设备密钥完成加密,密文返回给应用端;
- (4)释放Session,此时CloudHSM内的Session Key将释放不会占用存储槽位,而永久保存的密钥依然只有主密钥Master Key一个。
流程如下:
┌─────────────┐
│ 主密钥(HSM) │ AES-256 (MyAES256Key)
└──────┬──────┘
│ AES-CMAC KDF
│ (设备ID + MAC)
▼
┌─────────────┐
│设备密钥(HSM) │ AES-256 (DEVICE_xxx)
│ TOKEN=false │ Session Key,不持久化
└──────┬──────┘
│ AES-GCM加密
▼
┌─────────────┐
│ 业务数据密文│ IV(12) + [加密数据 + 认证标签(16)]
└─────────────┘
以上方式可以针对较大设备数量、但是每秒并发在几百QPS的情况下有效满足派生加密的需求,且加密和解密全程在CloudHSM内,即实现了最终目标密钥明文不出加密机。
由于前边Maven已经构建好了Jar包,这里直接运行就可以了:
mvn clean package
export HSM_USER=user01
export HSM_PASSWORD=1qazxsw2
java -jar target/cloudhsm-sessionkey-encrypt-1.0-SNAPSHOT.jar
返回结果如下:
使用用户: user01 连接到CloudHSM...
=== Session Key 派生完成 ===
设备ID: DEVICE123456
设备MAC: AA:BB:CC:DD:EE:FF
=== AES-256-GCM 加密结果 ===
密文 (Base64): 0oEcvVTi/Tlv8sJM0R6uauul4MCmkApBu5NEojjKjkGSCXhRpWrcHrV5c411dAsGr0ZRUGkYjEURks+TqW2SFddrNA==
由此看到派生后的Session Key在CloudHSM内加密成功。
接下来测试解密,将这个密文更新到CloudHSMSessionKeyDecrypt.java代码中的密文,然后重新构建,并执行:
mvn clean package
export HSM_USER=user01
export HSM_PASSWORD=1qazxsw2
java -cp target/cloudhsm-sessionkey-encrypt-1.0-SNAPSHOT.jar com.example.cloudhsm.CloudHSMSessionKeyDecrypt
解密后返回提示信息:
使用用户: user01 连接到CloudHSM...
=== Session Key 派生完成 ===
设备ID: DEVICE123456
设备MAC: AA:BB:CC:DD:EE:FF
=== AES-256-GCM 解密结果 ===
明文: Hello CloudHSM! This is a test message.
由此派生算法的例子完成。
五、在CloudHSM上使用Key Wrap和Unwrap做密钥导入导出
1、密钥导出机制
在前一个章节的Java SDK示例代码中,展示了一个生成派生密钥并将派生后的明文作为设备密钥返回给应用程序的例子。这个例子并不是密钥的明文导出,因为负责派生的主密钥是AES256,这个主密钥并未以明文形式或者密文形式被导出,而是借助主密钥、输入的设备序号、设备网卡MAC地址派生计算了一个新密钥。真正进行一个现有密钥的完整导出的唯一方法是使用Key Wrap/Unwrap功能。
除了在CloudHSM内部直接完成加密、解密的操作外,CloudHSM支持将密钥以加密形式导出,被称为Key Wrap。CloudHSM不支持直接将密钥的明文导出。CloudHSM的Key Wrap是以加密形式,经由某一个受信任(Trusted Key)主密钥来完成,将被导出的Key进行加密并把密文从CloudHSM输出到外部。做完Key Wrap后,CloudHSM内可选不保存此密钥,以释放CloudHSM密钥存储的槽位,避免过多密钥完全占用CloudHSM的存储能力。导出的密钥可在任何时间做unwrap导入,导入CloudHSM过程将由受信任(Trusted Key)主密钥来完成解密,在CloudHSM内重新让导入的密钥可用。
在主密钥是AES256类型的情况下,对一个密钥做Wrap导出,使用的算法是AESWrap/ECB/NoPadding (RFC 3394) 。这是符合NIST SP 800-38F标准的安全的算法,提供256位强度的安全。此外,为了控制那些密钥可以被导出,在创建密钥时候需要设置对应属性,主要包括如下:
Master Key主密钥属性:
- extractable=false,禁止导出密钥(本密钥自身是主密钥,禁止导出)
- trusted=true,本参数必须由CloudHSM admin额外手工设置,普通用户创建密钥时候无法添加此属性
被导出的密钥需要设置属性:
- extractable=true,允许导出
- wrap-with-trusted=true,仅允许受信任的密钥(trusted=true)对当前密钥加密后导出
下面是Java SDK的例子的介绍。在上文部署开发环境时候,有关代码已经一并被下载并打包了,因此这里直接运行即可。
2、创建用于Wrap Key的Master Key主密钥
本章节代码见本文开头Github的:src/main/java/com/example/cloudhsm/WrapDemoStep1GenerateMasterKey.java。
首先创建主密钥。在本文的样例代码中,已经为主密钥设置了对应的属性,主密钥本身不可导出,不可用于加密和解密,只能用于做Wrap导出时候的封装使用。
运行刚才已经构建好的Jar包,记得提前通过环境变量设置访问CloudHSM的用户名和密码。
mvn clean package
export HSM_USER=user01
export HSM_PASSWORD=1qazxsw2
java -jar target/wrap-demo-step-1-generate-master-key-1.0-SNAPSHOT.jar
返回结果如下:
使用用户 user01 连接到CloudHSM...
Master Key创建成功!
密钥标签: new-master-key
密钥算法: AES
注意:请管理员通过CLI将此密钥设置为trusted key
由此创建了一个标签为new-master-key的主密钥。接下来为其设置Trust信任密钥这个特殊属性。
3、通过CloudHSM CLI检索Master Key并设置属性为Trusted Key
先以普通加密用户身份登陆CloudHSM,检索密钥详细信息。这是由于CloudHSM管理员模式不能用于密钥检索等日常操作,日常检索密钥必须使用crypto-user级别的用户。
/opt/cloudhsm/bin/cloudhsm-cli interactive
login --username user01 --role crypto-user
输入密码后,登陆完成。执行如下命令检索刚才创建的lable是new-master-key的主密钥。
key list --filter attr.label=new-master-key
返回结果如下:
{
"error_code": 0,
"data": {
"matched_keys": [
{
"key-reference": "0x0000000000000ca5",
"attributes": {
"label": "new-master-key"
}
}
],
"total_key_count": 1,
"returned_key_count": 1
}
}
这里可以看到搜索结果,lable标签是new-master-key的密钥对应的key-reference是0x0000000000000ca5,记录下来并代入下边的命令,查看详细属性。
key list --filter key-reference=0x0000000000000ca5 --verbose
这个命令会返回当前Key所有的属性。返回如下:
{
"error_code": 0,
"data": {
"matched_keys": [
{
"key-reference": "0x0000000000000ca5",
"key-info": {
"key-owners": [
{
"username": "user01",
"key-coverage": "full"
}
],
"shared-users": [],
"key-quorum-values": {
"manage-key-quorum-value": 0,
"use-key-quorum-value": 0
},
"cluster-coverage": "full"
},
"attributes": {
"key-type": "aes",
"label": "new-master-key",
"id": "0x",
"check-value": "0xbe4119",
"class": "secret-key",
"encrypt": false,
"decrypt": false,
"token": true,
"always-sensitive": true,
"derive": false,
"destroyable": true,
"extractable": false,
"local": true,
"modifiable": true,
"never-extractable": true,
"private": true,
"sensitive": true,
"sign": true,
"trusted": false,
"unwrap": true,
"verify": true,
"wrap": true,
"wrap-with-trusted": false,
"key-length-bytes": 32
}
}
],
"total_key_count": 1,
"returned_key_count": 1
}
}
可以看到当前属性"trusted": false。接下来要切换到管理员身份,为其设置信任。执行quit命令退出当前普通用户身份。
以管理员身份使用CloudHSM CLI登陆:
/opt/cloudhsm/bin/cloudhsm-cli interactive
login --username admin --role admin
输入密码后,成为管理员权限,将如下命令中的key-reference替换为上文查询的结果。执行如下命令设置Trusted属性:
key set-attribute --filter key-reference=0x0000000000000ca5 --name trusted --value true
返回结果如下:
{
"error_code": 0,
"data": {
"message": "Attribute set successfully"
}
}
设置信任key属性完成。
4、生成Data Key并Wrap导出
本章节代码见本文开头Github的:src/main/java/com/example/cloudhsm/WrapDemoStep2GenerateDataKeyAndWrap.java。
继续前边的操作,在已经设置要CloudHSM用户名和密码的环境变量的情况下,运行如下命令:
mvn clean package
export HSM_USER=user01
export HSM_PASSWORD=1qazxsw2
java -jar target/wrap-demo-step-2-generate-data-key-and-wrap-1.0-SNAPSHOT.jar
返回结果如下:
使用用户 user01 连接到CloudHSM...
Data Key创建成功!
Data Key已被wrap导出:
Wrapped Key (Base64): Elob/RRLYZDLZeKRsfhRmsWD162cnghK0PHL12tVIwC925xdacJwYg==
请将此wrapped key用于Step3
由此可以看到,在CloudHSM内生成了新的Data Key,并且使用new-master-key主密钥做了wrap导出。现在可将这段Wraped key传入下一个程序,执行unwrap测试。
5、在CloudHSM上以Unwrap方式导入Data Key并对数据加密
本章节代码见本文开头Github的:src/main/java/com/example/cloudhsm/WrapDemoStep3UnwrapDataKeyAndEncryptd.java。
上一步生成了Data Key,并且Data Key被Wrap出来,以密文方式保存。现在可以把这个密文加入到代码WrapDemoStep3UnwrapDataKeyAndEncryptd.java中,然后重新构建并执行。
java -jar target/wrap-demo-step-3-unwrap-data-key-and-encryptd-1.0-SNAPSHOT.jar
返回结果如下:
使用用户 user01 连接到CloudHSM...
Wrapped Key密文: Elob/RRLYZDLZeKRsfhRmsWD162cnghK0PHL12tVIwC925xdacJwYg==
Data Key已成功unwrap导入到CloudHSM
使用算法: AES/GCM/NoPadding
原始消息: Hello CloudHSM! This is a test message.
加密结果: AAAAAAAAAAAAAAAAVYpezkTKDpVki+ZjL1Te+Rspxl9aOoaM8U2Q5dTOL6CfA7mDuCJiTjCbJSkyj16fh9NayI628A==
Session结束,data key已从CloudHSM中释放
6、在CloudHSM上以Unwrap方式导入Data Key并对密文做解密
本章节代码见本文开头Github的:src/main/java/com/example/cloudhsm/WrapDemoStep4UnwrapDataKeyAndDecryption.java。
上一步加密数据成功,下一步演示如何解密数据。首先用Wrapped Key的导入到CloudHSM,通过Unwrap操作恢复为Data key,并且传入要解密的密文,进行数据解密。解密之后,Data Key作为Session Key不长期保留在CloudHSM内,因此Session结束后直接释放。
java -jar target/wrap-demo-step-4-unwrap-data-key-and-decryption-1.0-SNAPSHOT.jar
返回结果如下:
使用用户 user01 连接到CloudHSM...
Wrapped Key密文: Elob/RRLYZDLZeKRsfhRmsWD162cnghK0PHL12tVIwC925xdacJwYg==
Step3的加密消息: IGdLOgFlzr5SLSZWBJmtd70P6aOtn7QiIAMdBPgYwKwxDvFQL/2NgU2aiZH7krexrIE8Dfg7rWDKMawvXl8Re1+yxw==
Data Key已成功unwrap导入到CloudHSM
使用算法: AES/GCM/NoPadding
Step3加密消息: IGdLOgFlzr5SLSZWBJmtd70P6aOtn7QiIAMdBPgYwKwxDvFQL/2NgU2aiZH7krexrIE8Dfg7rWDKMawvXl8Re1+yxw==
解密结果: Hello CloudHSM! This is a test message.
Session结束,data key已从CloudHSM中释放
由此可以看到,Unwrap后的Key解密成功。
六、CloudHSM最佳实践之加密算法和SDK选择
在之前的章节中,我们以Java为例展示了CloudHSM基本操作。除了Java的JCE Provider之外,CloudHSM还提供多种SDK,另一种常用的是PKCS #11。这里将二者对比如下。
1、PKCS #11 与 JCE Provider SDK
CloudHSM提供多种SDK,包括面向OpenSSL的OpenSSL Dynamic Engine,面向Windows环境的Key storage provider (KSP)。在Linux开发环境下,使用比较普遍的是适合C/C++的PKCS #11 library库,以及面向Java的JCE Provider。
PKCS #11 SDK:
- PKCS11 SDK 支持的操作系统主要是RHEL企业版8/9,以及Ubuntu LTS 24.04/22.04/20.04等,从这个网址下载获得:https://docs.aws.amazon.com/cloudhsm/latest/userguide/pkcs11-library-install.html
- PKCS11 SDK 支持的算法:https://docs.aws.amazon.com/cloudhsm/latest/userguide/pkcs11-mechanisms.html
- PKCS11 SDK 支持的密钥类型:https://docs.aws.amazon.com/cloudhsm/latest/userguide/pkcs11-key-types.html
JCE Provider SDK:
- JCE SDK 支持Java版本由OpenJDK 8/OpenJDK 11/OpenJDK 17/OpenJDK 21等版本,从这个网址下载获得:https://docs.aws.amazon.com/cloudhsm/latest/userguide/java-library-install_5.html
- JCE SDK 支持的算法:https://docs.aws.amazon.com/cloudhsm/latest/userguide/java-lib-supported_5.html
- JCE SDK 支持的密钥类型:https://docs.aws.amazon.com/cloudhsm/latest/userguide/java-lib-keys_5.html
2、两种SDK支持的密钥类型和加密算法共同之处
共同支持的密钥类型:
- AES: 128, 192, 256位
- Triple DES (3DES/DESede): 192位 (FIPS模式下2023年后禁用)
- EC (椭圆曲线): secp224r1 (P-224), secp256r1 (P-256), secp256k1 (Blockchain), secp384r1 (P-384), secp521r1 (P-521)
- RSA: 2048-4096位,256位递增
- GENERIC_SECRET: 1-800字节
共同支持的算法:
- 密钥生成: RSA, EC, AES, DESede, GenericSecret
- 加密/解密: AES (CBC, ECB, CTR, GCM), RSA (PKCS, OAEP), DES3
- 签名/验证: RSA (多种填充), ECDSA
- 摘要: SHA-1, SHA-224, SHA-256, SHA-384, SHA-512
- HMAC: HmacSHA1, HmacSHA224, HmacSHA256, HmacSHA384, HmacSHA512
- 密钥包装/解包: RSA, AES-GCM等
3、两种SDK各自专属能力
JCE Provider专有的密钥类型:
- HMAC: 支持SHA1, SHA224, SHA256, SHA384, SHA512的HMAC密钥
JCE Provider专有的算法:
- CMAC (Cipher-based MAC): AESCMAC
- 密钥协商: ECDH with KDF (X9.63 KDF SHA1/224/256/384/512)
- 密钥工厂: SecretKeyFactory, KeyFactory支持多种KeySpec
PKCS #11专有的机制:
- 密钥派生: CKM_SP800_108_COUNTER_KDF
- CloudHSM专有机制:
- CKM_CLOUDHSM_AES_GCM
- CKM_CLOUDHSM_AES_KEY_WRAP_NO_PAD
- CKM_CLOUDHSM_AES_KEY_WRAP_PKCS5_PAD
- CKM_CLOUDHSM_AES_KEY_WRAP_ZERO_PAD
4、FIPS规范对加密算法的要求
在创建CloudHSM时候,有FIPS和非FIPS选项,二者在加密算法上有区别。FIPS是美国NIST的算法标准,对加密算法的最低强度有要求,以白名单方式许可算法。2023年NIST调整了许可的算法清单,从2024年起使用3DES加密的场景不再被许可。详细说明如下网址:https://docs.aws.amazon.com/cloudhsm/latest/userguide/compliance-dep-notif.html
原始NIST文档出处:(参考Table1 & Table5)https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf
美国国家标准与技术研究院(NIST)建议,在2023年12月31日之后,不再支持Triple DES(DESede、3DES、DES3)加密以及使用PKCS#1 v1.5填充的RSA密钥包装和解包装。因此,我们的联邦信息处理标准(FIPS)模式集群将从2024年1月1日起停止对这些功能的支持。非FIPS模式的集群将继续支持这些功能。
如下使用算法不再被FIPS许可:
- Triple DES密钥生成
- PKCS#11库的CKM_DES3_KEY_GEN
- JCE Provider的DESede Keygen
- KMU的genSymKey with -t=21
使用Triple DES密钥进行加密(注意:允许解密操作)不再被FIPS许可:
- PKCS#11库:CKM_DES3_CBC encrypt、CKM_DES3_CBC_PAD encrypt和CKM_DES3_ECB encrypt
- JCE Provider:DESede/CBC/PKCS5Padding encrypt、DESede/CBC/NoPadding encrypt、DESede/ECB/Padding encrypt和DESede/ECB/NoPadding encrypt
使用PKCS#1 v1.5填充的RSA密钥包装、解包装、加密和解密不再被FIPS许可:
- PKCS#11 SDK的CKM_RSA_PKCS wrap、unwrap、encrypt和decrypt
- JCE SDK的RSA/ECB/PKCS1Padding wrap、unwrap、encrypt和decrypt
- KMU的wrapKey和unWrapKey with -m 12(注意:12是RSA_PKCS机制的值)
如果您的应用代码使用了以上算法,那么就不满足FIPS标准,需要创建非FIPS类型的CloudHSM,创建好的CloudHSM不支持修改类型。
5、关于PQC(Post Quantum Cryptography)后量子密码算法支持情况
目前CloudHSM不支持PQC算法,不管采用PKCS #11还是JCE Provider SDK,均不支持PQC算法。
如果要采用全套AWS的PKI体系的话,使用Private CA服务支持ML-DSA算法,满足FIPS204的合规要求。具体参考网址如下:
如果不希望采用全套AWS PKI,而只是将部分功能以API服务化,那么可选择KMS服务。KMS(Key Management Service)是一项密钥管理服务,提供完全API化的加解密和密钥管理能力,这个服务支持PQC算法:
- ML-DSA (Module-Lattice-Based Digital Signature Algorithm): AWS KMS已支持ML-DSA_44, ML-DSA_65, ML-DSA_87三种规格
- ML-KEM: AWS Transfer Family已支持ML-KEM量子抗性密钥交换
6、关于JCE Provider SDK的Java依赖库安全相关问题
CloudHSM服务自身的构建不依赖bcprov-jdk和log4j库。CloudHSM的集群管理配置通过AWS控制台网页界面操作,对CloudHSM的初始化和管理员操作通过命令行界面CLI操作(且支持mTLS保护),官方文档披露的信息中没有bcprov-jdk和log4j相关信息。
如果选择JCE Provider SDK,那么直接调用CloudHSM生成密钥、加密、解密、派生密钥等过程都不需要bcprov-jdk和log4j等库。将派生密钥作为临时密钥在CloudHSM内加密、解密等过程也同样不需要这些库。
如果您的应用程序需要在CloudHSM加密机之外、在应用程序内进行加密,那么应用程序很可能需要依赖bcprov-jdk和log4j等库,此时您需要自行管理版本引入,并确保最新版本以规避关键漏洞。
七、CloudHSM最佳实践之海量IoT设备场景的加密体系的设计
本章节探讨针对较大数据量的IoT物联网设备的场景下,如何结合CloudHSM的能力设计加解密机制。
1、CloudHSM密钥类型和密钥存储的限制
CloudHSM的密钥有两种类型:永久类型,以及会话密钥(Session Key)。永久类型会一直在CloudHSM内,无论当前是否使用,都会占用密钥槽位。而会话密钥(Session Key)在使用包括加密、解密等操作时会占用密钥槽位,会话结束后密钥将被释放不会保存在CloudHSM内,会话结束后不占用槽位。两种类型的密钥共用统一的数量限制。以2025年可用机型的hsm2m.medium型号为例,支持最大存储16666个Key,但如果是非对称密钥最大支持3333个。这个密钥数量,同时包含永久密钥和临时会话密钥(Session Key),他们总计数量上限是16666个,或3333(使用非对称密钥时)。
由此可以看出,如果有数万用户(或对应数万IoT设备),为每个用户分发一个独立的主密钥(Master Key)作为持久密钥、并直接存储在CloudHSM内的成本是非常高昂的。当设备数量很大时候很快就会达到单台CloudHSM设备上限,就需要在当前CloudHSM Cluster内扩展集群,增加CloudHSM自己的数量。目前hsm2m.medium系列支持一个集群28个节点。即便如此,也依然无法满足几万个IoT设备每个用户一个独立主密钥的使用方式,此外这样运行最高达28个节点集群,成本极其高昂。因此,就需要使用不同的密钥管理机制。这里介绍下派生密钥和信封加密两种方式。
此外CloudHSM支持900个并发连接,在一个两节点组成的集群上,完成RSA 2048-bit sign签名时候,理论性能是2000 ops/sec。以上数据在本文末尾的参考文档中可以查询到。
2、派生密钥
派生密钥的实现方式是,当需要加解密的时候,可以用本机的唯一标识符信息(如设备ID/序列号/网卡MAC等)与传入到CloudHSM,通过调用主密钥Master使用派生函数计算获得本设备的专属设备密钥。由于每个设备的唯一信息如设备ID/序列号/网卡MAC等是唯一不变的,因此派生密钥算法每次运行都会生成同一个的设备密钥,为本设备所专属,然后针对本设备的数据进行加密、解密。派生密钥方式适合设备在整个生命周期内不需要频繁更换密钥的场景。
在实际使用中,CloudHSM接收到客户端传入的用于计算派生密钥的唯一标识符后,会调用CloudHSM内的主密钥完成KDF派生函数计算,计算过程中Master Key主密钥是不离开CloudHSM边界的。计算完毕后,获得了针对本设备的派生密钥。关于派生后的设备密钥是否保存在CloudHSM以及是否离开CloudHSM,有如下两种场景:
- 派生后的设备密钥可以从CloudHSM中以明文方式返回给应用端,然后应用端进行加密,此时CloudHSM不存储每个设备密钥;在计算派生密钥的过程中,不占用CloudHSM的密钥槽位,CloudHSM本质上只有一个Master Key的占用。
- 如果希望进一步提升安全性,还可以把派生后的设备密钥以Session Key临时密钥的方式放在CloudHSM内,此时客户端应用继续给CloudHSM传入要加密的内容,CloudHSM将设备密钥用作Session Key这种临时Key方式,完成对数据的加密。这里需要注意,Session Key这种临时Key,也是占用CloudHSM存储槽位的,因此CloudHSM的总密钥限制是短期内当前处于有效状态的密钥,不论是短期的Session Key还是永久保存的Master Key。当Session结束后,设备密钥被释放,CloudHSM还是保存唯一的主密钥。
以上两种使用方式主要区别是最终数据加密发生的位置,前一种速度更快,因为使用应用端的算力进行加密,更适合大量并发场景、加解密负载较重的场景,但设备密钥的明文是离开了CloudHSM的(当然主密钥Master Key并未离开)。后一种方式在CloudHSM内完成所有数据加密操作,更安全但是更消耗CloudHSM计算资源,并且重点是消耗密钥槽位存储资源,因此整体并发会受到限制。
3、信封加密
(1) CloudHSM上的信封加密机制
信封加密方式则是所谓的二次加密,即每个设备有一个唯一的加密数据用的数据密钥(Data Key),这个Data Key的生成过程可以在CloudHSM也可以在客户端应用程序上,这个Data Key被主密钥Master Key进行加密,获得Data Key的明文。平时保留的是Data key的密文,不保留Data Key明文。当需要加密或者解密时候,先用Master Key对Data Key的密文进行解密,获得明文的Data Key,明文的Data Key对整个数据集做加密或者解密,完成操作。操作一旦完成,即可释放掉/删除掉明文的Data Key,只保留Data Key密文和加密好的数据集。
信封加密的特点是Data Key与Master Key独立。与派生加密使用Master Key+唯一标识符+KDF派生函数计算获得设备密钥不同,Data Key不是基于Master Key的派生的,是完全独立的一个密钥。Master Key对Data Key进行加密保护。与上一章节讨论派生密钥是否以Session Key的形式保留在CloudHSM内的相似,信封加密也有两种选择:
- 完全将Data Key明文发送给客户端,此时CloudHSM只占用唯一的Master Key槽位,适合高并发、客户端需要高频加解密的操作,但是安全性相对弱;
- 将Data Key以Session Key的方式短期保存在CloudHSM内部进行数据加密,此时数据加密也在CloudHSM内完成。此时将引入一个新的概念:Wrapping Key 封装密钥。
为什么会需要Wrapping Key 封装密钥这个概念呢,这是因为信封加密的机制与刚才的派生算法不同。派生算法KSF就是CloudHSM本身完成计算的,外部传入唯一标识符,CloudHSM内部使用Master key,当场计算获得针对本设备的设备密钥,作为Session Key占用CloudHSM一个槽位。而Data Key是独立于Master Key的,一般是按特定算法随机生成的,每次生成Data Key都不一样。因此一般需要预先生成好,在需要加解密时候再加载到CloudHSM内使用。而将一个外部已经存在的密钥传输到CloudHSM内需要使用的就是Unwrap Key。
例如预先为大量设备比如几万台设备生成好Data Key,生成过程在CloudHSM内部生成,生成的总数可能远大于16666/3333(即单台CloudHSM容量上限),由此可以分配生成。在CloudHSM内生成好这些Data Key后,使用Wrapping Key功能,将Data Key用Master Key加密(通常是AES),然后把加密后的Data Key密文保存到特定的S3存储桶/DynamoDB数据库。此外,还要保存Data Key和设备的对应关系,否则都是纯随机的就对不上了。当需要加解密的时候,是通过Unwrap方式来恢复。应用程序将Data Key的密文传入到CloudHSM,被CloudHSM用Master做Unwrap解密后获得Data Key明文,并以Session Key方式保存在CloudHSM内,也就是此时占用CloudHSM槽位。接下来CloudHSM有了Data Key明文就可以完成对数据的加解密计算。计算完毕后,丢弃当前Session,释放掉CloudHSM的密钥槽位。下次再需要加解密时候,再做Unwrap加载Data Key。
以上方式可以看出信封加密为每个设备预先生成了独立的Data Key,且Data Key不是派生推导出来的,不受设备唯一标识符信息关联,因此有极高的自由度可定期更换Data Key。不过信封加密对整个加密体系管理提出了更高要求,如果是在应用端加密,虽然有着更高吞吐,需要注意Data Key明文泄漏风险。如果是完全在CloudHSM内完成Data Key的加解密操作,那么需要使用Wrapping Key和Session Key机制,可显著提升安全。
(2) CloudHSM上的信封加密设计
Master Key 属性:
├── CKA_TOKEN = true (持久化存储)
├── CKA_EXTRACTABLE = false (不可导出) ✅
├── CKA_WRAP = true (允许wrap其他密钥)
├── CKA_UNWRAP = true (允许unwrap其他密钥)
└── CKA_TRUSTED = true (标记为可信密钥) ← 需要Admin/CO设置
Data Key 属性:
├── CKA_TOKEN = true/false (可以是Token或Session Key)
├── CKA_EXTRACTABLE = true (允许导出) ✅
├── CKA_WRAP_WITH_TRUSTED = true (只能被可信密钥wrap) ✅
├── CKA_ENCRYPT = true (允许加密)
└── CKA_DECRYPT = true (允许解密)
(3) 工作流程设计
┌─────────────────────────────────────────────────────────────────┐
│ Master Key初始化阶段(CloudHSM管理员/一次性) │
├─────────────────────────────────────────────────────────────────┤
│ 1. 生成 Master Key (AES-256, Token Key) │
│ 2. Admin/CO 设置 CKA_TRUSTED = true │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 设备注册阶段预分配Data Key(针对每个IoT设备) │
├─────────────────────────────────────────────────────────────────┤
│ 1. 生成 Data Key (CKA_WRAP_WITH_TRUSTED=true) │
│ 2. 用 Master Key wrap Data Key → 得到 Wrapped DEK │
│ 3. 存储 Wrapped DEK 到 S3/DynamoDB (关联设备MAC地址) │
│ 4. 删除 HSM 内的 Data Key → 释放槽位 │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 数据加密阶段(设备上传数据时) │
├─────────────────────────────────────────────────────────────────┤
│ 1. 根据设备标识信息从S3获取Data Key密文 (Wrapped Key) │
│ 2. 用 Master Key unwrap → 得到 Data Key 明文 (Session Key) │
│ 3. 用 Data Key 加密数据 → 得到数据集的密文 │
│ 4. 丢弃 Session Key (自动释放 Data Key 明文) │
│ 5. 返回密文给应用 │
└─────────────────────────────────────────────────────────────────┘
以上是在CloudHSM上大规模应用,即设备数量远超过CloudHSM单机存储密钥上限场景下的一个典型设计。
4、二者对比及混合使用
二者对比如下:
| 对比 | 派生加密 | 信封加密 |
|---|---|---|
| 生成设备密钥是否和设备唯一标识信息相关 | 是 | 否,完全随机生成 |
| 生成设备密钥是否Master Key主密钥相关 | 是 | 否,完全随机生成 |
| 加密解密前的操作 | 用Master Key+唯一标识符计算出本设备的派生密钥 | 用Master key主密钥对Data Key密文解密,获得出Data Key明文 |
| 是否需要额外存储设备密钥 | 不用存储,每次计算派生密钥结果一样 | Data Key随机生成的,需要额外存储每个设备的Data Key密文 |
| 大批量使用是否需要初始化 | 可选,临时计算派生密钥即可,也可批量生成好 | 建议预先生成,首次使用也可CloudHSM第一次生成Data Key但交互延迟更高,建议批量初始化Data Key并保存好密文 |
| 是否需要保存设备唯一标识和密钥关系 | 不用,因为传入设备标识符计算就可获得 | 需要,Data key随机生成,每次生成不一样,一旦初始化好了需要保存一份对应关系 |
| 是否需要使用Session Key方式 | 建议使用,由此设备密钥明文不离开CloudHSM | 建议使用,由此Data Key明文不离开CloudHSM |
| 设备密钥是否明文在应用端 | 可选,因为设备密钥是派生出来的,明文在应用端有风险 | 强烈不建议,如果应用端自己维护Data Key明文的生命周期,存在较高风险,建议完全由CloudHSM负责Data Key管理和封装 |
| 是否涉及Wrapping Key | 仅在备份Master Key时候需要 | 建议使用,以满足Data Key不脱离CloudHSM的要求 |
| 更换设备密钥是否方便 | 不方面,派生算法绑定了唯一标识符,必须更换标识符才能生成新的派生密钥 | 方便更换,Data Key可轮换,不影响整个加解密逻辑 |
从以上二者的逻辑可以看出,两者各有优缺点,主密钥的实现相对简单,本质上是一次加密过程;信封加密的实现相对复杂,本身是两次加密,并且Data的密文还需要额外的存储设备(如S3/DynamoDB等)占用成本。
在大规模使用中,可以混合两种场景,通过生成多个中间层密钥并进行信封加密的方式,充分的发挥派生算法的灵活性和信封加密的独立性。例如针对10万个设备,使用主密钥为1~1000个设备生成一个区间Key,然后1001~2000设备生成第二个区间Key。针对区间Key的使用和保管,按照信封加密的方式,平时只保存密文,需要的时候才通过主密钥Master Key解密为明文。针对每台设备上的数据加密,在使用派生算法定义出唯一的设备密钥,通过区域密钥和设备唯一标识符,获得唯一的设备密钥,最终用数据密钥进行加解密。混合使用虽然技术栈上更加复杂,但是安全性和管理便捷性进一步获得提升。
八、CloudHSM最佳实践之运维管理注意事项
在完成了上述章节的架构设计、测试后,本章节将测试环境转向生产。
1、多节点集群高可用
前文测试中,集群以单节点方式存在,此时页面上方可看到提示信息。如下截图。

在生产环境中,应采用至少两台节点满足高可用要求。CloudHSM会自动维护两个节点之间的密钥同步。因此只要创建第二个节点即可。
点击右下角的Create CloudHSM按钮。然后选择另一个可用区,并点击创建按钮。

在创建完成后,不需要修改Java代码的时候连接的集群。当Java代码连接到第一个IP后,会自动发现本集群内所有IP地址,无须手工指定所有IP。
2、独立账号运行CloudHSM
CloudHSM日常管理中虽然有独立的CloudHSM用户名、密码来保护密钥,但来自AWS管理员权限的误操作、恶意操作是具有破坏能力的。例如某个AWS IAM User可以登陆AWS控制台,且他是AWS帐户的Administrator权限,那么这个用户进入CloudHSM控制台,在不需要知道CloudHSM管理员用户名、密码的情况下,可以运行中的删除CloudHSM加密机。这产生了安全隐患。
应对办法:在有CloudHSM和其他服务混合部署的账号内,让所有用户使用低权限,不要使用AWS内置的AministratorAccess这样的超级权限,并且可在IAM Users、IAM Role的Policy内增加对CloudHSM的Deny规则。这样是非常有效的防护办法,不过对于拥有几十上百个IAM User和IAM Role的AWS帐户来说,逐个检查权限、增加Deny规则工作量较大,那么就可以使用下一个办法:在独立账号运行CloudHSM。
假设运行业务容器集群和数据库的AWS账号即12位AWS Account ID是555566667001,那么就可以在申请/创建一个全新的12位AWS Account ID,例如555566667002。两个帐户管理体系完全独立,A帐户的管理员权限也不能破坏B帐户。然后,在B帐户内创建CloudHSM加密机,B帐户的AWS控制台管理员权限和ClouHSM管理员密钥都有专门的安全团队负责。在A账号和B账号的VPC之间,使用VPC Peering进行网络互通。由此,即可大大提升安全性,避免误操作、入侵、恶意破坏导致的加密机失效。
3、跨区域备份
CloudHSM的备份是自动进行的,CloudHSM的备份默认保存90天,在创建集群的时候可选。对已经生成的备份,即使选择删除备份,那么系统也会保留7天后才会完全删除,以提供反悔的机会用于取回数据。
为了提升CloudHSM的业务连续性,也就是所谓“韧性”,可以将CloudHSM做跨区域备份,从当前AWS Region备份到相聚几百上千甚至另一个地球另一面另一个大洲的AWS Region,以避免火山、地震、洪水等不可抗力造成本AWS区域的数据损坏时候备份也一起损坏的风险。
跨区域备份默认是不启用的,需要从AWS控制台上手工执行备份,或者使用AWSCLI脚本、API程序等自动执行。
通过CloudHSM控制台手工执行备份的方法是,进入控制台,点击左下角的Backup备份按钮,从右侧选中要复制的备份,点击行动,从下拉框中选择跨区域复制。如下截图。

在下拉框中选择要复制的Region,点击复制。如下截图。

即可完成备份。
此外,备份也可通过AWSCLI进行,命令如下:
aws cloudhsm copy-backup-to-region \
--backup-id backup-xxxxxxxxx \
--destination-region us-west-2
如果需要定期的跨区域备份,可配置Lambda函数定期执行,将备份从当前区域复制到另外的区域。
4、仲裁机制的使用
为了进一步提升业务安全性,避免CloudHSM用户的单人操作权限的恶意破坏,CloudHSM还支持多人仲裁。在进行高风险操作时候,包括修改CloudHSM用户密码、修改密钥属性、签名等场景时候,可进行多用户仲裁操作,以M of N的机制,必须经过达到一定比例的多用户确认,才可完成高风险变更。
仲裁操作一般是强监管行业如金融Core Banking等行业。具体仲裁功能使用请参考CloudHSM文档。
九、参考文档
CloudHSM CLI下载
https://docs.aws.amazon.com/cloudhsm/latest/userguide/gs_cloudhsm_cli-install.html
CloudHSM 用户类型
https://docs.aws.amazon.com/cloudhsm/latest/userguide/understanding-users.html
CloudHSM 硬件机能限制(密钥个数等)
https://docs.aws.amazon.com/cloudhsm/latest/userguide/limits.html
CloudHSM Performance性能限制
https://docs.aws.amazon.com/cloudhsm/latest/userguide/performance.html
CloudHSM JCE Provider SDK下载
https://docs.aws.amazon.com/cloudhsm/latest/userguide/java-library-install_5.html
HSM user permissions table for CloudHSM CLI
https://docs.aws.amazon.com/cloudhsm/latest/userguide/user-permissions-table-chsm-cli.html
CloudHSM用户管理中的仲裁:
https://docs.aws.amazon.com/zh_cn/cloudhsm/latest/userguide/quorum-auth-chsm-cli.html
CloudHSM密钥管理中的仲裁:
https://docs.aws.amazon.com/zh_cn/cloudhsm/latest/userguide/key-quorum-auth-chsm-cli.html
最后修改于 2026-01-06