Refactor simple-ca: Remove JSON config and streamline AIA URL handling
- Removed the JSON configuration structure and related functions. - Introduced plain text file for AIA base URL management. - Updated CA and certificate creation functions to directly read/write AIA URL. - Simplified CA bundle rebuilding logic by directly reading subdirectories. - Enhanced test coverage for CA and certificate creation, including PFX generation. - Adjusted test cases to reflect changes in directory structure and file handling.
This commit is contained in:
+129
-40
@@ -26,12 +26,15 @@
|
||||
# before sourcing this file, or overridden per-call with --ca-dir. Once set by any
|
||||
# call, subsequent calls in the same session inherit it.
|
||||
#
|
||||
# Issuing CAs live in subdirectories of SIMPLE_CA_DIR:
|
||||
# Directory layout:
|
||||
# $SIMPLE_CA_DIR/ca_cert.pem — root CA certificate
|
||||
# $SIMPLE_CA_DIR/ca_key.pem — root CA private key
|
||||
# $SIMPLE_CA_DIR/{name}_cert.pem — certificates issued by the root CA
|
||||
# $SIMPLE_CA_DIR/{issuing_ca}/ca_cert.pem — issuing CA certificate
|
||||
# $SIMPLE_CA_DIR/{issuing_ca}/ca_key.pem — issuing CA private key
|
||||
# $SIMPLE_CA_DIR/{issuing_ca}/{name}_cert.pem — certificates issued by that issuing CA
|
||||
#
|
||||
# Certificates are always written to the directory of the CA that signs them.
|
||||
# Any subdirectory containing ca_cert.pem is treated as an issuing CA.
|
||||
|
||||
SIMPLE_CA_DIR="${SIMPLE_CA_DIR:-}"
|
||||
@@ -65,17 +68,32 @@ function make_ca() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--days)
|
||||
[[ -z "$2" || ! "$2" =~ ^[0-9]+$ ]] && { echo "ERROR: --days requires a positive integer." >&2; return 1; }
|
||||
if [[ -z "$2" || ! "$2" =~ ^[0-9]+$ ]]; then
|
||||
echo "ERROR: --days requires a positive integer." >&2
|
||||
return 1
|
||||
fi
|
||||
CA_DAYS="$2"; shift 2 ;;
|
||||
--issuing-ca)
|
||||
[[ -z "$2" ]] && { echo "ERROR: --issuing-ca requires a value." >&2; return 1; }
|
||||
[[ "$2" == "ca" ]] && { echo "ERROR: --issuing-ca cannot be 'ca'." >&2; return 1; }
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "ERROR: --issuing-ca requires a value." >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ "$2" == "ca" ]]; then
|
||||
echo "ERROR: --issuing-ca cannot be 'ca'." >&2
|
||||
return 1
|
||||
fi
|
||||
ISSUING_CA="$2"; shift 2 ;;
|
||||
--aia-base-url)
|
||||
[[ -z "$2" ]] && { echo "ERROR: --aia-base-url requires a value." >&2; return 1; }
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "ERROR: --aia-base-url requires a value." >&2
|
||||
return 1
|
||||
fi
|
||||
AIA_BASE_URL="$2"; shift 2 ;;
|
||||
--ca-dir)
|
||||
[[ -z "$2" ]] && { echo "ERROR: --ca-dir requires a value." >&2; return 1; }
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "ERROR: --ca-dir requires a value." >&2
|
||||
return 1
|
||||
fi
|
||||
SIMPLE_CA_DIR="$2"; shift 2 ;;
|
||||
*) break ;;
|
||||
esac
|
||||
@@ -84,10 +102,14 @@ function make_ca() {
|
||||
local CA_NAME="$1"
|
||||
_require_ca_dir || return 1
|
||||
|
||||
[[ -z "$CA_NAME" ]] && { echo "ERROR: CA name is required." >&2; return 1; }
|
||||
if [[ -z "$CA_NAME" ]]; then
|
||||
echo "ERROR: CA name is required." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
[[ -z "$AIA_BASE_URL" && -f "$SIMPLE_CA_DIR/aia_base_url.txt" ]] \
|
||||
&& AIA_BASE_URL="$(cat "$SIMPLE_CA_DIR/aia_base_url.txt")"
|
||||
if [[ -z "$AIA_BASE_URL" && -f "$SIMPLE_CA_DIR/aia_base_url.txt" ]]; then
|
||||
AIA_BASE_URL="$(cat "$SIMPLE_CA_DIR/aia_base_url.txt")"
|
||||
fi
|
||||
|
||||
local ROOT_CA_CERT="$SIMPLE_CA_DIR/ca_cert.pem"
|
||||
local ROOT_CA_KEY="$SIMPLE_CA_DIR/ca_key.pem"
|
||||
@@ -114,7 +136,9 @@ function make_ca() {
|
||||
return 1
|
||||
fi
|
||||
_rebuild_ca_bundle
|
||||
[[ -n "$AIA_BASE_URL" ]] && echo "$AIA_BASE_URL" > "$SIMPLE_CA_DIR/aia_base_url.txt"
|
||||
if [[ -n "$AIA_BASE_URL" ]]; then
|
||||
echo "$AIA_BASE_URL" > "$SIMPLE_CA_DIR/aia_base_url.txt"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -167,38 +191,62 @@ function make_cert() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--ca-dir)
|
||||
[[ -z "$2" ]] && { echo "ERROR: --ca-dir requires a value." >&2; return 1; }
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "ERROR: --ca-dir requires a value." >&2
|
||||
return 1
|
||||
fi
|
||||
SIMPLE_CA_DIR="$2"; shift 2 ;;
|
||||
--cert-dir)
|
||||
[[ -z "$2" ]] && { echo "ERROR: --cert-dir requires a value." >&2; return 1; }
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "ERROR: --cert-dir requires a value." >&2
|
||||
return 1
|
||||
fi
|
||||
CERT_DIR="$2"; shift 2 ;;
|
||||
--issuing-ca)
|
||||
[[ -z "$2" ]] && { echo "ERROR: --issuing-ca requires a value." >&2; return 1; }
|
||||
[[ "$2" == "ca" ]] && { echo "ERROR: --issuing-ca cannot be 'ca'." >&2; return 1; }
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "ERROR: --issuing-ca requires a value." >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ "$2" == "ca" ]]; then
|
||||
echo "ERROR: --issuing-ca cannot be 'ca'." >&2
|
||||
return 1
|
||||
fi
|
||||
ISSUING_CA="$2"; shift 2 ;;
|
||||
--days)
|
||||
[[ -z "$2" || ! "$2" =~ ^[0-9]+$ ]] && { echo "ERROR: --days requires a positive integer." >&2; return 1; }
|
||||
if [[ -z "$2" || ! "$2" =~ ^[0-9]+$ ]]; then
|
||||
echo "ERROR: --days requires a positive integer." >&2
|
||||
return 1
|
||||
fi
|
||||
CERT_DAYS="$2"; shift 2 ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
local CERT_SUBJECT_NAME="$1"
|
||||
[[ $# -gt 0 ]] && shift
|
||||
if [[ $# -gt 0 ]]; then
|
||||
shift
|
||||
fi
|
||||
|
||||
_require_ca_dir || return 1
|
||||
|
||||
[[ -z "$CERT_DIR" ]] && { echo "ERROR: --cert-dir is required." >&2; return 1; }
|
||||
[[ ! -d "$CERT_DIR" ]] && { echo "ERROR: Certificate directory '$CERT_DIR' does not exist." >&2; return 1; }
|
||||
[[ -z "$CERT_SUBJECT_NAME" ]] && { echo "ERROR: Subject name is required." >&2; return 1; }
|
||||
_is_dns "$CERT_SUBJECT_NAME" || { echo "ERROR: Invalid subject name '$CERT_SUBJECT_NAME'. Must be a valid DNS name." >&2; return 1; }
|
||||
if [[ -z "$CERT_SUBJECT_NAME" ]]; then
|
||||
echo "ERROR: Subject name is required." >&2
|
||||
return 1
|
||||
fi
|
||||
if ! _is_dns "$CERT_SUBJECT_NAME"; then
|
||||
echo "ERROR: Invalid subject name '$CERT_SUBJECT_NAME'. Must be a valid DNS name." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local SIGNING_DIR="$SIMPLE_CA_DIR${ISSUING_CA:+/$ISSUING_CA}"
|
||||
local SIGNING_CERT="$SIGNING_DIR/ca_cert.pem"
|
||||
local SIGNING_KEY="$SIGNING_DIR/ca_key.pem"
|
||||
CERT_DIR="${CERT_DIR:-$SIGNING_DIR}"
|
||||
|
||||
[[ ! -f "$SIGNING_CERT" || ! -f "$SIGNING_KEY" ]] \
|
||||
&& { echo "ERROR: Signing CA certificate and key not found in $SIGNING_DIR." >&2; return 1; }
|
||||
if [[ ! -f "$SIGNING_CERT" || ! -f "$SIGNING_KEY" ]]; then
|
||||
echo "ERROR: Signing CA certificate and key not found in $SIGNING_DIR." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local AIA_URL=""
|
||||
if [[ -f "$SIMPLE_CA_DIR/aia_base_url.txt" ]]; then
|
||||
@@ -211,9 +259,13 @@ function make_cert() {
|
||||
local SANS=("DNS:${CERT_SUBJECT_NAME}")
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
if _is_ip "$1"; then SANS+=("IP:$1")
|
||||
elif _is_dns "$1"; then SANS+=("DNS:$1")
|
||||
else { echo "ERROR: Invalid SAN entry '$1'." >&2; return 1; }
|
||||
if _is_ip "$1"; then
|
||||
SANS+=("IP:$1")
|
||||
elif _is_dns "$1"; then
|
||||
SANS+=("DNS:$1")
|
||||
else
|
||||
echo "ERROR: Invalid SAN entry '$1'." >&2
|
||||
return 1
|
||||
fi
|
||||
shift
|
||||
done
|
||||
@@ -255,19 +307,34 @@ function make_cert() {
|
||||
function make_pfx() {
|
||||
local ISSUING_CA=""
|
||||
local PFX_PASSWORD=""
|
||||
local APPLE_OPENSSL=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--ca-dir)
|
||||
[[ -z "$2" ]] && { echo "ERROR: --ca-dir requires a value." >&2; return 1; }
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "ERROR: --ca-dir requires a value." >&2
|
||||
return 1
|
||||
fi
|
||||
SIMPLE_CA_DIR="$2"; shift 2 ;;
|
||||
--issuing-ca)
|
||||
[[ -z "$2" ]] && { echo "ERROR: --issuing-ca requires a value." >&2; return 1; }
|
||||
[[ "$2" == "ca" ]] && { echo "ERROR: --issuing-ca cannot be 'ca'." >&2; return 1; }
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "ERROR: --issuing-ca requires a value." >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ "$2" == "ca" ]]; then
|
||||
echo "ERROR: --issuing-ca cannot be 'ca'." >&2
|
||||
return 1
|
||||
fi
|
||||
ISSUING_CA="$2"; shift 2 ;;
|
||||
--password)
|
||||
[[ -z "$2" ]] && { echo "ERROR: --password requires a value." >&2; return 1; }
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "ERROR: --password requires a value." >&2
|
||||
return 1
|
||||
fi
|
||||
PFX_PASSWORD="$2"; shift 2 ;;
|
||||
--apple-openssl)
|
||||
APPLE_OPENSSL=1; shift ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
@@ -275,24 +342,44 @@ function make_pfx() {
|
||||
local CERT_PATH="$1"
|
||||
_require_ca_dir || return 1
|
||||
|
||||
[[ -z "$CERT_PATH" ]] && { echo "ERROR: Certificate path is required." >&2; return 1; }
|
||||
if [[ -z "$CERT_PATH" ]]; then
|
||||
echo "ERROR: Certificate path is required." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local CERT_DIR CERT_NAME KEY_PATH
|
||||
CERT_DIR="$(dirname "$CERT_PATH")"
|
||||
CERT_NAME="$(basename "$CERT_PATH" _cert.pem)"
|
||||
KEY_PATH="$CERT_DIR/${CERT_NAME}_key.pem"
|
||||
|
||||
[[ ! -d "$CERT_DIR" ]] && { echo "ERROR: Certificate directory '$CERT_DIR' does not exist." >&2; return 1; }
|
||||
[[ ! -f "$CERT_PATH" || ! -f "$KEY_PATH" ]] && { echo "ERROR: Server certificate or key not found." >&2; return 1; }
|
||||
[[ ! -f "$SIMPLE_CA_DIR/ca_cert.pem" ]] && { echo "ERROR: Root CA certificate not found in $SIMPLE_CA_DIR." >&2; return 1; }
|
||||
|
||||
[[ -n "$ISSUING_CA" && ! -f "$SIMPLE_CA_DIR/$ISSUING_CA/ca_cert.pem" ]] \
|
||||
&& { echo "ERROR: Issuing CA certificate not found in $SIMPLE_CA_DIR/$ISSUING_CA." >&2; return 1; }
|
||||
|
||||
[[ -f "$CERT_DIR/${CERT_NAME}.pfx" ]] && { echo "PKCS#12 (PFX) file already exists, aborting generation." >&2; return 1; }
|
||||
if [[ ! -d "$CERT_DIR" ]]; then
|
||||
echo "ERROR: Certificate directory '$CERT_DIR' does not exist." >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ ! -f "$CERT_PATH" || ! -f "$KEY_PATH" ]]; then
|
||||
echo "ERROR: Server certificate or key not found." >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ ! -f "$SIMPLE_CA_DIR/ca_cert.pem" ]]; then
|
||||
echo "ERROR: Root CA certificate not found in $SIMPLE_CA_DIR." >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ -n "$ISSUING_CA" && ! -f "$SIMPLE_CA_DIR/$ISSUING_CA/ca_cert.pem" ]]; then
|
||||
echo "ERROR: Issuing CA certificate not found in $SIMPLE_CA_DIR/$ISSUING_CA." >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ -f "$CERT_DIR/${CERT_NAME}.pfx" ]]; then
|
||||
echo "PKCS#12 (PFX) file already exists, aborting generation." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
PFX_PASSWORD="${PFX_PASSWORD:-changeit}"
|
||||
|
||||
local OPENSSL_BIN="openssl"
|
||||
if [[ "$APPLE_OPENSSL" -eq 1 ]]; then
|
||||
OPENSSL_BIN="/usr/bin/openssl"
|
||||
fi
|
||||
|
||||
echo -n "Generating PKCS#12 (PFX) file..."
|
||||
|
||||
local CHAIN_FILE
|
||||
@@ -300,9 +387,11 @@ function make_pfx() {
|
||||
trap "rm -f '$CHAIN_FILE'" EXIT QUIT KILL INT HUP
|
||||
|
||||
cat "$SIMPLE_CA_DIR/ca_cert.pem" > "$CHAIN_FILE"
|
||||
[[ -n "$ISSUING_CA" ]] && cat "$SIMPLE_CA_DIR/$ISSUING_CA/ca_cert.pem" >> "$CHAIN_FILE"
|
||||
if [[ -n "$ISSUING_CA" ]]; then
|
||||
cat "$SIMPLE_CA_DIR/$ISSUING_CA/ca_cert.pem" >> "$CHAIN_FILE"
|
||||
fi
|
||||
|
||||
if ! openssl pkcs12 \
|
||||
if ! "$OPENSSL_BIN" pkcs12 \
|
||||
-export \
|
||||
-out "$CERT_DIR/${CERT_NAME}.pfx" \
|
||||
-inkey "$KEY_PATH" \
|
||||
|
||||
Reference in New Issue
Block a user