Blog
clouderatlsopensslkeytoolsecurity

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.

Data DynamicsApril 11, 202610 min read

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.

ItemRequirement
Signature algorithmsha256WithRSAEncryption (SHA-256)
Extended Key Usage (EKU)Must include both clientAuth and serverAuth
Key UsageDigitalSignature, KeyEncipherment
Subject Alternative Name (SAN)Required. Must include at least the host's FQDN. Add load balancer FQDN if needed
Wildcard certificatesNot allowed. Issue unique certificates per cluster node
KeyStoreOnly one PrivateKeyEntry per KeyStore
KeyStore passwordKeyStore 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 KeyStore

Cloudera 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/ca

3.2 CA private key

openssl genrsa -out ca.key 4096
chmod 600 ca.key

ca.key must never be leaked externally. Set permissions to 600 immediately 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." The critical flag 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.com

4.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%%.*}
EOF

Here's how each field maps to Cloudera requirements:

ext file fieldCloudera requirement
basicConstraints = CA:FALSEDeclares this certificate is a leaf and cannot sign other certificates
keyUsage = digitalSignature, keyEnciphermentThe two required Key Usage values
extendedKeyUsage = serverAuth, clientAuthBoth EKU values are required. Missing either will cause some Cloudera services to reject it
subjectAltName = @alt_namesSAN 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.com in 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 like DNS.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.srl for 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: criticalDigital Signature, Key Encipherment
  • X509v3 Extended Key Usage:TLS Web Server Authentication, TLS Web Client Authentication
  • X509v3 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 to env:STORE_PASSWORD to 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}" \
  -noprompt

The Cloudera documentation's condition that "KeyStore password and certificate password must be the same or both unset" is satisfied here. Keep srcstorepass and deststorepass at the same value. When you don't separately specify keypass, 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 JKS

Deploy 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.sh

However, 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.key should 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.srl must 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