Self-Signed Certificates for Internal Services
Self-signed certificates secure internal applications and services without relying on external Certificate Authorities. They're ideal for development, testing, internal APIs, and microservices requiring encryption. This guide covers creating a private CA, issuing certificates, managing trust stores, and Java keystore configuration.
Table of Contents
- Prerequisites
- Creating a Private Certificate Authority
- Generating Server Certificates
- Creating Client Certificates
- Trust Store Configuration
- Java Keystore Setup
- Nginx Configuration
- Apache Configuration
- Certificate Renewal
- Troubleshooting
- Conclusion
Prerequisites
Before creating self-signed certificates, ensure you have:
- OpenSSL installed
- Root or sudo access
- Internal domain names or IP addresses
- Text editor for configuration
- Java development kit (for keystore examples)
Creating a Private Certificate Authority
Create your own CA for signing internal certificates. This CA is trusted only within your organization.
Create CA directory structure:
mkdir -p ~/ca/{root,intermediate,certs,private,csr}
cd ~/ca
# Set proper permissions
chmod 700 private
chmod 755 certs
Generate CA private key (4096-bit RSA):
openssl genrsa -aes256 -out private/ca.key 4096
When prompted, enter a strong passphrase to protect the CA private key.
Create CA configuration file at ca.conf:
cat > ca.conf <<EOF
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = ./
new_certs_dir = certs
crl_dir = ./
database = index.txt
serial = serial
[ policy_loose ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
default_bits = 4096
string_mask = utf8only
default_md = sha256
x509_extensions = v3_ca
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
countryName_default = US
stateOrProvinceName_default = State
localityName_default = City
organizationName_default = Organization
organizationalUnitName_default = Department
commonName = Common Name
emailAddress = Email Address
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
[ v3_intermediate_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ usr_cert ]
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "OpenSSL Generated Client Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection
[ server_cert ]
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
[ crl_ext ]
authorityKeyIdentifier=keyid:always
[ ocsp ]
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, OCSPSigning
EOF
Generate CA certificate (self-signed, 10-year validity):
openssl req -config ca.conf \
-key private/ca.key \
-new -x509 -days 3650 \
-sha256 -extensions v3_ca \
-out root-ca.crt \
-subj "/C=US/ST=State/L=City/O=Organization/CN=Internal-CA"
Verify CA certificate:
openssl x509 -noout -text -in root-ca.crt
Initialize certificate database:
touch index.txt
echo 1000 > serial
Generating Server Certificates
Generate certificates for your internal services.
Create server certificate signing request (CSR):
# For a specific hostname
openssl req -config ca.conf \
-key private/server.key \
-new -sha256 \
-out csr/server.csr \
-keyout private/server.key \
-subj "/C=US/ST=State/L=City/O=Organization/CN=api.internal"
Or create a separate key first:
openssl genrsa -out private/server.key 2048
Create CSR for existing key:
openssl req -new \
-key private/server.key \
-out csr/server.csr \
-subj "/C=US/ST=State/L=City/O=Organization/CN=api.internal"
For certificates with multiple Subject Alternative Names (SANs), create extension file:
cat > csr/server-san.ext <<EOF
subjectAltName = DNS:api.internal,DNS:api,DNS:api.example.com,IP:192.168.1.10
EOF
Sign the server certificate with CA:
openssl ca -config ca.conf \
-extensions server_cert \
-days 730 \
-notext -md sha256 \
-in csr/server.csr \
-out certs/server.crt
When prompted, confirm with "y" twice.
Sign with SAN extension:
openssl x509 -req \
-in csr/server.csr \
-CA root-ca.crt \
-CAkey private/ca.key \
-CAcreateserial \
-out certs/server.crt \
-days 730 \
-sha256 \
-extfile csr/server-san.ext
Verify server certificate:
openssl x509 -noout -text -in certs/server.crt
openssl verify -CAfile root-ca.crt certs/server.crt
Creating Client Certificates
Generate certificates for client authentication.
Create client CSR:
openssl req -new \
-key private/client.key \
-keyout private/client.key \
-out csr/client.csr \
-subj "/C=US/ST=State/L=City/O=Organization/CN=client-app"
Create extension for client usage:
cat > csr/client.ext <<EOF
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "Client Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection
EOF
Sign client certificate:
openssl x509 -req \
-in csr/client.csr \
-CA root-ca.crt \
-CAkey private/ca.key \
-CAcreateserial \
-out certs/client.crt \
-days 730 \
-sha256 \
-extfile csr/client.ext
Create PKCS12 keystore (for browser/application import):
openssl pkcs12 -export \
-in certs/client.crt \
-inkey private/client.key \
-out certs/client.p12 \
-name "client-app" \
-passout pass:clientpassword
Trust Store Configuration
Make the CA certificate trusted on systems.
Linux - Add to system CA store:
# Copy CA certificate
sudo cp root-ca.crt /usr/local/share/ca-certificates/
# Update CA store
sudo update-ca-certificates
# Verify
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt server.crt
Export CA certificate in PEM format:
openssl x509 -in root-ca.crt -out root-ca.pem
Firefox - Manual import:
- Open Preferences > Privacy & Security > Certificates
- Click "View Certificates"
- Import Authority tab
- Select root-ca.crt
- Trust for website identification
Chrome - Ubuntu:
sudo cp root-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
Chrome uses system CA store on most platforms.
Python - Trust CA certificate:
import ssl
import certifi
# Create custom SSL context
context = ssl.create_default_context()
context.load_verify_locations('/path/to/root-ca.crt')
# Use in requests
import requests
response = requests.get('https://api.internal', verify='/path/to/root-ca.crt')
Node.js - Trust CA certificate:
export NODE_EXTRA_CA_CERTS=/path/to/root-ca.crt
Or in code:
const https = require('https');
const fs = require('fs');
const agent = new https.Agent({
ca: fs.readFileSync('/path/to/root-ca.crt')
});
https.get('https://api.internal', { agent }, (res) => {
console.log(res.statusCode);
});
Java Keystore Setup
Create Java keystores for internal certificates.
Import CA certificate:
keytool -import -alias internal-ca \
-file root-ca.crt \
-keystore truststore.jks \
-storepass trustpassword \
-noprompt
Create server keystore with certificate:
keytool -import -alias server \
-file certs/server.crt \
-keystore server.jks \
-storepass keystorepassword \
-noprompt
Or create from PKCS12:
openssl pkcs12 -export \
-in certs/server.crt \
-inkey private/server.key \
-out server.p12 \
-name server \
-passout pass:keystorepassword
keytool -importkeystore \
-srckeystore server.p12 \
-srcstoretype PKCS12 \
-srcstorepass keystorepassword \
-destkeystore server.jks \
-deststoretype JKS \
-deststorepass keystorepassword
Import client certificate:
keytool -import -alias client \
-file certs/client.crt \
-keystore client.jks \
-storepass keystorepassword \
-noprompt
Verify keystore contents:
keytool -list -v -keystore server.jks -storepass keystorepassword
Java application configuration:
# Via system properties
java -Djavax.net.ssl.trustStore=truststore.jks \
-Djavax.net.ssl.trustStorePassword=trustpassword \
-Djavax.net.ssl.keyStore=server.jks \
-Djavax.net.ssl.keyStorePassword=keystorepassword \
MyApp
# Via environment
export JAVA_OPTS="-Djavax.net.ssl.trustStore=/path/to/truststore.jks"
Nginx Configuration
Configure Nginx with self-signed certificates:
server {
listen 443 ssl http2;
server_name api.internal;
# Self-signed certificate and key
ssl_certificate /opt/certs/server.crt;
ssl_certificate_key /opt/private/server.key;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Client certificate authentication (optional)
ssl_client_certificate /opt/certs/client.crt;
ssl_verify_client optional;
location / {
proxy_pass http://backend:8080;
}
}
Test connection:
curl --cacert /opt/certs/root-ca.crt https://api.internal
With client certificate:
curl --cacert /opt/certs/root-ca.crt \
--cert /opt/certs/client.crt \
--key /opt/private/client.key \
https://api.internal
Apache Configuration
Configure Apache with self-signed certificates:
Listen 443 ssl
<VirtualHost *:443>
ServerName api.internal
SSLEngine on
SSLCertificateFile /opt/certs/server.crt
SSLCertificateKeyFile /opt/private/server.key
SSLProtocol TLSv1.2 TLSv1.3
SSLCipherSuite HIGH:!aNULL:!MD5
# Client certificate verification
SSLVerifyClient optional
SSLVerifyDepth 1
SSLCACertificatePath /opt/certs
ProxyPreserveHost On
ProxyPass / http://backend:8080/
ProxyPassReverse / http://backend:8080/
</VirtualHost>
Certificate Renewal
Renew self-signed certificates when expiring.
Check certificate expiration:
openssl x509 -noout -enddate -in certs/server.crt
Renew server certificate:
# New CSR
openssl req -new \
-key private/server.key \
-out csr/server-new.csr \
-subj "/C=US/ST=State/L=City/O=Organization/CN=api.internal"
# Sign with updated expiration
openssl x509 -req \
-in csr/server-new.csr \
-CA root-ca.crt \
-CAkey private/ca.key \
-CAcreateserial \
-out certs/server-new.crt \
-days 730 \
-sha256
# Backup and replace
cp certs/server.crt certs/server-old.crt
cp certs/server-new.crt certs/server.crt
# Reload services
systemctl reload nginx
Troubleshooting
Certificate trust issues:
# Verify certificate chain
openssl verify -CAfile root-ca.crt certs/server.crt
# Check certificate details
openssl x509 -in certs/server.crt -text -noout
# Test with curl
curl -v --cacert root-ca.crt https://api.internal
Hostname mismatch warnings:
# Check certificate CN and SANs
openssl x509 -in certs/server.crt -text | grep -A2 "Subject:\|Alternative"
# Regenerate with correct CN/SAN
Java keystore issues:
# List keystore
keytool -list -keystore truststore.jks
# View certificate in keystore
keytool -list -v -keystore truststore.jks -alias internal-ca
# Remove certificate
keytool -delete -keystore truststore.jks -alias internal-ca
Conclusion
Self-signed certificates provide encryption for internal services without external CA costs. This guide covered creating a private CA, issuing server and client certificates, configuring trust stores across operating systems, Java keystore setup, and web server configuration. For production internal services, maintain secure backups of CA keys, implement certificate rotation procedures, monitor expiration dates, and establish policies for certificate renewal. Self-signed certificates excel for development, testing, and internal-only services requiring encryption.


