Skip to content

Instantly share code, notes, and snippets.

@hasmukhlalpatel
Last active September 7, 2025 22:50
Show Gist options
  • Select an option

  • Save hasmukhlalpatel/91036336690135abe600e97831455544 to your computer and use it in GitHub Desktop.

Select an option

Save hasmukhlalpatel/91036336690135abe600e97831455544 to your computer and use it in GitHub Desktop.
Generate self sign certificates for local development

Generate self sign certificates for local development

To use the script: WSL commans to set environment variable in command prompt:

wsl -d k3s -- bash
export GLOBAL_CERT_PATH="$HOME/ssl/certs"
#export GLOBAL_CERT_PATH="/mnt/c/Users/$USER/TMP/ssl/certs"

Make it executable:

chmod +x cert_common.sh
chmod +x create_ca_cert.sh
chmod +x create_server_cert.sh
chmod +x check_certs.sh
chmod +x check_ca_cert.sh

Create CA certificate:

Usage:

chmod +x create_ca_cert.sh
./create_ca_cert.sh ca
./create_ca_cert.sh rootca --interactive

With environment variables:

GLOBAL_CERT_PATH="/opt/certs" \
CA_ORG="My Company" \
CA_EMAIL="admin@mycompany.com" \
./create_ca_cert.sh rootca.crt

Simple server certificate

Usage:

chmod +x create_server_cert.sh
./create_server_cert.sh server ca
./create_server_cert.sh web-server ca --domain example.com,www.example.com
./create_server_cert.sh web-server ca -d example.com,www.example.com
./create_server_cert.sh api-server ca --domain api.example.com --ip-address 192.168.1.100
./create_server_cert.sh api-server ca --d api.example.com -ip 192.168.1.100
#!/bin/bash
# Certificate Common Library
# Shared functions for certificate management scripts
# Usage: source ./cert_common.sh
# Default configuration - can be overridden by environment variables
GLOBAL_CERT_PATH="${GLOBAL_CERT_PATH:-$HOME/ssl/certs}"
CA_FOLDER="${CA_FOLDER:-CA}"
SERVER_FOLDER="${SERVER_FOLDER:-Server}"
KEY_SIZE="${KEY_SIZE:-2048}"
# Default certificate details for CA
DEFAULT_CA_COUNTRY="${CA_COUNTRY:-US}"
DEFAULT_CA_STATE="${CA_STATE:-California}"
DEFAULT_CA_CITY="${CA_CITY:-San Francisco}"
DEFAULT_CA_ORG="${CA_ORG:-My Organization}"
DEFAULT_CA_ORG_UNIT="${CA_ORG_UNIT:-IT Department}"
DEFAULT_CA_EMAIL="${CA_EMAIL:-admin@example.com}"
CA_VALIDITY_DAYS="${CA_VALIDITY_DAYS:-3650}" # 10 years
# Default certificate details for Server
DEFAULT_SERVER_COUNTRY="${SERVER_COUNTRY:-US}"
DEFAULT_SERVER_STATE="${SERVER_STATE:-California}"
DEFAULT_SERVER_CITY="${SERVER_CITY:-San Francisco}"
DEFAULT_SERVER_ORG="${SERVER_ORG:-My Organization}"
DEFAULT_SERVER_ORG_UNIT="${SERVER_ORG_UNIT:-IT Department}"
DEFAULT_SERVER_EMAIL="${SERVER_EMAIL:-server@example.com}"
SERVER_VALIDITY_DAYS="${SERVER_VALIDITY_DAYS:-365}" # 1 year
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to log messages with colors
log_info() {
echo -e "${BLUE}ℹ${NC} $1"
}
log_success() {
echo -e "${GREEN}✓${NC} $1"
}
log_warning() {
echo -e "${YELLOW}⚠${NC} $1"
}
log_error() {
echo -e "${RED}✗${NC} $1"
}
# Function to check if OpenSSL is available
check_openssl() {
if ! command -v openssl > /dev/null 2>&1; then
log_error "OpenSSL is not installed or not in PATH"
log_error "Please install OpenSSL to manage certificates"
exit 1
fi
}
# Function to create directory if it doesn't exist
create_directory() {
local dir_path="$1"
if [[ ! -d "$dir_path" ]]; then
log_info "Creating directory: $dir_path"
if ! mkdir -p "$dir_path"; then
log_error "Failed to create directory $dir_path"
log_error "Check permissions or run with sudo if needed"
exit 1
fi
log_success "Directory created successfully"
fi
}
# Function to check if file exists
check_file_exists() {
local file_path="$1"
local file_type="$2"
if [[ -f "$file_path" ]]; then
log_success "$file_type found: $file_path"
return 0
else
log_error "$file_type NOT found: $file_path"
return 1
fi
}
# Function to validate certificate format
validate_certificate() {
local cert_path="$1"
if [[ ! -f "$cert_path" ]]; then
log_error "Certificate file does not exist: $cert_path"
return 1
fi
if ! openssl x509 -in "$cert_path" -noout > /dev/null 2>&1; then
log_error "Invalid certificate format: $cert_path"
return 1
fi
return 0
}
# Function to check if certificate is a valid CA
validate_ca_certificate() {
local cert_path="$1"
if ! validate_certificate "$cert_path"; then
return 1
fi
# Check if certificate has CA:TRUE in basic constraints
local ca_constraint=$(openssl x509 -in "$cert_path" -noout -text 2>/dev/null | grep -A1 "Basic Constraints:" | grep "CA:TRUE")
if [[ -n "$ca_constraint" ]]; then
log_success "Certificate has proper CA constraints"
return 0
else
log_warning "Certificate does not have CA:TRUE constraint"
log_warning "This may not be a proper CA certificate"
return 1
fi
}
# Function to check certificate validity (not expired)
check_certificate_validity() {
local cert_path="$1"
if ! validate_certificate "$cert_path"; then
return 1
fi
if openssl x509 -in "$cert_path" -noout -checkend 0 > /dev/null 2>&1; then
log_success "Certificate is currently valid"
return 0
else
log_error "Certificate is expired or invalid"
return 1
fi
}
# Function to check certificate expiration warning
check_expiration_warning() {
local cert_path="$1"
local days_warning="${2:-30}"
if ! validate_certificate "$cert_path"; then
return 2
fi
# Check if certificate expires within warning period
if openssl x509 -in "$cert_path" -noout -checkend $((days_warning * 24 * 3600)) > /dev/null 2>&1; then
# Get actual days until expiration
local end_date=$(openssl x509 -in "$cert_path" -noout -enddate 2>/dev/null | sed 's/notAfter=//')
local end_epoch=$(date -d "$end_date" +%s 2>/dev/null)
local current_epoch=$(date +%s)
local days_left=$(( (end_epoch - current_epoch) / 86400 ))
if [[ $days_left -le $days_warning ]]; then
log_warning "Certificate expires in $days_left days"
return 1
else
log_success "Certificate expires in $days_left days (OK)"
return 0
fi
else
log_error "Certificate is expired"
return 2
fi
}
# Function to display certificate information
show_certificate_info() {
local cert_path="$1"
local title="${2:-Certificate Information}"
if ! validate_certificate "$cert_path"; then
return 1
fi
echo ""
echo "$title:"
echo "$(printf '=%.0s' $(seq 1 ${#title}))="
echo "Subject: $(openssl x509 -in "$cert_path" -noout -subject 2>/dev/null | sed 's/subject=//')"
echo "Issuer: $(openssl x509 -in "$cert_path" -noout -issuer 2>/dev/null | sed 's/issuer=//')"
echo "Valid From: $(openssl x509 -in "$cert_path" -noout -startdate 2>/dev/null | sed 's/notBefore=//')"
echo "Valid Until: $(openssl x509 -in "$cert_path" -noout -enddate 2>/dev/null | sed 's/notAfter=//')"
echo "Serial Number: $(openssl x509 -in "$cert_path" -noout -serial 2>/dev/null | sed 's/serial=//')"
# Show Subject Alternative Names for server certificates
local san=$(openssl x509 -in "$cert_path" -noout -text 2>/dev/null | grep -A1 "Subject Alternative Name:" | grep -v "Subject Alternative Name:")
if [[ -n "$san" ]]; then
echo "Subject Alternative Names: $san"
fi
echo "SHA1 Fingerprint: $(openssl x509 -in "$cert_path" -noout -fingerprint 2>/dev/null | sed 's/SHA1 Fingerprint=//')"
}
# Function to set file permissions securely
set_secure_permissions() {
local cert_path="$1"
local key_path="$2"
if [[ -f "$cert_path" ]]; then
chmod 644 "$cert_path"
fi
if [[ -f "$key_path" ]]; then
chmod 600 "$key_path"
fi
log_success "Permissions set correctly"
echo " Certificate: 644 (readable by all)"
echo " Private key: 600 (readable by owner only)"
}
# Function to verify certificate chain
verify_certificate_chain() {
local cert_path="$1"
local ca_cert_path="$2"
if ! validate_certificate "$cert_path" || ! validate_certificate "$ca_cert_path"; then
return 1
fi
echo ""
echo "Verifying certificate chain..."
echo "============================="
if openssl verify -CAfile "$ca_cert_path" "$cert_path" > /dev/null 2>&1; then
log_success "Certificate chain verification successful"
echo " Certificate is properly signed by the CA"
return 0
else
log_error "Certificate chain verification failed"
echo " Certificate may not be properly signed by the CA"
return 1
fi
}
# Function to collect certificate details interactively
collect_certificate_details() {
local cert_type="$1" # "ca" or "server"
local common_name_default="$2"
echo ""
echo "Enter ${cert_type^^} Certificate Details:"
echo "$(printf '=%.0s' $(seq 1 $((${#cert_type} + 22))))"
# Set defaults based on certificate type
if [[ "$cert_type" == "ca" ]]; then
local default_country="$DEFAULT_CA_COUNTRY"
local default_state="$DEFAULT_CA_STATE"
local default_city="$DEFAULT_CA_CITY"
local default_org="$DEFAULT_CA_ORG"
local default_org_unit="$DEFAULT_CA_ORG_UNIT"
local default_email="$DEFAULT_CA_EMAIL"
local default_cn="${common_name_default:-${DEFAULT_CA_ORG} Root CA}"
else
local default_country="$DEFAULT_SERVER_COUNTRY"
local default_state="$DEFAULT_SERVER_STATE"
local default_city="$DEFAULT_SERVER_CITY"
local default_org="$DEFAULT_SERVER_ORG"
local default_org_unit="$DEFAULT_SERVER_ORG_UNIT"
local default_email="$DEFAULT_SERVER_EMAIL"
local default_cn="${common_name_default:-localhost}"
fi
read -p "Country (2 letter code) [$default_country]: " CERT_COUNTRY
CERT_COUNTRY=${CERT_COUNTRY:-$default_country}
read -p "State/Province [$default_state]: " CERT_STATE
CERT_STATE=${CERT_STATE:-$default_state}
read -p "City/Locality [$default_city]: " CERT_CITY
CERT_CITY=${CERT_CITY:-$default_city}
read -p "Organization [$default_org]: " CERT_ORG
CERT_ORG=${CERT_ORG:-$default_org}
read -p "Organizational Unit [$default_org_unit]: " CERT_ORG_UNIT
CERT_ORG_UNIT=${CERT_ORG_UNIT:-$default_org_unit}
if [[ "$cert_type" == "ca" ]]; then
read -p "Common Name (CA Name) [$default_cn]: " CERT_COMMON_NAME
else
read -p "Common Name (Server FQDN) [$default_cn]: " CERT_COMMON_NAME
fi
CERT_COMMON_NAME=${CERT_COMMON_NAME:-$default_cn}
read -p "Email Address [$default_email]: " CERT_EMAIL
CERT_EMAIL=${CERT_EMAIL:-$default_email}
# Additional fields for server certificates
if [[ "$cert_type" == "server" ]]; then
if [[ -z "$CERT_DOMAINS" ]]; then
read -p "Additional domain names (comma-separated, optional): " CERT_DOMAINS
fi
if [[ -z "$CERT_IP_ADDRESSES" ]]; then
read -p "IP addresses (comma-separated, optional): " CERT_IP_ADDRESSES
fi
fi
echo ""
echo "${cert_type^^} Certificate Details Summary:"
echo "$(printf '=%.0s' $(seq 1 $((${#cert_type} + 30))))"
echo "Country: $CERT_COUNTRY"
echo "State: $CERT_STATE"
echo "City: $CERT_CITY"
echo "Organization: $CERT_ORG"
echo "Organizational Unit: $CERT_ORG_UNIT"
echo "Common Name: $CERT_COMMON_NAME"
echo "Email: $CERT_EMAIL"
if [[ "$cert_type" == "server" ]]; then
echo "Additional Domains: ${CERT_DOMAINS:-none}"
echo "IP Addresses: ${CERT_IP_ADDRESSES:-none}"
fi
echo "Key Size: $KEY_SIZE bits"
if [[ "$cert_type" == "ca" ]]; then
echo "Validity: $CA_VALIDITY_DAYS days"
else
echo "Validity: $SERVER_VALIDITY_DAYS days"
fi
echo ""
read -p "Proceed with these details? [Y/n]: " CONFIRM
if [[ $CONFIRM =~ ^[Nn] ]]; then
echo "${cert_type^} certificate creation cancelled"
exit 0
fi
}
# Function to set default certificate details
set_default_certificate_details() {
local cert_type="$1" # "ca" or "server"
local common_name_default="$2"
if [[ "$cert_type" == "ca" ]]; then
CERT_COUNTRY="$DEFAULT_CA_COUNTRY"
CERT_STATE="$DEFAULT_CA_STATE"
CERT_CITY="$DEFAULT_CA_CITY"
CERT_ORG="$DEFAULT_CA_ORG"
CERT_ORG_UNIT="$DEFAULT_CA_ORG_UNIT"
CERT_COMMON_NAME="${common_name_default:-${DEFAULT_CA_ORG} Root CA}"
CERT_EMAIL="$DEFAULT_CA_EMAIL"
else
CERT_COUNTRY="$DEFAULT_SERVER_COUNTRY"
CERT_STATE="$DEFAULT_SERVER_STATE"
CERT_CITY="$DEFAULT_SERVER_CITY"
CERT_ORG="$DEFAULT_SERVER_ORG"
CERT_ORG_UNIT="$DEFAULT_SERVER_ORG_UNIT"
CERT_COMMON_NAME="${common_name_default:-localhost}"
CERT_EMAIL="$DEFAULT_SERVER_EMAIL"
fi
}
# Function to construct certificate subject
get_certificate_subject() {
echo "/C=${CERT_COUNTRY}/ST=${CERT_STATE}/L=${CERT_CITY}/O=${CERT_ORG}/OU=${CERT_ORG_UNIT}/CN=${CERT_COMMON_NAME}/emailAddress=${CERT_EMAIL}"
}
# Function to get certificate paths (automatically adds .crt extension)
get_certificate_paths() {
local cert_name="$1"
local folder_type="$2" # "ca" or "server"
# Remove any existing extension and add .crt
local base_name="${cert_name%.*}"
local cert_filename="${base_name}.crt"
local key_filename="${base_name}.key"
if [[ "$folder_type" == "ca" ]]; then
local folder="$CA_FOLDER"
else
local folder="$SERVER_FOLDER"
fi
local cert_dir="${GLOBAL_CERT_PATH}/${folder}"
local cert_path="${cert_dir}/${cert_filename}"
local key_path="${cert_dir}/${key_filename}"
echo "$cert_dir|$cert_path|$key_path|$cert_filename|$key_filename"
}
# Function to check if certificate should be created (exists check)
should_create_certificate() {
local cert_path="$1"
local force_overwrite="$2"
local cert_type="$3"
if [[ -f "$cert_path" ]] && [[ "$force_overwrite" != "true" ]]; then
log_success "${cert_type^} certificate already exists: $cert_path"
echo ""
echo "Certificate information:"
show_certificate_info "$cert_path"
echo ""
echo "Use --force to overwrite existing certificate"
return 1 # Don't create
fi
return 0 # Should create
}
# Function to parse common command line arguments
parse_common_args() {
FORCE_OVERWRITE=false
INTERACTIVE_MODE=false
local remaining_args=()
while [[ $# -gt 0 ]]; do
case $1 in
-f|--force)
FORCE_OVERWRITE=true
shift
;;
-i|--interactive)
INTERACTIVE_MODE=true
shift
;;
-h|--help)
return 2 # Signal to show usage
;;
*)
remaining_args+=("$1")
shift
;;
esac
done
# Return remaining args as separate echo statements
for arg in "${remaining_args[@]}"; do
echo "$arg"
done
return 0
}
# Function to display script header
show_script_header() {
local script_name="$1"
local description="$2"
echo "$script_name"
echo "$(printf '=%.0s' $(seq 1 ${#script_name}))"
if [[ -n "$description" ]]; then
echo "$description"
echo ""
fi
}
#!/bin/bash
# CA Certificate Generator Script
# Usage: ./create_ca_cert.sh <ca_cert_name> [options]
# Source common functions
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ -f "${SCRIPT_DIR}/cert_common.sh" ]]; then
source "${SCRIPT_DIR}/cert_common.sh"
else
echo "Error: cert_common.sh not found in ${SCRIPT_DIR}"
echo "Please ensure cert_common.sh is in the same directory as this script"
exit 1
fi
# Function to display usage
usage() {
echo "Usage: $0 <ca_cert_name> [options]"
echo ""
echo "Parameters:"
echo " ca_cert_name Name of the CA certificate file (e.g., ca.crt)"
echo ""
echo "Options:"
echo " -f, --force Overwrite existing certificate"
echo " -i, --interactive Interactive mode for certificate details"
echo " -h, --help Show this help message"
echo ""
echo "Environment Variables:"
echo " GLOBAL_CERT_PATH Global certificate path (default: /etc/ssl/certs)"
echo " CA_FOLDER CA folder name (default: CA)"
echo " KEY_SIZE RSA key size (default: 2048)"
echo " CA_VALIDITY_DAYS Certificate validity in days (default: 3650)"
echo " CA_COUNTRY Country (default: US)"
echo " CA_STATE State (default: California)"
echo " CA_CITY City (default: San Francisco)"
echo " CA_ORG Organization (default: My Organization)"
echo " CA_ORG_UNIT Organizational Unit (default: IT Department)"
echo " CA_EMAIL Email (default: admin@example.com)"
echo ""
echo "Examples:"
echo " $0 ca.crt"
echo " $0 rootca.crt --interactive"
echo " GLOBAL_CERT_PATH=/opt/certs $0 ca.crt --force"
exit 1
}
# Function to create CA certificate
create_ca_certificate() {
local cert_path="$1"
local key_path="$2"
local cert_dir=$(dirname "$cert_path")
local cert_basename=$(basename "$cert_path" .crt)
local pfx_file_path="${cert_dir}/${cert_basename}.pfx"
local pfx_password="changeme"
echo "Generating CA certificate..."
echo "==========================="
local subject=$(get_certificate_subject)
log_info "Subject: $subject"
log_info "Certificate path: $cert_path"
log_info "Private key path: $key_path"
log_info "PFX file path: $pfx_file_path"
echo ""
# Generate private key and certificate in one command
if openssl req -x509 -newkey rsa:${KEY_SIZE} -keyout "$key_path" -out "$cert_path" \
-days ${CA_VALIDITY_DAYS} -nodes -subj "$subject" \
-extensions v3_ca -config <(
echo '[req]'
echo 'distinguished_name = req'
echo '[v3_ca]'
echo 'subjectKeyIdentifier = hash'
echo 'authorityKeyIdentifier = keyid:always,issuer'
echo 'basicConstraints = critical, CA:true'
echo 'keyUsage = critical, keyCertSign, cRLSign'
) 2>/dev/null; then
log_success "CA certificate generated successfully"
# Set appropriate permissions
set_secure_permissions "$cert_path" "$key_path"
# Optionally create PFX file for compatibility: -passout pass:"$pfx_password" , -passout pass: for null password
if openssl pkcs12 -export -out "$pfx_file_path" -inkey "$key_path" -in "$cert_path" -password pass:"$pfx_password" 2>/dev/null; then
log_success "PFX file created at $pfx_file_path"
else
log_error "Failed to create PFX file"
fi
return 0
else
log_error "Failed to generate CA certificate"
return 1
fi
}
# Main execution
main() {
local ca_cert_name=""
# Parse command line arguments using common function
local remaining_args=($(parse_common_args "$@"))
local parse_result=$?
if [[ $parse_result -eq 2 ]]; then
usage
fi
# Get certificate name from remaining args
if [[ ${#remaining_args[@]} -eq 1 ]]; then
ca_cert_name="${remaining_args[0]}"
else
log_error "CA certificate name is required"
usage
fi
# Check OpenSSL availability
check_openssl
# Get certificate paths - FIXED: Properly parse the pipe-separated string
local paths=$(get_certificate_paths "$ca_cert_name" "ca")
IFS='|' read -r ca_dir cert_path key_path cert_filename key_filename <<< "$paths"
show_script_header "CA Certificate Generator" "Creates a new Certificate Authority certificate and private key"
log_info "Certificate name: $ca_cert_name"
log_info "Generated files: $cert_filename, $key_filename"
log_info "CA directory: $ca_dir"
log_info "Certificate path: $cert_path"
log_info "Private key path: $key_path"
echo ""
# Check if certificate should be created
if ! should_create_certificate "$cert_path" "$FORCE_OVERWRITE" "CA"; then
exit 0
fi
# Create CA directory if needed
create_directory "$ca_dir"
# Collect or set certificate details
if [[ "$INTERACTIVE_MODE" == true ]]; then
collect_certificate_details "ca"
else
set_default_certificate_details "ca" "${ca_cert_name} Root CA"
log_info "Using default certificate details (use --interactive for custom details)"
fi
# Generate CA certificate
if create_ca_certificate "$cert_path" "$key_path"; then
show_certificate_info "$cert_path" "Generated CA Certificate Information"
echo ""
log_success "CA certificate creation completed successfully!"
echo ""
echo "Files created:"
echo " Certificate: $cert_path"
echo " Private key: $key_path"
echo ""
echo "Next steps:"
echo "- Keep the private key secure"
echo "- Use this CA to sign server certificates"
echo "- Install the CA certificate on client systems if needed"
else
log_error "CA certificate creation failed"
exit 1
fi
}
# Run main function with all parameters
main "$@"
#!/bin/bash
# Server Certificate Generator Script
# Usage: ./create_server_cert.sh <server_cert_name> <ca_cert_name> [options]
# Source common functions
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ -f "${SCRIPT_DIR}/cert_common.sh" ]]; then
source "${SCRIPT_DIR}/cert_common.sh"
else
echo "Error: cert_common.sh not found in ${SCRIPT_DIR}"
echo "Please ensure cert_common.sh is in the same directory as this script"
exit 1
fi
# Function to display usage
usage() {
echo "Usage: $0 <server_cert_name> <ca_cert_name> [options]"
echo ""
echo "Parameters:"
echo " server_cert_name Name of the server certificate (without extension, e.g., server, web-server)"
echo " ca_cert_name Name of the CA certificate (without extension, e.g., ca, rootca)"
echo ""
echo "Options:"
echo " -f, --force Overwrite existing server certificate"
echo " -i, --interactive Interactive mode for certificate details"
echo " -d, --domain Domain name(s) for the certificate (comma-separated)"
echo " -ip, --ip-address IP address(es) for the certificate (comma-separated)"
echo " -h, --help Show this help message"
echo ""
echo "Environment Variables:"
echo " GLOBAL_CERT_PATH Global certificate path (default: /etc/ssl/certs)"
echo " CA_FOLDER CA folder name (default: CA)"
echo " SERVER_FOLDER Server folder name (default: Server)"
echo " KEY_SIZE RSA key size (default: 2048)"
echo " SERVER_VALIDITY_DAYS Certificate validity in days (default: 365)"
echo " SERVER_COUNTRY Country (default: US)"
echo " SERVER_STATE State (default: California)"
echo " SERVER_CITY City (default: San Francisco)"
echo " SERVER_ORG Organization (default: My Organization)"
echo " SERVER_ORG_UNIT Organizational Unit (default: IT Department)"
echo " SERVER_EMAIL Email (default: server@example.com)"
echo ""
echo "Examples:"
echo " $0 server ca"
echo " $0 web-server ca --domain example.com,www.example.com"
echo " $0 api-server ca --domain api.example.com --ip 192.168.1.100"
echo " $0 server ca --interactive --force"
exit 1
}
# Function to check if CA certificate and key exist
check_ca_files() {
local ca_cert_path="$1"
local ca_key_path="$2"
echo "Checking CA certificate and key..."
echo "================================="
if ! check_file_exists "$ca_cert_path" "CA certificate"; then
log_error "Please create a CA certificate first using create_ca_cert.sh"
return 1
fi
if ! check_file_exists "$ca_key_path" "CA private key"; then
log_error "CA private key is required to sign server certificates"
return 1
fi
# Validate CA certificate
if ! validate_ca_certificate "$ca_cert_path"; then
return 1
fi
return 0
}
# Function to generate SAN (Subject Alternative Name) extension
generate_san_config() {
local config_file="$1"
local alt_names=""
local counter=1
# Add common name as first SAN entry
alt_names="DNS.${counter} = ${CERT_COMMON_NAME}"
((counter++))
# Add additional domains
if [[ -n "$CERT_DOMAINS" ]]; then
IFS=',' read -ra DOMAIN_ARRAY <<< "$CERT_DOMAINS"
for domain in "${DOMAIN_ARRAY[@]}"; do
domain=$(echo "$domain" | xargs) # Trim whitespace
if [[ -n "$domain" ]]; then
alt_names="${alt_names}\nDNS.${counter} = ${domain}"
((counter++))
fi
done
fi
# Add IP addresses
if [[ -n "$CERT_IP_ADDRESSES" ]]; then
local ip_counter=1
IFS=',' read -ra IP_ARRAY <<< "$CERT_IP_ADDRESSES"
for ip in "${IP_ARRAY[@]}"; do
ip=$(echo "$ip" | xargs) # Trim whitespace
if [[ -n "$ip" ]]; then
alt_names="${alt_names}\nIP.${ip_counter} = ${ip}"
((ip_counter++))
fi
done
fi
# Create OpenSSL config file for server certificate
cat > "$config_file" << EOF
[req]
distinguished_name = req
req_extensions = v3_req
prompt = no
[v3_req]
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[alt_names]
$(echo -e "$alt_names")
EOF
}
# Function to create server certificate
create_server_certificate() {
local server_cert_path="$1"
local server_key_path="$2"
local ca_cert_path="$3"
local ca_key_path="$4"
local pfx_password="changeme"
local cert_dir=$(dirname "$server_cert_path")
local cert_basename=$(basename "$server_cert_path" .crt)
local pfx_file_path="${cert_dir}/${cert_basename}.pfx"
local server_chain_file_path="${cert_dir}/${cert_basename}-chain.crt"
local server_fullchain_file_path="${cert_dir}/${cert_basename}-fullchain.pfx"
echo ""
echo "Generating server certificate..."
echo "==============================="
local subject=$(get_certificate_subject)
log_info "Subject: $subject"
log_info "Server certificate: $server_cert_path"
log_info "Server private key: $server_key_path"
log_info "CA certificate: $ca_cert_path"
log_info "Server chain: $server_chain_file_path"
log_info "PFX file: $pfx_file_path"
log_info "Server Full chain: $server_fullchain_file_path"
echo ""
# Create temporary files
local csr_file=$(mktemp /tmp/server.csr.XXXXXX)
local config_file=$(mktemp /tmp/server.conf.XXXXXX)
# Generate SAN configuration
generate_san_config "$config_file"
echo "Generated certificate configuration:"
echo "===================================="
cat "$config_file"
echo ""
# Generate private key
echo "Generating private key..."
if ! openssl genrsa -out "$server_key_path" ${KEY_SIZE} > /dev/null 2>&1; then
log_error "Failed to generate private key"
rm -f "$csr_file" "$config_file"
return 1
fi
log_success "Private key generated"
# Generate certificate signing request (CSR)
echo "Generating certificate signing request..."
if ! openssl req -new -key "$server_key_path" -out "$csr_file" \
-subj "$subject" -config "$config_file" > /dev/null 2>&1; then
log_error "Failed to generate CSR"
rm -f "$csr_file" "$config_file"
return 1
fi
log_success "CSR generated"
# Sign the certificate with CA
echo "Signing certificate with CA..."
if openssl x509 -req -in "$csr_file" -CA "$ca_cert_path" -CAkey "$ca_key_path" \
-CAcreateserial -out "$server_cert_path" -days ${SERVER_VALIDITY_DAYS} \
-extensions v3_req -extfile "$config_file" > /dev/null 2>&1; then
log_success "Server certificate signed successfully"
# Set appropriate permissions
set_secure_permissions "$server_cert_path" "$server_key_path"
# Clean up temporary files
rm -f "$csr_file" "$config_file"
log_info "Generating PFX file: $pfx_file_path"
if openssl pkcs12 -export -out "$pfx_file_path" -inkey "$server_key_path" \
-in "$server_cert_path" -certfile "$ca_cert_path" -password pass:"$pfx_password" > /dev/null 2>&1; then
log_success "PFX file created successfully"
log_info "PFX password: $pfx_password"
else
log_error "Failed to create PFX file"
fi
# Create certificate chain file
log_info "Creating certificate chain file: $server_chain_file_path"
cat "$server_cert_path" "$ca_cert_path" > "$server_chain_file_path"
# Create full chain PFX file
log_info "Creating full chain PFX file: $server_fullchain_file_path"
if openssl pkcs12 -export -out "$server_fullchain_file_path" -inkey "$server_key_path" \
-in "$server_chain_file_path" -password pass:"$pfx_password" > /dev/null 2>&1; then
log_success "Full chain PFX file created successfully"
log_info "Full chain PFX password: $pfx_password"
else
log_error "Failed to create full chain PFX file"
fi
return 0
else
log_error "Failed to sign server certificate"
rm -f "$csr_file" "$config_file"
return 1
fi
}
# Function to parse server-specific arguments
parse_server_args() {
local cert_domains=""
local cert_ip_addresses=""
local force_overwrite=false
local interactive_mode=false
local remaining_args=()
while [[ $# -gt 0 ]]; do
case $1 in
-d|--domain)
if [[ -n "$2" ]]; then
cert_domains="$2"
shift 2
else
log_error "Option --domain requires a value"
exit 1
fi
;;
-ip|--ip-address)
if [[ -n "$2" ]]; then
cert_ip_addresses="$2"
shift 2
else
log_error "Option --ip-address requires a value"
exit 1
fi
;;
-f|--force)
force_overwrite=true
shift
;;
-i|--interactive)
interactive_mode=true
shift
;;
-h|--help)
usage
exit 0
;;
-*)
log_error "Unknown option: $1"
usage
exit 1
;;
*)
remaining_args+=("$1")
shift
;;
esac
done
# Return formatted output that can be eval'd
echo "CERT_DOMAINS='$cert_domains'"
echo "CERT_IP_ADDRESSES='$cert_ip_addresses'"
echo "FORCE_OVERWRITE='$force_overwrite'"
echo "INTERACTIVE_MODE='$interactive_mode'"
echo "remaining_args=(${remaining_args[*]})"
}
# Main execution
main() {
eval "$(parse_server_args "$@")"
echo ""
echo "=== After function call ==="
echo "Exported CERT_DOMAINS: '$CERT_DOMAINS'"
echo "Exported CERT_IP_ADDRESSES: '$CERT_IP_ADDRESSES'"
echo "Exported FORCE_OVERWRITE: '$FORCE_OVERWRITE'"
echo "Exported INTERACTIVE_MODE: '$INTERACTIVE_MODE'"
echo "Remaining args: ${remaining_args[*]}"
echo ""
local server_cert_name=""
local ca_cert_name=""
# Parse server-specific arguments
##local remaining_args = $REMAINING_ARGS[@]
##mapfile -t remaining_args < <(parse_server_args "$@")
# Get certificate names from remaining args
if [[ ${#remaining_args[@]} -eq 2 ]]; then
server_cert_name="${remaining_args[0]}"
ca_cert_name="${remaining_args[1]}"
else
log_error "Both server certificate name and CA certificate name are required"
usage
fi
# Check OpenSSL availability
check_openssl
# Get certificate paths - FIXED: Properly parse the pipe-separated string
local ca_paths=$(get_certificate_paths "$ca_cert_name" "ca")
IFS='|' read -r ca_dir ca_cert_path ca_key_path ca_cert_filename ca_key_filename <<< "$ca_paths"
local server_paths=$(get_certificate_paths "$server_cert_name" "server")
IFS='|' read -r server_dir server_cert_path server_key_path server_cert_filename server_key_filename <<< "$server_paths"
show_script_header "Server Certificate Generator" "Creates server certificates signed by a CA certificate"
log_info "Server certificate name: $server_cert_name"
log_info "CA certificate name: $ca_cert_name"
log_info "Generated files: $server_cert_filename, $server_key_filename"
log_info "Server directory: $server_dir"
log_info "CA directory: $ca_dir"
if [[ -n "$CERT_DOMAINS" ]]; then
log_info "Additional domains: $CERT_DOMAINS"
fi
if [[ -n "$CERT_IP_ADDRESSES" ]]; then
log_info "IP addresses: $CERT_IP_ADDRESSES"
fi
echo ""
# Check CA certificate and key exist
if ! check_ca_files "$ca_cert_path" "$ca_key_path"; then
echo ""
log_error "Cannot proceed without valid CA certificate and key"
exit 1
fi
echo ""
# Check if server certificate should be created
if ! should_create_certificate "$server_cert_path" "$FORCE_OVERWRITE" "server"; then
exit 0
fi
# Create server directory if needed
create_directory "$server_dir"
# Collect or set certificate details
if [[ "$INTERACTIVE_MODE" == true ]]; then
collect_certificate_details "server"
else
set_default_certificate_details "server" "$server_cert_name"
log_info "Using default certificate details (use --interactive for custom details)"
fi
# Generate server certificate
if create_server_certificate "$server_cert_path" "$server_key_path" "$ca_cert_path" "$ca_key_path"; then
show_certificate_info "$server_cert_path" "Generated Server Certificate Information"
verify_certificate_chain "$server_cert_path" "$ca_cert_path"
echo ""
log_success "Server certificate creation completed successfully!"
echo ""
echo "Files created:"
echo " Server certificate: $server_cert_path"
echo " Server private key: $server_key_path"
echo ""
echo "Next steps:"
echo "- Configure your server to use these certificate files"
echo "- Ensure the CA certificate is trusted by clients"
echo "- Keep the private key secure and never share it"
else
log_error "Server certificate creation failed"
exit 1
fi
}
# Run main function with all parameters
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment