Skip to content

Latest commit

 

History

History
492 lines (462 loc) · 20.7 KB

File metadata and controls

492 lines (462 loc) · 20.7 KB

Kryptonite for Kafka: Client-Side 🔒 Field-Level 🔓 Cryptography for Apache Kafka®

Donate

Disclaimer: This is an UNOFFICIAL community project!

HTTP API based on Quarkus Funqy

Kryptonite for Kafka provides a lightweight Quarkus-based standalone service exposing an HTTP API implemented with Funqy. It's primary use case is to allow client applications, written in languages / runtimes other than Java / JVM, to participate in end-to-end encryption scenarios build on top of Kafka. Learn how to build, configure, and run the HTTP API service to encrypt and decrypt payload fields based on simple usage examples.

Build

Either you build this project from sources via Maven or you can download pre-built, self-contained packages of the latest artefacts. Starting with Kryptonite for Kafka 0.4.0, the pre-built Funqy HTTP API service can be downloaded directly from the release pages.

Configuration

Before running the Quarkus application make sure to specify your individual configuration options in application.properties when you build form sources. In case you run the pre-built binaries, you have to properly override any of the mandatory/default settings when starting the application.

The default application.properties file which ships with the sources and pre-built binaries looks as follows:

#############################################
# Kryptonite for Kafka HTTP API configuration
#############################################
#
# MANDATORY config settings
#
cipher.data.keys=[]
cipher.data.key.identifier=
#
# OPTIONAL config settings with the following defaults
#
key.source=CONFIG
kms.type=NONE
kms.config={}
kek.type=NONE
kek.config={}
kek.uri=gcp-kms://
dynamic.key.id.prefix=__#
path.delimiter=.
field.mode=ELEMENT
cipher.algorithm=TINK/AES_GCM
#############################################

As can be seen from the comments, the first two properties (cipher.data.keys=[] and cipher.data.key.identifier=) are mandatory. All other properties are either optional or are specified with reasonable defaults. For a detailed explanation of each configuration option and possible / valid values take a closer look at the table below:

Name Description Type Default Valid Values
cipher.data.keys JSON array with plain or encrypted data key objects specifying the key identifiers together with key sets for encryption / decryption which are defined in Tink's key specification format. The contained keyset objects are mandatory if kms.type=NONE but the array may be left empty for e.g. kms.type=AZ_KV_SECRETS in order to resolve keysets from a remote KMS such as Azure Key Vault. NOTE: Irrespective of their origin, all plain or encrypted keysets (see the example values in the right column) are expected to be valid tink keyset descriptions in JSON format. JSON array
[]
JSON array either empty or holding N data key config objects each of which refers to a tink keyset in JSON format (see "material" field)
  • plain data key config example:
  • [
      {
        "identifier": "my-demo-secret-key-123",
        "material": {
          "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"
            }
          ]
        }
      }
    ]
        
  • encrypted data key config example:
  • [
      {
        "identifier": "my-demo-secret-key-123",
        "material": {
          "encryptedKeyset": "<ENCRYPTED_AND_BASE64_ENCODED_KEYSET_HERE>",
          "keysetInfo": {
            "primaryKeyId": 123456789,
            "keyInfo": [
              {
                "typeUrl": "type.googleapis.com/google.crypto.tink.AesSivKey",
                "status": "ENABLED",
                "keyId": 123456789,
                "outputPrefixType": "TINK"
              }
            ]
          }
        }
      }
    ]
        
cipher.data.key.identifier keyset identifier to be used as default data encryption keyset for all UDF calls which don't refer to a specific keyset identifier in the parameter list string
!no default!
non-empty string referring to an existing identifier for a keyset
key.source defines the nature and origin of the keysets:
  • plain data keysets in cipher_data_keys (key.source=CONFIG)
  • encrypted data keysets in cipher_data_keys (key.source=CONFIG_ENCRYPTED)
  • plain data keysets residing in a cloud/remote key management system (key.source=KMS)
  • encrypted data keysets residing in a cloud/remote key management system (key.source=KMS_ENCRYPTED)
When using the KMS options refer to the kms.type and kms.config settings. When using encrypted data keysets refer to the kek_type, kek.config and kek.uri settings as well.
string
CONFIG
CONFIG
CONFIG_ENCRYPTED
KMS
KMS_ENCRYPTED
kms.type defines if:
  • data keysets are read from the config directly kms.source=CONFIG | CONFIG_ENCRYPTED
  • data keysets are resolved from a remote/cloud key management system (currently only supports Azure Key Vault) kms.source=KMS | KMS_ENCRYPTED
string
NONE
NONE
AZ_KV_SECRETS
kms.config JSON object specifying KMS-specific client authentication settings. Currently only supports Azure Key Vault kms_type=AZ_KV_SECRETS JSON object
{}
JSON object defining the KMS-specific client authentication settings, e.g. for Azure Key Vault:
{
  "clientId": "...",
  "tenantId": "...",
  "clientSecret": "...",
  "keyVaultUrl": "..."
}
    
kek.type defines if KMS key encryption - currently only supports Google Cloud KMS - is used for encrypting data keysets and must be specified when using kms_source=CONFIG_ENCRYPTED | KMS_ENCRYPTED string
NONE
NONE
GCP
kek.config JSON object specifying KMS-specific client authentication settings (currently only supports Google Cloud KMS) kek_type=GCP JSON object
{}
JSON object specifying the KMS-specific client authentication settings, e.g. for Google Cloud KMS:
{
  "type": "service_account",
  "project_id": "...",
  "private_key_id": "...",
  "private_key": "-----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY-----\n",
  "client_email": "...",
  "client_id": "...",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "..."
}
kek.uri URI referring to the key encryption key stored in the respective remote/cloud KMS, currently only support Google Cloud KMS string
!no default!
a valid and supported Tink key encryption key URI, e.g. pointing to a key in Google Cloud KMS (kek_type=GCP)
gcp-kms://...

OpenAPI specification

The HTTP service supports endpoints to encrypt and decrypt HTTP payloads or fields thereof. After configuring and launching the Quarkus application in dev mode (maven: ./mvnw quarkus:dev | quarkus cli: quarkus dev) you can directly access the Swagger UI to experiments with the exposed endpoints. This is the underlying openAPI spec:

openapi: 3.0.3
info:
  title: Kryptonite for Kafka's HTTP API
  description: Open API spec for the Cipher Field Resource based on Quarkus Funqy
  version: "0.1.0"
  license:
    name: Apache 2.0
    url: http://www.apache.org/licenses/LICENSE-2.0.html

servers:
  - url: http://localhost:8080
    description: (dev mode server)
tags:
  - name: encryption
    description: endpoints for encrypting payloads or fields thereof
  - name: decryption
    description: endpoints for decrypting payloads or fields thereof
paths:
  /encrypt/value:
    post:
      tags:
        - encryption
      requestBody:
        description: The request body may contain any value representing a valid JSON type (string, number, boolean, array, object, null).
        content:
          application/json:
            schema:
              oneOf:
                - type: string
                - type: number
                - type: boolean
                - type: array
                  items:
                    type: object
                - type: object
                  nullable: true
      responses:
        200:
          description: The Base64 encoded ciphertext using the `CipherFieldResource` encryption settings as stated in `application.properties`.
          content:
            application/json:
              schema:
                type: string

  /encrypt/array-elements:
    post:
      tags:
        - encryption
      requestBody:
        description: The request body may contain a JSON array containing elements with any valid JSON type (string, number, boolean, array, object, null).
        content:
          application/json:
            schema:
              type: array
              items:
                type: object
                nullable: true
      responses:
        200:
          description: The JSON array containing the Base64 encoded ciphertexts of all elements using the `CipherFieldResource` encryption settings as stated in `application.properties`.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: string

  /encrypt/map-entries:
    post:
      tags:
        - encryption
      requestBody:
        description: The request body may contain a JSON object containing properties with any valid JSON type (string, number, boolean, array, object, null).
        content:
          application/json:
            schema:
              type: object
      responses:
        200:
          description: The JSON object containing the Base64 encoded ciphertexts of all properties using the `CipherFieldResource` encryption settings as stated in `application.properties`.
          content:
            application/json:
              schema:
                type: object

  /encrypt/value-with-config:
    post:
      tags:
        - encryption
      requestBody:
        description: The request body may contain a JSON object containing a `data` property of type JSON object which itself may contain properties of any valid JSON type (string, number, boolean, array, object, null). Additionally, the JSON object can define an optional `fieldConfig` property, which is an array containing individual configuration to apply specific encryption settings for particular properties found in the `data` property.
        content:
          application/json:
            schema:
              type: object
              properties:
                data:
                  type: object
                fieldConfig:
                  type: array
                  items:
                    $ref: '#/components/schemas/FieldConfig'      
      responses:
        200:
          description: The JSON object containing the Base64 encoded ciphertexts of all properties using the settings as provided in the `fieldConfig` array of the request body and/or the `CipherFieldResource` encryption settings as stated in `application.properties`.
          content:
            application/json:
              schema:
                type: object

  /decrypt/value:
    post:
      tags:
        - decryption
      requestBody:
        description: The request body must contain a valid Base64 encoded string representing the ciphertext of an encrypted value.
        content:
          application/json:
            schema:
              type: string
      responses:
        200:
          description: The response body may contain any valid JSON type (string, number, boolean, array, object, null) as the result of a successful decryption.
          content:
            application/json:
              schema:
                oneOf:
                  - type: string
                  - type: number
                  - type: boolean
                  - type: array
                    items:
                      type: object
                  - type: object
                    nullable: true

  /decrypt/array-elements:
    post:
      tags:
        - decryption
      requestBody:
        description: The request body may contain a JSON array containing elements of type string. Each element is expected to be a valid Base64 encoded string representing the ciphertext of an encrypted value.
        content:
          application/json:
            schema:
              type: array
              items:
                type: string
                nullable: true
      responses:
        200:
          description: The JSON array containing elements of any valid JSON type (string, number, boolean, array, object, null) as the result of a successful decryption.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  nullable: true

  /decrypt/map-entries:
    post:
      tags:
        - decryption
      requestBody:
        description: The request body may contain a JSON object containing properties of type string. Each property is expected to be a valid Base64 encoded string representing the ciphertext of an encrypted value.
        content:
          application/json:
            schema:
              type: object
      responses:
        200:
          description: The JSON object containing properties of any valid JSON type (string, number, boolean, array, object, null) as the result of a successful decryption.
          content:
            application/json:
              schema:
                type: object

  /decrypt/value-with-config:
    post:
      tags:
        - decryption
      requestBody:
        description: The request body may contain a JSON object containing a `data` property of type JSON object which itself may contain properties of any valid JSON type (string, number, boolean, array, object, null). Additionally, the JSON object can define an optional `fieldConfig` property, which is an array containing individual configuration to apply specific decryption settings for particular properties found in the `data` property.
        content:
          application/json:
            schema:
              type: object
              properties:
                data:
                  type: object
                fieldConfig:
                  type: array
                  items:
                    $ref: '#/components/schemas/FieldConfig'      
      responses:
        200:
          description: The JSON object containing the properties of any valid JSON type (string, number, boolean, array, object, null) as the result of a successful decryption operation using the settings as provided in the `fieldConfig` array of the request body.
          content:
            application/json:
              schema:
                type: object

components:
  schemas:
    FieldConfig:
      properties:
        name:
          type: string
        algorithm:
          type: string
          enum: [TINK/AES_GCM, TINK/AES_GCM_SIV]
        keyId:
          type: string
        schema:
          type: object
          nullable: true
        fieldMode:
          type: string
          enum: [OBJECT, ELEMENT]

HTTP API Usage Examples:

The example requests are using a demo configuration as application.properties:

#############################################
# Kryptonite for Kafka HTTP API configuration
#############################################
#
# MANDATORY config settings
cipher.data.keys=[{"identifier":"keyA","material":{"primaryKeyId":1000000001,"key":[{"keyData":{"typeUrl":"type.googleapis.com/google.crypto.tink.AesGcmKey","value":"GhDRulECKAC8/19NMXDjeCjK","keyMaterialType":"SYMMETRIC"},"status":"ENABLED","keyId":1000000001,"outputPrefixType":"TINK"}]}},{"identifier":"keyB","material":{"primaryKeyId":1000000002,"key":[{"keyData":{"typeUrl":"type.googleapis.com/google.crypto.tink.AesGcmKey","value":"GiBIZWxsbyFXb3JsZEZVQ0sxYWJjZGprbCQxMjM0NTY3OA==","keyMaterialType":"SYMMETRIC"},"status":"ENABLED","keyId":1000000002,"outputPrefixType":"TINK"}]}},{"identifier":"key9","material":{"primaryKeyId":1000000003,"key":[{"keyData":{"typeUrl":"type.googleapis.com/google.crypto.tink.AesSivKey","value":"EkByiHi3H9shy2FO5UWgStNMmgqF629esenhnm0wZZArUkEU1/9l9J3ajJQI0GxDwzM1WFZK587W0xVB8KK4dqnz","keyMaterialType":"SYMMETRIC"},"status":"ENABLED","keyId":1000000003,"outputPrefixType":"TINK"}]}},{"identifier":"key8","material":{"primaryKeyId":1000000004,"key":[{"keyData":{"typeUrl":"type.googleapis.com/google.crypto.tink.AesSivKey","value":"EkBWT3ZL7DmAN91erW3xAzMFDWMaQx34Su3VlaMiTWzjVDbKsH3IRr2HQFnaMvvVz2RH/+eYXn3zvAzWJbReCto/","keyMaterialType":"SYMMETRIC"},"status":"ENABLED","keyId":1000000004,"outputPrefixType":"TINK"}]}}]
cipher.data.key.identifier=keyA
#
# OPTIONAL config settings with the following defaults
key.source=CONFIG
kms.type=NONE
kms.config={}
kek.type=NONE
kek.config={}
kek.uri=gcp-kms://
dynamic.key.id.prefix=__#
path.delimiter=.
field.mode=ELEMENT
cipher.algorithm=TINK/AES_GCM
#############################################

NOTE: The contained secret keys in this application.properties file are used for tests and public demos in various different places and are thus compromised by definition. Never use these secret keys to protect sensitive real world data in any of your production workloads!

An exported Postman collection can be found in this file src/main/resources/META-INF/funqy-kryptonite-http-api-samples.postman_collection.json which contains 16 example requests to play with. Additionally, the same example requests are also available as publicly shared Postman workspace collection. You can fork them to conveniently run the requests in your local environment via the Postman desktop app by clicking the button below:

Run in Postman