Simplifying Self-Signed Certificates for Multi-Host Environments
Please note: This post is to aide in quickly deploying proof-of-concept or test environments. Netwrix does not recommend the use of self-signed certificates in production.
The Challenge
Weβre setting up a multi-host Proof of Concept (PoC) for Netwrix Privilege Secure, including:
- Secure Remote Access
- Remote Proxy
- Remote Action Service
The environment includes 6 TLS-enabled hosts:
β’ portal.domain.com
β’ gateway.domain.com
β’ guacd.domain.com
β’ npsa.domain.com
β’ remoteproxy.domain.com
β’ remoteactionservice.domain.com
Managing self-signed certificates across all these hosts can be a real pain:
- Manual certificate generation for each hostname
- Properly configuring SANs (Subject Alternative Names)
- Creating and managing full certificate chains
- Installing multiple self-signed certs on every client machine
- Generating different file formats based on platform requirements
Result: Complex, error-prone, and time-consuming.
The Solution
To solve this, I used vibe coding to generate simple automated Bash script that:
Generates a dedicated Root CA for the PoC
Only one Root CA needs to be installed in trust stores β all issued certs are trusted automatically
Lets you name the Root CA based on the customer or PoC
Outputs all common formats:
.key
,.crt
,.cer
,.pem
,.pfx
, and-chain.crt
Installs the Root CA into the Linux trust store (ideal for WSL or Guacamole hosting)
Supports custom validity durations and password-protected
.pfx
filesGenerates Windows-ready PFX files for use with IIS, RDP, etc.
Reusable - Re-run the script to generate a new Root CA and new Certificates with Keys for Hosts either in your lab or customer environment
How It Works
- Prompt for:
- Number of hosts
- Hostnames
- Root CA name
- PFX password
- Certificate validity (in days)
- Automatically generates all certs, keys, and formats
- Installs the Root CA to the Linux trust store
- Delivers PFX bundles for Windows platforms
What to Do After Generating Certificates
On Linux
- Root CA is added to
/usr/local/share/ca-certificates/
- Run
update-ca-certificates
(done by the script) - Use
.crt + .key
or-chain.crt
in:- NGINX
- Apache
- Guacamole Daemon
.cer
and.pem
formats are also available for tools and APIs
On Windows
- Import the
.pfx
file:- Installs the cert + key in Local Machine > Personal
- Installs the CA in Trusted Root Certification Authorities
- Use in:
- IIS (bindings)
- RDP
- Remote Proxy and Action Service
Keep the private key secure β do not share it!
Prerequisites
- Linux VM or WSL with OpenSSL installed (should be present by default)
- Root or sudo privileges to install the Root CA
- Basic comfort with the terminal
The Script
#!/bin/bash
echo "π Certificate Generator for Multiple Hosts"
# Prompt for number of hosts
read -p "How many hostnames do you want to generate certificates for? " HOST_COUNT
# Prompt for Root CA name
read -p "Enter the desired Root CA Name: " CA_NAME
# Prompt for certificate validity period
read -p "Enter number of days the certificates should be valid for [Default: 365]: " CERT_VALIDITY
CERT_VALIDITY=${CERT_VALIDITY:-365}
# Prompt for PFX password (hidden input)
read -s -p "Enter password for PFX files: " PFX_PASSWORD
echo ""
# Set base directories
BASE_CERT_DIR="/opt/poc_certs"
CA_DIR="$BASE_CERT_DIR/ca"
HOSTS_DIR="$BASE_CERT_DIR/hosts"
# Create directories
mkdir -p "$CA_DIR" "$HOSTS_DIR"
CA_KEY="$CA_DIR/${CA_NAME// /_}_rootCA.key"
CA_CRT="$CA_DIR/${CA_NAME// /_}_rootCA.crt"
# Generate Root CA if not already exists
if [[ ! -f "$CA_KEY" ]]; then
echo "π Generating Root CA..."
openssl req -x509 -newkey rsa:4096 -nodes \
-keyout "$CA_KEY" \
-out "$CA_CRT" \
-days 1825 -subj "/CN=$CA_NAME"
fi
# Process hostnames
for ((i=1; i<=HOST_COUNT; i++)); do
read -p "Enter hostname #$i: " HOSTNAME
CERT_DIR="$HOSTS_DIR/$HOSTNAME"
mkdir -p "$CERT_DIR"
CERT_KEY="$CERT_DIR/$HOSTNAME.key"
CERT_CSR="$CERT_DIR/$HOSTNAME.csr"
CERT_CRT="$CERT_DIR/$HOSTNAME.crt"
CERT_CHAIN="$CERT_DIR/$HOSTNAME-chain.crt"
CERT_PFX="$CERT_DIR/$HOSTNAME.pfx"
CERT_CER="$CERT_DIR/$HOSTNAME.cer"
CERT_PEM="$CERT_DIR/$HOSTNAME.pem"
SAN_CONFIG="$CERT_DIR/$HOSTNAME-san.cnf"
echo "π§ Generating certificate for $HOSTNAME..."
# Create SAN config
cat > "$SAN_CONFIG" <<EOF
[req]
distinguished_name=req_distinguished_name
req_extensions=v3_req
[req_distinguished_name]
[v3_req]
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = $HOSTNAME
EOF
# Generate private key
openssl genrsa -out "$CERT_KEY" 4096
# Create CSR with SAN
openssl req -new -key "$CERT_KEY" -out "$CERT_CSR" -subj "/CN=$HOSTNAME" -config "$SAN_CONFIG"
# Generate cert with SAN and extended key usage
openssl x509 -req -in "$CERT_CSR" -CA "$CA_CRT" -CAkey "$CA_KEY" -CAcreateserial \
-out "$CERT_CRT" -days "$CERT_VALIDITY" -extfile "$SAN_CONFIG" -extensions v3_req
if [[ -f "$CERT_CRT" ]]; then
cat "$CERT_CRT" "$CA_CRT" > "$CERT_CHAIN"
echo "β
Certificate created: $CERT_CRT"
else
echo "β Failed to generate certificate for $HOSTNAME"
continue
fi
# Generate PFX
openssl pkcs12 -export -out "$CERT_PFX" \
-inkey "$CERT_KEY" \
-in "$CERT_CHAIN" \
-certfile "$CA_CRT" \
-password pass:$PFX_PASSWORD
echo "π¦ PFX created: $CERT_PFX"
# Generate .cer (DER format)
openssl x509 -in "$CERT_CRT" -outform DER -out "$CERT_CER"
echo "π CER file created: $CERT_CER"
# Generate .pem (full chain)
cat "$CERT_CHAIN" > "$CERT_PEM"
echo "π PEM file created: $CERT_PEM"
# Cleanup
rm -f "$SAN_CONFIG"
done
# Install CA cert into Linux system
echo "π§ Installing Root CA on Linux..."
CA_INSTALL_PATH="/usr/local/share/ca-certificates/${CA_NAME// /_}_rootCA.crt"
cp "$CA_CRT" "$CA_INSTALL_PATH"
update-ca-certificates
echo ""
echo "π All certificates and PFX files generated under $HOSTS_DIR"
echo "π Root CA: $CA_CRT"
How to Use the Certificate Generator Script
Follow these simple steps to generate self-signed certificates for multiple hosts in just a few minutes.
Step 1: Create the Script File in Linux
Open a terminal and create a new file called generate_certs
:
bash
CopyEdit
nano generate_certs
Step 2: Paste the Script
Copy the full certificate generation script into and paste it into the file you created using nano.
Then save and exit:
- Press
CTRL + O
to write (save) the file - Press
ENTER
to confirm - Press
CTRL + X
to exit
Step 3: Make the Script Executable & Run It
Now, make the script executable and run it:
bash
CopyEdit
chmod +x generate_certs
./generate_certs
Alternatively, you can run it directly using:
bash
CopyEdit
bash generate_certs
Step 4: Follow the Prompts
The script will walk you through the setup interactively:
Enter the number of hostnames (e.g., 6 for a PoC)
Provide each hostname one by one (e.g.,
portal.domain.com
)Set a name for your Root CA (e.g.,
CustomerPoC Root CA
)Enter a password to protect
.pfx
filesChoose how long the certificates should remain valid (in days)
Thatβs it β the script takes care of everything from SANs to full chains!
Step 5: View the Generated Certificates
After completion, all certificates will be available under:
bash
CopyEdit
/opt/poc_certs/
Each hostname will have its own folder containing:
hostname.key
β Private Keyhostname.crt
β Certificatehostname-chain.crt
β Certificate with Root CAhostname.pfx
β Password-protected Windows-ready filehostname.cer
,hostname.pem
β Alternate formats
Benefits
Massive time-saver vs. manual cert generation
Handles SANs and multi-host use cases cleanly
Provides all formats, cross-platform
One-click Linux trust store integration
Secure, customized cert generation for every PoC or environment
Final Thoughts
Self-signed certificates often get a bad reputation β but they donβt have to be painful.
This solution brings order, automation, and cross-platform support to the process, making it ideal for multi-host PoCs like Netwrix Privilege Secure deployments.
Need help? Please reach out to me!
Bonus
Update: Windows Support Added!
You can now generate the same multi-host certificates using PowerShell on Windows.
Same prompts: number of hosts, Root CA name, validity (days), PFX password, hostnames
Outputs
.key
, .crt
, .cer
, .pem
, .pfx
, full chains β all under C:\poc_certs
Requires Shining Light Productions OpenSSL installed on the Windows Host
If youβre working entirely from a Windows host.
The Script
# generate-certs.ps1
Write-Host "π Certificate Generator for Multiple Hosts (PowerShell Edition)"
# Prompt for inputs
$hostCount = Read-Host "Enter number of hostnames"
$caName = Read-Host "Enter Root CA name"
$validDays = Read-Host "Enter certificate validity in days"
$pfxPassword = Read-Host "Enter password for PFX files" -AsSecureString
# Setup directories
$baseDir = "C:\poc_certs"
$caDir = Join-Path $baseDir "ca"
$hostsDir = Join-Path $baseDir "hosts"
New-Item -Path $caDir, $hostsDir -ItemType Directory -Force | Out-Null
$caKey = Join-Path $caDir "$caName`_rootCA.key"
$caCert = Join-Path $caDir "$caName`_rootCA.crt"
# Generate Root CA if it doesn't exist
if (-not (Test-Path $caKey)) {
Write-Host "π§ Generating Root CA..."
openssl req -x509 -newkey rsa:4096 -nodes `
-keyout $caKey -out $caCert -days $validDays `
-subj "/C=US/ST=State/L=City/O=MyOrg/CN=$caName"
}
# Generate certs per host
for ($i = 1; $i -le $hostCount; $i++) {
$hostname = Read-Host "Enter hostname #$i"
$certDir = Join-Path $hostsDir $hostname
New-Item -Path $certDir -ItemType Directory -Force | Out-Null
$keyFile = Join-Path $certDir "$hostname.key"
$csrFile = Join-Path $certDir "$hostname.csr"
$crtFile = Join-Path $certDir "$hostname.crt"
$chainFile = Join-Path $certDir "$hostname-chain.crt"
$pfxFile = Join-Path $certDir "$hostname.pfx"
$cerFile = Join-Path $certDir "$hostname.cer"
$pemFile = Join-Path $certDir "$hostname.pem"
$sanFile = Join-Path $certDir "$hostname-san.cnf"
@"
[req]
distinguished_name=req_distinguished_name
req_extensions=v3_req
[req_distinguished_name]
[v3_req]
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = $hostname
"@ | Set-Content $sanFile
# Generate private key and CSR
openssl genrsa -out $keyFile 4096
openssl req -new -key $keyFile -out $csrFile -subj "/CN=$hostname" -config $sanFile
# Sign certificate with Root CA and SAN extension
openssl x509 -req -in $csrFile -CA $caCert -CAkey $caKey -CAcreateserial -out $crtFile -days $validDays -extfile $sanFile -extensions v3_req
if (Test-Path $crtFile) {
# Create full chain file
Get-Content $crtFile, $caCert | Set-Content $chainFile
# Copy cert as .cer file
Copy-Item $crtFile $cerFile
# Create PEM combining key + chain
Get-Content $keyFile, $chainFile | Set-Content $pemFile
# Convert SecureString password to plain text
$pfxPasswordPlain = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($pfxPassword)
)
# Generate password protected PFX
openssl pkcs12 -export -out $pfxFile -inkey $keyFile -in $chainFile -certfile $caCert -password pass:$pfxPasswordPlain
Write-Host "β
Certificate for $hostname generated."
} else {
Write-Host "β Failed to generate certificate for $hostname"
}
# Cleanup SAN config file
Remove-Item $sanFile
}
Write-Host "π Certificates generated at: $baseDir"
Need help? Please reach out to me!