亚马逊AWS官方博客

CloudHSM的Java SDK使用及IoT场景加密体系设计最佳实践(上)

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 140-3 Level 3验证的HSM中执行。KMS默认使用AWS管理的多租户HSM,也可通过CloudHSM key stores使用CloudHSM作为自定义密钥存储。这两个服务都可以独立使用。

KMS和CloudHSM的选择决策主要基于合规要求、控制需求和集成复杂度:

选择KMS的场景:

  • 不需要单租户加密机的合规要求
  • 希望与AWS服务深度集成和简化密钥管理
  • 希望使用基于HTTP API的方式访问服务以简化应用集成开发
  • 希望使用AWS IAM及CloudTrail等原生服务来进行密钥的访问控制和使用审计

选择CloudHSM的场景:

  • 有强制使用单租户加密机的要求
  • 希望通过PKCS11,JCE等通用标准来集成加密服务
  • 需要自主完全控制密钥(例如导出密钥)
  • 需要使用KMS所不支持的特定的加密算法或密钥类型

KMS + CloudHSM组合的场景:

  • 既需要KMS的API便利性,又需要CloudHSM的合规性和控制力
  • 通过KMS的CloudHSM key stores功能,结合两者优势
  • 适合需要满足严格合规要求但希望简化开发的场景

本文介绍仅使用CloudHSM的场景,创建CloudHSM集群开始介绍。

二、CloudHSM集群的创建和初始化

1、创建CloudHSM集群

首先登录到AWS控制台,选择要部署CloudHSM的区域。然后进入CloudHSM服务界面,点击创建集群按钮。如下截图。

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

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

说明:FIPS模式的加密机有更好的合规性,但是不支持Triple DES及RSA PKCS#1 V1.5 padding这样安全性不被FIPS认可的加解密算法,若您的应用必须使用这些算法,请创建non-FIPS模式的集群。已创建好的CloudHSM集群不支持修改FIPS模式。有关于两种模式的详细区别,请参考此文档

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

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

2、初始化创建第一台CloudHSM主机并获取证书签名申请(CSR

在上一步点击了初始化之后,向导第一步是询问在哪个可用区创建第一台CloudHSM。这里可任意选择。如下截图。

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

下载完成后,下一步会提示上传签署好的证书,这里因为创建CloudHSM加密机还没有完成,因此可暂时跳过此步继续后面的“3、创建私钥”步骤。如下截图。

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

3、创建私钥

本文假设一个全新的实验环境,当前没有现成的CA,因此使用OpenSSL库自己创建一个CA,并导入到CloudHSM。

找一台Linux机器,可以是开发者本机,也可以是AWS云上EC2。在安装有OpenSSL库(且最新版本、避免安全隐患)的Linux/MacOS环境下,执行如下命令:

注意:该CA将用于CloudHSM的CLI和SDK验证CloudHSM集群的身份,因此请妥善保管该本地CA的私钥,避免泄露。对于生产环境,建议使用安全的方式管理该CA私钥,例如使用线下HSM加密机。

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集群证书

在上一步创建好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.crt两个文件。后续要使用<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管理机来操作CloudHSM加密机。创建该EC2时,请选择支持的操作系统及架构

本示例所创建的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 俄勒冈区域,那么可以在安全组内添加一条规则,允许来源地址范围是com.amazonaws.us-west-2.ec2-instance-connect的预定义prefix-list访问本机的TCP协议22端口。由此您将可以使用EC2控制台上的EC2 Instance Connect功能连接到SSH。如下截图。

说明:EC2 Instance Connect服务为每个一个region定义了不同的prefix-list,因此请根据您的实际情况替换其中的region字段:com.amazonaws.<region>.ec2-instance-connect

完成上述安全组配置后,您还需要确保自己的IAM用户具备使用EC2 Instance Connect功能的权限,详见该文档中的说明。之后,您便可以使用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 wget https://s3.amazonaws.com/cloudhsmv2-software/CloudHsmClient/Noble/cloudhsm-cli_latest_u24.04_amd64.deb
sudo apt install ./cloudhsm-cli_latest_u24.04_amd64.deb

4CLI登录、设置初始管理员密码并激活CloudHSM

激活之前,将上一步上传到CloudHSM的证书也复制到执行CloudHSM CLI的EC2的/opt/cloudhsm/etc/customerCA.crt位置。然后利用configure-cli工具指定HSM节点的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代码调用中,将会使用这个密钥进行操作。

7、在CloudHSM内删除密钥

要删除密钥,必须以CU(Crypto User)身份登录,且只有密钥创建者可以删除密钥。本例中,刚才以user01的身份创建的密钥,那么执行如下命令:

/opt/cloudhsm/bin/cloudhsm-cli interactive
login --username user01 --role crypto-user

以Label为标记删除密钥,命令如下:

key delete --filter key-reference=0x0000000000003773

然后即可删除。

四、使用Java语言JCE Provider SDK调用CloudHSM实现密钥创建、加密、解密的例子

1、开发环境准备及JCE Provider SDK安装

本文以在AWS云端使用EC2虚拟机的Ubuntu 24.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 24.04系统x86_64架构处理器为例,从官网下载最新版本的CloudHSM的JCE Provider SDK:

wget https://s3.amazonaws.com/cloudhsmv2-software/CloudHsmClient/Noble/cloudhsm-jce_latest_u24.04_amd64.deb
sudo apt install ./cloudhsm-jce_latest_u24.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,因此会编译fat-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、要加密的明文、要解密的密文的路径和文件名是在代码中,如果改动的话要重新编译代码。

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。设置单节点运行禁用可用性检查:

注意:生产环境请至少部署两台HSM节点,并分布于不同的可用区。生产环境请勿disable-key-availability-check以避免因密钥丢失而导致无法解密数据。

sudo /opt/cloudhsm/bin/configure-jce --disable-key-availability-check

运行前,通过环境变量加载CloudHSM的用户user01的用户名和密码:

export HSM_USER=user01
export HSM_PASSWORD=.......

运行Java代码:

java -jar target/cloudhsm-keygen-1.0-SNAPSHOT.jar

运行后返回结果如下:

使用用户 user01 连接到CloudHSM...
AES256 密钥创建成功!
密钥标签: MyAES256Key
密钥算法: AES

可看到密钥创建成功。

为了验证密钥创建成功,可通过CloudHSM CLI登录,查看刚创建的密钥是否存在。

现在使用cloudhsm-cli登录去确认密钥创建成功。如果您的集群是单节点,还要额外增加一条--disable-key-availability-check参数来关闭cloudhsm-cli对于密钥可用性的检查。否则后续查询时候会报告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

由此即可显示详细密钥属性:

aws-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=.......
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=.......
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.

解密成功!

备注:以上代码示例中,使用的是密钥的Label标签属性。但Label是不唯一的,可以生成重名密钥。在CloudHSM内,密钥的唯一标识是Key Reference ID,因此可使用这个唯一ID来避免重名问题。本文开头Github的:src/main/java/com/example/cloudhsm/CloudHSMEncryptionByKeyRefID.java 这段代码就是以Key Reference ID为例进行的调用。

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=.......
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并发在相对可接受的情况下(例如单机压力CloudHSM几百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=.......
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=.......
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 CLI下载: https://docs.aws.amazon.com/cloudhsm/latest/userguide/gscloudhsmcli-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

使用mTLS双向证书认证:https://docs.aws.amazon.com/cloudhsm/latest/userguide/getting-started-setup-mtls.html

关于Key Wrap和Unwrap密钥导出和导入、密钥迁移、加密算法选择、海量IoT设备场景的加密体系的设计、运维管理最佳实践等话题,请参考本文下篇

*前述特定亚马逊云科技生成式人工智能相关的服务目前在亚马逊云科技海外区域可用。亚马逊云科技中国区域相关云服务由西云数据和光环新网运营,具体信息以中国区域官网为准。

本篇作者

刘辛酉

亚马逊云科技解决方案架构师,曾服务于 Parallels、Siemens,在 Atos 担任管理服务部首席架构师。拥有十余年数据中心、互联网技术经验,长期为制造、汽车等行业头部客户提供 IT 咨询和服务。加入亚马逊云科技后负责零售、快消、食品和制造等行业客户。擅长硬件、网络、安全等领域设计。

王旭东

亚马逊云科技安全产品解决方案架构师,负责帮助客户进行安全解决方案的架构设计。在加入亚马逊云科技之前,曾在互联网 SaaS 企业负责公司基础架构安全建设及治理。


AWS 架构师中心:云端创新的引领者

探索 AWS 架构师中心,获取经实战验证的最佳实践与架构指南,助您高效构建安全、可靠的云上应用