Creating Self-Signed TLS Certificates That Meet Cloudera Requirements
How to use openssl to create an internal CA and per-host certificates that satisfy sha256WithRSAEncryption, SAN, and clientAuth + serverAuth requirements.
In production, using certificates issued by a public CA is the standard practice. However, for internal test clusters or PoC environments, creating your own CA and issuing per-host certificates is much faster. This post documents the exact procedure for generating self-signed certificates that satisfy the TLS certificate requirements of Cloudera CDP Private Cloud Base. If you need automation, you can use TheOpenCloudEngine/certificates-generator directly — this post explains what that script does internally and why each option is necessary, line by line.
1. What Cloudera Requires
Let's first summarize the documented requirements in a single table. Your generation script must satisfy all of these conditions simultaneously.
| Item | Requirement |
|---|---|
| Signature algorithm | sha256WithRSAEncryption (SHA-256) |
| Extended Key Usage (EKU) | Must include both clientAuth and serverAuth |
| Key Usage | DigitalSignature, KeyEncipherment |
| Subject Alternative Name (SAN) | Required. Must include at least the host's FQDN. Add load balancer FQDN if needed |
| Wildcard certificates | Not allowed. Issue unique certificates per cluster node |
| KeyStore | Only one PrivateKeyEntry per KeyStore |
| KeyStore password | KeyStore password and certificate password must be the same or both unset. Must be identical across all cluster nodes |
Additionally, the documentation recommends "use a public CA if possible and avoid self-signed," but this post covers the proper approach when self-signed is needed for test/internal environments.
2. Overall Flow
[1] Create Internal CA
|
v
ca.key + ca.crt
|
+----------+----------+
v v v
[2] host01 host02 host03
.key .key .key
.csr .csr .csr
.ext (SAN) .ext .ext
.crt (CA-signed) .crt .crt
|
v
[3] Verification (openssl verify)
|
v
[4] Package as JKS TrustStore / PKCS12 KeyStoreCloudera nodes ultimately need two types of keystores:
- KeyStore — The node's own private key + signed leaf cert. Different per node.
- TrustStore — Shared across all nodes. Contains only the internal CA's public cert. All nodes share the same file.
3. Creating the Internal CA
The CA only needs to be created once. The convention is to set a long expiration (10 years / 3,650 days) and use a large key size of 4,096 bits. Since losing the CA renders all subordinate certificates meaningless, it makes sense to enforce strong security on the CA key.
3.1 Directory setup
mkdir -p certs/ca
cd certs/ca3.2 CA private key
openssl genrsa -out ca.key 4096
chmod 600 ca.key
ca.keymust never be leaked externally. Set permissions to600immediately after generation, and ensure it's never included in deployment artifacts.
3.3 CA self-signed certificate
openssl req -x509 -new -nodes \
-key ca.key \
-sha256 \
-days 3650 \
-out ca.crt \
-subj "/C=KR/ST=Gyeonggi-do/L=Yongin-si/O=Data Dynamics/OU=Infrastructure/CN=Data Dynamics Internal CA" \
-extensions v3_ca \
-config <(cat <<'EOF'
[req]
distinguished_name = req_dn
prompt = no
[req_dn]
CN = Data Dynamics Internal CA
[v3_ca]
basicConstraints = critical, CA:TRUE
keyUsage = critical, keyCertSign, cRLSign
subjectKeyIdentifier = hash
EOF
)Three important points here:
-sha256: Specifies the signature algorithm required by Cloudera. Without this option, the openssl default may be MD5 or SHA1, which would fail validation.basicConstraints = critical, CA:TRUE: Declares that this certificate is a "CA that can sign other certificates." Thecriticalflag makes this check mandatory.keyUsage = critical, keyCertSign, cRLSign: Grants only the minimum Key Usage needed for CA operations. This CA can only "sign other certificates" and "sign CRLs" — it cannot act as a server itself.
3.4 Verify the CA certificate
openssl x509 -in ca.crt -noout -text | \
grep -A1 -E "Signature Algorithm|Basic Constraints|Key Usage|Subject:"If the output shows Signature Algorithm: sha256WithRSAEncryption, CA:TRUE, and Certificate Sign, CRL Sign, everything is correct.
4. Issuing Per-Host Certificates
Now for the steps you'll repeat for each cluster node. It's convenient to list the hosts in a file like hosts.txt and loop through them.
hosts.txt
---------
node01.example.com
node02.example.com
node03.example.com
cm-server.example.com4.1 Directory and paths
HOST="node01.example.com"
mkdir -p "certs/${HOST}"
cd "certs/${HOST}"4.2 Node private key
openssl genrsa -out "${HOST}.key" 2048
chmod 600 "${HOST}.key"2,048 bits is sufficient for node certificates. Unlike the CA, they have shorter lifespans (typically under 825 days, explained below) and a renewal cycle, so there's no need to go up to 4,096.
4.3 CSR (Certificate Signing Request)
openssl req -new \
-key "${HOST}.key" \
-out "${HOST}.csr" \
-subj "/C=KR/ST=Gyeonggi-do/L=Yongin-si/O=Data Dynamics/OU=Infrastructure/CN=${HOST}"CN=${HOST}: Historically, CN was the standard for hostname. It's still conventionally populated, but remember that modern TLS clients (Java 11+, modern browsers, gRPC, etc.) ignore CN and only check SAN. Without SAN, CN alone will never validate.
4.4 Extension file (.ext) — The core of Cloudera requirements
Prepare a separate file with the extensions that openssl will attach when signing the CSR. This file determines whether the Cloudera requirements are met.
cat > "${HOST}.ext" <<EOF
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
[alt_names]
DNS.1 = ${HOST}
DNS.2 = ${HOST%%.*}
EOFHere's how each field maps to Cloudera requirements:
| ext file field | Cloudera requirement |
|---|---|
basicConstraints = CA:FALSE | Declares this certificate is a leaf and cannot sign other certificates |
keyUsage = digitalSignature, keyEncipherment | The two required Key Usage values |
extendedKeyUsage = serverAuth, clientAuth | Both EKU values are required. Missing either will cause some Cloudera services to reject it |
subjectAltName = @alt_names | SAN required |
DNS.1 = ${HOST} | FQDN included |
DNS.2 = ${HOST%%.*} | Bare hostname (short name). For environments where inter-node communication uses short names |
Note: Some open-source scripts include wildcards like
DNS.3 = *.example.comin the SAN, but the Cloudera CDP Private Cloud Base 7.3.1 requirements explicitly state "certificates must not use wildcards." Do not include wildcard SANs — list only the exact FQDN per node. If you have a load balancer in front, add the LB FQDN explicitly likeDNS.3 = lb.example.com.
4.5 Sign the CSR with the CA
openssl x509 -req \
-in "${HOST}.csr" \
-CA ../ca/ca.crt \
-CAkey ../ca/ca.key \
-CAcreateserial \
-out "${HOST}.crt" \
-days 825 \
-sha256 \
-extfile "${HOST}.ext"-sha256: Explicitly specifies SHA-256 as the signature algorithm. The core Cloudera requirement.-days 825: 825 days. This is the maximum leaf certificate validity currently allowed by most browsers and libraries (the limit applied by Apple/Google to browsers since 2020). Cloudera itself doesn't enforce this value, but if you want the same certificate to be trusted by monitoring dashboard HTTPS UIs, keeping it at 825 days or less is the safe choice.-extfile: Injects the extension file created above. Without this, openssl applies only default extensions and all EKU/SAN are lost.-CAcreateserial: Automatically generates the CA's serial number file (ca.srl) on first run. From the second host onward, you can replace with-CAserial ../ca/ca.srlfor the same effect.
4.6 Assembling the chain file
For deployment convenience, it's common to create a chain file combining "leaf cert + CA cert" into one file.
cat "${HOST}.crt" ../ca/ca.crt > "${HOST}-chain.pem"A full bundle combining "private key + chain" is also useful (some servers like nginx and HAProxy require this format):
cat "${HOST}.key" "${HOST}.crt" ../ca/ca.crt > "${HOST}-full.pem"
chmod 600 "${HOST}-full.pem"5. Verifying the Generated Certificates
5.1 Did the CA actually sign it?
openssl verify -CAfile ../ca/ca.crt "${HOST}.crt"If node01.example.com.crt: OK appears, verification passes.
5.2 Are all Cloudera-required extensions present?
openssl x509 -in "${HOST}.crt" -noout -text | \
grep -A1 -E "Signature Algorithm|Key Usage|Extended Key Usage|Subject Alternative Name"You must confirm the following in the output:
Signature Algorithm: sha256WithRSAEncryption— appears twice (tbsCertificate and outer)X509v3 Key Usage: critical→Digital Signature, Key EnciphermentX509v3 Extended Key Usage:→TLS Web Server Authentication, TLS Web Client AuthenticationX509v3 Subject Alternative Name:→DNS:node01.example.com, DNS:node01
If anything is missing, re-check the .ext file and the -extfile option.
6. KeyStore / TrustStore Packaging
Cloudera services and Java-based components (HiveServer2, Impala coordinator, KMS, etc.) expect Java KeyStore format. The standard procedure for converting openssl-generated .key/.crt to JKS is as follows.
6.1 Bundle into PKCS12 first
Since openssl cannot create JKS directly, first bundle into PKCS12 and then convert with keytool.
openssl pkcs12 -export \
-in "${HOST}-chain.pem" \
-inkey "${HOST}.key" \
-name "${HOST}" \
-out "${HOST}.p12" \
-password pass:"${STORE_PASSWORD}"-name "${HOST}": Internal alias within the KeyStore. Used for node identification.-password: In production, switching toenv:STORE_PASSWORDto read from environment variables is safer.
6.2 Convert to JKS
keytool -importkeystore \
-srckeystore "${HOST}.p12" \
-srcstoretype PKCS12 \
-srcstorepass "${STORE_PASSWORD}" \
-destkeystore "${HOST}.jks" \
-deststoretype JKS \
-deststorepass "${STORE_PASSWORD}" \
-nopromptThe Cloudera documentation's condition that "KeyStore password and certificate password must be the same or both unset" is satisfied here. Keep
srcstorepassanddeststorepassat the same value. When you don't separately specifykeypass, keytool automatically uses the same value as storepass, so this condition is naturally met.
6.3 Create the shared TrustStore (CA only)
The TrustStore is shared across the entire cluster. It contains only the CA certificate — no private keys.
keytool -importcert -noprompt \
-file ../ca/ca.crt \
-alias internal-ca \
-keystore truststore.jks \
-storepass "${TRUSTSTORE_PASSWORD}" \
-storetype JKSDeploy this single truststore.jks file to all nodes.
7. Automation — Reference Script
Running the above procedure manually every time is inefficient. Here's the open-source script we referenced while preparing this post:
This script takes hosts.txt as input and automatically repeats steps 3-6, generating .key, .csr, .crt, .ext, chain files, and full bundles all at once in the certs/<host>/ directory. Here's how to use it:
git clone https://github.com/TheOpenCloudEngine/certificates-generator.git
cd certificates-generator
# Enter cluster node FQDNs one per line in hosts.txt
cat > hosts.txt <<EOF
node01.example.com
node02.example.com
node03.example.com
cm-server.example.com
EOF
./generate_certs.shHowever, before using it in production, make sure to verify these two things:
- Whether the CA Subject at the top of the script (
C,ST,O,CN) conforms to your organization's rules. The defaults are samples and need to be updated with your company information. - If there's a section that adds wildcard domains to the SAN, remove it. As explained earlier, Cloudera CDP Private Cloud Base 7.3.1 prohibits wildcards. Modify the loop to include only per-node FQDNs.
With just these two adjustments for your environment, you can essentially issue a certificate bundle that meets Cloudera requirements in under 5 minutes.
8. Operational Considerations
- CA key storage: The
ca.keyshould be stored in an encrypted vault (HashiCorp Vault, AWS KMS, on-premises HSM, etc.) as a matter of principle. Never commit it to a regular Git repository. If compromised, all node certificates signed by this CA simultaneously lose trust. - Expiration monitoring: Monitor expiration dates for both leaf certificates (825 days) and the CA certificate (3,650 days). When the CA expires, the scope of reissuance spreads to all nodes.
- Serial file management:
ca.srlmust be carried forward for subsequent issuances. If lost or if multiple teams issue certificates in parallel, the same serial number could be assigned to two certificates, causing collisions. It's safer to designate a single CA workstation centrally. - Practice renewal procedures in advance: Document a "renew 2 weeks before expiration" policy and run an actual renewal at least once a year to verify the procedure is valid. The most common failure pattern is scrambling to find the script when the first expiration date arrives.
This post covered only the certificate creation side. For importing the created CA certificate into a TrustStore on the JDBC client side and actually connecting to Impala, continue with "Applying TLS + LDAP Authentication to Cloudera Impala JDBC".
If you have questions or encounter edge cases unique to your environment, please let us know.
— Data Dynamics Engineering Team