Skip to content

Key Management

All Kryptonite for Kafka modules support different options to source the required key material. You can define this with the key_source configuration parameter. The right choice primarily depends on your security posture and preferred operational model.


Overview

key_source=CONFIG           → plain keysets inline in config
key_source=CONFIG_ENCRYPTED → KEK-encrypted keysets inline in config
key_source=KMS              → plain keysets in cloud secret manager
key_source=KMS_ENCRYPTED    → KEK-encrypted keysets in cloud secret manager
key_source=NONE             → no keysets required (KMS-based envelope encryption only)

Plain keysets in configuration

The simplest and least secure mode. Plain keysets are embedded directly in the cipher_data_keys configuration parameter.

When to use?

Development & testing, or demos. Also, it could be a legitimate choice for environments where configuration is already protected in other ways (e.g. Kubernetes Secrets, Vault-injected env vars).

Risk

Key material might become visible or accidentally exposed. For example, this could happen in Kafka Connect environments where connector configurations might be accessible via the Kafka Connect REST API. Avoid this configuration for any serious production workload without extra access controls in place.

Plain keyset example

  {
    "identifier": "my-key",
    "material": {
      "primaryKeyId": 10000,
      "key": [
        {
          "keyData": {
            "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey",
            "value": "<BASE64_ENCODED_KEY_HERE>",
            "keyMaterialType": "SYMMETRIC"
          },
          "status": "ENABLED",
          "keyId": 10000,
          "outputPrefixType": "TINK"
        }
      ]
    }
  }

Tip

Generate plain keysets with the Keyset Tool.

Tip

For Kafka Connect, it's recommended to use the file config provider. Find a concrete example to store keysets in an external properties file instead of exposing it directly in the connector's configuration here.


Encrypted keysets in configuration

Encrypted keysets give you moderate security while still being able to directly reference keys inside the configuration. A cloud KMS key encryption key (KEK) is used to decrypt the keysets at runtime. The plain keysets are never stored anywhere.

When to use?

You'd like to ship encrypted keysets as part of the configuration but do not want to store keysets externally in a cloud secret manager.

Requires: key_source=CONFIG_ENCRYPTED, depends on kek_type, kek_uri, kek_config, and cipher_data_keys.

Encrypted keyset example

Note, that the material field takes the encrypted keyset form:

  {
    "identifier": "my-key",
    "material": {
      "encryptedKeyset": "<ENCRYPTED_AND_BASE64_ENCODED_KEYSET_HERE>",
      "keysetInfo": {
        "primaryKeyId": 10000,
        "keyInfo": [
          {
            "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey",
            "status": "ENABLED",
            "keyId": 10000,
            "outputPrefixType": "TINK"
          }
        ]
      }
    }
  }

Tip

Generate encrypted keysets in FULL format with the Keyset Tool.


Plain keysets in cloud secret manager

Store plain keysets as secrets in a cloud secret manager to get reasonable key material protection. Modules either fetch them lazily on demand or load them eagerly during initialization with the configured cloud provider credentials.

When to use?

You already manage other types of application secrets centrally via any supported cloud provider KMS (GCP Secret Manager, AWS Secrets Manager, Azure Key Vault) and you want keysets to be treated the same.

Requires: key_source=KMS, depends on kms_type and kms_config. When using a cloud secret manager, cipher_data_keys can be deliberately set to the empty JSON array [].

Plain keyset example

For cloud secret manager usage, plain keysets must be generated in RAW format with the keyset tool.

Note

The wrapping structure with identifierand material fields isn't present due to format RAW. In this case, the keysets are identified directly by the secret name in the cloud secret manager.

{
  "primaryKeyId": 10000,
  "key": [
    {
      "keyData": {
        "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey",
        "value": "<BASE64_ENCODED_KEY_HERE>",
        "keyMaterialType": "SYMMETRIC"
      },
      "status": "ENABLED",
      "keyId": 10000,
      "outputPrefixType": "TINK"
    }
  ]
}

Tip

Generate plain keysets in RAW format with the Keyset Tool.


Encrypted keysets in cloud secret manager

The most secure mode. This setup stores keysets encrypted in a cloud secret manager, and a separate cloud KMS key encryption key decrypts them at runtime.

When to use?

Production environments requiring strong keyset protection. An attacker who gains access to the secret manager secrets still cannot make use of any of the keysets without having access to the KEK in the KMS.

Requires: key_source=KMS_ENCRYPTED, depends on kms_type,kms_config, kek_type, kek_uri, and kek_config. When using a cloud secret manager cipher_data_keys can be deliberately set to the empty JSON array [].

Encrypted keyset example

For cloud secret manager usage, encrypted keysets must be generated in RAW format with the keyset tool.

Note

The wrapping structure with identifierand material fields isn't present due to format RAW. In this case, the keysets are identified directly by the secret name in the cloud secret manager.

{
  "encryptedKeyset": "<ENCRYPTED_AND_BASE64_ENCODED_KEYSET_HERE>",
  "keysetInfo": {
    "primaryKeyId": 10000,
    "keyInfo": [
      {
        "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey",
        "status": "ENABLED",
        "keyId": 10000,
        "outputPrefixType": "TINK"
      }
    ]
  }
}

Tip

Generate encrypted keysets in RAW format with the Keyset Tool.


No keysets

When using key_source=NONE, no Tink keysets are loaded or required. This only works if the configured cipher_algorithm is TINK/AES_GCM_ENVELOPE_KMS which is always backed by a cloud KMS key encryption key (KEK) used to wrap random, ephemeral data encryption keys (DEK). This is the right choice when you must rely only on envelope encryption and do not want to work directly with Tink keysets at all.

When to use?

You are using TINK/AES_GCM_ENVELOPE_KMS for all fields, hence no Tink keyset management is required. The cipher_data_keys setting can be left empty [] in this case.

Required setup

key_source=NONE requires envelope_kek_configs to contain at least one valid KEK entry that is configured as the default envelope_kek_identifier. Startup fails with a clear error if envelope encryption is not properly configured. You also have to provide a valid edek_store_configuration.

Incompatibility Note

Cipher algorithms TINK/AES_GCM, TINK/AES_GCM_SIV, TINK/AES_GCM_ENVELOPE_KEYSET, or CUSTOM/MYSTO_FPE_FF3_1 are not compatible with this setting as they all require at least one Tink keyset.

Learn more about KMS-based envelope encryption.


Secret naming conventions

Depending on which cloud secret manager is configured to store plain or encrypted keysets, the following secret name prefixes must be considered to distinguish plain from encrypted keysets:

Type Mandatory Prefix Key Identifier Example Full Secret Name
Plain keysets k4k-tink-plain_ key_123 k4k-tink-plain_key_123
Encrypted keysets k4k-tink-encrypted_ key_XYZ k4k-tink-encrypted_key_XYZ

Full secret name: (k4k-tink-plain | k4k-tink-encrypted)_<key_identifier>

Type Mandatory Prefix Key Identifier Example Full Secret Name
Plain keysets k4k/tink_plain/ key_123 k4k/tink_plain/key_123
Encrypted keysets k4k/tink_encrypted/ key_XYZ k4k/tink_encrypted/key_XYZ

Full secret name: k4k/(tink_plain | tink_encrypted)/<key_identifier>

The secret name is the keyset identifier as is. No prefix is assumed. Azure supports separate vault instances to properly isolate plain from encrypted keysets. Do not mix and match plain and encrypted keysets in the same key vault!

Full secret name: <key_identifier>


Tink Keyset Format

All keyset material, regardless of the key source, must conform to Tink's plain or encrypted keyset specification.

Plain keyset examples

  • AES-GCM (probabilistic encryption)
{
  "primaryKeyId": 123456789,
  "key": [
    {
      "keyData": {
        "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey",
        "value": "<BASE64_ENCODED_KEY_HERE>",
        "keyMaterialType": "SYMMETRIC"
      },
      "status": "ENABLED",
      "keyId": 123456789,
      "outputPrefixType": "TINK"
    }
  ]
}
  • AES-GCM-SIV (deterministic encryption)
{
  "primaryKeyId": 123456789,
  "key": [
    {
      "keyData": {
        "typeUrl": "type.googleapis.com/google.crypto.tink.AesSivKey",
        "value": "<BASE64_ENCODED_KEY_HERE>",
        "keyMaterialType": "SYMMETRIC"
      },
      "status": "ENABLED",
      "keyId": 123456789,
      "outputPrefixType": "TINK"
    }
  ]
}
  • FPE FF3-1 (format-preserving encryption)
{
  "primaryKeyId": 2000001,
  "key": [
    {
      "keyData": {
        "typeUrl": "io.github.hpgrahsl.kryptonite/crypto.custom.mysto.fpe.FpeKey",
        "value": "<BASE64_ENCODED_KEY_HERE>",
        "keyMaterialType": "SYMMETRIC"
      },
      "status": "ENABLED",
      "keyId": 2000001,
      "outputPrefixType": "RAW"
    }
  ]
}

Note the custom typeUrl and outputPrefixType: RAW for FPE keysets.


Key Rotation with Keysets

Tink keysets natively support more than one key. The primaryKeyId determines which key is used for new encryptions. Older keys are supposed to remain in the keyset for decryption of existing ciphertexts.

  1. Generate a new keyset with more than one key (e.g., -n 2 with the Keyset Tool)
  2. One of the keys in the keyset must be the primary key, initially it's the first one
  3. Any new encryption operations use the currently designated primary key
  4. A different key in the keyset can be promoted to become the primary key at any time
  5. Once the primary key changes, old ciphertexts remain decryptable as long as the old primary key is still resolvable within the configured keyset.

See Keyset Tool for examples of generating multi-key keysets.


Key Rotation with Envelope Encryption

Envelope encryption introduces a two-level key hierarchy — a short-lived Data Encryption Key (DEK) that encrypts the actual field data, and a long-lived Key Encryption Key (KEK) that wraps the DEK. Rotation operates differently at each level, and the two envelope variants behave differently.

DEK Rotation: automatic and continuous

DEKs are ephemeral by design and rotate automatically without any manual intervention. A DEK session is considered expired when either of the following thresholds is reached:

  • dek_max_encryptions — upper limit for field encrypt operations performed with the current DEK (default: 100000)
  • dek_ttl_minutes — longest allowed age of the current DEK session in minutes (default: 720, i.e. 12 hours)

Whichever threshold is hit first triggers the creation of a new DEK on the next encrypt call. Old DEKs are not discarded. They remain resolvable on the decrypt path as long as their wrapped form is present in the EdekStore (KMS-based variant) or inline in the ciphertext bundle (keyset-based variant). Tune these settings to control how frequently new DEKs are generated.

Tip

Decrease dek_ttl_minutes or dek_max_encryptions to rotate DEKs more aggressively. Increase them to reduce the frequency of KMS wrap calls (KMS-based variant only) at the cost of longer DEK lifetimes.

KEK Rotation: on-demand and manual

KEK rotation is not automatic and must be triggered explicitly from the outside. The procedure differs between the two envelope variants:

The KEK is a Tink keyset. Rotation follows the standard Tink keyset rotation process: add a new key to the keyset, promote it to primary, and keep the old key for decryption of existing ciphertexts. Update the cipher_data_keys configuration accordingly. All newly created DEK sessions will be wrapped with the new primary key. Existing ciphertexts remain decryptable as long as the old key is still present in the keyset.

The KEK lives only in the cloud KMS and never leaves it. Rotation is performed directly in the cloud provider's KMS console or API. Create a new key version to be used for future DEK wrap/unwrap operations and keep the old version for existing data. No configuration change in Kryptonite is required as long as the kek_uri keeps referring to the KEK in question. The existing wrapped DEKs in the EdekStore remain intact and can still be unwrapped with the old key version as long as it stays enabled in the KMS.

Warning

Rotating the KEK does not automatically re-wrap existing DEKs. Any DEKs wrapped with the old KEK version remain valid as long as that version is not disabled or deleted in the KMS. Plan KEK rotation carefully and ensure old key versions are kept active long enough to cover all existing ciphertexts in your Kafka topics.