KMS
Protocol: JSON 1.1 (X-Amz-Target: TrentService.*)
Endpoint: POST http://localhost:4566/
Supported Actions
| Action | Description |
|---|---|
CreateKey |
Create a new KMS key |
GenerateRandom |
Generate random bytes |
GetPublicKey |
Get public key material for asymmetric keys |
DescribeKey |
Get key metadata |
ListKeys |
List all keys |
CreateGrant |
Create a grant for a KMS key |
ListGrants |
List grants for a KMS key |
ListRetirableGrants |
List grants retirable by a principal |
RevokeGrant |
Revoke (administratively delete) a grant |
RetireGrant |
Retire a grant (token- or key+grant-based) |
Encrypt |
Encrypt plaintext with a key |
Decrypt |
Decrypt ciphertext |
ReEncrypt |
Re-encrypt under a different key |
GenerateDataKey |
Generate a data key (plaintext + encrypted) |
GenerateDataKeyWithoutPlaintext |
Generate only the encrypted data key |
Sign |
Sign a message with an asymmetric key |
Verify |
Verify a signature |
GenerateMac |
Generate a MAC with an HMAC key |
VerifyMac |
Verify a MAC with an HMAC key |
CreateAlias |
Create a friendly name for a key |
UpdateAlias |
Repoint an alias at a different key |
DeleteAlias |
Remove an alias |
ListAliases |
List all aliases |
ScheduleKeyDeletion |
Mark a key for deletion |
CancelKeyDeletion |
Cancel pending deletion |
TagResource |
Tag a key |
UntagResource |
Remove tags |
ListResourceTags |
List tags |
GetKeyPolicy |
Get a key's resource policy |
PutKeyPolicy |
Update a key's resource policy |
ListKeyPolicies |
List a key's policy names (always the single default policy) |
UpdateKeyDescription |
Update a key's description |
GetKeyRotationStatus |
Check if automatic key rotation is enabled |
EnableKeyRotation |
Enable automatic key rotation (symmetric keys only) |
DisableKeyRotation |
Disable automatic key rotation |
EnableKey |
Enable a key |
DisableKey |
Disable a key |
RotateKeyOnDemand |
Rotate key material on demand (symmetric keys only) |
GetParametersForImport |
Get the wrapping key and import token for an EXTERNAL key |
ImportKeyMaterial |
Import key material into an EXTERNAL key |
DeleteImportedKeyMaterial |
Delete imported key material, returning the key to PendingImport |
Asymmetric Encryption
Encrypt, Decrypt, and ReEncrypt apply real RSAES-OAEP for RSA keys (RSA_2048, RSA_3072, RSA_4096) when EncryptionAlgorithm is RSAES_OAEP_SHA_1 or RSAES_OAEP_SHA_256. The ciphertext is raw RSA output of the modulus length, for example exactly 256 bytes for RSA_2048. A ciphertext produced locally with the public key from GetPublicKey decrypts the same way it does on real AWS, which makes the usual envelope pattern work. Only the encrypting side needs the public key. As on real AWS, asymmetric Decrypt requires KeyId, an EncryptionContext is rejected for asymmetric keys, and plaintext larger than the OAEP capacity of the key fails validation.
Symmetric keys keep the emulator's internal ciphertext format, described below, which is not compatible with ciphertexts from real AWS KMS.
Symmetric Ciphertext Envelope
Encrypt, Decrypt, ReEncrypt and GenerateDataKey protect SYMMETRIC_DEFAULT plaintext with
real AES-256-GCM, using a per-key data-encryption key ("backing key") that is generated when the
key is created, or is the material imported into an Origin=EXTERNAL key, and is never exposed by
any API. The blob is opaque bytes, base64-encoded in JSON exactly like real AWS KMS, but
internally it is a versioned envelope:
offset size field
0 4 magic "KMS3" (0x4B 0x4D 0x53 0x33)
4 1 format version (currently 1)
5 2 key id length (big-endian unsigned short)
7 N key id (UTF-8)
7+N 2 backing key id length (big-endian unsigned short)
9+N M backing key id (UTF-8)
9+N+M 12 AES-GCM IV (random, generated per call)
21+N+M ... AES-256-GCM ciphertext, followed by the 16-byte GCM tag
The key id lets Decrypt identify the key from the blob alone, matching AWS KMS, which does not
require KeyId on Decrypt for symmetric keys. The GCM additional authenticated data (AAD) is
every header byte up to and including the IV, plus the SHA-256 fingerprint of the canonicalized
EncryptionContext. Binding the header into the AAD means decrypting with the wrong key, the
wrong backing key version, or the wrong EncryptionContext, and any bit flip anywhere in the
blob (header, IV, ciphertext or tag), all fail GCM tag verification the same way and surface as
InvalidCiphertextException, never a plaintext.
RotateKeyOnDemand mints a new backing key and switches future encryptions to it, but keeps prior
backing keys in the key's state, so ciphertext encrypted before a rotation keeps decrypting after
it, matching real AWS KMS, which also retains prior backing keys.
Legacy blob formats (read-only)
Two older, unauthenticated formats are still accepted by Decrypt for backward compatibility with
ciphertext produced by earlier versions of this emulator, but are never produced by Encrypt
anymore:
kms:v2:<keyId>:<nonceHex>:<contextFingerprintHex>:<base64(plaintext)>kms:<keyId>:<base64(plaintext)>
Neither format used real key material: the payload was the plaintext itself, base64-encoded, so
anyone holding a v1 or v2 blob could read the plaintext directly, and a tampered blob still
"decrypted" to the original value. Any ciphertext already persisted in this shape (for example,
stored in a database from before this fix) keeps decrypting so existing data is not orphaned, but
new calls to Encrypt always produce the AES-GCM envelope described above. Keys created before
backing keys existed generate their backing key material lazily the first time they are used for
a cryptographic operation, and persist it from then on.
Imported Key Material
CreateKey accepts Origin=EXTERNAL, which creates a key with no key material in state
PendingImport. GetParametersForImport returns a real RSA public key and an import token;
material wrapped with that public key by a standard client is unwrapped by ImportKeyMaterial,
which puts the key in state Enabled. Wrapping material against the wrong key, or with a
different algorithm than the one requested, fails with InvalidCiphertextException the same way
it does on AWS.
Supported WrappingAlgorithm values are RSAES_OAEP_SHA_256 and RSAES_OAEP_SHA_1, over
WrappingKeySpec RSA_2048, RSA_3072 or RSA_4096. RSAES_PKCS1_V1_5 is rejected, matching
AWS, which stopped supporting it on October 10, 2023. The RSA_AES_KEY_WRAP_* variants exist for
material longer than an RSA modulus can hold and are also rejected: no importable key spec here
carries more than 64 bytes.
An import token is scoped to one key and spent by the import that uses it, and a second
GetParametersForImport call invalidates the token the previous one returned. Tokens expire 24
hours after they are issued.
ExpirationModel=KEY_MATERIAL_EXPIRES (the default) requires ValidTo, which must be in the
future and no more than 365 days out. Once ValidTo passes, the material is dropped and the key
returns to PendingImport, as does DeleteImportedKeyMaterial. Expiry is evaluated when the key
is next read rather than on a timer, which is not observable through the API. Deleting the
material of a key that is already in PendingDeletion leaves that state in place.
A SYMMETRIC_DEFAULT key with Origin=EXTERNAL encrypts under the imported material itself: it
is the backing key named in the ciphertext envelope described above, and no other material is
ever generated for the key. Deleting or expiring the material removes that backing key, so
ciphertext produced under it decrypts again only once the same material has been re-imported.
A key in PendingImport rejects cryptographic operations, EnableKey and DisableKey with
KMSInvalidStateException. CancelKeyDeletion on a key whose material was never imported, or was
deleted or expired while it was pending deletion, returns it to PendingImport rather than to a
usable state it could not serve. DeleteImportedKeyMaterial on a key that holds no material
succeeds, as it does on AWS. ImportKeyMaterial and DeleteImportedKeyMaterial return a
KeyMaterialId, derived from the key id and the material as AWS derives it. Re-importing
requires the same material the key was first given; different material is rejected with
IncorrectKeyMaterialException.
Automatic key rotation is rejected for keys with imported material, matching AWS: KMS does not own the material and cannot rotate it.
Deviations:
Origin=EXTERNALis supported only forSYMMETRIC_DEFAULTand theHMAC_*key specs, whose material is a raw byte string. Real AWS KMS also imports asymmetric material as a DER-encoded key pair; here an asymmetric spec withOrigin=EXTERNALis rejected atCreateKeywithUnsupportedOperationExceptionrather than creating a key that could never sign or decrypt.- Holding several imported key materials on one symmetric key, which real KMS uses for on-demand
rotation of imported material, is not emulated.
ImportType=NEW_KEY_MATERIALon a key that already has key material is rejected withUnsupportedOperationException, andListKeyRotationsis not implemented.
Grant Support Scope
Grant lifecycle operations (CreateGrant, ListGrants, ListRetirableGrants, RevokeGrant, RetireGrant) are supported. However, grant lifecycle support does not imply grant-based authorization enforcement on cryptographic operations (Encrypt, Decrypt, Sign, Verify, GenerateDataKey, etc.). Grants are stored and queryable but are not evaluated during crypto operations.
Configuration
| Variable | Default | Description |
|---|---|---|
FLOCI_SERVICES_KMS_ENABLED |
true |
Enable or disable the service |
Examples
export AWS_ENDPOINT_URL=http://localhost:4566
# Create a symmetric key
KEY_ID=$(aws kms create-key \
--description "My encryption key" \
--query KeyMetadata.KeyId --output text \
--endpoint-url $AWS_ENDPOINT_URL)
# Create an alias
aws kms create-alias \
--alias-name alias/my-key \
--target-key-id $KEY_ID \
--endpoint-url $AWS_ENDPOINT_URL
# Encrypt
CIPHER=$(aws kms encrypt \
--key-id alias/my-key \
--plaintext "Hello, World!" \
--query CiphertextBlob --output text \
--endpoint-url $AWS_ENDPOINT_URL)
# Decrypt
aws kms decrypt \
--ciphertext-blob $CIPHER \
--query Plaintext --output text \
--endpoint-url $AWS_ENDPOINT_URL | base64 --decode
# Generate a data key (envelope encryption)
aws kms generate-data-key \
--key-id alias/my-key \
--key-spec AES_256 \
--endpoint-url $AWS_ENDPOINT_URL
CreateKey also accepts a reserved creation-time tag key, floci:override-id, when tests need a deterministic KeyId. Floci uses the tag value as the created key id, strips the reserved tag from stored resource tags, and rejects attempts to add floci:* tags later via TagResource.