AWS Builder Center

Bulk Migrate Asymmetric Keys from AWS CloudHSM to AWS KMS

Need to move hundreds or thousands of asymmetric keys from AWS CloudHSM to AWS KMS? AWS documents how to migrate a single key, but offers no systematic way to do it at scale. This guide introduces an open-source toolkit of scripts that discovers, batches, migrates, and verifies RSA and ECC keys in bulk, while ensuring private key material never leaves the HSM in plaintext. The scripts have already been used to migrate more than 3,000 keys.

Background

The Problem

AWS CloudHSM and AWS KMS solve different problems. Teams often start on CloudHSM for full control over a dedicated hardware security module, then later decide to move to KMS for its lower operational overhead, native AWS service integration, and simpler key management.
The challenge is the migration itself:
  • The official AWS Security Blog walks through migrating a single asymmetric key, with many manual openssl and cloudhsm-cli steps.
  • There is no documented approach for migrating keys in bulk.
  • Doing thousands of keys by hand is slow, error-prone, and hard to audit.
  • The private key material must never be exposed in plaintext during the move.

The Solution

cloudhsm-to-kms-keys-migration  is a set of shell and Python scripts that automate the full bulk-migration lifecycle:
  • Discover the keys in your CloudHSM cluster and export them to JSON.
  • Analyze and batch them into manageable groups (50 keys per file) for safer, resumable processing.
  • Migrate RSA and ECC keys into KMS using the secure wrapped-import workflow.
  • Verify every migrated key with a sign/verify test against its original public key.
  • Record every result to timestamped success and failure files for a clear audit trail.

Why This Matters

  • Private keys stay protected: Key material is wrapped (encrypted) inside the HSM and imported into KMS as EXTERNAL key material. The plaintext private key never leaves CloudHSM.
  • Scales to thousands of keys: Batching, logging, and skip-on-mismatch logic make large migrations repeatable and resumable.
  • Built-in verification: Each key is validated with a real sign/verify round trip, so you know the migration actually worked, not just that it ran.
  • Full audit trail: Timestamped logs and per-batch success/failure files document exactly what happened.

How It Works

KMS lets you import key material generated outside of KMS. The migration uses this capability so that the private key is moved without ever being exposed:
1
2
3
4
5
6
7
8
9
10
CloudHSM (private key) AWS KMS
| |
| 1. Create empty KMS key (EXTERNAL) |
|<-----------------------------------------|
| 2. Get wrapping public key + token |
|<-----------------------------------------|
| 3. Import wrapping key into CloudHSM |
| 4. Wrap private key inside the HSM |
| 5. Import wrapped material ------------->|
| 6. Sign/verify test ------------------->|
The private key is encrypted (wrapped) inside the HSM using an RSA wrapping key that KMS provides. Only the wrapped (encrypted) blob is sent to KMS, where it is unwrapped internally. At no point does the plaintext private key cross the network.
The scripts perform these seven steps automatically for each key in a batch:
  1. Create an empty KMS key with --origin EXTERNAL.
  2. Download the wrapping public key and import token from KMS.
  3. Import the wrapping key into CloudHSM.
  4. Wrap the private key inside CloudHSM using the wrapping key.
  5. Import the wrapped key material into KMS.
  6. Validate the imported key with a sign/verify test using the original public key.
  7. Record the result to a timestamped success or failure file.

Supported Key Types

TypeVariantsMatched by attribute
ECsecp256k1, prime256v1 / secp256r1ec-point
RSA2048, 3072, 4096-bitmodulus
The scripts pair each private key with its corresponding public key using these attributes. Keys without a matching public key are skipped automatically and logged.

Prerequisites

Before you begin, ensure you have:
  1. A Linux or Windows machine. CloudHSM-CLI is not available on macOS.
  2. An activated CloudHSM cluster with a crypto-user configured.
  3. CloudHSM-CLI installed.
  4. The AWS CLI installed and authenticated with permissions for KMS (kms:CreateKey, kms:GetParametersForImport, kms:ImportKeyMaterial, kms:Sign).
  5. openssl, jq, and python3 available on your path.
Set the CloudHSM environment variables so the scripts can authenticate to the cluster:
1
2
3
export PATH=$PATH:/opt/cloudhsm/bin
export CLOUDHSM_ROLE=crypto-user
export CLOUDHSM_PIN="<username>:<password>"
Note: Replace the CLOUDHSM_PIN value with your own crypto-user credentials.

Step 1: Clone the Repository

The scripts live under the security/cloudhsm-to-kms-keys-migration directory of the aws-samples/sample-apj-sup-sa repository:
1
2
git clone https://github.com/aws-samples/sample-apj-sup-sa.git
cd sample-apj-sup-sa/security/cloudhsm-to-kms-keys-migration

Step 2: Discover Your Keys

List the keys in your CloudHSM cluster and export them to JSON. The listing scripts accept an optional regex pattern so you can target a subset of keys.
1
2
3
# List private and public keys (optionally filtered by a label pattern)
./list-cloudhsm-private-keys.sh "" private_keys.json
./list-cloudhsm-public-keys.sh "" public_keys.json
To get a quick count and breakdown by key type before migrating:
1
./count-cloudhsm-keys-by-regex.sh ".*"

Step 3: Analyze and Split into Batches

Review the distribution of key types in your export:
1
python3 count-key-types-from-json.py private_keys.json
Split the keys into batches of 50 per file. Smaller batches make large migrations safer and easier to resume if something needs attention:
1
python3 split-keys-in-json.py
This produces batch files under split_keys_output/, separated by key type:
1
2
3
4
5
split_keys_output/
├── ec_keys_part_001.json
├── ec_keys_part_002.json
├── rsa_keys_part_001.json
└── rsa_keys_part_002.json

Step 4: Migrate the Keys

Run the appropriate migration script for each batch. EC and RSA keys use separate scripts:
1
2
3
4
5
# Migrate a batch of EC keys
./migrate-cloudhsm-ec-keys-to-kms.sh split_keys_output/ec_keys_part_001.json

# Migrate a batch of RSA keys
./migrate-cloudhsm-rsa-keys-to-kms.sh split_keys_output/rsa_keys_part_001.json
Each script runs the full seven-step wrapped-import workflow for every key in the batch and writes a sign/verify-validated result for each one.
Tip: Console output is intentionally minimal. Full per-key detail is written to a timestamped log_YYYYMMDD_HHMM.log file, which is the place to look if you need to investigate a failure.

Step 5: Review the Results

After each run, the scripts write timestamped result files so you can confirm exactly which keys migrated successfully and which need attention:
1
2
3
result_success_*_YYYYMMDD_HHMM.txt
result_failed_*_YYYYMMDD_HHMM.txt
log_YYYYMMDD_HHMM.log
Because every migrated key is validated with a sign/verify test against its original public key, a key that appears in the success file has been cryptographically confirmed to work in KMS.

Step 6: Clean Up (Optional)

Each migration imports a temporary RSA wrapping key into CloudHSM. Over a large migration these accumulate under the kms_wrapping_key_for_* prefix and can be removed afterward.
Important: Always preview a deletion pattern with the count script before running any delete. This shows you exactly which keys match so you do not remove anything unintended.
1
2
3
4
5
# Preview what matches first
./count-cloudhsm-keys-by-regex.sh "kms_wrapping_key_for_.*"

# Then delete if the count looks correct
./delete-cloudhsm-keys-by-regex.sh "kms_wrapping_key_for_.*"

Trying It Out Safely

If you want to test the workflow before touching real keys, the repository includes generate-cloudhsm-test-keys.sh to create sample RSA and EC keys in your cluster. Run a full discover → split → migrate → verify cycle against these test keys first.
Note: Cryptographic key migration is high-risk. Always run a complete dry run in a non-production cluster before migrating real keys, and confirm the sign/verify results for every key. The repository is shared as-is for educational use; review the scripts and validate them against your own environment and security requirements before relying on them.

Summary

Migrating asymmetric keys from CloudHSM to KMS one at a time does not scale. This toolkit automates the entire bulk-migration lifecycle:
  • Discover keys from CloudHSM and export them to JSON
  • Analyze and batch them into groups of 50 for safe, resumable processing
  • Migrate RSA and ECC keys using the secure wrapped-import workflow, so private key material never leaves the HSM in plaintext
  • Verify every key with a sign/verify test against its original public key
  • Audit the outcome through timestamped logs and success/failure files
The scripts have been used to migrate more than 3,000 keys, turning a tedious and risky manual process into a repeatable, verifiable one. The full source is available on GitHub.

References

Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article