Skip to main content
Skip table of contents

Zero Touch PQC PKI Deployment with Kubernetes, Helm, and PQC Composite Certificates

ENTERPRISE

In this guide, you will learn how to deploy a PQC-ready PKI in Kubernetes using automation and real HSMs. The outcome is a PQC-enabled PKI with a root CA and subordinate CAs, deployed in separate Kubernetes namespaces, backed by SecuroSys and Fortanix HSMs, and initialized entirely through Helm and EJBCA ConfigDump.

The guide demonstrates all the steps of the configuration: generating keys on HSMs, creating and subordinating CAs, issuing certificates, and finalizing the environment through automated Helm upgrades, all without manual intervention.

To conclude, the guide demonstrates real-world use cases of the deployed PQC PKI using off-the-shelf software:

  • Hybrid and fully quantum-safe TLS connections using standard Apache, curl, and OpenSSL

  • PQC CMS signing and verification using SignServer and OpenSSL

About PQC Composite Signatures and Certificates

About PQC Composite Signatures and Certificates - Click to read more

Composite signatures and certificates combine a post-quantum algorithm (ML-DSA) with a classical algorithm such as RSA-PSS or ECDSA, creating a hybrid signature that remains secure even if one of the algorithms is later compromised. This dual approach provides a practical hedge against uncertainty during the transition to Post-Quantum Cryptography.

Video

Prerequisites

For this guide, a special build of EJBCA including PQC composite certificates was used. Please get in touch with your sales representative if you are interested in testing PQC composite certificates.

Before you begin, you need:

  • Kubernetes deployed, version 1.30 or later. MicroK8s was used for this tutorial

  • Internet access

  • Helm installed

  • An available load balancer. Metal LB was used for this tutorial

Step 1 - Create namespaces for the deployments

  1. Check that the environment is fresh and that nothing is there:

BASH
kubectl get all -A
  1. Create namespaces for the TechMeetup deployment

BASH
kubectl create namespace forseti
kubectl create namespace jarls
kubectl create namespace karls

Step 2 - Deploy Database

  1. Add the groundhog2k helm repo:

BASH
helm repo add groundhog2k https://groundhog2k.github.io/helm-charts
  1. Create a secret for MARIADB_ROOT_PASSWORD:

BASH
kubectl -n forseti create secret generic mariadb-secret --from-literal=MARIADB_ROOT_PASSWORD=foo123
kubectl -n forseti create secret generic mariadb-user-secret --from-literal=MARIADB_DATABASE=jarl --from-literal=MARIADB_USER=jarl --from-literal=MARIADB_PASSWORD=foo123
  1. Create a Directory tree for the K8s configuration:

BASH
mkdir -p ~/configs/mariadb  ~/configs/root  ~/configs/sub
  1. Create the MariaDB helm mariadb-override.yaml file:

BASH
cat > ~/configs/mariadb/mariadb-override.yaml << EOF
## Default values for MariaDB deployment

## MariaDB docker image
image:
  registry: "docker.io"
  repository: "mariadb"
  pullPolicy: IfNotPresent
  # Overrides the image tag whose default is the chart appVersion.
  tag: "latest"

## Optional service account
serviceAccount:
  # Specifies whether a service account should be created
  create: true
  # Annotations to add to the service account
  annotations: {}
  # The name of the service account to use.
  # If not set and create is true, a name is generated using the fullname template
  name: ""

# Resource limits and requests
resources: 
   limits:
     cpu: 2
     memory: 2048Mi
   requests:
     cpu: 1000m
     memory: 1024Mi

## Use Kubernetes Deployment instead of StatefulSet
useDeployment: false

## Database configuration
settings:
  ## Arguments for the container entrypoint process
  arguments:
    - --character-set-server=utf8mb4
    - --collation-server=utf8mb4_unicode_ci
  ## Optional existing secret for the MariaDB root password
  existingSecret: mariadb-secret

  ## Set true to skip loading timezone data during init
  skipTZInfo: false

## Storage parameters
storage:
  ## Alternative set requestedSize to define a size for a dynamically created PVC
  requestedSize: 10Gi

userDatabase:
  existingSecret: mariadb-user-secret
  name:
    secretKey: MARIADB_DATABASE
  user:
    secretKey: MARIADB_USER
  password:
    secretKey: MARIADB_PASSWORD

EOF
  1. Deploy MariaDB using the Helm chart with override:

BASH
helm install -n forseti forseti-mariadb groundhog2k/mariadb -f ~/configs/mariadb/mariadb-override.yaml
# Uninstall
helm uninstall -n forseti forseti-mariadb
  1. Exec into the mariadb container:

BASH
kubectl -n forseti exec -it pod/forseti-mariadb-0 -c mariadb -- /bin/bash
  1. Access the database SQL:

BASH
mariadb -u root -p
  1. Type in the password for the DB root account

  2. Create the additional databases:

BASH
CREATE DATABASE karl CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
GRANT ALL PRIVILEGES ON karl.* TO 'karl'@'%' IDENTIFIED BY 'foo123';
CREATE DATABASE thrall01 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
GRANT ALL PRIVILEGES ON tharll01.* TO 'thrall01'@'%' IDENTIFIED BY 'foo123';
CREATE DATABASE thrall02 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
GRANT ALL PRIVILEGES ON tharll02.* TO 'thrall02'@'%' IDENTIFIED BY 'foo123';
  1. Enter exit to exit the SQL shell

  2. Enter exit to exit the container exec session

Step 3 - View DB logs

Use the following command to view the pod logs for the database:

BASH
kubectl -n forseti logs -f pod/forseti-mariadb-0 -c mariadb

Step 4 - Deploy Root CA

  1. Create the after-deployed-pre.sh configmap file:

BASH
cat > ~/configs/root/after-deployed-pre.yaml << EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: after-deployed-pre
data:
  after-deployed-pre.sh: |
    #!/bin/bash
    if /opt/keyfactor/bin/ejbca.sh cryptotoken list | grep "RootCa"; then
        return 0
    else
        /opt/keyfactor/bin/ejbca.sh cryptotoken create --token RootCa --autoactivate true --pin "\${HSM_TOKEN_PIN}" --type SecurosysCryptoToken --securosysauthenticationtype token --securosysauthtoken "" --securosysrestapi "https://primusdev.cloudshsm.com" --securosysapprovalkey 30 --securosysauthcert "" --securosysmanagementkey "" --securosysoperationkey "" --securosysservicekey ""
        # Generate keys on HSM
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token RootCa --alias signKeyEc01 --keyspec secp521r1 --key-usage SIGN
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token RootCa --alias defaultKeyEc01 --keyspec 4096 --key-usage SIGN_ENCRYPT
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token RootCa --alias signKeyPq01 --keyspec ML-DSA-87 --key-usage SIGN
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token RootCa --alias defaultKeyPq01 --keyspec 4096 --key-usage SIGN_ENCRYPT
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token RootCa --alias signKeyHyPq01 --keyspec ML-DSA-87 --key-usage SIGN
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token RootCa --alias signKeyHyEc01 --keyspec secp521r1 --key-usage SIGN
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token RootCa --alias defaultKeyHy01 --keyspec 4096 --key-usage SIGN_ENCRYPT
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token RootCa --alias testKey-Ec --keyspec secp256r1 --key-usage SIGN
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token RootCa --alias testKey-Ml-Dsa --keyspec ML-DSA-87 --key-usage SIGN
    fi
    # Create Soft Crypto Token for Composite keys
    /opt/keyfactor/bin/ejbca.sh cryptotoken create --token RootCompositeCa --autoactivate true --type SoftCryptoToken --pin foo123
    # Generate keys on soft token
    /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token RootCompositeCa --alias signKey001 --keyspec MLDSA87-ECDSA-P521-SHA512
    /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token RootCompositeCa --alias defaultKey001 --keyspec 4096
    /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token RootCompositeCa --alias testKey --keyspec MLDSA87-ECDSA-P521-SHA512
EOF
  1. Create the configmap for the after-deployed-pre.shfile:

BASH
kubectl -n jarls apply -f ~/configs/root/after-deployed-pre.yaml
  1. Create a secret for the Securosys HSM Token PIN:

BASH
kubectl -n jarls create secret generic configdump-secrets --from-literal=HSM_TOKEN_PIN=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.ey
  1. Create the Root configdump configmap:

cat > ~/configs/root/configdump.json << EOF ... Click to see the full command
BASH
cat > ~/configs/root/configdump.json << EOF
{
  "certification-authorities": {
    "Pqc-Root-G1": {
      "Object Type": "Certification Authority",
      "Version": 3,
      "Name": "Pqc-Root-G1",
      "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-MlDsa-G1",
      "Default Certificate Profile": "Not used",
      "Use Append-Only Table": false,
      "CA Token": {
        "Signature Algorithm": "ML-DSA-87",
        "Encryption Algorithm": "SHA256WithRSA",
        "Crypto Token": "RootCa",
        "Default Key": "defaultKeyPq01",
        "Certificate Signing Key": "signKeyPq01",
        "CRL Signing Key": "signKeyPq01",
        "Key Encryption Key": "defaultKeyPq01",
        "Test Key": "testKey-Ml-Dsa",
        "Key Sequence Format": "Numeric",
        "Key Sequence": "00000"
      },
      "Enforce Unique Public Keys": true,
      "Enforce key renewal": false,
      "Enforce Unique DN": true,
      "User Storage": true,
      "Key encrypt padding algorithm": "RSA_OAEP",
      "Add Compromised Keys To Block List": false,
      "Certificate Storage": true,
      "Accept Revocations for Non-Existing Entries": false,
      "Subject DN": "CN=Kefactor TechMeetup PQC Root CA G1,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE",
      "Signed By": "Self Signed",
      "Validity": "10y",
      "Use UTF-8 in Policy Notice Text": true,
      "LDAP DN Order": false,
      "Authority Key Id Used": true,
      "CRL Number Used": true,
      "Partitioned CRL Used": false,
      "CRL Expiration Period": "1y",
      "CRL Issue Interval": "0m",
      "CRL Overlap Time": "0m",
      "Delta CRL Period": "0m",
      "Generate CRL Upon Revocation": false,
      "Allow Changing Revocation Reason": false,
      "Default CRL Distribution Point": "http://crl.techmeetup.test/crls/pqc-root-g1.crl",
      "AIA CA Issuer URIs": [
        "http://aia.techmeetup.test/aia/pqc-root-g1.crt"
      ],
      "Finish User": true,
      "Request Processor": null
    },
    "Composite-Root-G1": {
      "Object Type": "Certification Authority",
      "Version": 3,
      "Name": "Composite-Root-G1",
      "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": "RootCaComposite-EcMlDsa-G1",
      "Default Certificate Profile": "Not used",
      "Use Append-Only Table": false,
      "CA Token": {
        "Signature Algorithm": "MLDSA87-ECDSA-P521-SHA512",
        "Encryption Algorithm": "SHA512WithRSA",
        "Crypto Token": "RootCompositeCa",
        "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": true,
      "Enforce key renewal": false,
      "Enforce Unique DN": true,
      "User Storage": true,
      "Key encrypt padding algorithm": "RSA_OAEP",
      "Add Compromised Keys To Block List": false,
      "Certificate Storage": true,
      "Accept Revocations for Non-Existing Entries": false,
      "Subject DN": "CN=Kefactor TechMeetup Composite Root CA G1,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE",
      "Signed By": "Self Signed",
      "Validity": "10y",
      "Use UTF-8 in Policy Notice Text": true,
      "LDAP DN Order": false,
      "Authority Key Id Used": true,
      "CRL Number Used": true,
      "Partitioned CRL Used": false,
      "CRL Expiration Period": "1y",
      "CRL Issue Interval": "0m",
      "CRL Overlap Time": "0m",
      "Delta CRL Period": "0m",
      "Generate CRL Upon Revocation": false,
      "Allow Changing Revocation Reason": false,
      "Default CRL Distribution Point": "http://crl.techmeetup.test/crls/composite-root-g1.crl",
      "AIA CA Issuer URIs": [
        "http://aia.techmeetup.test/aia/composite-root-g1.crt"
      ],
      "Finish User": true,
      "Request Processor": null
    },
    "Hybrid-Root-G1": {
      "Object Type": "Certification Authority",
      "Version": 3,
      "Name": "Hybrid-Root-G1",
      "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": "RootCaHybrid-EcMlDsa-G1",
      "Default Certificate Profile": "Not used",
      "Use Append-Only Table": false,
      "CA Token": {
        "Signature Algorithm": "SHA512withECDSA",
        "Alternative Signature Algorithm": "ML-DSA-87",
        "Encryption Algorithm": "SHA512WithRSA",
        "Crypto Token": "RootCa",
        "Default Key": "defaultKeyHy01",
        "Certificate Signing Key": "signKeyHyEc01",
        "Alternative Certificate Signing Key": "signKeyHyPq01",
        "CRL Signing Key": "signKeyHyEc01",
        "Key Encryption Key": "defaultKeyHy01",
        "Test Key": "testKey-Ec",
        "Key Sequence Format": "Numeric",
        "Key Sequence": "00000"
      },
      "Enforce Unique Public Keys": true,
      "Enforce key renewal": false,
      "Enforce Unique DN": true,
      "User Storage": true,
      "Key encrypt padding algorithm": "RSA_OAEP",
      "Add Compromised Keys To Block List": false,
      "Certificate Storage": true,
      "Accept Revocations for Non-Existing Entries": false,
      "Subject DN": "CN=Kefactor TechMeetup Hybrid Root CA G1,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE",
      "Signed By": "Self Signed",
      "Validity": "10y",
      "Use UTF-8 in Policy Notice Text": true,
      "LDAP DN Order": false,
      "Authority Key Id Used": true,
      "CRL Number Used": true,
      "Partitioned CRL Used": false,
      "CRL Expiration Period": "1y",
      "CRL Issue Interval": "0m",
      "CRL Overlap Time": "0m",
      "Delta CRL Period": "0m",
      "Generate CRL Upon Revocation": false,
      "Allow Changing Revocation Reason": false,
      "Default CRL Distribution Point": "http://crl.techmeetup.test/crls/hybrid-root-g1.crl",
      "AIA CA Issuer URIs": [
        "http://aia.techmeetup.test/aia/hybrid-root-g1.crt"
      ],
      "Finish User": true,
      "Request Processor": null
    },
    "Ecdsa-Root-G1": {
      "Object Type": "Certification Authority",
      "Version": 3,
      "Name": "Ecdsa-Root-G1",
      "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-Ec-G1",
      "Default Certificate Profile": "Not used",
      "Use Append-Only Table": false,
      "CA Token": {
        "Signature Algorithm": "SHA512withECDSA",
        "Encryption Algorithm": "SHA512WithRSA",
        "Crypto Token": "RootCa",
        "Default Key": "defaultKeyEc01",
        "Certificate Signing Key": "signKeyEc01",
        "CRL Signing Key": "signKeyEc01",
        "Key Encryption Key": "defaultKeyEc01",
        "Test Key": "testKey-Ec",
        "Key Sequence Format": "Numeric",
        "Key Sequence": "00000"
      },
      "Enforce Unique Public Keys": true,
      "Enforce key renewal": false,
      "Enforce Unique DN": true,
      "User Storage": true,
      "Key encrypt padding algorithm": "RSA_OAEP",
      "Add Compromised Keys To Block List": false,
      "Certificate Storage": true,
      "Accept Revocations for Non-Existing Entries": false,
      "Subject DN": "CN=Kefactor TechMeetup Root CA G1,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE",
      "Signed By": "Self Signed",
      "Validity": "10y",
      "Use UTF-8 in Policy Notice Text": true,
      "LDAP DN Order": false,
      "Authority Key Id Used": true,
      "CRL Number Used": true,
      "Partitioned CRL Used": false,
      "CRL Expiration Period": "1y",
      "CRL Issue Interval": "0m",
      "CRL Overlap Time": "0m",
      "Delta CRL Period": "0m",
      "Generate CRL Upon Revocation": false,
      "Allow Changing Revocation Reason": false,
      "Default CRL Distribution Point": "http://crl.techmeetup.test/crls/ecdsa-root-g1.crl",
      "AIA CA Issuer URIs": [
        "http://aia.techmeetup.test/aia/ecdsa-root-g1.crt"
      ],
      "Finish User": true,
      "Request Processor": null
    }
  },
  "certificate-profiles": {
    "SubCaHybrid-EcMlDsa-G1": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "SubCaHybrid-EcMlDsa-G1",
      "Type": "Sub CA",
      "Available Key Algorithms": [
        "ECDSA"
      ],
      "Available Elliptic Curves": [
        "P-384"
      ],
      "Available Bit Lengths": [
        3072,
        4096
      ],
      "Signature Algorithm": "SHA384withECDSA",
      "Alternative Signature Used": true,
      "Available Alternative Key Algorithms": [
        "ML-DSA-65"
      ],
      "Alternative Signature Algorithm": "Inherit from Issuing CA",
      "Validity": "5y",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Allow Validity Override": true,
      "Path Length Constraint Used": true,
      "Authority Key Identifier Used": false,
      "Key Usage": [
        "Digital Signature",
        "Key Certificate Sign",
        "CRL Sign"
      ],
      "Extended Key Usage": [],
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "CRL Distribution Point Used": true,
      "CRL Use CA Defined Settings": true,
      "Authority Information Access Used": true,
      "OCSP Service Locator URL Is CA Defined": true,
      "OCSP Issuer URL Is CA Defined": true,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": []
    },
    "RootCaComposite-EcMlDsa-G1": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "RootCaComposite-EcMlDsa-G1",
      "Type": "Root CA",
      "Available Key Algorithms": [
        "MLDSA87-ECDSA-P521-SHA512"
      ],
      "Available Elliptic Curves": [
        "P-521"
      ],
      "Available Bit Lengths": [
        3072,
        4096
      ],
      "Signature Algorithm": "MLDSA87-ECDSA-P521-SHA512",
      "Validity": "10y",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Allow Validity Override": true,
      "Authority Key Identifier Used": false,
      "Key Usage": [
        "Digital Signature",
        "Key Certificate Sign",
        "CRL Sign"
      ],
      "Extended Key Usage": [],
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": []
    },
    "RootCa-MlDsa-G1": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "RootCa-MlDsa-G1",
      "Type": "Root CA",
      "Available Key Algorithms": [
        "ML-DSA-87"
      ],
      "Available Elliptic Curves": [
        "P-521"
      ],
      "Available Bit Lengths": [
        3072,
        4096
      ],
      "Signature Algorithm": "ML-DSA-87",
      "Validity": "10y",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Allow Validity Override": true,
      "Authority Key Identifier Used": false,
      "Key Usage": [
        "Digital Signature",
        "Key Certificate Sign",
        "CRL Sign"
      ],
      "Extended Key Usage": [],
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": []
    },
    "RootCa-Ec-G1": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "RootCa-Ec-G1",
      "Type": "Root CA",
      "Available Key Algorithms": [
        "ECDSA"
      ],
      "Available Elliptic Curves": [
        "P-521"
      ],
      "Available Bit Lengths": [
        3072,
        4096
      ],
      "Signature Algorithm": "SHA512withECDSA",
      "Validity": "10y",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Allow Validity Override": true,
      "Authority Key Identifier Used": false,
      "Key Usage": [
        "Digital Signature",
        "Key Certificate Sign",
        "CRL Sign"
      ],
      "Extended Key Usage": [],
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": []
    },
    "SubCaComposite-EcMlDsa-G1": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "SubCaComposite-EcMlDsa-G1",
      "Type": "Sub CA",
      "Available Key Algorithms": [
        "MLDSA87-ECDSA-P521-SHA512"
      ],
      "Available Elliptic Curves": [
        "P-521"
      ],
      "Available Bit Lengths": [
        3072,
        4096
      ],
      "Signature Algorithm": "MLDSA87-ECDSA-P521-SHA512",
      "Validity": "5y",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Allow Validity Override": true,
      "Path Length Constraint Used": true,
      "Authority Key Identifier Used": false,
      "Key Usage": [
        "Digital Signature",
        "Key Certificate Sign",
        "CRL Sign"
      ],
      "Extended Key Usage": [],
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "CRL Distribution Point Used": true,
      "CRL Use CA Defined Settings": true,
      "Authority Information Access Used": true,
      "OCSP Service Locator URL Is CA Defined": true,
      "OCSP Issuer URL Is CA Defined": true,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": []
    },
    "SubCa-Ec-G1": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "SubCa-Ec-G1",
      "Type": "Sub CA",
      "Available Key Algorithms": [
        "ECDSA"
      ],
      "Available Elliptic Curves": [
        "P-384"
      ],
      "Available Bit Lengths": [
        3072,
        4096
      ],
      "Signature Algorithm": "SHA384withECDSA",
      "Validity": "5y",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Allow Validity Override": true,
      "Path Length Constraint Used": true,
      "Authority Key Identifier Used": false,
      "Key Usage": [
        "Digital Signature",
        "Key Certificate Sign",
        "CRL Sign"
      ],
      "Extended Key Usage": [],
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "CRL Distribution Point Used": true,
      "CRL Use CA Defined Settings": true,
      "Authority Information Access Used": true,
      "OCSP Service Locator URL Is CA Defined": true,
      "OCSP Issuer URL Is CA Defined": true,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": []
    },
    "SubCa-MlDsa-G1": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "SubCa-MlDsa-G1",
      "Type": "Sub CA",
      "Available Key Algorithms": [
        "ML-DSA-65"
      ],
      "Available Elliptic Curves": [
        "P-521"
      ],
      "Available Bit Lengths": [
        3072,
        4096
      ],
      "Signature Algorithm": "Inherit from Issuing CA",
      "Validity": "5y",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Allow Validity Override": true,
      "Path Length Constraint Used": true,
      "Authority Key Identifier Used": false,
      "Key Usage": [
        "Digital Signature",
        "Key Certificate Sign",
        "CRL Sign"
      ],
      "Extended Key Usage": [],
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "CRL Distribution Point Used": true,
      "CRL Use CA Defined Settings": true,
      "Authority Information Access Used": true,
      "OCSP Service Locator URL Is CA Defined": true,
      "OCSP Issuer URL Is CA Defined": true,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": []
    },
    "RootCaHybrid-EcMlDsa-G1": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "RootCaHybrid-EcMlDsa-G1",
      "Type": "Root CA",
      "Available Key Algorithms": [
        "ECDSA"
      ],
      "Available Elliptic Curves": [
        "P-521"
      ],
      "Available Bit Lengths": [
        3072,
        4096
      ],
      "Signature Algorithm": "SHA512withECDSA",
      "Alternative Signature Used": true,
      "Available Alternative Key Algorithms": [
        "ML-DSA-87"
      ],
      "Alternative Signature Algorithm": "ML-DSA-87",
      "Validity": "10y",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Allow Validity Override": true,
      "Authority Key Identifier Used": false,
      "Key Usage": [
        "Digital Signature",
        "Key Certificate Sign",
        "CRL Sign"
      ],
      "Extended Key Usage": [],
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": []
    }
  },
  "end-entity-profiles": {
    "subCa": {
      "Object Type": "End Entity Profile",
      "Version": 1,
      "Name": "subCa",
      "Description": "",
      "Default CA": "Ecdsa-Root-G1",
      "Available CAs": [
        "Composite-Root-G1",
        "Ecdsa-Root-G1",
        "Hybrid-Root-G1",
        "Pqc-Root-G1"
      ],
      "Default Certificate Profile": "SubCa-Ec-G1",
      "Available Certificate Profiles": [
        "SubCa-Ec-G1",
        "SubCa-MlDsa-G1",
        "SubCaComposite-EcMlDsa-G1",
        "SubCaHybrid-EcMlDsa-G1"
      ],
      "Default Token Type": "User Generated",
      "Available Token Types": [
        "User Generated"
      ],
      "Subject DN": {
        "CN": [
          {
            "Required": true
          }
        ],
        "OU": [
          {
            "Required": true,
            "Modifiable": false,
            "Values": [
              "Certification Authorities"
            ]
          }
        ],
        "O": [
          {
            "Required": true,
            "Modifiable": false,
            "Values": [
              "Keyfactor TechMeetup"
            ]
          }
        ],
        "C": [
          {
            "Required": true,
            "Modifiable": false,
            "Values": [
              "SE"
            ]
          }
        ]
      }
    }
  }  
}
EOF
  1. Create the configmap for the Root configdump:

BASH
kubectl -n jarls create configmap ca-init-configmap --from-file=configdump.json=configs/root/configdump.json
  1. Create the database secret:

BASH
kubectl -n jarls create secret generic root-db-credentials \
    --from-literal=DATABASE_USER=jarl \
    --from-literal=DATABASE_PASSWORD=foo123
  1. Create the Password Encryption Key (PEK) secret:

BASH
kubectl -n jarls create secret generic root-pek --from-literal=PASSWORD_ENCRYPTION_KEY=Rkjir6RVtRp6rPF1rpzhnbedVV01DYsJM1Ol94
  1. Create the root-override.yaml values file:

cat > ~/configs/root/root-override.yaml << EOF ... Click to see the full command
BASH
cat > ~/configs/root/root-override.yaml << EOF
ejbca:
  importEjbcaConfFiles: false
  importExternalCas: false
  importJvmTruststore: false
  configdumpImport:
    enabled: true
    initialize: true
    configMapName: ca-init-configmap
    configMapKey: configdump.json
    inlineConfigdump:
  importAppserverKeystore: false
  importAppserverTruststore: false
  useEphemeralH2Database: false
  useH2Persistence: false
  env:
    TLS_SETUP_ENABLED: "later"
    DATABASE_JDBC_URL: "jdbc:mariadb://forseti-mariadb.forseti.svc.cluster.local:3306/jarl?characterEncoding=utf8"
    LOG_AUDIT_TO_DB: false
    LOG_LEVEL_APP: "INFO"
    LOG_LEVEL_SERVER: "INFO"
    #DEBUG: full
    EJBCA_RNGALGORITHM: "BCSP800Hybrid"
    OBSERVABLE_BIND: "127.0.0.1"
    METRICS_ENABLED: false
    OCSP_CHECK_SIGN_CERT_VALIDITY: true
    OCSP_NON_EXISTING_IS_GOOD: false
    SMTP_DESTINATION: "localhost"
    SMTP_DESTINATION_PORT: "25"
    SMTP_FROM: no-reply@localhost
    SMTP_TLS_ENABLED: false
    SMTP_SSL_ENABLED: true
    SMTP_USERNAME: ejbca-mail
  envRaw:
    - name: DATABASE_USER
      valueFrom:
       secretKeyRef:
         name: root-db-credentials
         key: DATABASE_USER
    - name: DATABASE_PASSWORD
      valueFrom:
       secretKeyRef:
         name: root-db-credentials
         key: DATABASE_PASSWORD
    - name: PASSWORD_ENCRYPTION_KEY
      valueFrom:
       secretKeyRef:
         name: root-pek
         key: PASSWORD_ENCRYPTION_KEY
    - name: HSM_TOKEN_PIN
      valueFrom:
       secretKeyRef:
         name: configdump-secrets
         key: HSM_TOKEN_PIN         
  initContainers: []
  sidecarContainers: []
  volumes:
    - name: after-deployed-pre
      configMap:
        name: after-deployed-pre
        defaultMode: 0777
        items:
          - key: "after-deployed-pre.sh"
            path: "after-deployed-pre.sh"
  volumeMounts:
    - name: after-deployed-pre
      mountPath: /opt/keyfactor/bin/internal/after-deployed-pre.sh
      subPath: after-deployed-pre.sh
hsm:
  image:
  imagePullPolicy: IfNotPresent
  enabled: false
  softhsm:
    enabled: false
  luna:
    enabled: false   
  utimaco:
    enabled: false
  nshield:
    enabled: false
  awsCloudHsm:
    enabled: false
replicaCount: 1
updateStrategy: {}
image: 
  repository: keyfactor/ejbca-ee
  tag: latest
  pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
  create: true
  annotations: {}
  name: ""
podLabels: {}
podAnnotations: {}
podSecurityContext: {}
serviceAnnotations: {}
securityContext: {}
services:
  proxyHttp:
    enabled: false
  proxyAJP:
    enabled: false
  directHttp:
    enabled: false
  sidecarPorts: []
nginx:
  image: nginx:1.29.2
  enabled: true
  initializeWithSelfSignedTls: true
  host: "root.jarl.svc"
  externalConfiguration:
  mountInternalNginxCert: false
  #secretInternalNginxCert: "internal-nginx-credential-secret-ca"
  service:
    type: ClusterIP
    bindIP: 0.0.0.0
    httpPort: 80
    httpsPort: 443
  loadBalancerAccess:
    enableStickySessionClientIp: false
    enableReplicaSpecificAccess: false
  additionalHosts:
httpd:
  image: httpd:2.4
  enabled: false
ingress:
  enabled: false
resources:
  limits:
    cpu: "1"
    memory: "2048Mi"
  requests:
    cpu: 1000m
    memory: "2048Mi"
autoscaling:
  enabled: false
  behavior: {}
podDisruptionBudget:
  create: false
  minAvailable: 1
  maxUnavailable: ""
nodeSelector: {}
affinity: {}
tolerations: []
topologySpreadConstraints: []
priorityClassName: ""
schedulerName: ""
terminationGracePeriodSeconds: ""
networkPolicy:
EOF
  1. Deploy the Root CA

BASH
helm install -n jarls root -f ~/configs/root/root-override.yaml oci://repo.keyfactor.com/charts/ejbca
# Upgrade
helm upgrade -n jarls root -f configs/root/root-override.yaml oci://repo.keyfactor.com/charts/ejbca
  1. Export the Root CA certs from the Root instance:

BASH
kubectl -n jarls exec -it pod/root-ejbca-0 -c ejbca -- /opt/keyfactor/bin/ejbca.sh ca getcacert --caname Ecdsa-Root-G1 -f /var/tmp/Ecdsa-Root-G1.crt
kubectl -n jarls exec -it pod/root-ejbca-0 -c ejbca -- /opt/keyfactor/bin/ejbca.sh ca getcacert --caname Pqc-Root-G1 -f /var/tmp/Pqc-Root-G1.crt
kubectl -n jarls exec -it pod/root-ejbca-0 -c ejbca -- /opt/keyfactor/bin/ejbca.sh ca getcacert --caname Composite-Root-G1 -f /var/tmp/Composite-Root-G1.crt
kubectl -n jarls exec -it pod/root-ejbca-0 -c ejbca -- /opt/keyfactor/bin/ejbca.sh ca getcacert --caname Hybrid-Root-G1 -f /var/tmp/Hybrid-Root-G1.crt
EcdsaRootG1="$(kubectl -n jarls exec -it pod/root-ejbca-0 -c ejbca -- cat /var/tmp/Ecdsa-Root-G1.crt)"
PqcRootG1="$(kubectl -n jarls exec -it pod/root-ejbca-0 -c ejbca -- cat /var/tmp/Pqc-Root-G1.crt)"
CompositeRootG1="$(kubectl -n jarls exec -it pod/root-ejbca-0 -c ejbca -- cat /var/tmp/Composite-Root-G1.crt)"
HybridRootG1="$(kubectl -n jarls exec -it pod/root-ejbca-0 -c ejbca -- cat /var/tmp/Hybrid-Root-G1.crt)"
echo "$EcdsaRootG1" > ~/configs/root/Ecdsa-Root-G1.crt
echo "$PqcRootG1" > ~/configs/root/Pqc-Root-G1.crt
echo "$CompositeRootG1" > ~/configs/root/Composite-Root-G1.crt
echo "$HybridRootG1" > ~/configs/root/Hybrid-Root-G1.crt
  1. Create a configmap file of the root CA certificates:

BASH
kubectl -n karls create configmap root-ca-files --from-file=configs/root/Ecdsa-Root-G1.crt --from-file=configs/root/Pqc-Root-G1.crt --from-file=configs/root/Composite-Root-G1.crt --from-file=configs/root/Hybrid-Root-G1.crt

Step 5 - Deploy Sub CA

  1. Create the after-deployed-pre.sh configmap file:

BASH
cat > ~/configs/sub/after-deployed-pre.yaml << EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: after-deployed-pre
data:
  after-deployed-pre.sh: |
    #!/bin/bash
    if /opt/keyfactor/bin/ejbca.sh cryptotoken list | grep "SubCA"; then
        return 0
    else
        /opt/keyfactor/bin/ejbca.sh cryptotoken create --token SubCA --autoactivate true --pin "\${HSM_TOKEN_PIN}" --type FortanixCryptoToken
        # Generate keys on HSM
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token SubCA --alias signKeyEc001 --keyspec secp384r1 --key-usage SIGN
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token SubCA --alias defaultKeyEc001 --keyspec 4096 --key-usage SIGN_ENCRYPT
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token SubCA --alias signKeyPq001 --keyspec ML-DSA-65 --key-usage SIGN
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token SubCA --alias defaultKeyPq001 --keyspec 4096 --key-usage SIGN_ENCRYPT
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token SubCA --alias signKeyHyPq001 --keyspec ML-DSA-65 --key-usage SIGN
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token SubCA --alias signKeyHyEc001 --keyspec secp384r1 --key-usage SIGN
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token SubCA --alias defaultKeyHy001 --keyspec 4096 --key-usage SIGN_ENCRYPT
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token SubCA --alias testKey-ec --keyspec secp256r1 --key-usage SIGN
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token SubCA --alias testKey-ml-dsa --keyspec ML-DSA-65 --key-usage SIGN
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token SubCA --alias signKeyMgmt001 --keyspec secp384r1 --key-usage SIGN
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token SubCA --alias defaultKeyMgmt001 --keyspec 4096 --key-usage SIGN_ENCRYPT
    fi
    if /opt/keyfactor/bin/ejbca.sh cryptotoken list | grep "SubCompositeCa"; then
        return 0
    else
        # Create Soft Crypto Token for Composite keys
        /opt/keyfactor/bin/ejbca.sh cryptotoken create --token SubCompositeCa --autoactivate true --type SoftCryptoToken --pin foo123
        # Generate keys on soft token
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token SubCompositeCa --alias signKey001 --keyspec MLDSA87-ECDSA-P521-SHA512
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token SubCompositeCa --alias defaultKey001 --keyspec 4096
        /opt/keyfactor/bin/ejbca.sh cryptotoken generatekey --token SubCompositeCa --alias testKey --keyspec MLDSA87-ECDSA-P521-SHA512
    fi
EOF
  1. Create the configmap in K8s for the after-deployed-pre.shfile:

BASH
kubectl -n karls apply -f ~/configs/sub/after-deployed-pre.yaml
# Replace
kubectl -n karls replace -f ~/configs/sub/after-deployed-pre.yaml
# Delete
kubectl -n karls delete cm/after-deployed-pre
  1. Create the after-init.post file:

BASH
cat > ~/configs/sub/after-deployed-post.yaml << EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: after-deployed-post
data:
  after-deployed-post.sh: |
    #!/bin/bash
    if /opt/keyfactor/bin/ejbca.sh ca listcas | grep "Ecdsa-Sub-G1"; then
        return 0
    else
        /opt/keyfactor/bin/ejbca.sh ca init --caname Ecdsa-Sub-G1 --dn "CN=Kefactor TechMeetup Sub CA G1,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE" \
        -s SHA384withECDSA --tokenName SubCA --tokenprop /opt/keyfactor/tokenprops/Ecdsa-Sub-G1.properties --signedby External -externalcachain /opt/keyfactor/chain/Ecdsa-Root-G1.crt \
        --policy null -v 1825 --tokenPass "\$HSM_TOKEN_PIN"
        base64 Ecdsa-Sub-G1_csr.der > /var/tmp/Ecdsa-Sub-G1.pem
    fi
    if /opt/keyfactor/bin/ejbca.sh ca listcas | grep "Pqc-Sub-G1"; then
        return 0
    else
        /opt/keyfactor/bin/ejbca.sh ca init --caname Pqc-Sub-G1 --dn "CN=Kefactor TechMeetup PQC Sub CA G1,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE" \
        -s ML-DSA-65 --tokenName SubCA --tokenprop /opt/keyfactor/tokenprops/Pqc-Sub-G1.properties --signedby External -externalcachain /opt/keyfactor/chain/Pqc-Root-G1.crt \
        --policy null -v 1825 --tokenPass "\$HSM_TOKEN_PIN"
        base64 Pqc-Sub-G1_csr.der > /var/tmp/Pqc-Sub-G1.pem
    fi
    if /opt/keyfactor/bin/ejbca.sh ca listcas | grep "Composite-Sub-G1"; then
        return 0
    else
        /opt/keyfactor/bin/ejbca.sh ca init --caname Composite-Sub-G1 --dn "CN=Kefactor TechMeetup Composite Sub CA G1,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE" \
        -s MLDSA87-ECDSA-P521-SHA512 --tokenName SubCompositeCa --tokenprop /opt/keyfactor/tokenprops/Composite-Sub-G1.properties --signedby External -externalcachain /opt/keyfactor/chain/Composite-Root-G1.crt \
        --policy null -v 1825 --tokenPass "\$HSM_TOKEN_PIN"
        base64 Composite-Sub-G1_csr.der > /var/tmp/Composite-Sub-G1.pem
    fi
    if /opt/keyfactor/bin/ejbca.sh ca listcas | grep "Hybrid-Sub-G1"; then
        return 0
    else
        /opt/keyfactor/bin/ejbca.sh ca init --caname Hybrid-Sub-G1 --dn "CN=Kefactor TechMeetup Hybrid Sub CA G1,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE" \
        -s SHA384withECDSA --tokenName SubCA --tokenprop /opt/keyfactor/tokenprops/Hybrid-Sub-G1.properties --signedby External -externalcachain /opt/keyfactor/chain/Hybrid-Root-G1.crt \
        --policy null -v 1825 --altsigalg ML-DSA-65 --tokenPass "\$HSM_TOKEN_PIN"
        base64 Hybrid-Sub-G1_csr.der > /var/tmp/Hybrid-Sub-G1.pem
    fi
EOF
  1. Create a configmap in K8s for the after-init.post file:

BASH
kubectl -n karls apply -f ~/configs/sub/after-deployed-post.yaml
# Replace
kubectl -n karls replace -f ~/configs/sub/after-deployed-post.yaml
# Delete
kubectl -n karls delete cm/after-deployed-post
# View
kubectl -n karls get cm/after-deployed-post -o yaml
  1. Create token property files for the Sub CAs:

BASH
cat > ~/configs/sub/Ecdsa-Sub-G1.properties << EOF
certSignKey signKeyEc001
crlSignKey signKeyEc001
keyEncryptKey defaultKeyEc001
testKey testKey-ec
defaultKey defaultKeyEc001
EOF
cat > ~/configs/sub/Pqc-Sub-G1.properties << EOF
certSignKey signKeyPq001
crlSignKey signKeyPq001
keyEncryptKey defaultKeyPq001
testKey testKey-ml-dsa
defaultKey defaultKeyEc001
EOF
cat > ~/configs/sub/Composite-Sub-G1.properties << EOF
certSignKey signKey001
crlSignKey signKey001
keyEncryptKey defaultKey001
testKey testKey
defaultKey defaultKey001
EOF
cat > ~/configs/sub/Hybrid-Sub-G1.properties << EOF
certSignKey signKeyHyEc001
crlSignKey signKeyHyEc001
keyEncryptKey defaultKeyHy001
testKey testKey-ec
defaultKey defaultKeyHy001
alternativeCertSignKey signKeyHyPq001
EOF
  1. Create a configmap in K8s for the Sub CA token properties:

BASH
kubectl -n karls create configmap ca-token-properties --from-file=configs/sub/Ecdsa-Sub-G1.properties --from-file=configs/sub/Pqc-Sub-G1.properties --from-file=configs/sub/Composite-Sub-G1.properties --from-file=configs/sub/Hybrid-Sub-G1.properties
# Delete
kubectl -n karls delete cm/ca-token-properties
  1. Create a secret for the Fortanix HSM Token PIN:

BASH
kubectl -n karls create secret generic configdump-secrets --from-literal=HSM_TOKEN_PIN=YWVhN2Q4YjctZGM3My00ZD
  1. Create a configmap file for the management CA:

cat > ~/configs/sub/configdump.json << EOF ... Click to see the full command
BASH
cat > ~/configs/sub/configdump.json << EOF
{
"certification-authorities": {
    "ManagementCA": {
      "Object Type": "Certification Authority",
      "Version": 3,
      "Name": "ManagementCA",
      "Type of CA": "X.509",
      "Description": "Created using configdump",
      "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": "managementCA",
      "Default Certificate Profile": "Not used",
      "Use Append-Only Table": false,
      "CA Token": {
        "Signature Algorithm": "SHA384WithECDSA",
        "Encryption Algorithm": "SHA256WithRSA",
        "Crypto Token": "SubCA",
        "Default Key": "defaultKeyMgmt001",
        "Certificate Signing Key": "signKeyMgmt001",
        "CRL Signing Key": "signKeyMgmt001",
        "Key Encryption Key": "defaultKeyMgmt001",
        "Test Key": "testKey-ec",
        "Key Sequence Format": "Numeric",
        "Key Sequence": "00000"
      },
      "Enforce Unique Public Keys": true,
      "Enforce key renewal": false,
      "Enforce Unique DN": true,
      "User Storage": true,
      "Key encrypt padding algorithm": "RSA_OAEP",
      "Add Compromised Keys To Block List": false,
      "Certificate Storage": true,
      "Accept Revocations for Non-Existing Entries": false,
      "Subject DN": "CN=Keyfactor TechMeetup Management CA,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE",
      "Signed By": "Self Signed",
      "Validity": "10y",
      "Use UTF-8 in Policy Notice Text": false,
      "LDAP DN Order": false,
      "Authority Key Id Used": true,
      "CRL Number Used": true,
      "CA Issuer URI": [
        "http://aia.techmeetup.test/aia/mgmtca.crt"
      ],
      "Partitioned CRL Used": false,
      "CRL Expiration Period": "3d",
      "CRL Issue Interval": "1d",
      "CRL Overlap Time": "10m",
      "Delta CRL Period": "0m",
      "Generate CRL Upon Revocation": false,
      "Allow Changing Revocation Reason": false,
      "Default CRL Distribution Point": "http://crl.techmeetup.test/crls/mgmtca.crl",
      "OCSP Service Default URI": "http://ocsp.techmeetup.test/ocsp",
      "AIA CA Issuer URIs": [
        "http://aia.techmeetup.test/aia/mgmtca.crt"
      ],
      "CRL Publishers": [],
      "Finish User": true,
      "Request Processor": null
    }
  },
"certificate-profiles": {
      "managementCA": {
        "Object Type": "Certificate Profile",
        "Version": 1,
        "Name": "managementCA",
        "Type": "Root CA",
        "Available Key Algorithms": [
          "ECDSA"
        ],
        "Available Elliptic Curves": [
          "P-384"
        ],
        "Available Bit Lengths": [
          3072,
          4096
        ],
        "Signature Algorithm": "SHA384withECDSA",
        "Validity": "10y",
        "Description": "",
        "Overridable Extension OIDs": [],
        "Non-overridable Extension OIDs": [],
        "Allow Validity Override": true,
        "Path Length Constraint Used": true,
        "Authority Key Identifier Used": false,
        "Key Usage": [
          "Digital Signature",
          "Key Certificate Sign",
          "CRL Sign"
        ],
        "Extended Key Usage": [],
        "Subject Alternative Name Used": false,
        "Subject Alternative Name Searchable": false,
        "Issuer Alternative Name Used": false,
        "CVC Access Rights (Inspection System)": [],
        "LDAP DN Order": false,
        "Available CAs": [
          "Any CA"
        ],
        "SSH Extensions": {
          "no-touch-required": "",
          "permit-X11-forwarding": "",
          "permit-agent-forwarding": "",
          "permit-port-forwarding": "",
          "permit-pty": "",
          "permit-user-rc": ""
        },
        "Account Binding Namespace": []
     }
   }  
}
EOF
  1. Create the configmap for the SubCA configdump:

BASH
kubectl -n karls create configmap ca-init-configmap --from-file=configdump.json=configs/sub/configdump.json
# Delete
kubectl -n karls delete cm/ca-init-configmap
# View the contents
kubectl -n karls get cm/ca-init-configmap -o yaml
  1. Create the database secret:

BASH
kubectl -n karls create secret generic sub-db-credentials \
    --from-literal=DATABASE_USER=karl \
    --from-literal=DATABASE_PASSWORD=foo123
  1. Create the Password Encryption Key (PEK) secret:

BASH
kubectl -n karls create secret generic sub-pek --from-literal=PASSWORD_ENCRYPTION_KEY=Rkjir6RVtRp6rPF1rpzhdfejwRV01DYsJM1Ol94
  1. Create the sub-override.yaml values file:

cat > ~/configs/sub/sub-override.yaml << EOF ... Click to see the full command
BASH
cat > ~/configs/sub/sub-override.yaml << EOF
ejbca:
  importEjbcaConfFiles: false
  importExternalCas: false
  importJvmTruststore: false
  configdumpImport:
    enabled: true
    initialize: true
    configMapName: ca-init-configmap
    configMapKey: configdump.json
    inlineConfigdump:
  importAppserverKeystore: false
  importAppserverTruststore: false
  useEphemeralH2Database: false
  useH2Persistence: false
  env:
    TLS_SETUP_ENABLED: "later"
    DATABASE_JDBC_URL: "jdbc:mariadb://forseti-mariadb.forseti.svc.cluster.local:3306/karl?characterEncoding=utf8"
    LOG_AUDIT_TO_DB: false
    LOG_LEVEL_APP: "INFO"
    LOG_LEVEL_SERVER: "INFO"
    #DEBUG: full
    EJBCA_RNGALGORITHM: "BCSP800Hybrid"
    OBSERVABLE_BIND: "127.0.0.1"
    METRICS_ENABLED: false
    OCSP_CHECK_SIGN_CERT_VALIDITY: true
    OCSP_NON_EXISTING_IS_GOOD: false
    SMTP_DESTINATION: "localhost"
    SMTP_DESTINATION_PORT: "25"
    SMTP_FROM: no-reply@localhost
    SMTP_TLS_ENABLED: false
    SMTP_SSL_ENABLED: true
    SMTP_USERNAME: ejbca-mail
  envRaw:
    - name: DATABASE_USER
      valueFrom:
       secretKeyRef:
         name: sub-db-credentials
         key: DATABASE_USER
    - name: DATABASE_PASSWORD
      valueFrom:
       secretKeyRef:
         name: sub-db-credentials
         key: DATABASE_PASSWORD
    - name: PASSWORD_ENCRYPTION_KEY
      valueFrom:
       secretKeyRef:
         name: sub-pek
         key: PASSWORD_ENCRYPTION_KEY
    - name: HSM_TOKEN_PIN
      valueFrom:
       secretKeyRef:
         name: configdump-secrets
         key: HSM_TOKEN_PIN         
  initContainers: []
  sidecarContainers: []
  volumes:
    - name: after-deployed-pre
      configMap:
        name: after-deployed-pre
        defaultMode: 0777
        items:
          - key: "after-deployed-pre.sh"
            path: "after-deployed-pre.sh"
    - name: after-deployed-post
      configMap:
        name: after-deployed-post
        defaultMode: 0777
        items:
          - key: "after-deployed-post.sh"
            path: "after-deployed-post.sh"
    - name: root-ca-files
      configMap:
        name: root-ca-files
    - name: ca-token-properties
      configMap:
        name: ca-token-properties
  volumeMounts:
    - name: after-deployed-pre
      mountPath: /opt/keyfactor/bin/internal/after-deployed-pre.sh
      subPath: after-deployed-pre.sh
    - name: after-deployed-post
      mountPath: /opt/keyfactor/bin/internal/after-deployed-post.sh
      subPath: after-deployed-post.sh
    - name: root-ca-files
      mountPath: /opt/keyfactor/chain
    - name: ca-token-properties
      mountPath: /opt/keyfactor/tokenprops
hsm:
  image:
  imagePullPolicy: IfNotPresent
  enabled: false
  softhsm:
    enabled: false
  luna:
    enabled: false   
  utimaco:
    enabled: false
  nshield:
    enabled: false
  awsCloudHsm:
    enabled: false
replicaCount: 1
updateStrategy: {}
image: 
  repository: keyfactor/ejbca-ee
  tag: latest
  pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
  create: true
  annotations: {}
  name: ""
podLabels: {}
podAnnotations: {}
podSecurityContext: {}
serviceAnnotations: {}
securityContext: {}
services:
  proxyHttp:
    enabled: false
  proxyAJP:
    enabled: false
  directHttp:
    enabled: false
  sidecarPorts: []
nginx:
  image: nginx:1.29.2
  enabled: true
  initializeWithSelfSignedTls: true
  host: "enroll.techmeetup.test"
  externalConfiguration:
  mountInternalNginxCert: false
  #secretInternalNginxCert: "internal-nginx-credential-secret-ca"
  service:
    type: LoadBalancer
    bindIP: 0.0.0.0
    httpPort: 80
    httpsPort: 443
  loadBalancerAccess:
    enableStickySessionClientIp: true
    enableReplicaSpecificAccess: false
  additionalHosts:
httpd:
  image: httpd:2.4
  enabled: false
ingress:
  enabled: false
resources:
  limits:
    cpu: "2"
    memory: "2048Mi"
  requests:
    cpu: "1"
    memory: "2048Mi"
autoscaling:
  enabled: false
  behavior: {}
podDisruptionBudget:
  create: false
  minAvailable: 1
  maxUnavailable: ""
nodeSelector: {}
affinity: {}
tolerations: []
topologySpreadConstraints: []
priorityClassName: ""
schedulerName: ""
terminationGracePeriodSeconds: ""
networkPolicy:
EOF
  1. Deploy the Issuing CA

BASH
helm install -n karls sub -f ~/configs/sub/sub-override.yaml oci://repo.keyfactor.com/charts/ejbca
# Tails logs
kubectl -n karls logs -f pod/sub-ejbca-0 -c ejbca
  1. Export CSRs to the host machine

BASH
EcdsasubG1="$(kubectl -n karls exec -it pod/sub-ejbca-0 -c ejbca -- cat /var/tmp/Ecdsa-Sub-G1.pem)"
PqcsubG1="$(kubectl -n karls exec -it pod/sub-ejbca-0 -c ejbca -- cat /var/tmp/Pqc-Sub-G1.pem)"
CompositesubG1="$(kubectl -n karls exec -it pod/sub-ejbca-0 -c ejbca -- cat /var/tmp/Composite-Sub-G1.pem)"
HybridsubG1="$(kubectl -n karls exec -it pod/sub-ejbca-0 -c ejbca -- cat /var/tmp/Hybrid-Sub-G1.pem)"

echo "-----BEGIN CERTIFICATE REQUEST-----
$EcdsasubG1
-----END CERTIFICATE REQUEST-----" > ~/configs/sub/Ecdsa-Sub-G1.csr
echo "-----BEGIN CERTIFICATE REQUEST-----
$PqcsubG1
-----END CERTIFICATE REQUEST-----" > ~/configs/sub/Pqc-Sub-G1.csr
echo "-----BEGIN CERTIFICATE REQUEST-----
$CompositesubG1
-----END CERTIFICATE REQUEST-----" > ~/configs/sub/Composite-Sub-G1.csr
echo "-----BEGIN CERTIFICATE REQUEST-----
$HybridsubG1
-----END CERTIFICATE REQUEST-----" > ~/configs/sub/Hybrid-Sub-G1.csr
  1. Create a configmap of the Sub CA CSRs for the Root CA:

BASH
kubectl -n jarls create configmap sub-ca-csrs --from-file=configs/sub/Ecdsa-Sub-G1.csr --from-file=configs/sub/Pqc-Sub-G1.csr --from-file=configs/sub/Composite-Sub-G1.csr --from-file=configs/sub/Hybrid-Sub-G1.csr
# Delete
kubectl -n jarls delete cm/sub-ca-csrs
# View
kubectl -n jarls get cm/sub-ca-csrs -o yaml
  1. Create the root-after-deployed-post.yaml file:

BASH
cat > ~/configs/root/after-deployed-post.yaml << EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: after-deployed-post
data:
  after-deployed-post.sh: |
    #!/bin/bash

    /opt/keyfactor/bin/ejbca.sh ra addendentity --username Ecdsa-Sub-G1 --dn "CN=Kefactor TechMeetup Sub CA G1,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE" \
        --caname Ecdsa-Root-G1 --type 1 --token USERGENERATED --password foo123 \
        --certprofile SubCa-Ec-G1 --eeprofile subCa

    /opt/keyfactor/bin/ejbca.sh ra addendentity --username Pqc-Sub-G1 --dn "CN=Kefactor TechMeetup PQC Sub CA G1,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE" \
        --caname Pqc-Root-G1 --type 1 --token USERGENERATED --password foo123 \
        --certprofile SubCa-MlDsa-G1 --eeprofile subCa

    /opt/keyfactor/bin/ejbca.sh ra addendentity --username Composite-Sub-G1 --dn "CN=Kefactor TechMeetup Composite Sub CA G1,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE" \
        --caname Composite-Root-G1 --type 1 --token USERGENERATED --password foo123 \
        --certprofile SubCaComposite-EcMlDsa-G1 --eeprofile subCa

    /opt/keyfactor/bin/ejbca.sh ra addendentity --username Hybrid-Sub-G1 --dn "CN=Kefactor TechMeetup Hybrid Sub CA G1,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE" \
        --caname Hybrid-Root-G1 --type 1 --token USERGENERATED --password foo123 \
        --certprofile SubCaHybrid-EcMlDsa-G1 --eeprofile subCa

    /opt/keyfactor/bin/ejbca.sh createcert --username Ecdsa-Sub-G1 --password foo123 -c /opt/keyfactor/subcsrs/Ecdsa-Sub-G1.csr -f /var/tmp/Ecdsa-Sub-G1.crt
    /opt/keyfactor/bin/ejbca.sh createcert --username Pqc-Sub-G1 --password foo123 -c /opt/keyfactor/subcsrs/Pqc-Sub-G1.csr -f /var/tmp/Pqc-Sub-G1.crt
    /opt/keyfactor/bin/ejbca.sh createcert --username Composite-Sub-G1 --password foo123 -c /opt/keyfactor/subcsrs/Composite-Sub-G1.csr -f /var/tmp/Composite-Sub-G1.crt
    /opt/keyfactor/bin/ejbca.sh createcert --username Hybrid-Sub-G1 --password foo123 -c /opt/keyfactor/subcsrs/Hybrid-Sub-G1.csr -f /var/tmp/Hybrid-Sub-G1.crt
EOF
  1. Create k8's configmap of the Root after-deployed-post.yaml file:

BASH
kubectl -n jarls apply -f ~/configs/root/after-deployed-post.yaml
# Replace
kubectl -n jarls replace -f ~/configs/root/after-deployed-post.yaml
# Delete
kubectl -n jarls delete cm/after-deployed-post
# View
kubectl -n jarls get cm/after-deployed-post -o yaml
  1. Create a new override values root-override-p2.yaml file:

cat > ~/configs/root/root-override-p2.yaml << EOF ... Click to see the full command
BASH
cat > ~/configs/root/root-override-p2.yaml << EOF
ejbca:
  importEjbcaConfFiles: false
  importExternalCas: false
  importJvmTruststore: false
  configdumpImport:
    enabled: false
    inlineConfigdump:
  importAppserverKeystore: false
  importAppserverTruststore: false
  useEphemeralH2Database: false
  useH2Persistence: false
  env:
    TLS_SETUP_ENABLED: "later"
    DATABASE_JDBC_URL: "jdbc:mariadb://forseti-mariadb.forseti.svc.cluster.local:3306/jarl?characterEncoding=utf8"
    LOG_AUDIT_TO_DB: false
    LOG_LEVEL_APP: "INFO"
    LOG_LEVEL_SERVER: "INFO"
    #DEBUG: full
    EJBCA_RNGALGORITHM: "BCSP800Hybrid"
    OBSERVABLE_BIND: "127.0.0.1"
    METRICS_ENABLED: false
    OCSP_CHECK_SIGN_CERT_VALIDITY: true
    OCSP_NON_EXISTING_IS_GOOD: false
    SMTP_DESTINATION: "localhost"
    SMTP_DESTINATION_PORT: "25"
    SMTP_FROM: no-reply@localhost
    SMTP_TLS_ENABLED: false
    SMTP_SSL_ENABLED: true
    SMTP_USERNAME: ejbca-mail
  envRaw:
    - name: DATABASE_USER
      valueFrom:
       secretKeyRef:
         name: root-db-credentials
         key: DATABASE_USER
    - name: DATABASE_PASSWORD
      valueFrom:
       secretKeyRef:
         name: root-db-credentials
         key: DATABASE_PASSWORD
    - name: PASSWORD_ENCRYPTION_KEY
      valueFrom:
       secretKeyRef:
         name: root-pek
         key: PASSWORD_ENCRYPTION_KEY
    - name: HSM_TOKEN_PIN
      valueFrom:
       secretKeyRef:
         name: configdump-secrets
         key: HSM_TOKEN_PIN         
  initContainers: []
  sidecarContainers: []
  volumes:
    - name: after-deployed-post
      configMap:
        name: after-deployed-post
        defaultMode: 0777
        items:
          - key: "after-deployed-post.sh"
            path: "after-deployed-post.sh"
    - name: sub-ca-csrs
      configMap:
        name: sub-ca-csrs
  volumeMounts:
    - name: after-deployed-post
      mountPath: /opt/keyfactor/bin/internal/after-deployed-post.sh
      subPath: after-deployed-post.sh
    - name: sub-ca-csrs
      mountPath: /opt/keyfactor/subcsrs
hsm:
  image:
  imagePullPolicy: IfNotPresent
  enabled: false
  softhsm:
    enabled: false
  luna:
    enabled: false   
  utimaco:
    enabled: false
  nshield:
    enabled: false
  awsCloudHsm:
    enabled: false
replicaCount: 1
updateStrategy: {}
image: 
  repository: keyfactor/ejbca-ee
  tag: latest
  pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
  create: true
  annotations: {}
  name: ""
podLabels: {}
podAnnotations: {}
podSecurityContext: {}
serviceAnnotations: {}
securityContext: {}
services:
  proxyHttp:
    enabled: false
  proxyAJP:
    enabled: false
  directHttp:
    enabled: false
  sidecarPorts: []
nginx:
  image: nginx:1.29.2
  enabled: true
  initializeWithSelfSignedTls: true
  host: "root.jarl.svc"
  externalConfiguration:
  mountInternalNginxCert: false
  #secretInternalNginxCert: "internal-nginx-credential-secret-ca"
  service:
    type: ClusterIP
    bindIP: 0.0.0.0
    httpPort: 80
    httpsPort: 443
  loadBalancerAccess:
    enableStickySessionClientIp: false
    enableReplicaSpecificAccess: false
  additionalHosts:
httpd:
  image: httpd:2.4
  enabled: false
ingress:
  enabled: false
resources:
  limits:
    cpu: "1"
    memory: "2048Mi"
  requests:
    cpu: 1000m
    memory: "2048Mi"
autoscaling:
  enabled: false
  behavior: {}
podDisruptionBudget:
  create: false
  minAvailable: 1
  maxUnavailable: ""
nodeSelector: {}
affinity: {}
tolerations: []
topologySpreadConstraints: []
priorityClassName: ""
schedulerName: ""
terminationGracePeriodSeconds: ""
networkPolicy:
EOF
  1. Use Helm to upgrade the deployment for the Root CA

BASH
helm upgrade -n jarls root -f configs/root/root-override-p2.yaml oci://repo.keyfactor.com/charts/ejbca
#Install
helm install -n jarls root -f configs/root/root-override-p2.yaml oci://repo.keyfactor.com/charts/ejbca
# Tails logs
kubectl -n jarls logs -f pod/root-ejbca-0 -c ejbca
# Exec 
kubectl -n jarls exec -it pod/root-ejbca-0 -c ejbca -- /bin/bash
  1. Export signed CA certificates to host machine

BASH
EcdsaSubG1="$(kubectl -n jarls exec -it pod/root-ejbca-0 -c ejbca -- cat /var/tmp/Ecdsa-Sub-G1.crt)"
PqcSubG1="$(kubectl -n jarls exec -it pod/root-ejbca-0 -c ejbca -- cat /var/tmp/Pqc-Sub-G1.crt)"
CompositeSubG1="$(kubectl -n jarls exec -it pod/root-ejbca-0 -c ejbca -- cat /var/tmp/Composite-Sub-G1.crt)"
HybridSubG1="$(kubectl -n jarls exec -it pod/root-ejbca-0 -c ejbca -- cat /var/tmp/Hybrid-Sub-G1.crt)"
echo "$EcdsaSubG1" > ~/configs/sub/Ecdsa-Sub-G1.crt
echo "$PqcSubG1" > ~/configs/sub/Pqc-Sub-G1.crt
echo "$CompositeSubG1" > ~/configs/sub/Composite-Sub-G1.crt
echo "$HybridSubG1" > ~/configs/sub/Hybrid-Sub-G1.crt
  1. Create a configmap file of the signed Sub CA certificates:

BASH
kubectl -n karls create configmap sub-ca-crts --from-file=configs/sub/Ecdsa-Sub-G1.crt --from-file=configs/sub/Pqc-Sub-G1.crt --from-file=configs/sub/Composite-Sub-G1.crt --from-file=configs/sub/Hybrid-Sub-G1.crt
# Delete
kubectl -n karls delete cm/sub-ca-crts
# View
kubectl -n karls get cm/sub-ca-crts -o yaml
  1. Create the after-deployed-pre-p2.yaml file:

BASH
cat > ~/configs/sub/after-deployed-pre-p2.yaml << EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: after-deployed-pre-p2
data:
  after-deployed-pre.sh: |
    #!/bin/bash
    baseDir="\$1"
    tempDir="\$2"
    ID="\$3"

    /opt/keyfactor/bin/ejbca.sh ca importcacert --caname Ecdsa-Sub-G1 -f /opt/keyfactor/importcacrt/Ecdsa-Sub-G1.crt
    /opt/keyfactor/bin/ejbca.sh ca importcacert --caname Pqc-Sub-G1 -f /opt/keyfactor/importcacrt/Pqc-Sub-G1.crt
    /opt/keyfactor/bin/ejbca.sh ca importcacert --caname Composite-Sub-G1 -f /opt/keyfactor/importcacrt/Composite-Sub-G1.crt
    /opt/keyfactor/bin/ejbca.sh ca importcacert --caname Hybrid-Sub-G1 -f /opt/keyfactor/importcacrt/Hybrid-Sub-G1.crt

    \${baseDir}/bin/configdump.sh import -l "\${baseDir}/configdump/stage.d/configdump.json" \
                --overwrite update --ignore-errors --non-interactive continue --resolve-reference default --expand-variables

    /opt/keyfactor/bin/ejbca.sh ra addendentity --username \$HTTPSERVER_HOSTNAME  --dn "CN=\$HTTPSERVER_HOSTNAME,OU=TLS Servers,O=Keyfactor TechMeetup,C=SE" \
      --caname ManagementCA --type 1 --token PEM --altname dNSName=\$HTTPSERVER_HOSTNAME \
      --certprofile tlsServer-Ec-30d --eeprofile tlsServer --password NOTUSED
    /opt/keyfactor/bin/ejbca.shra setendentitystatus --username \$HTTPSERVER_HOSTNAME -S 10
    /opt/keyfactor/bin/ejbca.sh ra setclearpwd \$HTTPSERVER_HOSTNAME NOTUSED
    /opt/keyfactor/bin/ejbca.sh batch \$HTTPSERVER_HOSTNAME -dir /var/tmp/ --keyalg ECDSA --keyspec secp384r1

    /opt/keyfactor/bin/ejbca.sh ra addendentity --username \$ADMIN_USERNAME  --dn "CN=\$ADMIN_USERNAME,OU=User Authentication,O=Keyfactor TechMeetup,C=SE" \
      --caname ManagementCA --type 1 --token P12 --certprofile userAuth-1yr --eeprofile userAuth --password foo123
    /opt/keyfactor/bin/ejbca.sh ra setendentitystatus --username \$ADMIN_USERNAME -S 10
EOF
  1. Create the configmap in K8s for the after-deployed-pre-p2.yaml file:

BASH
kubectl -n karls apply -f ~/configs/sub/after-deployed-pre-p2.yaml
# Replace
kubectl -n karls replace -f ~/configs/sub/after-deployed-pre-p2.yaml
# Delete
kubectl -n karls delete cm/after-deployed-pre-p2
# View
kubectl -n karls get cm/after-deployed-pre -o yaml
  1. Create the configdump of CA settings for post configuration (enrollment aliases, CP, EEPs, services):

cat > ~/configs/sub/post-configdump.json << EOF ... – Click to see the full command
CODE
cat > ~/configs/sub/post-configdump.json << EOF
{
  "certification-authorities": {
    "Pqc-Sub-G1": {
      "Object Type": "Certification Authority",
      "Version": 3,
      "Name": "Pqc-Sub-G1",
      "Type of CA": "X.509",
      "Description": "Pqc-Sub-G1created using CLI",
      "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": "SUBCA",
      "Default Certificate Profile": "Not used",
      "Use Append-Only Table": false,
      "CA Token": {
        "Signature Algorithm": "ML-DSA-65",
        "Encryption Algorithm": "SHA256WithRSA",
        "Crypto Token": "SubCA",
        "Default Key": "defaultKeyEc001",
        "Certificate Signing Key": "signKeyPq001",
        "CRL Signing Key": "signKeyPq001",
        "Key Encryption Key": "defaultKeyPq001",
        "Test Key": "testKey-ml-dsa",
        "Key Sequence Format": "Numeric",
        "Key Sequence": "00000"
      },
      "Enforce Unique Public Keys": true,
      "Enforce key renewal": false,
      "Enforce Unique DN": true,
      "User Storage": true,
      "Key encrypt padding algorithm": "RSA_OAEP",
      "Add Compromised Keys To Block List": false,
      "Certificate Storage": true,
      "Accept Revocations for Non-Existing Entries": false,
      "Subject DN": "CN=Kefactor TechMeetup PQC Sub CA G1,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE",
      "Signed By": "Signed by External CA",
      "Validity": "1825d",
      "Subject Alternative Name": null,
      "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,
      "Default CRL Distribution Point": "http://crl.techmeetup.test/crls/pqc-sub-g1.crl",
      "OCSP Service Default URI": "http://ocsp.techmeetup.test/ocsp",
      "AIA CA Issuer URIs": [
        "http://aia.techmeetup.test/aia/pqc-sub-g1.crt"
      ],
      "Finish User": true,
      "Request Processor": null
    },
    "Composite-Sub-G1": {
      "Object Type": "Certification Authority",
      "Version": 3,
      "Name": "Composite-Sub-G1",
      "Type of CA": "X.509",
      "Description": "Composite-Sub-G1created using CLI",
      "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": "SUBCA",
      "Default Certificate Profile": "Not used",
      "Use Append-Only Table": false,
      "CA Token": {
        "Signature Algorithm": "MLDSA87-ECDSA-P521-SHA512",
        "Encryption Algorithm": "SHA512WithRSA",
        "Crypto Token": "SubCompositeCa",
        "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": true,
      "Enforce key renewal": false,
      "Enforce Unique DN": true,
      "User Storage": true,
      "Key encrypt padding algorithm": "RSA_OAEP",
      "Add Compromised Keys To Block List": false,
      "Certificate Storage": true,
      "Accept Revocations for Non-Existing Entries": false,
      "Subject DN": "CN=Kefactor TechMeetup Composite Sub CA G1,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE",
      "Signed By": "Signed by External CA",
      "Validity": "0d",
      "Subject Alternative Name": "None",
      "Use UTF-8 in Policy Notice Text": true,
      "LDAP DN Order": false,
      "Authority Key Id Used": true,
      "CRL Number Used": true,
      "Partitioned CRL Used": false,
      "CRL Expiration Period": "3d",
      "CRL Issue Interval": "1d",
      "CRL Overlap Time": "0m",
      "Delta CRL Period": "0m",
      "Generate CRL Upon Revocation": false,
      "Allow Changing Revocation Reason": false,
      "Default CRL Distribution Point": "http://crl.techmeetup.test/crls/composite-sub-g1.crl",
      "OCSP Service Default URI": "http://ocsp.techmeetup.test/ocsp",
      "AIA CA Issuer URIs": [
        "http://aia.techmeetup.test/aia/composite-sub-g1.crt"
      ],
      "Finish User": true,
      "Request Processor": null
    },
    "Ecdsa-Sub-G1": {
      "Object Type": "Certification Authority",
      "Version": 3,
      "Name": "Ecdsa-Sub-G1",
      "Type of CA": "X.509",
      "Description": "Ecdsa-Sub-G1created using CLI",
      "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": "SUBCA",
      "Default Certificate Profile": "Not used",
      "Use Append-Only Table": false,
      "CA Token": {
        "Signature Algorithm": "SHA384withECDSA",
        "Encryption Algorithm": "SHA384WithRSA",
        "Crypto Token": "SubCA",
        "Default Key": "defaultKeyEc001",
        "Certificate Signing Key": "signKeyEc001",
        "CRL Signing Key": "signKeyEc001",
        "Key Encryption Key": "defaultKeyEc001",
        "Test Key": "testKey-ec",
        "Key Sequence Format": "Numeric",
        "Key Sequence": "00000"
      },
      "Enforce Unique Public Keys": true,
      "Enforce key renewal": false,
      "Enforce Unique DN": true,
      "User Storage": true,
      "Key encrypt padding algorithm": "RSA_OAEP",
      "Add Compromised Keys To Block List": false,
      "Certificate Storage": true,
      "Accept Revocations for Non-Existing Entries": false,
      "Subject DN": "CN=Kefactor TechMeetup Sub CA G1,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE",
      "Signed By": "Signed by External CA",
      "Validity": "1825d",
      "Subject Alternative Name": null,
      "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,
      "Default CRL Distribution Point": "http://crl.techmeetup.test/crls/ecdsa-sub-g1.crl",
      "OCSP Service Default URI": "http://ocsp.techmeetup.test/ocsp",
      "AIA CA Issuer URIs": [
        "http://aia.techmeetup.test/aia/ecdsa-sub-g1.crt"
      ],
      "Finish User": true,
      "Request Processor": null
    },
    "Hybrid-Sub-G1": {
      "Object Type": "Certification Authority",
      "Version": 3,
      "Name": "Hybrid-Sub-G1",
      "Type of CA": "X.509",
      "Description": "Hybrid-Sub-G1created using CLI",
      "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": "SUBCA",
      "Default Certificate Profile": "Not used",
      "Use Append-Only Table": false,
      "CA Token": {
        "Signature Algorithm": "SHA384withECDSA",
        "Alternative Signature Algorithm": "ML-DSA-65",
        "Encryption Algorithm": "SHA384WithRSA",
        "Crypto Token": "SubCA",
        "Default Key": "defaultKeyHy001",
        "Certificate Signing Key": "signKeyHyEc001",
        "Alternative Certificate Signing Key": "signKeyHyPq001",
        "CRL Signing Key": "signKeyHyEc001",
        "Key Encryption Key": "defaultKeyHy001",
        "Test Key": "testKey-ec",
        "Key Sequence Format": "Numeric",
        "Key Sequence": "00000"
      },
      "Enforce Unique Public Keys": true,
      "Enforce key renewal": false,
      "Enforce Unique DN": true,
      "User Storage": true,
      "Key encrypt padding algorithm": "RSA_OAEP",
      "Add Compromised Keys To Block List": false,
      "Certificate Storage": true,
      "Accept Revocations for Non-Existing Entries": false,
      "Subject DN": "CN=Kefactor TechMeetup Hybrid Sub CA G1,OU=Certification Authorities,O=Keyfactor TechMeetup,C=SE",
      "Signed By": "Signed by External CA",
      "Validity": "1825d",
      "Subject Alternative Name": null,
      "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,
      "Default CRL Distribution Point": "http://crl.techmeetup.test/crls/hybrid-sub-g1.crl",
      "OCSP Service Default URI": "http://ocsp.techmeetup.test/ocsp",
      "AIA CA Issuer URIs": [
        "http://aia.techmeetup.test/aia/hybrid-sub-g1.crt"
      ],
      "Finish User": true,
      "Request Processor": null
    }
  },
  "certificate-profiles": {
    "cmsSigner-Hybrid-30d": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "cmsSigner-Hybrid-30d",
      "Type": "End Entity",
      "Available Key Algorithms": [
        "ECDSA"
      ],
      "Available Elliptic Curves": [
        "P-384"
      ],
      "Available Bit Lengths": [
        0,
        110,
        112,
        113,
        126,
        128,
        131,
        160,
        161,
        162,
        163,
        189,
        190,
        191,
        192,
        193,
        224,
        225,
        232,
        233,
        236,
        237,
        238,
        239,
        256,
        257,
        281,
        282,
        289,
        320,
        353,
        384,
        407,
        409,
        418,
        512,
        521,
        570,
        1024,
        1536,
        2048,
        3072,
        4096,
        6144,
        8192
      ],
      "Signature Algorithm": "Inherit from Issuing CA",
      "Alternative Signature Used": true,
      "Available Alternative Key Algorithms": [
        "ML-DSA-44"
      ],
      "Alternative Signature Algorithm": "Inherit from Issuing CA",
      "Validity": "1mo",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Basic Constraints Used": false,
      "Forbid encryption usage for ECC keys": true,
      "Key Usage": [
        "Digital Signature"
      ],
      "Extended Key Usage Used": true,
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "CRL Distribution Point Used": true,
      "CRL Use CA Defined Settings": true,
      "Authority Information Access Used": true,
      "OCSP Service Locator URL Is CA Defined": true,
      "OCSP Issuer URL Is CA Defined": true,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [
        "DG3",
        "DG4"
      ],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": [],
      "Extended Key Usage": [
        "Code Signing",
        "E-mail Protection"
      ]
    },
    "cmsSigner-MlDsa-30d": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "cmsSigner-MlDsa-30d",
      "Type": "End Entity",
      "Available Key Algorithms": [
        "ML-DSA-44"
      ],
      "Available Elliptic Curves": [
        "P-384"
      ],
      "Available Bit Lengths": [
        0,
        110,
        112,
        113,
        126,
        128,
        131,
        160,
        161,
        162,
        163,
        189,
        190,
        191,
        192,
        193,
        224,
        225,
        232,
        233,
        236,
        237,
        238,
        239,
        256,
        257,
        281,
        282,
        289,
        320,
        353,
        384,
        407,
        409,
        418,
        512,
        521,
        570,
        1024,
        1536,
        2048,
        3072,
        4096,
        6144,
        8192
      ],
      "Signature Algorithm": "Inherit from Issuing CA",
      "Validity": "1mo",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Basic Constraints Used": false,
      "Forbid encryption usage for ECC keys": true,
      "Key Usage": [
        "Digital Signature"
      ],
      "Extended Key Usage Used": true,
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "CRL Distribution Point Used": true,
      "CRL Use CA Defined Settings": true,
      "Authority Information Access Used": true,
      "OCSP Service Locator URL Is CA Defined": true,
      "OCSP Issuer URL Is CA Defined": true,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [
        "DG3",
        "DG4"
      ],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": [],
      "Extended Key Usage": [
        "Code Signing",
        "E-mail Protection"
      ]
    },
    "cmsSigner-Composite-30d": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "cmsSigner-Composite-30d",
      "Type": "End Entity",
      "Available Key Algorithms": [
        "MLDSA87-ECDSA-P521-SHA512"
      ],
      "Available Elliptic Curves": [
        "P-384"
      ],
      "Available Bit Lengths": [
        0,
        110,
        112,
        113,
        126,
        128,
        131,
        160,
        161,
        162,
        163,
        189,
        190,
        191,
        192,
        193,
        224,
        225,
        232,
        233,
        236,
        237,
        238,
        239,
        256,
        257,
        281,
        282,
        289,
        320,
        353,
        384,
        407,
        409,
        418,
        512,
        521,
        570,
        1024,
        1536,
        2048,
        3072,
        4096,
        6144,
        8192
      ],
      "Signature Algorithm": "Inherit from Issuing CA",
      "Validity": "1mo",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Basic Constraints Used": false,
      "Forbid encryption usage for ECC keys": true,
      "Key Usage": [
        "Digital Signature"
      ],
      "Extended Key Usage Used": true,
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "CRL Distribution Point Used": true,
      "CRL Use CA Defined Settings": true,
      "Authority Information Access Used": true,
      "OCSP Service Locator URL Is CA Defined": true,
      "OCSP Issuer URL Is CA Defined": true,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [
        "DG3",
        "DG4"
      ],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": [],
      "Extended Key Usage": [
        "Code Signing",
        "E-mail Protection"
      ]
    },
    "tlsServer-Hybrid-30d": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "tlsServer-Hybrid-30d",
      "Type": "End Entity",
      "Available Key Algorithms": [
        "ECDSA"
      ],
      "Available Elliptic Curves": [
        "P-384"
      ],
      "Available Bit Lengths": [
        0,
        110,
        112,
        113,
        126,
        128,
        131,
        160,
        161,
        162,
        163,
        189,
        190,
        191,
        192,
        193,
        224,
        225,
        232,
        233,
        236,
        237,
        238,
        239,
        256,
        257,
        281,
        282,
        289,
        320,
        353,
        384,
        407,
        409,
        418,
        512,
        521,
        570,
        1024,
        1536,
        2048,
        3072,
        4096,
        6144,
        8192
      ],
      "Signature Algorithm": "Inherit from Issuing CA",
      "Alternative Signature Used": true,
      "Available Alternative Key Algorithms": [
        "ML-DSA-44"
      ],
      "Alternative Signature Algorithm": "Inherit from Issuing CA",
      "Validity": "1mo",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Basic Constraints Used": false,
      "Forbid encryption usage for ECC keys": true,
      "Key Usage": [
        "Digital Signature"
      ],
      "Extended Key Usage Used": true,
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "CRL Distribution Point Used": true,
      "CRL Use CA Defined Settings": true,
      "Authority Information Access Used": true,
      "OCSP Service Locator URL Is CA Defined": true,
      "OCSP Issuer URL Is CA Defined": true,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [
        "DG3",
        "DG4"
      ],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": [],
      "Extended Key Usage": [
        "Server Authentication"
      ]
    },
    "tlsServer-Ec-30d": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "tlsServer-Ec-30d",
      "Type": "End Entity",
      "Available Key Algorithms": [
        "ECDSA"
      ],
      "Available Elliptic Curves": [
        "P-384"
      ],
      "Available Bit Lengths": [
        0,
        110,
        112,
        113,
        126,
        128,
        131,
        160,
        161,
        162,
        163,
        189,
        190,
        191,
        192,
        193,
        224,
        225,
        232,
        233,
        236,
        237,
        238,
        239,
        256,
        257,
        281,
        282,
        289,
        320,
        353,
        384,
        407,
        409,
        418,
        512,
        521,
        570,
        1024,
        1536,
        2048,
        3072,
        4096,
        6144,
        8192
      ],
      "Signature Algorithm": "Inherit from Issuing CA",
      "Validity": "1mo",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Basic Constraints Used": false,
      "Forbid encryption usage for ECC keys": true,
      "Key Usage": [
        "Digital Signature"
      ],
      "Extended Key Usage Used": true,
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "CRL Distribution Point Used": true,
      "CRL Use CA Defined Settings": true,
      "Authority Information Access Used": true,
      "OCSP Service Locator URL Is CA Defined": true,
      "OCSP Issuer URL Is CA Defined": true,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [
        "DG3",
        "DG4"
      ],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": [],
      "Extended Key Usage": [
        "Server Authentication"
      ]
    },
    "userAuth-1yr": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "userAuth-1yr",
      "Type": "End Entity",
      "Available Key Algorithms": [
        "ECDSA"
      ],
      "Available Elliptic Curves": [
        "P-384"
      ],
      "Available Bit Lengths": [
        0,
        110,
        112,
        113,
        126,
        128,
        131,
        160,
        161,
        162,
        163,
        189,
        190,
        191,
        192,
        193,
        224,
        225,
        232,
        233,
        236,
        237,
        238,
        239,
        256,
        257,
        281,
        282,
        289,
        320,
        353,
        384,
        407,
        409,
        418,
        512,
        521,
        570,
        1024,
        1536,
        2048,
        3072,
        4096,
        6144,
        8192
      ],
      "Signature Algorithm": "Inherit from Issuing CA",
      "Validity": "1y",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Basic Constraints Used": false,
      "Forbid encryption usage for ECC keys": true,
      "Key Usage": [
        "Digital Signature"
      ],
      "Extended Key Usage Used": true,
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "CRL Distribution Point Used": true,
      "CRL Use CA Defined Settings": true,
      "Authority Information Access Used": true,
      "OCSP Service Locator URL Is CA Defined": true,
      "OCSP Issuer URL Is CA Defined": true,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [
        "DG3",
        "DG4"
      ],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": [],
      "Extended Key Usage": [
        "Client Authentication"
      ]
    },
    "cmsSigner-Ec-30d": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "cmsSigner-Ec-30d",
      "Type": "End Entity",
      "Available Key Algorithms": [
        "ECDSA"
      ],
      "Available Elliptic Curves": [
        "P-384"
      ],
      "Available Bit Lengths": [
        0,
        110,
        112,
        113,
        126,
        128,
        131,
        160,
        161,
        162,
        163,
        189,
        190,
        191,
        192,
        193,
        224,
        225,
        232,
        233,
        236,
        237,
        238,
        239,
        256,
        257,
        281,
        282,
        289,
        320,
        353,
        384,
        407,
        409,
        418,
        512,
        521,
        570,
        1024,
        1536,
        2048,
        3072,
        4096,
        6144,
        8192
      ],
      "Signature Algorithm": "Inherit from Issuing CA",
      "Validity": "1mo",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Basic Constraints Used": false,
      "Forbid encryption usage for ECC keys": true,
      "Key Usage": [
        "Digital Signature"
      ],
      "Extended Key Usage Used": true,
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "CRL Distribution Point Used": true,
      "CRL Use CA Defined Settings": true,
      "Authority Information Access Used": true,
      "OCSP Service Locator URL Is CA Defined": true,
      "OCSP Issuer URL Is CA Defined": true,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [
        "DG3",
        "DG4"
      ],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": [],
      "Extended Key Usage": [
        "Code Signing",
        "E-mail Protection"
      ]
    },
    "tlsServer-Composite-30d": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "tlsServer-Composite-30d",
      "Type": "End Entity",
      "Available Key Algorithms": [
        "MLDSA87-ECDSA-P521-SHA512"
      ],
      "Available Elliptic Curves": [
        "P-384"
      ],
      "Available Bit Lengths": [
        0,
        110,
        112,
        113,
        126,
        128,
        131,
        160,
        161,
        162,
        163,
        189,
        190,
        191,
        192,
        193,
        224,
        225,
        232,
        233,
        236,
        237,
        238,
        239,
        256,
        257,
        281,
        282,
        289,
        320,
        353,
        384,
        407,
        409,
        418,
        512,
        521,
        570,
        1024,
        1536,
        2048,
        3072,
        4096,
        6144,
        8192
      ],
      "Signature Algorithm": "Inherit from Issuing CA",
      "Validity": "1mo",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Basic Constraints Used": false,
      "Forbid encryption usage for ECC keys": true,
      "Key Usage": [
        "Digital Signature"
      ],
      "Extended Key Usage Used": true,
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "CRL Distribution Point Used": true,
      "CRL Use CA Defined Settings": true,
      "Authority Information Access Used": true,
      "OCSP Service Locator URL Is CA Defined": true,
      "OCSP Issuer URL Is CA Defined": true,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [
        "DG3",
        "DG4"
      ],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": [],
      "Extended Key Usage": [
        "SSH Server"
      ]
    },
    "tlsServer-MlDsa-30d": {
      "Object Type": "Certificate Profile",
      "Version": 1,
      "Name": "tlsServer-MlDsa-30d",
      "Type": "End Entity",
      "Available Key Algorithms": [
        "ML-DSA-44"
      ],
      "Available Elliptic Curves": [
        "P-384"
      ],
      "Available Bit Lengths": [
        0,
        110,
        112,
        113,
        126,
        128,
        131,
        160,
        161,
        162,
        163,
        189,
        190,
        191,
        192,
        193,
        224,
        225,
        232,
        233,
        236,
        237,
        238,
        239,
        256,
        257,
        281,
        282,
        289,
        320,
        353,
        384,
        407,
        409,
        418,
        512,
        521,
        570,
        1024,
        1536,
        2048,
        3072,
        4096,
        6144,
        8192
      ],
      "Signature Algorithm": "Inherit from Issuing CA",
      "Validity": "1mo",
      "Description": "",
      "Overridable Extension OIDs": [],
      "Non-overridable Extension OIDs": [],
      "Basic Constraints Used": false,
      "Forbid encryption usage for ECC keys": true,
      "Key Usage": [
        "Digital Signature"
      ],
      "Extended Key Usage Used": true,
      "Subject Alternative Name Used": false,
      "Issuer Alternative Name Used": false,
      "CRL Distribution Point Used": true,
      "CRL Use CA Defined Settings": true,
      "Authority Information Access Used": true,
      "OCSP Service Locator URL Is CA Defined": true,
      "OCSP Issuer URL Is CA Defined": true,
      "Microsoft ObjectSid Security Extension Used": false,
      "CVC Access Rights (Inspection System)": [
        "DG3",
        "DG4"
      ],
      "LDAP DN Order": false,
      "Available CAs": [
        "Any CA"
      ],
      "SSH Extensions": {
        "no-touch-required": "",
        "permit-X11-forwarding": "",
        "permit-agent-forwarding": "",
        "permit-port-forwarding": "",
        "permit-pty": "",
        "permit-user-rc": ""
      },
      "Account Binding Namespace": [],
      "Extended Key Usage": [
        "SSH Server"
      ]
    }
  },
"end-entity-profiles": {
    "userAuth": {
      "Object Type": "End Entity Profile",
      "Version": 1,
      "Name": "userAuth",
      "Description": "",
      "Default CA": "ManagementCA",
      "Available CAs": [
        "ManagementCA"
      ],
      "Default Certificate Profile": "userAuth-1yr",
      "Available Certificate Profiles": [
        "userAuth-1yr"
      ],
      "Default Token Type": "User Generated",
      "Available Token Types": [
        "User Generated",
        "PKCS12 File"
      ],
      "Subject DN": {
        "CN": [
          {
            "Required": true
          }
        ],
        "OU": [
          {
            "Required": true,
            "Modifiable": false,
            "Values": [
              "User Authentication"
            ]
          }
        ],
        "O": [
          {
            "Required": true,
            "Modifiable": false,
            "Values": [
              "Keyfactor TechMeetup"
            ]
          }
        ],
        "C": [
          {
            "Required": true,
            "Modifiable": false,
            "Values": [
              "SE"
            ]
          }
        ]
      },
      "Subject Alternative Name": {
        "UPN": [
          {
            "Required": false
          }
        ]
      }
    },
    "tlsServer": {
      "Object Type": "End Entity Profile",
      "Version": 1,
      "Name": "tlsServer",
      "Description": "",
      "Default CA": "Ecdsa-Sub-G1",
      "Available CAs": [
        "Composite-Sub-G1",
        "Ecdsa-Sub-G1",
        "Hybrid-Sub-G1",
        "ManagementCA",
        "Pqc-Sub-G1"
      ],
      "Default Certificate Profile": "tlsServer-Ec-30d",
      "Available Certificate Profiles": [
        "tlsServer-Composite-30d",
        "tlsServer-Ec-30d",
        "tlsServer-Hybrid-30d",
        "tlsServer-MlDsa-30d"
      ],
      "Default Token Type": "User Generated",
      "Available Token Types": [
        "User Generated",
        "PEM File"
      ],
      "Subject DN": {
        "CN": [
          {
            "Required": true
          }
        ],
        "OU": [
          {
            "Required": true,
            "Modifiable": false,
            "Values": [
              "TLS Servers"
            ]
          }
        ],
        "O": [
          {
            "Required": true,
            "Modifiable": false,
            "Values": [
              "Keyfactor TechMeetup"
            ]
          }
        ],
        "C": [
          {
            "Required": true,
            "Modifiable": false,
            "Values": [
              "SE"
            ]
          }
        ]
      },
      "Subject Alternative Name": {
        "DNSNAME": [
          {
            "Required": true
          },
          {
            "Required": false
          },
          {
            "Required": false
          }
        ]
      }
    },
    "Signer": {
      "Object Type": "End Entity Profile",
      "Version": 1,
      "Name": "Signer",
      "Description": "",
      "Default CA": "Ecdsa-Sub-G1",
      "Available CAs": [
        "Composite-Sub-G1",
        "Ecdsa-Sub-G1",
        "Hybrid-Sub-G1",
        "Pqc-Sub-G1"
      ],
      "Default Certificate Profile": "cmsSigner-Ec-30d",
      "Available Certificate Profiles": [
        "cmsSigner-Composite-30d",
        "cmsSigner-Ec-30d",
        "cmsSigner-Hybrid-30d",
        "cmsSigner-MlDsa-30d"
      ],
      "Default Token Type": "User Generated",
      "Available Token Types": [
        "User Generated"
      ],
      "Subject DN": {
        "CN": [
          {
            "Required": true
          }
        ],
        "OU": [
          {
            "Required": true,
            "Modifiable": false,
            "Values": [
              "Signers"
            ]
          }
        ],
        "O": [
          {
            "Required": true,
            "Modifiable": false,
            "Values": [
              "Keyfactor TechMeetup"
            ]
          }
        ],
        "C": [
          {
            "Required": true,
            "Modifiable": false,
            "Values": [
              "SE"
            ]
          }
        ]
      }
    }
  },
"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": "adminSvenRajala"
        },
        {
          "Token Type": "CliAuthenticationToken",
          "Issuer": null,
          "Match With": "USERNAME",
          "Match Value": "ejbca"
        },
        {
          "Token Type": "PublicAccessAuthenticationToken",
          "Issuer": null,
          "Match With": "TRANSPORT_ANY",
          "Match Value": ""
        }
      ],
      "Namespace": "",
      "RA Style Id": 0,
      "Access Rules": { "/": "Allow" }
    }
  }  
}
EOF
  1. Create the configmap in K8s for the configdump part 2:

BASH
kubectl -n karls create configmap ca-post-configmap --from-file=post-configdump.json=configs/sub/post-configdump.json
# Delete
kubectl -n karls delete cm/ca-post-configmap
# View the contents
kubectl -n karls get cm/ca-post-configmap -o yaml
  1. Create new deployment override values:

cat > ~/configs/sub/sub-override-p2.yaml << EOF ... Click to see the full command
BASH
cat > ~/configs/sub/sub-override-p2.yaml << EOF
ejbca:
  importEjbcaConfFiles: false
  importExternalCas: false
  importJvmTruststore: false
  configdumpImport:
    enabled: true
    initialize: false
    configMapName: ca-post-configmap
    configMapKey: post-configdump.json
    inlineConfigdump:
  importAppserverKeystore: false
  importAppserverTruststore: false
  useEphemeralH2Database: false
  useH2Persistence: false
  env:
    TLS_SETUP_ENABLED: "later"
    HTTPSERVER_HOSTNAME: "enroll.techmeetup.test"
    ADMIN_USERNAME: "adminSvenRajala"
    DATABASE_JDBC_URL: "jdbc:mariadb://forseti-mariadb.forseti.svc.cluster.local:3306/karl?characterEncoding=utf8"
    LOG_AUDIT_TO_DB: false
    LOG_LEVEL_APP: "INFO"
    LOG_LEVEL_SERVER: "INFO"
    #DEBUG: full
    EJBCA_RNGALGORITHM: "BCSP800Hybrid"
    OBSERVABLE_BIND: "127.0.0.1"
    METRICS_ENABLED: false
    OCSP_CHECK_SIGN_CERT_VALIDITY: true
    OCSP_NON_EXISTING_IS_GOOD: false
    SMTP_DESTINATION: "localhost"
    SMTP_DESTINATION_PORT: "25"
    SMTP_FROM: no-reply@localhost
    SMTP_TLS_ENABLED: false
    SMTP_SSL_ENABLED: true
    SMTP_USERNAME: ejbca-mail
  envRaw:
    - name: DATABASE_USER
      valueFrom:
       secretKeyRef:
         name: sub-db-credentials
         key: DATABASE_USER
    - name: DATABASE_PASSWORD
      valueFrom:
       secretKeyRef:
         name: sub-db-credentials
         key: DATABASE_PASSWORD
    - name: PASSWORD_ENCRYPTION_KEY
      valueFrom:
       secretKeyRef:
         name: sub-pek
         key: PASSWORD_ENCRYPTION_KEY
    - name: HSM_TOKEN_PIN
      valueFrom:
       secretKeyRef:
         name: configdump-secrets
         key: HSM_TOKEN_PIN         
  initContainers: []
  sidecarContainers: []
  volumes:
    - name: after-deployed-pre-p2
      configMap:
        name: after-deployed-pre-p2
        defaultMode: 0777
        items:
          - key: "after-deployed-pre.sh"
            path: "after-deployed-pre.sh"
    - name: sub-ca-crts
      configMap:
        name: sub-ca-crts
  volumeMounts:
    - name: after-deployed-pre-p2
      mountPath: /opt/keyfactor/bin/internal/after-deployed-pre.sh
      subPath: after-deployed-pre.sh
    - name: sub-ca-crts
      mountPath: /opt/keyfactor/importcacrt
hsm:
  image:
  imagePullPolicy: IfNotPresent
  enabled: false
  softhsm:
    enabled: false
  luna:
    enabled: false   
  utimaco:
    enabled: false
  nshield:
    enabled: false
  awsCloudHsm:
    enabled: false
replicaCount: 1
updateStrategy: {}
image: 
  repository: keyfactor/ejbca-ee
  tag: latest
  pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
  create: true
  annotations: {}
  name: ""
podLabels: {}
podAnnotations: {}
podSecurityContext: {}
serviceAnnotations: {}
securityContext: {}
services:
  proxyHttp:
    enabled: false
  proxyAJP:
    enabled: false
  directHttp:
    enabled: false
  sidecarPorts: []
nginx:
  image: nginx:1.29.2
  enabled: true
  initializeWithSelfSignedTls: true
  host: "enroll.techmeetup.test"
  externalConfiguration:
  mountInternalNginxCert: false
  #secretInternalNginxCert: "tls-enroll-techmeetup-test"
  service:
    type: LoadBalancer
    bindIP: 0.0.0.0
    httpPort: 80
    httpsPort: 443
  loadBalancerAccess:
    enableStickySessionClientIp: true
    enableReplicaSpecificAccess: false
  additionalHosts:
httpd:
  image: httpd:2.4
  enabled: false
ingress:
  enabled: false
resources:
  limits:
    cpu: "2"
    memory: "2048Mi"
  requests:
    cpu: "1"
    memory: "2048Mi"
autoscaling:
  enabled: false
  behavior: {}
podDisruptionBudget:
  create: false
  minAvailable: 1
  maxUnavailable: ""
nodeSelector: {}
affinity: {}
tolerations: []
topologySpreadConstraints: []
priorityClassName: ""
schedulerName: ""
terminationGracePeriodSeconds: ""
networkPolicy:
EOF
  1. Update Helm using the new deployment override values:

BASH
helm upgrade -n karls sub -f configs/sub/sub-override-p2.yaml oci://repo.keyfactor.com/charts/ejbca
#Install
helm install -n karls sub -f configs/sub/sub-override-p2.yaml oci://repo.keyfactor.com/charts/ejbca
# Tails logs
kubectl -n karls logs -f pod/sub-ejbca-0 -c ejbca
# Exec 
kubectl -n karls exec -it pod/sub-ejbca-0 -c ejbca -- /bin/bash

Step 6 - Stage TLS certificate

  1. Copy the certificate, key, and CA cert out of the container:

BASH
subTlsCert="$(kubectl -n karls exec -it pod/sub-ejbca-0 -c ejbca -- cat /var/tmp/pem/enroll.techmeetup.test.pem)"
subTlsKey="$(kubectl -n karls exec -it pod/sub-ejbca-0 -c ejbca -- cat /var/tmp/pem/enroll.techmeetup.test-Key.pem)"
subTlsCa="$(kubectl -n karls exec -it pod/sub-ejbca-0 -c ejbca -- cat /var/tmp/pem/enroll.techmeetup.test-CA.pem)"
echo "$subTlsCert" > ~/configs/sub/enroll.techmeetup.test.pem
echo "$subTlsKey" > ~/configs/sub/enroll.techmeetup.test-Key.pem
echo "$subTlsCa" > ~/configs/sub/enroll.techmeetup.test-CA.pem
  1. Create a secret for the nginx TLS cert, key, and CA chain:

BASH
kubectl -n karls create secret generic tls-enroll-techmeetup-test \
 --from-file=enroll.techmeetup.test.pem=configs/sub/enroll.techmeetup.test.pem \
 --from-file=enroll.techmeetup.test-Key.pem=configs/sub/enroll.techmeetup.test-Key.pem \
 --from-file=enroll.techmeetup.test-CA.pem=configs/sub/enroll.techmeetup.test-CA.pem
  1. Create Helm override file for the finale:

cat > ~/configs/sub/sub-override-final.yaml << EOF ... Click to see the full command
BASH
cat > ~/configs/sub/sub-override-final.yaml << EOF
ejbca:
  importEjbcaConfFiles: false
  importExternalCas: false
  importJvmTruststore: false
  configdumpImport:
    enabled: false
    initialize: false
    configMapName: ca-init-configmap
    configMapKey: configdump.json
    inlineConfigdump:
  importAppserverKeystore: false
  importAppserverTruststore: false
  useEphemeralH2Database: false
  useH2Persistence: false
  env:
    TLS_SETUP_ENABLED: "later"
    DATABASE_JDBC_URL: "jdbc:mariadb://forseti-mariadb.forseti.svc.cluster.local:3306/karl?characterEncoding=utf8"
    LOG_AUDIT_TO_DB: false
    LOG_LEVEL_APP: "INFO"
    LOG_LEVEL_SERVER: "INFO"
    #DEBUG: full
    EJBCA_RNGALGORITHM: "BCSP800Hybrid"
    OBSERVABLE_BIND: "127.0.0.1"
    METRICS_ENABLED: false
    OCSP_CHECK_SIGN_CERT_VALIDITY: true
    OCSP_NON_EXISTING_IS_GOOD: false
    SMTP_DESTINATION: "localhost"
    SMTP_DESTINATION_PORT: "25"
    SMTP_FROM: no-reply@localhost
    SMTP_TLS_ENABLED: false
    SMTP_SSL_ENABLED: true
    SMTP_USERNAME: ejbca-mail
  envRaw:
    - name: DATABASE_USER
      valueFrom:
       secretKeyRef:
         name: sub-db-credentials
         key: DATABASE_USER
    - name: DATABASE_PASSWORD
      valueFrom:
       secretKeyRef:
         name: sub-db-credentials
         key: DATABASE_PASSWORD
    - name: PASSWORD_ENCRYPTION_KEY
      valueFrom:
       secretKeyRef:
         name: sub-pek
         key: PASSWORD_ENCRYPTION_KEY       
  initContainers: []
  sidecarContainers: []
  volumes: []
  volumeMounts: []
hsm:
  image:
  imagePullPolicy: IfNotPresent
  enabled: false
  softhsm:
    enabled: false
  luna:
    enabled: false   
  utimaco:
    enabled: false
  nshield:
    enabled: false
  awsCloudHsm:
    enabled: false
replicaCount: 1
updateStrategy: {}
image: 
  repository: keyfactor/ejbca-ee
  tag: latest
  pullPolicy: IfNotPresent
imagePullSecrets: []
services:
  proxyHttp:
    enabled: false
  proxyAJP:
    enabled: false
  directHttp:
    enabled: false
  sidecarPorts: []
nginx:
  image: nginx:1.29.2
  enabled: true
  initializeWithSelfSignedTls: false
  host: "enroll.techmeetup.test"
  externalConfiguration:
  mountInternalNginxCert: true
  secretInternalNginxCert: "tls-enroll-techmeetup-test"
  service:
    type: LoadBalancer
    bindIP: 0.0.0.0
    httpPort: 80
    httpsPort: 443
  loadBalancerAccess:
    enableStickySessionClientIp: true
    enableReplicaSpecificAccess: false
  additionalHosts:
httpd:
  image: httpd:2.4
  enabled: false
ingress:
  enabled: false
resources:
  limits:
    cpu: "2"
    memory: "2048Mi"
  requests:
    cpu: "1"
    memory: "2048Mi"
EOF
  1. Upgrade helm deployment with updated values file:

BASH
helm upgrade -n karls sub -f configs/sub/sub-override-final.yaml oci://repo.keyfactor.com/charts/ejbca
#Install
helm install -n karls sub -f configs/sub/sub-override-final.yaml oci://repo.keyfactor.com/charts/ejbca
# Tails logs
kubectl -n karls logs -f pod/sub-ejbca-0 -c ejbca
# Exec 
kubectl -n karls exec -it pod/sub-ejbca-0 -c ejbca -- /bin/bash

Next steps

In this guide, you learned how to deploy a fully automated, zero-touch PQC-ready PKI using composite certificates in Kubernetes using Helm, configuration-as-code, and real HSMs, and how to validate and use it today with off-the-shelf TLS and CMS tooling.

Here are some next steps we recommend:

Contact us

Request a live demo with one of our experts — whether you want to explore workflows hands-on or discuss your specific needs.

Request a Demo

JavaScript errors detected

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

If this problem persists, please contact our support.