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
opensslandcloudhsm-clisteps. - 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
EXTERNALkey 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:
- Create an empty KMS key with
--origin EXTERNAL. - Download the wrapping public key and import token from KMS.
- Import the wrapping key into CloudHSM.
- Wrap the private key inside CloudHSM using the wrapping key.
- Import the wrapped key material into KMS.
- Validate the imported key with a sign/verify test using the original public key.
- Record the result to a timestamped success or failure file.
Supported Key Types
| Type | Variants | Matched by attribute |
|---|---|---|
| EC | secp256k1, prime256v1 / secp256r1 | ec-point |
| RSA | 2048, 3072, 4096-bit | modulus |
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:
- A Linux or Windows machine. CloudHSM-CLI is not available on macOS.
- An activated CloudHSM cluster with a crypto-user configured.
- CloudHSM-CLI installed.
- The AWS CLI installed and authenticated with permissions for KMS (
kms:CreateKey,kms:GetParametersForImport,kms:ImportKeyMaterial,kms:Sign). openssl,jq, andpython3available 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-migrationStep 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.jsonTo 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.jsonSplit 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.pyThis 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.jsonStep 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.jsonEach 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.logBecause 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
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article