Posted on July 5 2023 under linux, bash, and security
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#!/bin/bash
show_help() {
echo "$(basename "$0") [OPTIONS] COMMONNAME"
echo
echo "Options:"
echo " -h|-?|--help Show this help text"
echo " -p|--path File path to store results, default: ."
echo " -m|--modulus Key modulus (size), default: 2048"
echo " --hash Hash algorithm, default: sha256"
echo " -d|--days Days valid, default: 3650"
echo " -t|--template Certificate Template Name, default: WebServer"
echo
echo "Distinguished Name Components:"
echo " -c|--country Country Code"
echo " -s|--state State or Province"
echo " -l|--locality City"
echo " -o|--organization Organization"
echo " --organizational-unit Organizational Unit"
echo
echo "Subject Alternative Names:"
echo " -a|--alternate-names Additional SANs as a comma-separated list. The"
echo " common name is always prepended as the first"
echo " SAN. This can contain additional SANs, e.g.:"
echo " \"DNS:www.example.com,IP:192.0.2.1,IP:2001:db8::1\""
exit 1
}
# Set defaults
FILEPATH="."
MODULUS=2048
HASH="sha256"
DAYS=3650
TEMPLATE="WebServer"
# Parse arguments
while :; do
if [[ ! -n "$1" ]]; then break; fi
case $1 in
-h|-\?|--help) show_help; exit ;;
-p|--path) FILEPATH=$2; shift ;;
-m|--modulus) MODULUS=$2; shift ;;
--hash) HASH=$2; shift ;;
-d|--days) DAYS=$2; shift ;;
-t|--template) TEMPLATE=$2; shift ;;
-c|--country) C=$2; shift ;;
-s|--state) ST=$2; shift ;;
-l|--locality) L=$2; shift ;;
-o|--organization) O=$2; shift ;;
--organizational-unit) OU=$2; shift ;;
-a|--alternate-names) ALTS=$2; shift ;;
--) shift; break ;;
-?*) ;;
*) if [[ ! -n "$CN" ]]; then CN="$1"; else break; fi
esac
shift
done
if [[ ! -n "$CN" ]]; then
printf 'Error: A common name is required.\n' >&2
exit 1
fi
# Build file paths
KEYFILE="$FILEPATH/$CN.key"
CSRFILE="$FILEPATH/$CN.csr"
CRTFILE="$FILEPATH/$CN.crt"
# Build distinguished name
SUBJECT="/CN=$CN"
if [[ -n "$OU" ]]; then SUBJECT="/OU=$OU$SUBJECT"; fi
if [[ -n "$O" ]]; then SUBJECT="/O=$O$SUBJECT"; fi
if [[ -n "$L" ]]; then SUBJECT="/L=$L$SUBJECT"; fi
if [[ -n "$ST" ]]; then SUBJECT="/ST=$ST$SUBJECT"; fi
if [[ -n "$C" ]]; then SUBJECT="/C=$C$SUBJECT"; fi
# Build list of SANs
SANS="DNS:$CN"
if [[ -n "$ALTS" ]]; then SANS="$SANS,$ALTS"; fi
# Generate a new key
openssl genrsa -out $KEYFILE $MODULUS
chmod 600 $KEYFILE
echo "Generated a new private key in $KEYFILE"
# Generate a new CSR
openssl req -new -utf8 -out $CSRFILE -key $KEYFILE \
-$HASH -subj "$SUBJECT" \
-addext "subjectKeyIdentifier = hash" \
-addext "basicConstraints = critical, CA:FALSE" \
-addext "keyUsage = digitalSignature, nonRepudiation, keyEncipherment" \
-addext "extendedKeyUsage = clientAuth, serverAuth" \
-addext "1.3.6.1.4.1.311.20.2 = ASN1:UTF8String:$TEMPLATE" \
-addext "subjectAltName = $SANS"
echo "Generated a new certificate signing request in $CSRFILE"
# Self-sign the CSR
openssl req -x509 -utf8 -in $CSRFILE -out $CRTFILE -key $KEYFILE \
-$HASH -days $DAYS \
-addext "keyUsage = digitalSignature, nonRepudiation, keyEncipherment" \
-addext "extendedKeyUsage = clientAuth, serverAuth" \
-addext "1.3.6.1.4.1.311.20.2 = ASN1:UTF8String:$TEMPLATE" \
-addext "subjectAltName = $SANS"
echo "Generated a new self-signed certificate in $CRTFILE"