Skip to main content
Skip table of contents

Deploy EJBCA as CA with automation with SoftHSM2

ENTERPRISE

This outlines how to deploy a Certificate Authority (CA) using the EJBCA Enterprise configuration export/import tool EJBCA ConfigDump and a SoftHSM2 sidecar.

Prerequisites

Before you begin, you should be familiar with how to deploy EJBCA CA in Kubernetes.

EJBCA installation with ConfigDump import does not support replication during the installation stage. Instead, use helm install with ConfigDump and replicaCount: 1. After installation, perform a helm upgrade to the desired replicaCount.

Prepare database and other credentials

  • Prepare a database for EJBCA in Kubernetes or externally

  • Create secrets containing the username and password

  • Configure these essential values in values.yaml

    • The database URL

    • The database credential secrets

    • Ingress URL

Create slots and keys in HSM

Create slots in the HSM before installing or deploying EJBCA. The EJBCA container will load the slots as configured from ConfigDump and load the keys into those slots.

This example uses a SoftHSM2, which is intended for non-production use, and slots and keys will be created automatically when running the Helm install command. To persist key materials across application restarts, utilize a Kubernetes PersistentVolumeClaim (PVC).

To create a PVC named softhsm-pvc, run:

BASH
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: softhsm-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 250Mi
EOF

Prepare configuration dump

ConfigDump objects used in the following examples were created using the EJBCA ConfigDump REST Interface. The provided samples will demonstrate how to initialize an EJBCA instance with:

  • Crypto Tokens: Two Hardware Security Module (HSM) backed crypto tokens with authentication codes provided using Kubernetes secrets.

  • Certification Authorities (CAs): Two CAs created using keys from the HSM and supplied subjectDN, validity, etc.

  • End Entity Profile: An End Entity Profile for issuing server certificates with up to four DNS Names.

  • Roles and Role Members: Roles used for certificate authentication with CN (common name) SuperAdmin and AutomationAdmin.

  • Protocols: Configuration that enables REST, ACME, and other protocols.

Separate confidential data and create Kubernetes secrets

Sensitive data in ConfigDump exports can be replaced with references to Kubernetes Secret keys.

For example, given a configuration dump snippet with an Authentication Code supperSecretPassword:

JSON
"crypto-tokens": {
  "ManagementCA": {
    "Object Type": "Crypto Token",
    "Authentication Code": "supperSecretPassword",
    ...
  }
}

You can first create a secret to store the Authentication Code using an arbitrary key, for example, AUTHENTICATION_CODE:

BASH
kubectl create secret generic configdump-secrets \
    --from-literal=AUTHENTICATION_CODE=supperSecretPassword

Then, reference this key in an inline or mounted ConfigDump file when deploying EJBCA:

JSON
"crypto-tokens": {
  "ManagementCA": {
    "Object Type": "Crypto Token",
    "Authentication Code": "${AUTHENTICATION_CODE}",
    ...
  }
}

Subsequent sections of this guide will use a Kubernetes secret with the following content:

BASH
kubectl create secret generic configdump-secrets \
    --from-literal=CA_TOKEN_PASSWORD=foo123 \
    --from-literal=PEER_TOKEN_PASSWORD=bar123

Prepare configuration dump as a Kubernetes ConfigMap

Prepare a Kubernetes ConfigMap with your EJBCA configuration dump file.

Example filename: ejbca-ca-init-configmap.yaml

YAML
apiVersion: v1
kind: ConfigMap
metadata:
  name: ejbca-ca-init-configmap
data:
  initialize-hsm-slots-softhsm2.sh: |
    #!/bin/bash
    cp --preserve --recursive /opt/keyfactor/p11proxy-client/* /mnt/driver/
    slot_count=$(softhsm2-util --show-slots | grep -cE "Slot [0-9]+")
    if [ "$slot_count" -gt "1" ]; then
        exit 0
    fi
    softhsm2-util --init-token --free --label CA_SLOT --so-pin "$CA_TOKEN_PASSWORD" --pin "$CA_TOKEN_PASSWORD"
    softhsm2-util --init-token --free --label IKB_SLOT --so-pin "$CA_TOKEN_PASSWORD" --pin "$PEER_TOKEN_PASSWORD"
    
  after-deployed-pre.sh: |
    #!/bin/bash
    echo "web.reqcert=false" >> /opt/keyfactor/ejbca/conf/web.properties
    common_args="--password $CA_TOKEN_PASSWORD --lib-file /opt/keyfactor/p11proxy-client/p11proxy-client.so --slot-ref SLOT_LABEL --slot CA_SLOT"
    if p11ng-cli.sh listkeypairs $common_args | grep -q "signkey001"; then
        return 0
    fi
    p11ng-cli.sh generatekeypair $common_args --key-spec RSA2048 --alias signkey001
    p11ng-cli.sh generatekeypair $common_args --key-spec RSA2048 --alias defaultkey001 --key-usage SIGN_ENCRYPT
    p11ng-cli.sh generatekeypair $common_args --key-spec RSA2048 --alias signkey002
    p11ng-cli.sh generatekeypair $common_args --key-spec RSA2048 --alias defaultkey002 --key-usage SIGN_ENCRYPT
    p11ng-cli.sh generatekeypair $common_args --key-spec RSA2048 --alias testkey

  configdump.json: |
    {
      "crypto-tokens": {
        "CaToken001": {
          "Object Type": "Crypto Token",
          "Version": 2,
          "Name": "CaToken001",
          "Used": true,
          "PKCS11 Library": "/opt/keyfactor/p11proxy-client/p11proxy-client.so",
          "PKCS11 Reference Type": "SLOT_LABEL",
          "PKCS11 Reference": "CA_SLOT",
          "PKCS11 Attribute File": "",
          "Key Pair Info": [],
          "Authentication Code": "${CA_TOKEN_PASSWORD}",
          "Type": "Pkcs11NgCryptoToken",
          "Active": true,
          "Auto Activation": true
        },
        "PeerToken": {
          "Object Type": "Crypto Token",
          "Version": 2,
          "Name": "PeerToken",
          "Used": true,
          "PKCS11 Library": "/opt/keyfactor/p11proxy-client/p11proxy-client.so",
          "PKCS11 Reference Type": "SLOT_LABEL",
          "PKCS11 Reference": "IKB_SLOT",
          "PKCS11 Attribute File": "",
          "Key Pair Info": [],
          "Authentication Code": "${PEER_TOKEN_PASSWORD}",
          "Type": "Pkcs11NgCryptoToken",
          "Active": true,
          "Auto Activation": true
        }
      },
      "certification-authorities": {
        "ManagementCA": {
          "Object Type": "Certification Authority",
          "Version": 2,
          "Name": "ManagementCA",
          "Type of CA": "X.509",
          "Serial Number Octet Size": 20,
          "Pre-produce OCSP Responses": false,
          "Microsoft CA Compatible Mode Used": false,
          "Store responses on-demand": false,
          "Pre-produce OCSP Responses Upon certificate issuance/revocation": false,
          "Certificate Profile": "ROOTCA",
          "Default Certificate Profile": "Not used",
          "Use Append-Only Table": false,
          "CA Token": {
            "Signature Algorithm": "SHA256WithRSA",
            "Encryption Algorithm": "SHA256WithRSA",
            "Crypto Token": "CaToken001",
            "Default Key": "defaultkey001",
            "Certificate Signing Key": "signkey001",
            "CRL Signing Key": "signkey001",
            "Key Encryption Key": "defaultkey001",
            "Test Key": "testkey",
            "Key Sequence Format": "Numeric",
            "Key Sequence": "00000"
          },
          "Enforce Unique Public Keys": false,
          "Enforce key renewal": false,
          "Enforce Unique DN": false,
          "User Storage": true,
          "Certificate Storage": true,
          "Accept Revocations for Non-Existing Entries": false,
          "Subject DN": "CN=ManagementCA,OU=SomeOrg",
          "Signed By": "Self Signed",
          "Validity": "20y",
          "Subject Alternative Name": "None",
          "Use UTF-8 in Policy Notice Text": true,
          "LDAP DN Order": true,
          "Authority Key Id Used": true,
          "CRL Number Used": true,
          "Partitioned CRL Used": false,
          "CRL Expiration Period": "1d",
          "CRL Issue Interval": "0m",
          "CRL Overlap Time": "10m",
          "Delta CRL Period": "0m",
          "Generate CRL Upon Revocation": false,
          "Allow Changing Revocation Reason": false,
          "Finish User": true,
          "CA Healthcheck Enabled": true,
          "Request Processor": null
        },
        "RootCA": {
          "Object Type": "Certification Authority",
          "Version": 2,
          "Name": "RootCA",
          "Type of CA": "X.509",
          "Serial Number Octet Size": 20,
          "Pre-produce OCSP Responses": false,
          "Microsoft CA Compatible Mode Used": false,
          "Store responses on-demand": false,
          "Pre-produce OCSP Responses Upon certificate issuance/revocation": false,
          "Certificate Profile": "ROOTCA",
          "Default Certificate Profile": "Not used",
          "Use Append-Only Table": false,
          "CA Token": {
            "Signature Algorithm": "SHA256WithRSA",
            "Encryption Algorithm": "SHA256WithRSA",
            "Crypto Token": "CaToken001",
            "Default Key": "defaultkey002",
            "Certificate Signing Key": "signkey002",
            "CRL Signing Key": "signkey002",
            "Key Encryption Key": "defaultkey002",
            "Test Key": "testkey",
            "Key Sequence Format": "Numeric",
            "Key Sequence": "00000"
          },
          "Enforce Unique Public Keys": false,
          "Enforce key renewal": false,
          "Enforce Unique DN": false,
          "User Storage": true,
          "Certificate Storage": true,
          "Accept Revocations for Non-Existing Entries": false,
          "Subject DN": "CN=RootCA,C=SE",
          "Signed By": "Self Signed",
          "Validity": "20y",
          "Subject Alternative Name": "None",
          "Use UTF-8 in Policy Notice Text": true,
          "LDAP DN Order": true,
          "Authority Key Id Used": true,
          "CRL Number Used": true,
          "Partitioned CRL Used": false,
          "CRL Expiration Period": "1d",
          "CRL Issue Interval": "0m",
          "CRL Overlap Time": "10m",
          "Delta CRL Period": "0m",
          "Generate CRL Upon Revocation": false,
          "Allow Changing Revocation Reason": false,
          "Finish User": true,
          "CA Healthcheck Enabled": true,
          "Request Processor": null
        }
      },
      "end-entity-profiles": {
        "eep001": {
          "Object Type": "End Entity Profile",
          "Version": 1,
          "Name": "ServerEndEntityProfile",
          "Description": "",
          "Default CA": "ManagementCA",
          "Available CAs": ["ManagementCA", "RootCA"],
          "Default Certificate Profile": "SERVER",
          "Available Certificate Profiles": ["SERVER"],
          "Default Token Type": "PEM File",
          "Available Token Types": [
            "User Generated",
            "PKCS12 File",
            "BCFKS File",
            "JKS File",
            "PEM File"
          ],
          "Subject DN": {
            "CN": [
              {
                "Required": true
              }
            ]
          },
          "Subject Alternative Name": {
            "DNSNAME": [
              {
                "Required": false
              },
              {
                "Required": false
              },
              {
                "Required": false
              },
              {
                "Required": false
              }
            ]
          }
        }
      },
      "available-protocols": {
        "available-protocol-configuration": {
          "Object Type": "Available Protocols",
          "Version": 1,
          "Name": "available-protocol-configuration",
          "ACME": true,
          "Certstore": true,
          "CMP": true,
          "CRLstore": true,
          "EST": true,
          "MSAE": true,
          "OCSP": true,
          "SCEP": true,
          "RA Web": true,
          "REST CA Management": true,
          "REST Certificate Management": true,
          "REST Coap Management": true,
          "REST Crypto Token Management": true,
          "REST End Entity Management": true,
          "REST End Entity Management V2": true,
          "REST Configdump": true,
          "REST Certificate Management V2": true,
          "REST SSH V1": true,
          "REST System V1": true,
          "Webdist": true,
          "Web Service": true,
          "ITS Certificate Management": false,
          "Custom header name for REST calls from browser": "X-Keyfactor-Requested-With"
        }
      },
      "admin-roles": {
        "Super Administrator Role": {
          "Object Type": "Role",
          "Version": 1,
          "Name": "Super Administrator Role",
          "Role Members": [
            {
              "Token Type": "CertificateAuthenticationToken",
              "Issuer": "ManagementCA",
              "Match With": "WITH_COMMONNAME",
              "Match Value": "AutomationAdmin"
            },
            {
              "Token Type": "CertificateAuthenticationToken",
              "Issuer": "ManagementCA",
              "Match With": "WITH_COMMONNAME",
              "Match Value": "SuperAdmin"
            },
            {
              "Token Type": "CliAuthenticationToken",
              "Issuer": null,
              "Match With": "USERNAME",
              "Match Value": "ejbca"
            },
            {
              "Token Type": "PublicAccessAuthenticationToken",
              "Issuer": null,
              "Match With": "TRANSPORT_CONFIDENTIAL",
              "Match Value": ""
            }
          ],
          "Namespace": "",
          "RA Style Id": 0,
          "Access Rules": {
            "/": "Allow"
          }
        }
      }
    }

Create the ConfigMap:

BASH
kubectl apply -f ejbca-ca-init-configmap.yaml

Configure Deployment

The following shows an example Helm chart values file modified to deploy the EJBCA CA utilizing a SoftHSM sidecar.

Example filename: ejbca-ca-values.yaml

YAML
ejbca:
  env:
    DATABASE_JDBC_URL: "jdbc:mariadb://mariadb:3306/ejbca?characterEncoding=utf8"
    DATABASE_USER: ejbca
  envRaw:
    - name: DATABASE_PASSWORD
      valueFrom:
        secretKeyRef:
          name: mariadb-passwords
          key: mariadb-password
  envFrom:
    - secretRef:
        name: configdump-secrets
  configdumpImport:
    enabled: true
    initialize: true
    configMapName: ejbca-ca-init-configmap
    configMapKey: configdump.json
  # More convenient way to integrate with HSM will be available soon
  # Extra init containers to be added to the deployment
  initContainers:
    - name: hsm-driver-init
      image: registry.primekey.com/primekey/hsm-driver-softhsm:1.0.8
      command:
        ["sh", "-c", "/opt/keyfactor/init/initialize-hsm-slots-softhsm2.sh"]
      volumeMounts:
        - name: p11proxy-client
          mountPath: /mnt/driver/
        - name: hsm-slot-init
          mountPath: /opt/keyfactor/init/initialize-hsm-slots-softhsm2.sh
          subPath: initialize-hsm-slots-softhsm2.sh
        - name: tokens
          mountPath: /mnt/tokens
      env:
        - name: SOFTHSM2_CREATE_TOKENS
          value: "false"
      envFrom:
        - secretRef:
            name: configdump-secrets
  # Extra sidecar containers to be added to the deployment
  sidecarContainers:
    - name: hsm
      image: registry.primekey.com/primekey/hsm-driver-softhsm:1.0.8
      imagePullPolicy: IfNotPresent
      env:
        - name: SOFTHSM2_CREATE_TOKENS
          value: "false"
        - name: SOFTHSM2_LOG_LEVEL
          value: INFO
      volumeMounts:
        - name: tokens
          mountPath: /mnt/tokens
  # Extra volumes to be added to the deployment
  volumes:
    - name: p11proxy-client
      emptyDir: {}
    - name: tokens
      persistentVolumeClaim:
        claimName: softhsm-pvc
    - name: after-deployed-pre
      configMap:
        name: ejbca-ca-init-configmap
        defaultMode: 0777
        items:
          - key: "after-deployed-pre.sh"
            path: "after-deployed-pre.sh"
    - name: hsm-slot-init
      configMap:
        name: ejbca-ca-init-configmap
        defaultMode: 0777
        items:
          - key: "initialize-hsm-slots-softhsm2.sh"
            path: "initialize-hsm-slots-softhsm2.sh"
  # Extra volume mounts to be added to the deployment
  volumeMounts:
    - name: p11proxy-client
      mountPath: /opt/keyfactor/p11proxy-client
    - name: after-deployed-pre
      mountPath: /opt/keyfactor/bin/internal/after-deployed-pre.sh
      subPath: after-deployed-pre.sh
# needed to make softhsm volume mount to work
podSecurityContext:
  fsGroup: 10001
  
ingress:
  enabled: true
  hosts:
    - host: "ejbca.example.com"
      paths:
        - path: /
          pathType: Prefix

#imagePullSecrets:
#  - name: keyfactor-registry

Install EJBCA

Deploy EJBCA to a Kubernetes cluster using the previously prepared Helm chart values file:

BASH
helm install ejbca-ca -f ejbca-ca-values.yaml \
    oci://repo.keyfactor.com/charts/ejbca --version <version>

Secure EJBCA

Once EJBCA is deployed, enroll a Super Administrator certificate, create a TLS server certificate, and configure EJBCA to use them with client certificate authentication.

Issue SuperAdmin certificate

To issue a certificate for the SuperAdmin:

  1. In EJBCA, click RA Web to access the EJBCA RA user interface.

  2. Select Enroll > Make New Request and specify the following:

    • For Certificate Type, i applicable, select EMPTY.

    • For Certificate subtype, select ENDUSER.

    • For CA, select ManagementCA.

    • For Key-pair generation, select By the CA.

    • For Key algorithm, select for example, RSA-2048.

    • For the Required Subject DN Attributes, Common Name (CN), specify SuperAdmin.

    • For Provide User Credentials, enter a username and password.

  3. Click Download PKCS#12 to download the certificate.

Your certificate is saved as a SuperAdmin.p12 file.

Configure TLS and client certificate authentication

Steps describing how to secure access to EJBCA using a TLS server certificate with client certificate authentication are outlined on the Deploy CA in Kubernetes page.

To configure EJBCA:

  1. Access EJBCA Admin Web by navigating your browser to the host configured in ejbca-ca-values.yaml.

  2. On the CA Structure & CRLs page, click Download PEM file to download the ManagementCA.cacert.pem file.

  3. Proceed to the Deploy CA in Kubernetes page and follow the guide starting from section Enroll server TLS certificate.

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.