Issue Multiple Certificates at Once Using a Bulk of CSRs
One of the use cases for EJBCA is to create a bulk of certificates, given a bulk of PKCS #10 CSRs. To achieve this, you must integrate with one of EJBCA's APIs for certificate enrollment using a script. This guide uses EJBCA's REST Certificate Management API which allows you to enroll for certificates by sending JSON payloads to EJBCA.
Using this guide, you will learn how to do the following:
- Step 1 - Configure CA Instance: Issue an RA client keystore and configure access.
- Step 2 - Configure RA Instance: Enable the REST Certificate Management API.
- Step 3 - Configure File Server: Create a script for issuing multiple certificates at once using the REST Certificate Management API and set up monitoring of a directory using systemd.
- Step 4 - Test Bulk Issuance: Create a PKCS #10 CSR using OpenSSL and test issuance of a certificate to make sure everything is working properly.
Architecture
The recommended setup is shown in the figure below.
This setup consists of the following components:
- A hardware security module storing the private key of the CA.
- An RA instance running EJBCA, acting as a proxy between the file server and the CA.
- A CA instance running EJBCA. The CA is connected to the RA using a peer connector.
- A firewall at the perimeter of the CA security zone shielding the CA from the outside world.
- A Linux file server storing new CSRs and the certificates to be distributed. The file server makes requests to the RA instance using EJBCA's REST Certificate Management API.
Step 1 - Configure CA Instance
Start by configuring the CA instance according to the following instructions.
Issue RA Client Keystore
You need to issue an RA client keystore to the file server. The keystore is used by the file server to authenticate to the RA instance when doing REST API calls.
Issue the keystore using the following settings:
Key usage | Digital signature |
---|---|
Extended Key Usage | Client Authentication |
Format | P12 |
Make a note of the subject DN of the certificate, since it is needed in the next step.
Configure RA Client Access
Next, give the RA client certificate access to perform RA functions on the CA. Do this by creating a new role member in EJBCA.
- Open the CA Web on the CA instance and click Roles and Access Rules.
- Click Add to add a new role.
- Edit the access rules for the newly created role:
- Use RA Administrators as role template, select the appropriate CA and end entity profile.
- Clear Approve End Entities, Key Recover End Entities and Revoke End Entities.
- Clear View Audit Log.
- Go to Advanced Mode and set
/administrator
to allow. - Add a new role member to this role using the subject DN of the certificate you created in the previous step.
Allow REST API Calls Over Peers
Next, configure the role for the peer connector to allow processing of REST API calls.
- Click Peer Systems on the menu.
- Click Authorized requests for the peer connector connected to the RA.
- Ensure that access to the appropriate CA and end entity profile is enabled.
- In the Process requests from protocols section, enable REST.
- Click Roles and Access Rules on the menu and edit the access rules for the peer connector role used by the RA instance, in advanced mode. Set the access rule
/administrator
to allow.
Configure Certificate Profile
The only way to provide a subject DN when submitting CSRs using the REST Certificate Management API is to include this information in the CSR. However, apart from the public key, information in the CSR is ignored by default. To use the subject DN of the CSR in the certificate, you need to enable Allow Subject DN Override by CSR in the certificate profile.
- Click Certificate Profiles on the menu.
- Edit the appropriate certificate profile and enable Allow Subject DN Override by CSR in the Permissions section.
Step 2 - Configure RA Instance
Once the CA instance has been configured, configure the RA instance according to the following instructions.
Enable the REST Certificate Management API
The REST Certificate Management API is disabled by default but can be enabled in the system configuration.
- Go to the CA Web on the RA instance and click System Configuration on the menu.
- Click the tab Protocol Configuration.
- Enable REST Certificate Management.
Step 3 - Configure File Server
Finally, create the necessary scripts on the file server to do the bulk enrollment.
Install Dependencies
The following instructions use cURL to do the REST API call, and jq to create JSON payloads.
On Debian based distributions, these packages may be installed directly using apt.
Install Dependencies on Debian
apt install curl jq
If you are using RHEL 8, you must add the EPEL repository to get access to the jq package.
Install Dependencies on RHEL
dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
yum install curl jq
Create Necessary Directories
Create the following folder structure:
Folder Structure for the RA Plugin
/opt/ejbca-ra-plug/
├── certificates
├── csr
├── failed
The certificates
folder will contain any issued certificates and the csr
folder will contain CSRs to be sent to the CA. Once a CSR is processed, it is either removed if the corresponding certificate was issued correctly, or put in the failed
folder if the certificate could not be issued for some reason.
Transfer Keystore to File Server
Transfer the keystore keystore.p12
you issued previously and place it in the /opt/ejbca-ra-plug
directory.
Create Script for Bulk Issuance
To create the script for the bulk issuance, do the following:
Create the following script:
/opt/ejbca-ra-plug/ra.sh
BASH#!/bin/sh hostname= port= client_cert="/opt/ejbca-ra-plug/keystore.p12:foo123" cert_profile_name= ee_profile_name= ca_name= while [ "$(ls -A /opt/ejbca-ra-plug/csr)" ]; do for file in /opt/ejbca-ra-plug/csr/*.csr; do [ -e "$file" ] || continue # Use the CN in the CSR as the username of the end entity username=$(openssl req -in ${file} -inform PEM -noout -subject -nameopt multiline | sed -n 's/ *commonName *= //p') password="foo123" csr=$(cat ${file}) template='{"certificate_request":$csr, "certificate_profile_name":$cp, "end_entity_profile_name":$eep, "certificate_authority_name":$ca, "username":$ee, "password":$password}' json_payload=$(jq -n \ --arg csr "$csr" \ --arg cp "$cert_profile_name" \ --arg eep "$ee_profile_name" \ --arg ca "$ca_name" \ --arg ee "$username" \ --arg password "$password" \ "$template") http_code=$(curl -X POST -s \ -o '/tmp/response.json' \ -w '%{http_code}' \ --cert-type P12 \ --cert "$client_cert" \ -H 'Content-Type: application/json' \ --data "$json_payload" \ "https://$hostname:$port/ejbca/ejbca-rest-api/v1/certificate/pkcs10enroll") exit_code="$?" if [ "$exit_code" -ne 0 ]; then logger "Failed to issue certificate for '$username'. cURL exited with exit code $exit_code." mv "$file" /opt/ejbca-ra-plug/failed/ continue fi response=$(cat '/tmp/response.json') if [ "$http_code" -ne '201' ]; then logger "Failed to issue certificate for '$username'. Server returned HTTP status code $http_code." logger "Response from server: $response" mv "$file" /opt/ejbca-ra-plug/failed/ continue fi certificate=$(cat '/tmp/response.json' | jq -r .certificate) if [ "$certificate" = 'null' ]; then logger "Failed to issue certificate for '$username'. No certificate found in response." logger "Response from server: $response" mv "$file" /opt/ejbca-ra-plug/failed/ continue fi filename=$(basename "$file" .csr) echo '-----BEGIN CERTIFICATE-----' > "/opt/ejbca-ra-plug/certificates/$filename.pem" echo "$certificate" >> "/opt/ejbca-ra-plug/certificates/$filename.pem" echo '-----END CERTIFICATE-----' >> "/opt/ejbca-ra-plug/certificates/$filename.pem" rm "$file" logger "Succesfully issued certificate for '$username'." done done⏎
Set the hostname, the port, the password of the keystore, the name of the end entity and certificate profile, and the name of the CA signing the certificate.
Ensure the file is executable and only readable by
root
.Set permissions for ra.sh
BASHchmod 700 /opt/ejbca-ra-plug/ra.sh
Set Up Monitoring using Systemd
Next, set up monitoring of the CSR directory in order to issue a new certificate automatically every time you drop a CSR in the/opt/ejbca-ra-plug/csr
directory.
To monitor the directory using systemd,
do the following:
Create the systemd monitoring script.
/etc/systemd/system/ra.path
BASH[Unit] Description=EJBCA RA Plug [Path] DirectoryNotEmpty=/opt/ejbca-ra-plug/csr [Install] WantedBy=multi-user.target
Create the systemd service file.
/etc/systemd/system/ra.service
BASH[Unit] Description=EJBCA RA Service [Install] WantedBy=multi-user.target [Service] Type=oneshot ExecStart=/opt/ejbca-ra-plug/ra.sh
Start monitoring.
CODEsystemctl daemon-reload systemctl enable ra.path --now
Step 4 - Test Bulk Issuance
To test the bulk issuance, do the following:
To test your script, create a key and CSR using OpenSSL.
Create a CSR using OpenSSL
BASHopenssl req -nodes -newkey rsa:2048 -keyout test.key -out test.csr -subj "/C=SE/O=PrimeKey Solutions AB/CN=Bulk Issuance Test"
Move the CSR to the
/opt/ejbca-ra-plug/csr
directory.Move the CSR
BASHmv test.csr /opt/ejbca-ra-plug/csr/
- The certificate should now be available in the
/opt/ejbca-ra-plug/certificates
directory.
Troubleshooting
Check the Syslog
Errors are written to syslog.
> journalctl -xe
-- Unit ra.service has begun starting up.
Nov 23 20:43:04 rhel8 root[2657]: Failed to issue certificate for 'Bulk Issuance Test'. cURL exited with exit code 60.
Nov 23 20:43:04 rhel8 systemd[1]: Started EJBCA RA Service.
-- Subject: Unit ra.service has finished start-up
-- Defined-By: systemd
-- Support: https://access.redhat.com/support
--
-- Unit ra.service has finished starting up.
In this example, cURL failed with exit code 60, which means that the TLS server certificate of the RA instance is untrusted. If you encounter this problem, import the issuer into the file server's truststore.
Enable Verbose Logging in cURL
You can run cURL with-v
for more verbose logging.
Use PEM Files Instead of PKCS #12
Some older versions of cURL do not support PKCS #12 files. In that case, you may split the PKCS #12 file into a client certificate, CA certificate and private key.
Split PKCS #12 file using OpenSSL
openssl pkcs12 -in /opt/ejbca-ra-plug/keystore.p12 -out /opt/ejbca-ra-plug/ca.pem -cacerts -nokeys
openssl pkcs12 -in /opt/ejbca-ra-plug/keystore.p12 -out /opt/ejbca-ra-plug/cert.pem -clcerts -nokeys
openssl pkcs12 -in /opt/ejbca-ra-plug/keystore.p12 -out /opt/ejbca-ra-plug/key.pem.tmp -nocerts
openssl pkcs8 -in /opt/ejbca-ra-plug/key.pem.tmp -out /opt/ejbca-ra-plug/key.pem
rm -f /opt/ejbca-ra-plug/key.pem.tmp
Then specify these files when you run cURL:
pem_password=
curl -X POST -s \
-o '/tmp/response.json' \
-w '%{http_code}' \
--cacert '/opt/ejbca-ra-plug/ca.pem' \
--key '/opt/ejbca-ra-plug/key.pem' \
--cert "/opt/ejbca-ra-plug/cert.pem:$pem_password" \
-H 'Content-Type: application/json' \
--data "$json_payload" \
"https://$hostname/ejbca/ejbca-rest-api/v1/certificate/pkcs10enroll