-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathgenx509.sh
executable file
·158 lines (130 loc) · 4.71 KB
/
genx509.sh
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#!/usr/bin/env bash
#
# Interactive script to generate a X.509 private key and corresponding
# Certificate Signing Request (CSR), or create a self-signed certificate.
# For increased security, the private key is not written to disk.
#
# Can also use an existing private key, by piping the PEM formatted value to
# the script.
#
#
#
#
# Array variables - the first item in each variable is treated as the default.
SIGN_TYPES=(CA-signed Self-signed)
KEY_TYPES=(ECC RSA)
EC_CURVES=(prime256v1 secp384r1)
RSA_SIZES=(2048 3072 4096)
# Help message if not enough parameters are provided
if [ $# -lt 1 ]; then
me=$(basename "${BASH_SOURCE[0]}")
echo "Usage: $me cn <san_1 san_2 san_3 san_4 ... san_n>
The first argument is cn (Common Name).
Optional further arguments are treated as Subject Altenative Names.
It is also possible to reuse an existing private key by providing it
to STDIN.
Examples:
# Generate a new CSR and key for a hostname and a few SANs
$me www.uni.edu uni.edu old_uni.edu
# The same, but reuse an existing private key
cat privkey.pem | $me www.uni.edu uni.old other.tld
# Similar, but fetch the private key from an ansible vault
ansible-vault view privkey.vault | $me www.uni.edu uni.old other.tld
# Similar, but fetch the private key from a variable in an ansible vaulted YAML file
ansible-vault view vault.yml | yq -r .privkey | $me www.uni.edu uni.old other.tld
# Generate a self signed key pair for a SAML Service Provider
$me 'My Fancy New Service'
"
exit 1
fi
# Supporting function, to mimic bash' "select"
function selectWithDefault() {
local item i=0 numItems=$#
# Print numbered menu items, based on the arguments passed.
for item; do
# Add an asterisk to the first (default) option
printf '%s\n' "$((++i))) $item$([[ $i == 1 ]] && echo ' *')"
done >&2 # Print to stderr, as `select` does.
# Prompt the user for the index of the desired item.
while :; do
printf %s "${PS3-#? }" >&2 # Print the prompt string to stderr, as `select` does.
read -r index < /dev/tty
# Make sure that the input is either empty or that a valid index was entered.
[[ -z $index ]] && break # empty input
(( index >= 1 && index <= numItems )) 2>/dev/null || { echo "Invalid selection. Please try again." >&2; continue; }
break
done
# Output the selected item, if any.
if [[ -n $index ]]; then
printf %s "${@: index:1}"
else
# Default: first item
printf %s "${1}"
fi
}
# Check if we received a valid private key in STDIN
if ! test -t 0 ; then
INPUT="$(< /dev/stdin)"
if echo "$INPUT" | openssl pkey -noout; then
PRIVATE_KEY=$(echo "$INPUT" | openssl pkey)
else
exit 1
fi
fi
# Select how the certificate should be signed.
# Defaults to CA signed, which results in a CSR.
# It self-signed it selected, then the key type will default to RSA, as this is
# common, for instance for SAML certificates.
echo "Select signing type"
SIGN_TYPE=$(selectWithDefault "${SIGN_TYPES[@]}")
if [ "$SIGN_TYPE" = "Self-signed" ]; then
KEY_TYPES=(RSA ECC)
fi
# Key generation flags
if [ -z "$PRIVATE_KEY" ]; then
# This is only needed when we do NOT have a valid private key from STDIN
echo "Select key type"
KEY_TYPE=$(selectWithDefault "${KEY_TYPES[@]}")
if [ "$KEY_TYPE" = "ECC" ]; then
# Select ECC curve
echo "Select curve"
EC_CURVE=$(selectWithDefault "${EC_CURVES[@]}")
EC_PARAMS=$(mktemp)
openssl ecparam -name "${EC_CURVE}" -out "${EC_PARAMS}"
KEY_SPEC="-newkey ec:${EC_PARAMS} -keyout /dev/stdout 2>/dev/null"
elif [ "$KEY_TYPE" = "RSA" ]; then
# Select RSA key size
echo "Select RSA size"
RSA_SIZE=$(selectWithDefault "${RSA_SIZES[@]}")
KEY_SPEC="-newkey rsa:${RSA_SIZE} -keyout /dev/stdout 2>/dev/null"
else
echo "Not implemented yet"
fi
else
# We use the private key from STDIN
KEY_SPEC="-new -key <(echo -n \"${PRIVATE_KEY}\")"
fi
if [ "${SIGN_TYPE}" = "CA-signed" ]; then
if [ $# -eq 1 ]; then
# One argument => simple scenario
eval "openssl req -nodes -subj '/CN=$1/' $KEY_SPEC"
else
# More than one argument => first arg is CN, the rest is the list of SANs
s="subjectAltName="
for san in "$@"; do s="${s}DNS:${san},"; done
echo eval "openssl req -nodes -subj '/CN=${1}/' ${KEY_SPEC} \
-reqexts SAN -extensions SAN \
-config <(printf \"[req]\ndistinguished_name=rdn\n[rdn]\n[SAN]\n${s::-1}\")"
fi
elif [ "$SIGN_TYPE" = "Self-signed" ]; then
# https://en.wikipedia.org/wiki/Year_2038_problem
days=$((( $((2**31)) - $(date +%s))/86400-1))
# For self-signed there is ONLY "CN=blah", and NO SubjAltNames.
eval "openssl req -nodes -days ${days} -x509 -subj '/CN=$*/' ${KEY_SPEC}"
else
echo "Not implemented yet"
fi
# Clean up temporary EC params file
if [ -n "${EC_PARAMS}" ]; then
rm "${EC_PARAMS}"
fi