亚马逊AWS官方博客

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

本文是下篇,使用的样例代码、初始化环境接上篇内容。

一、在CloudHSM上使用Key WrapUnwrap做密钥导入导出

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。此外,为了控制哪些密钥可以被导出,在创建密钥时候需要设置对应属性,主要包括如下:

Master Key主密钥属性:

  • extractable=false,禁止导出密钥(本密钥自身是主密钥,禁止导出)
  • trusted=true,本参数必须由CloudHSM admin额外手工设置,普通用户创建密钥时候无法添加此属性

被导出的密钥需要设置属性:

  • extractable=true,允许导出
  • wrap-with-trusted=true,仅允许受信任的密钥(trusted=true)对当前密钥加密后导出

下面是Java SDK的例子的介绍。在上文部署开发环境时候,有关代码已经一并被下载并打包了,因此这里直接运行即可。

2、创建用于Wrap KeyMaster Key主密钥

本章节代码见本文开头Github的:src/main/java/com/example/cloudhsm/WrapDemoStep1GenerateMasterKey.java。

首先创建主密钥。在本文的样例代码中,已经为主密钥设置了对应的属性,主密钥本身不可导出,不可用于加密和解密,只能用于做Wrap导出时候的封装使用。

运行刚才已经构建好的Jar包,记得提前通过环境变量设置访问CloudHSM的用户名和密码。

mvn clean package
export HSM_USER=user01
export HSM_PASSWORD=.......
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

输入密码后,登录完成。执行如下命令检索刚才创建的label是 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
  }
}

这里可以看到搜索结果,label标签是new-master-key的密钥对应的key-reference0x0000000000000ca5,记录下来并代入下边的命令,查看详细属性。

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 KeyWrap导出

本章节代码见本文开头Github的:src/main/java/com/example/cloudhsm/WrapDemoStep2GenerateDataKeyAndWrap.java。

继续前边的操作,在已经设置要CloudHSM用户名和密码的环境变量的情况下,运行如下命令:

mvn clean package
export HSM_USER=user01
export HSM_PASSWORD=.......
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解密成功。

二、使用非对称密钥和Unwrap机制实现私钥迁移到CloudHSM

1、私钥迁移背景

假设AWS云之外的受保护的位置有一个Private Key即被迁移的私钥,这个Private Key当前以明文形式存在,不过这个环境受到高度保护,明文是不允许离开这个环境的。现在,业务要求将这个Private Key从当前位置导入到海外的AWS CloudHSM加密机内部。由于受到明文不准离开保护范围的制约,需要有一种加密导入/导出机制来完成这个私钥迁移。

为此我们设计如下一种策略:

  • 在AWS海外的CloudHSM生成一个非对称密钥叫做migration key,将private key私钥保存在CloudHSM加密机内,将public key公钥导出;
  • 将上一步migration key的public key传输到AWS云之外受保护的环境内,用这个public key对被迁移的private key做加密,获得private key的密文,并将这个密文放到海外AWS云上可操作CloudHSM加密机的环境内(管理用虚拟机上)
  • 在CloudHSM上执行Key unwrap操作,使用migration key的private key解密,将被迁移的key的密文解密,获得被迁移的private key明文,此时明文仅保存在CloudHSM加密机内,加密机外没有明文存在
  • 上一步过程中,对这个private key设置属性,包括CloudHSM的禁止导出属性等,用于提高防护级别

现在进入代码示例。

2、在CloudHSM内生成Migration Key非对称密钥

本章节代码见本文开头Github的:src/main/java/com/example/cloudhsm/MigrateProtectKeyStep1.java

执行如下命令创建Migration Key。这里创建非对称密钥用的是RSA4096。

mvn clean package
export HSM_USER=user01
export HSM_PASSWORD=1qazxsw2
java -jar target/migrate-protect-key-step1-1.0-SNAPSHOT.jar

创建完毕返回如下:

使用用户: user01 连接到CloudHSM...
RSA-4096 密钥对创建成功!
密钥标签: migration-key
公钥算法: RSA
公钥已保存: ../openssl-key/migration-key-public.pem

此代码会在CloudHSM内生成非对称密钥,其中Private Key明文会保存在密码机内,Public Key会在保存到本机的 ../openssl-keys/ 目录下,文件名是 migration-key-public.pem 文件。

接下来使用CloudHSM CLI查看CloudHSM内的密钥。

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

输入密码完成登录,执行如下命令查看刚导入的密钥详细属性。

key list --filter attr.label=migration-key-public --verbose
key list --filter attr.label=migration-key-private --verbose

查看密钥返回结果如下:

{
  "error_code": 0,
  "data": {
    "matched_keys": [
      {
        "key-reference": "0x0000000000001689",
        "attributes": {
          "label": "migration-key-private"
        }
      },
      {
        "key-reference": "0x0000000000002fdd",
        "attributes": {
          "label": "TestKeyForPlainTextExport"
        }
      },
      {
        "key-reference": "0x00000000000032cb",
        "attributes": {
          "label": "migration-key-public"
        }
      }
    ],
    "total_key_count": 3,
    "returned_key_count": 3
  }
}

由此生成migration key完成。

3、在AWS云外对被迁移的Private Key的明文做加密

本章节代码见本文开头Github的:src/main/java/com/example/cloudhsm/MigrateProtectKeyStep2.java

现在我们使用一段Java代码模拟在AWS云外对被迁移的Private Key的明文做加密。注意,以下命令模拟的是AWS云外的操作,因此是不需要连接CloudHSM的。即便不使用Java代码,也可以使用其他加密库完成。这里使用的加密算法是RSA-OAEP-SHA512。

mvn clean package
java -jar target/migrate-protect-key-step2-1.0-SNAPSHOT.jar

返回如下:

步骤1: 转换 EC 私钥为 PKCS8 DER 格式...
PKCS8 DER 文件: ../openssl-key/ec_private_key_pkcs8.der
文件大小: 185 字节

步骤2: 使用 RSA-OAEP-SHA512 加密 PKCS8 密钥...
公钥文件: ../openssl-key/migration-key-public.pem

加密成功!
加密文件: ../openssl-key/ec_private_key.pem-encrypted
文件大小: 512 字节

由此在本地加密了被迁移的Private Key,加密后的密文保存在 ../openssl-key/ec_private_key.pem-encrypted 文件中。

4、将被迁移的Private Key的密文通过unwrap导入到CloudHSM

本章节代码见本文开头Github的:src/main/java/com/example/cloudhsm/MigrateProtectKeyStep3.java

现在调用CloudHSM,将刚才AWS云外加密好的密文导入到CloudHSM内做unwrap,从而解密出这个密钥的明文。运行如下命令:

mvn clean package
export HSM_USER=user01
export HSM_PASSWORD=1qazxsw2
java -jar target/migrate-protect-key-step3-1.0-SNAPSHOT.jar

返回如下:

用用户: user01 连接到CloudHSM...
加密文件大小: 512 字节
找到 migration-key-private
EC 私钥已成功 unwrap 导入到 CloudHSM
密钥标签: imported-ec-key
密钥算法: EC

导入成功。此时登录CloudHSM CLI查看密钥:

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

输入密码后登录成功,通过密钥标签查看密钥属性。在导入时候使用的标签是imported-ec-key

key list --filter attr.label=imported-ec-key --verbose

返回结果可看到导入密钥成功。

 "error_code": 0,
  "data": {
    "matched_keys": [
      {
        "key-reference": "0x000000000000124b",
        "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": "ec",
          "label": "imported-ec-key",
          "id": "0x",
          "check-value": "0xd13f67",
          "class": "private-key",
          "encrypt": false,
          "decrypt": true,
          "token": true,
          "always-sensitive": false,
          "derive": false,
          "destroyable": true,
          "extractable": false,
          "local": false,
          "modifiable": true,
          "never-extractable": false,
          "private": true,
          "sensitive": true,
          "sign": true,
          "trusted": false,
          "unwrap": true,
          "verify": false,
          "wrap": false,
          "wrap-with-trusted": false,
          "key-length-bytes": 185,
          "ec-point": "0x0461048618eb4af2f42cb3ad76ccde9c8ff0d929bce69077e29b3c2cd58fbdb2d16e6124bbfe75f5308e4ada71225f03194398b95d3644f5d68da1082fb1bb6dd7593d67ceae88d2bd2d777ba258fb9c3e0b336547264713a31640bda9bf2ba100f96c",
          "curve": "secp384r1"
        }
      }
    ],
    "total_key_count": 1,
    "returned_key_count": 1
  }
}

三、CloudHSM最佳实践之加密算法和SDK选择

1SDK选择

在之前的章节中,我们以Java为例展示了CloudHSM基本操作。除了Java的JCE Provider之外,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、关于PQCPost Quantum Cryptography)后量子密码算法支持情况

目前CloudHSM不支持PQC算法,不管采用PKCS #11还是JCE Provider SDK,均不支持PQC算法。

如果要采用全套AWS的PKI体系的话,使用Private CA服务支持ML-DSA算法,满足FIPS204的合规要求。具体参考网址如下:

https://aws.amazon.com/cn/about-aws/whats-new/2025/11/aws-private-ca-post-quantum-digital-certificates/

如果不希望采用全套AWS PKI,而只是将部分功能以API服务化,那么可选择KMS服务。KMS(Key Management Service)是一项密钥管理服务,提供完全API化的加解密和密钥管理能力,这个服务支持PQC算法:

  • ML-DSA (Module-Lattice-Based Digital Signature Algorithm): AWS KMS已支持ML-DSA44, ML-DSA65, ML-DSA_87三种规格
  • ML-KEM: AWS Transfer Family已支持ML-KEM量子抗性密钥交换

四、CloudHSM最佳实践之海量IoT设备场景的加密体系的设计

本章节探讨针对较大数据量的IoT物联网设备的场景下,如何结合CloudHSM的能力设计加解密机制。

1CloudHSM密钥类型和密钥存储的限制

CloudHSM的密钥有两种类型:永久类型,以及会话密钥(Session Key)。永久类型会一直在CloudHSM内,无论当前是否使用,都会占用密钥槽位。而会话密钥(Session Key)在使用包括加密、解密等操作时会占用密钥槽位,会话结束后密钥将被释放不会保存在CloudHSM内,会话结束后不占用槽位。两种类型的密钥共用统一的数量限制。以2025年可用机型的hsm2m.medium型号集群为例,支持最大存储16666个Key,但如果是非对称密钥最大支持3333个。这个密钥数量,同时包含永久密钥和临时会话密钥(Session Key),他们总计数量上限是16666个,或3333(使用非对称密钥时)。对于需要使用大量密钥的场景,例如为每个IoT设备分配单独的加解密密钥时,总的密钥数量会远超过CloudHSM单集群存储密钥上限,此时虽然添加更多的CloudHSM集群可以实现密钥总数的增加,但是同时由于需要创建更多的HSM节点以及管理更多的CloudHSM集群,这就带来了额外的成本以及运维的开销。因此,通常还可考虑以下两种密钥体系设计来应对使用海量密钥的场景。

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 封装密钥这个概念呢,这是因为信封加密的机制与刚才的派生算法不同。派生算法KDF就是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和设备的对应关系,否则都是纯随机的就对不上了。当需要加解密业务数据的时候,应用程序将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明文泄漏风险。

(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. 返回密文给应用                                                 │
└─────────────────────────────────────────────────────────────────┘

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
设备密钥是否明文在应用端 不建议,明文在应用端有风险,应尽量确保备密钥明文不离开CloudHSM 不建议,如果应用端自己维护Data Key明文的生命周期,存在较高风险,建议完全由CloudHSM负责Data Key管理和封装
是否涉及Wrapping Key 当需要将密钥导出CloudHSM时涉及 当需要将密钥导入或导出CloudHSM时涉及
更换设备密钥是否方便 不方面,派生算法绑定了唯一标识符,必须更换标识符或Master key才能生成新的派生密钥 方便更换,Data Key可轮换,不影响整个加解密逻辑

从以上二者的逻辑可以看出,两者各有优缺点,主密钥的实现相对简单,本质上是一次加密过程;信封加密的实现相对复杂,本身是两次加密,并且Data的密文还需要额外的存储设备(如S3/DynamoDB等)占用成本,但安全性获得显著提升。

五、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文档。

说明:除上述最佳实践外,更多内容可以参考本文档中所列内容。

5、使用mTLS提高访问安全性(可选)

CloudHSM支持在所有客户端请求使用mTLS双向证书认证,包括CloudHSM CLI命令行客户端、JCE Provider SDK等。前文为了快速上手,并未开启这个配置。这里介绍如何开启双向证书验证。

(1) 生成Root证书(带有密码保护)

前文的操作中,我们在本地生成了CA证书,然后上传到CloudHSM进行激活。激活后才可以开始在CloudHSM上创建用户、生成证书。注意,这个CA证书和私钥,是不能用于mTLS验证的。为了安全起见,我们要独立生成一对专用的mTLS证书,仅用于mTLS连接。

在本地开发环境,MacOS或者Linux系统上有最新OpenSSL库的环境上,运行如下命令生成证书。当询问密码时候,为证书设置密码。

openssl genrsa -out mtls-ca.key -aes256 4096

由此获得mtls-ca.key文件。

接下来生成自签名 Root CA 证书(有效期25年)。

openssl req -new -x509 -days 9130 -key mtls-ca.key -out mtls-ca.crt

在回答一系列问题后,获得CA证书mtls-ca.crt

设置文件权限以便于后文使用。

chmod 600 mtls-ca.key
chmod 644 mtls-ca.crt

设置权限完毕。

(2) 生成客户端证书(无密码保护)

生成客户端密钥。这一步不会询问是否设置密码。

openssl genrsa -out mtls-client.key 4096

然后在当前目录下获得了mtls-client.key文件。

接下来继续生成CSR。

openssl req -new -key mtls-client.key -out mtls-client.csr

在运行以上命令时候,需要Common Name字段即CN(通常填写server FQDN or YOUR name),必须与上一步创建的CA证书的Common Name不一样。否则后续证书无法完成mTLS需要的校验。

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]:bitipcman
Organizational Unit Name (eg, section) []:DEV
Common Name (e.g. server FQDN or YOUR name) []:blog.bitipcman.com
Email Address []:do-not-reply@bitipcman.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

为方便起见,还可以用参数传递,而不用一项项输入。

openssl req -new -key mtls-client.key -out mtls-client.csr \
  -subj "/C=CN/ST=BEIJING/L=BJ/O=bitipcman/OU=DEV/CN=blog.bitipcman.com/emailAddress=do-not-reply@bitipcman.com"

创建CSR完毕。当前目录下获得mtls-client.csr文件。

接下来CA签署。输入如下命令。

openssl x509 -req -days 3650 -in mtls-client.csr \
  -CA mtls-ca.crt -CAkey mtls-ca.key -CAcreateserial \
  -out mtls-client.crt

在CA签署的时候,会要求输入上一步创建CA证书时候设置的密码。输入后,签署成功。

Certificate request self-signature ok
subject=C = CN, ST = BEIJING, L = BJ, O = bitipcman, OU = DEV, CN = blog.bitipcman.com, emailAddress = do-not-reply@bitipcman.com
Enter pass phrase for mtls-ca.key:

由此获得了mtls-client.crt文件。

拼接设置证书链。

# 创建客户端证书链(客户端证书 + Root CA)
cat mtls-client.crt mtls-ca.crt > mtls-client.pem

执行如下命令验证证书链:

openssl verify -CAfile mtls-ca.crt mtls-client.crt

返回OK标识正常。

设置文件权限:

chmod 600 mtls-client.key
chmod 644 mtls-client.crt mtls-client.pem

接下来需要妥善的、安全的保管有关证书密钥。其中mtls-ca.crt需要注册到CloudHSM作为Trust Anchor,而客户端SDK需要使用mtls-client.pem标识请求方自己的身份。

(3) CloudHSM上将CA注册为Trust Anchor

首先使用CloudHSM CLI,以没有mTLS的方式正常登陆到CloudHSM。

/opt/cloudhsm/bin/cloudhsm-cli interactive

然后以admin身份登录。注意mTLS管理设置必须使用admin,普通crypto user身份只能用于密钥加解密。

login --username admin --role admin

登陆成功后,执行如下命令。执行操作前,需要确保CA文件路径正确。

cluster mtls register-trust-anchor --path /home/ubuntu/environment/cloudhsm/mTLS/mtls-ca.crt

执行成功后,返回结果如下:

{
  "error_code": 0,
  "data": {
    "trust_anchor": {
      "certificate-reference": "0x01",
      "certificate": "-----BEGIN CERTIFICATE-----\nxxxxxxxxxxx
      xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
      xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
      xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
      xxxxxxxx=\n-----END CERTIFICATE-----\n",
      "cluster-coverage": "full"
    }
  }
}

注意这里的trust_anchor的ID是0x01。执行如下命令确认下注册成功。

cluster mtls list-trust-anchors

现在可以执行quit退出CloudHSM CLI。

注意此时CloudHSM同时允许非mTLS连接和mTLS连接,我们要使用mTLS方式连接测试认证通过后,才会开启强制mTLS的参数。

(4) CloudHSM CLI上使用双向证书

将客户端证书复制到要执行CloudHSM CLI的环境的指定路径下,并使用chown命令设置为当前操作用户为文件owner,然后设置路径。

sudo cp mtls-client.pem /opt/cloudhsm/etc/
sudo cp mtls-client.key /opt/cloudhsm/etc/
sudo chown ubuntu:ubuntu /opt/cloudhsm/etc/mtls-client.key
sudo chown ubuntu:ubuntu /opt/cloudhsm/etc/mtls-client.pem
chmod 600 /opt/cloudhsm/etc/mtls-client.key
chmod 644 /opt/cloudhsm/etc/mtls-client.pem

为CloudHSM CLI配置mTLS连接到其配置文件:

sudo /opt/cloudhsm/bin/configure-cli \
  --client-cert-hsm-tls-file /opt/cloudhsm/etc/mtls-client.pem \
  --client-key-hsm-tls-file /opt/cloudhsm/etc/mtls-client.key

配置成功的话,不会有任何输出(没有报错就是成功)。

现在做下mTLS的连接测试。

/opt/cloudhsm/bin/cloudhsm-cli interactive

用一般操作者身份登陆:

login --username user01 --role crypto-user

输入密码,登陆成功。

(5) JCE Provider SDK上使用双向证书

上一步完成了对CloudHSM CLI配置mTLS的过程。接下来配置JCE Provider SDK。JCE Provider SDK和CloudHSM CLI一样,也有一个配置文件,我们需要修改这个配置文件,加入mTLS的证书设置。

将证书复制到正确的路径下(与CloudHSM CLI的路径和权限要求一致)。

sudo cp mtls-client.pem /opt/cloudhsm/etc/
sudo cp mtls-client.key /opt/cloudhsm/etc/
sudo chown ubuntu:ubuntu /opt/cloudhsm/etc/mtls-client.key
sudo chown ubuntu:ubuntu /opt/cloudhsm/etc/mtls-client.pem
chmod 600 /opt/cloudhsm/etc/mtls-client.key
chmod 644 /opt/cloudhsm/etc/mtls-client.pem

执行如下命令完成配置。

sudo /opt/cloudhsm/bin/configure-jce \
  --client-cert-hsm-tls-file /opt/cloudhsm/etc/mtls-client.pem \
  --client-key-hsm-tls-file /opt/cloudhsm/etc/mtls-client.key

配置成功的话,不会有任何输出(没有报错就是成功)。

现在运行Java程序,测试访问成功。

(6) 开启强制要求mTLS认证

当CloudHSM CLI和Java SDK都使用mTLS连接测试成功后,可以打开强制使用mTLS的开关了。首先用CloudHSM CLI连接上去。注意这里必须是已经使用mTLS的方式连接,才可以打开强制mTLS的配置。

/opt/cloudhsm/bin/cloudhsm-cli interactive

使用管理员身份登陆:

login --username admin --role admin

执行命令:

cluster mtls set-enforcement --level cluster

返回如下信息标识成功:

{
  "error_code": 0,
  "data": {
    "message": "Mtls enforcement level set to Cluster successfully"
  }
}

从这一刻起,非mTLS的会话已经无法登陆了,并且如果此时有非mTLS的长连接一直存在,也会被终止掉,只有后续mTLS能连接。

(7) 注意事项

在hsm2m.medium机型上,最多支持同时注册2个trust anchors。如果需要替换,未来可以使用如下命令删除:

cluster mtls deregister-trust-anchor --certificate-reference 0x01

即可删除已经设置的CA。

六、参考文档

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

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

本篇作者

刘辛酉

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

王旭东

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


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

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