DotnetGenDevcerts/dotnet-gen-devcerts.sh
2026-02-10 14:32:02 +01:00

316 lines
7.4 KiB
Bash
Executable file

#!/usr/bin/bash
set -euo pipefail
term_blue='\033[1;34m'
term_green='\033[0;32m'
term_yellow='\033[0;33m'
term_red='\033[0;31m'
term_reset='\033[0m'
cat <<EOF
==========================================
| |
| .NET Gen-Devcerts - v1.0.0 |
| |
| Copyright (c) 2026 Zervó Zadachin. |
| All Rights Reserved. |
| |
==========================================
EOF
SAVE_LOCAL=0
QUIET=0
NO_CLEAN=0
IGNORE_ERROR=0
if [ -z "${SSL_CERT_DIR+x}" ]; then
export SSL_CERT_DIR="$HOME/.aspnet/dev-certs/trust:/usr/lib/ssl/certs"
fi
# ========================
# COMMON FUNCTIONS
# ========================
# Print command usage
_usage() {
cat <<EOF
Usage: $0 [options]
Generates a new valid ASP.NET Core self-signed certificate for localhost.
Clears any existing certs in .NET certificate stores.
The new certificate will be imported into the system certificate store.
Options:
--help Show this usage information.
--save Copy the generated certificate to the home directory.
--quiet Reduce logging verbosity to only warnings and errors.
--noclean Skips the pre-cleaning of dotnet cert stores.
--force Do not exit on non-fatal errors. Causes unpredictable behaviour.
EOF
exit 1
}
# Show an INFO message
# $1: message string
_msg_info() {
local _msg="${1}"
if [ "$QUIET" = 0 ]; then
printf '[Gen-Devcerts] %bINFO%b: %s\n' "${term_blue}" "${term_reset}" "${_msg}"
fi
}
# Show a WARNING message
# $1: message string
_msg_warning() {
local _msg="${1}"
printf '[Gen-Devcerts] %bWARNING%b: %s\n' "${term_yellow}" "${term_reset}" "${_msg}" >&2
}
# Show an ERROR message then exit with status
# $1: message string
# $2: exit code number (with 0 does not exit)
_msg_error() {
local _msg="${1}"
local _error=${2}
printf '[Gen-Devcerts] %bERROR%b: %s\n' "${term_red}" "${term_reset}" "${_msg}" >&2
if ((_error > 0)); then
exit "${_error}"
fi
}
# ========================
# MAIN LOGIC
# ========================
# Parse options
OPTS=$(getopt -o sqnfh --long save,quiet,noclean,force,help -- "$@")
if [[ $? -ne 0 ]]; then
_msg_info "Use --help for help."
_msg_error "Invalid options"
fi
eval set -- "$OPTS"
while true; do
case "$1" in
--save | -s)
SAVE_LOCAL=1
shift
;;
--quiet | -q)
QUIET=1
shift
;;
--noclean | -n)
NO_CLEAN=1
shift
;;
--force | -f)
IGNORE_ERROR=1
shift
;;
--help | -h)
_usage
exit 1
;;
--)
shift
break
;;
*)
_msg_info "Use --help for help."
_msg_error "Unknown option: $1" 1
;;
esac
done
# Detect distro
. /etc/os-release
_msg_info "Found distribution '$ID'"
case "$ID" in
debian | ubuntu)
PLATFORM_SCRIPT="debian"
;;
arch)
PLATFORM_SCRIPT="arch"
;;
fedora | rhel | centos)
PLATFORM_SCRIPT="fedora"
;;
*)
PLATFORM_SCRIPT=""
if [ "$IGNORE_ERROR" = 1 ]; then
_msg_warning "Running on unsupported distribution. Platform-specific scripts will not be executed."
else
_msg_error "Running on unsupported distribution." 1
fi
;;
esac
# Create working directory
_msg_info "Creating working directory"
WORK_DIR="$(mktemp -d)"
_msg_info "Temp WorkDir at: '$WORK_DIR'"
# Set file paths
KEYFILE="$WORK_DIR/dotnet-devcert.key"
CRTFILE="$WORK_DIR/dotnet-devcert.crt"
PFXFILE="$WORK_DIR/dotnet-devcert.pfx"
NSSDB_PATHS="$HOME/.pki/nssdb \
$HOME/snap/chromium/current/.pki/nssdb \
$HOME/snap/postman/current/.pki/nssdb"
CONF_PATH="$WORK_DIR/localhost.conf"
# Post-variable functions
cleanup() {
_msg_info "Cleaning up"
rm -R $WORK_DIR
}
configure_nssdb() {
certutil -d sql:"$1" -D -n dotnet-devcert
certutil -d sql:"$1" -A -t "CP,," -n dotnet-devcert -i $CRTFILE
}
# Write config file
_msg_info "Writing certificate config"
cat >>$CONF_PATH <<EOF
[req]
prompt = no
default_bits = 2048
distinguished_name = subject
req_extensions = req_ext
x509_extensions = x509_ext
[ subject ]
commonName = localhost
[req_ext]
basicConstraints = critical, CA:true
subjectAltName = @alt_names
[x509_ext]
basicConstraints = critical, CA:true
keyUsage = critical, keyCertSign, cRLSign, digitalSignature,keyEncipherment
extendedKeyUsage = critical, serverAuth
subjectAltName = critical, @alt_names
1.3.6.1.4.1.311.84.1.1 = ASN1:UTF8String:ASP.NET Core HTTPS development certificate # Needed for import with dotnet dev-certs
[alt_names]
DNS.1 = localhost
EOF
# Remove old certificates
if [ "$NO_CLEAN" = 1 ]; then
_msg_warning "Skipping certificate pre-cleaning"
else
_msg_info "Removing old certificates"
dotnet dev-certs https --clean || true
rm -rf ~/.dotnet/devcrt/* || true
rm -rf ~/.aspnet/https/* || true
rm -f ~/.dotnet/corefx/cryptography/x509stores/my/* || true
fi
# Generate new certificates
_msg_info "Generating new certificates"
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout $KEYFILE -out $CRTFILE -config $CONF_PATH --passout pass:
openssl pkcs12 -export -out $PFXFILE -inkey $KEYFILE -in $CRTFILE --passout pass:
# Configure NSSDBs
_msg_info "Configuring NSSDBs"
for NSSDB in $NSSDB_PATHS; do
if [ -d "$NSSDB" ]; then
_msg_info "Configuring NSSDB for '$NSSDB'"
configure_nssdb "$NSSDB"
fi
done
# Enable sudo append if necessary
SUDO=''
if [ "$(id -u)" -ne 0 ]; then
SUDO='sudo'
fi
# Import certificate to dotnet
_msg_info "Importing certificate with dotnet dev-certs"
dotnet dev-certs https --clean --import $PFXFILE -p ""
# Save local home copy if enabled
if [ "$SAVE_LOCAL" = 1 ]; then
cp $CRTFILE $HOME
_msg_info "Saved certificate to $HOME/$(basename $CRTFILE)"
cp $PFXFILE $HOME
_msg_info "Saved certificate to $HOME/$(basename $PFXFILE)"
fi
# Main logic, specifically platform script execution, continues after platform script definitions.
# ========================
# PLATFORM SCRIPTS
# ========================
postrun_arch() {
local OLD_CRT=/etc/ca-certificates/trust-source/localhost.p11-kit
if [ -f "$OLD_CRT" ]; then
_msg_info "Removing old certificate from trust store"
$SUDO rm "$OLD_CRT" || true
fi
_msg_info "Adding new certificate to trust store"
$SUDO trust anchor --store $CRTFILE
$SUDO trust extract-compat
local FINGERPRINT="$(openssl x509 -noout -fingerprint -sha1 -inform pem -in /etc/ca-certificates/trust-source/localhost.p11-kit)"
_msg_info "Stored: $FINGERPRINT"
}
postrun_debian() {
_msg_info "Removing old certificate from system"
$SUDO rm /etc/ssl/certs/dotnet-devcert.pem || true
_msg_info "Installing new certificate on system"
$SUDO cp $CRTFILE "/usr/local/share/ca-certificates"
$SUDO update-ca-certificates
local FINGERPRINT="$(openssl x509 -noout -fingerprint -sha1 -inform pem -in /usr/local/share/ca-certificates/aspnet/https.crt)"
_msg_info "Stored: $FINGERPRINT"
}
postrun_fedora() {
_msg_info "Adding new certificate to trust store"
$SUDO cp $CRTFILE "/etc/pki/ca-trust/source/anchors/"
$SUDO update-ca-trust
}
# ========================
# POST-RUN LOGIC
# ========================
_msg_info "Running post-gen platform scripts"
# Run platform script here
case "$PLATFORM_SCRIPT" in
debian)
postrun_debian
;;
arch)
postrun_arch
;;
fedora)
postrun_fedora
;;
*)
_msg_warning "Post-gen skipped: unknown distribution"
;;
esac
# Perform cleanup
cleanup
# Announce completion
printf "%bCertificates (re)generated successfully! %b\n" "${term_green}" "${term_reset}"