1#! /bin/bash
2#
3# Copyright 2016-2021 The OpenSSL Project Authors. All Rights Reserved.
4# Copyright (c) 2016 Viktor Dukhovni <openssl-users@dukhovni.org>.
5# All rights reserved.
6#
7# Licensed under the OpenSSL license (the "License").  You may not use
8# this file except in compliance with the License.  You can obtain a copy
9# in the file LICENSE in the source distribution or at
10# https://www.openssl.org/source/license.html
11
12# This file is dual-licensed and is also available under other terms.
13# Please contact the author.
14
15# 100 years should be enough for now
16if [ -z "$DAYS" ]; then
17    DAYS=36525
18fi
19
20if [ -z "$OPENSSL_SIGALG" ]; then
21    OPENSSL_SIGALG=sha256
22fi
23
24if [ -z "$REQMASK" ]; then
25    REQMASK=utf8only
26fi
27
28stderr_onerror() {
29    (
30        err=$("$@" >&3 2>&1) || {
31            printf "%s\n" "$err" >&2
32            exit 1
33        }
34    ) 3>&1
35}
36
37key() {
38    local key=$1; shift
39
40    local alg=rsa
41    if [ -n "$OPENSSL_KEYALG" ]; then
42        alg=$OPENSSL_KEYALG
43    fi
44
45    local bits=2048
46    if [ -n "$OPENSSL_KEYBITS" ]; then
47        bits=$OPENSSL_KEYBITS
48    fi
49
50    if [ ! -f "${key}.pem" ]; then
51        args=(-algorithm "$alg")
52        case $alg in
53        rsa) args=("${args[@]}" -pkeyopt rsa_keygen_bits:$bits );;
54        ec)  args=("${args[@]}" -pkeyopt "ec_paramgen_curve:$bits")
55               args=("${args[@]}" -pkeyopt ec_param_enc:named_curve);;
56        dsa)  args=(-paramfile "$bits");;
57        ed25519)  ;;
58        ed448)  ;;
59        *) printf "Unsupported key algorithm: %s\n" "$alg" >&2; return 1;;
60        esac
61        stderr_onerror \
62            openssl genpkey "${args[@]}" -out "${key}.pem"
63    fi
64}
65
66# Usage: $0 req keyname dn1 dn2 ...
67req() {
68    local key=$1; shift
69
70    key "$key"
71    local errs
72
73    stderr_onerror \
74        openssl req -new -"${OPENSSL_SIGALG}" -key "${key}.pem" \
75            -config <(printf "string_mask=%s\n[req]\n%s\n%s\n[dn]\n" \
76              "$REQMASK" "prompt = no" "distinguished_name = dn"
77                      for dn in "$@"; do echo "$dn"; done)
78}
79
80req_nocn() {
81    local key=$1; shift
82
83    key "$key"
84    stderr_onerror \
85        openssl req -new -"${OPENSSL_SIGALG}" -subj / -key "${key}.pem" \
86            -config <(printf "[req]\n%s\n[dn]\nCN_default =\n" \
87		      "distinguished_name = dn")
88}
89
90cert() {
91    local cert=$1; shift
92    local exts=$1; shift
93
94    stderr_onerror \
95        openssl x509 -req -"${OPENSSL_SIGALG}" -out "${cert}.pem" \
96            -extfile <(printf "%s\n" "$exts") "$@"
97}
98
99genroot() {
100    local cn=$1; shift
101    local key=$1; shift
102    local cert=$1; shift
103    local skid="subjectKeyIdentifier = hash"
104    local akid="authorityKeyIdentifier = keyid"
105
106    exts=$(printf "%s\n%s\n%s\n" "$skid" "$akid" "basicConstraints = critical,CA:true")
107    for eku in "$@"
108    do
109        exts=$(printf "%s\nextendedKeyUsage = %s\n" "$exts" "$eku")
110    done
111    csr=$(req "$key" "CN = $cn") || return 1
112    echo "$csr" |
113       cert "$cert" "$exts" -signkey "${key}.pem" -set_serial 1 -days "${DAYS}"
114}
115
116genca() {
117    local OPTIND=1
118    local purpose=
119
120    while getopts p: o
121    do
122        case $o in
123        p) purpose="$OPTARG";;
124        *) echo "Usage: $0 genca [-p EKU] cn keyname certname cakeyname cacertname" >&2
125           return 1;;
126        esac
127    done
128
129    shift $((OPTIND - 1))
130    local cn=$1; shift
131    local key=$1; shift
132    local cert=$1; shift
133    local cakey=$1; shift
134    local cacert=$1; shift
135    local skid="subjectKeyIdentifier = hash"
136    local akid="authorityKeyIdentifier = keyid"
137
138    exts=$(printf "%s\n%s\n%s\n" "$skid" "$akid" "basicConstraints = critical,CA:true")
139    if [ -n "$purpose" ]; then
140        exts=$(printf "%s\nextendedKeyUsage = %s\n" "$exts" "$purpose")
141    fi
142    if [ -n "$NC" ]; then
143        exts=$(printf "%s\nnameConstraints = %s\n" "$exts" "$NC")
144    fi
145    csr=$(req "$key" "CN = $cn") || return 1
146    echo "$csr" |
147        cert "$cert" "$exts" -CA "${cacert}.pem" -CAkey "${cakey}.pem" \
148	    -set_serial 2 -days "${DAYS}" "$@"
149}
150
151gen_nonbc_ca() {
152    local cn=$1; shift
153    local key=$1; shift
154    local cert=$1; shift
155    local cakey=$1; shift
156    local cacert=$1; shift
157    local skid="subjectKeyIdentifier = hash"
158    local akid="authorityKeyIdentifier = keyid"
159
160    exts=$(printf "%s\n%s\n%s\n" "$skid" "$akid")
161    exts=$(printf "%s\nkeyUsage = %s\n" "$exts" "keyCertSign, cRLSign")
162    for eku in "$@"
163    do
164        exts=$(printf "%s\nextendedKeyUsage = %s\n" "$exts" "$eku")
165    done
166    csr=$(req "$key" "CN = $cn") || return 1
167    echo "$csr" |
168        cert "$cert" "$exts" -CA "${cacert}.pem" -CAkey "${cakey}.pem" \
169	    -set_serial 2 -days "${DAYS}"
170}
171
172# Usage: $0 genpc keyname certname eekeyname eecertname pcext1 pcext2 ...
173#
174# Note: takes csr on stdin, so must be used with $0 req like this:
175#
176# $0 req keyname dn | $0 genpc keyname certname eekeyname eecertname pcext ...
177genpc() {
178    local key=$1; shift
179    local cert=$1; shift
180    local cakey=$1; shift
181    local ca=$1; shift
182
183    exts=$(printf "%s\n%s\n%s\n%s\n" \
184	    "subjectKeyIdentifier = hash" \
185	    "authorityKeyIdentifier = keyid, issuer:always" \
186	    "basicConstraints = CA:false" \
187	    "proxyCertInfo = critical, @pcexts";
188           echo "[pcexts]";
189           for x in "$@"; do echo $x; done)
190    cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
191	 -set_serial 2 -days "${DAYS}"
192}
193
194# Usage: $0 geneealt keyname certname eekeyname eecertname alt1 alt2 ...
195#
196# Note: takes csr on stdin, so must be used with $0 req like this:
197#
198# $0 req keyname dn | $0 geneealt keyname certname eekeyname eecertname alt ...
199geneealt() {
200    local key=$1; shift
201    local cert=$1; shift
202    local cakey=$1; shift
203    local ca=$1; shift
204
205    exts=$(printf "%s\n%s\n%s\n%s\n" \
206	    "subjectKeyIdentifier = hash" \
207	    "authorityKeyIdentifier = keyid" \
208	    "basicConstraints = CA:false" \
209	    "subjectAltName = @alts";
210           echo "[alts]";
211           for x in "$@"; do echo $x; done)
212    cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
213	 -set_serial 2 -days "${DAYS}"
214}
215
216genee() {
217    local OPTIND=1
218    local purpose=serverAuth
219
220    while getopts p: o
221    do
222        case $o in
223        p) purpose="$OPTARG";;
224        *) echo "Usage: $0 genee [-p EKU] cn keyname certname cakeyname cacertname" >&2
225           return 1;;
226        esac
227    done
228
229    shift $((OPTIND - 1))
230    local cn=$1; shift
231    local key=$1; shift
232    local cert=$1; shift
233    local cakey=$1; shift
234    local ca=$1; shift
235
236    exts=$(printf "%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \
237	    "subjectKeyIdentifier = hash" \
238	    "authorityKeyIdentifier = keyid, issuer" \
239	    "basicConstraints = CA:false" \
240	    "extendedKeyUsage = $purpose" \
241	    "subjectAltName = @alts" "DNS=${cn}")
242    csr=$(req "$key" "CN = $cn") || return 1
243    echo "$csr" |
244	cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
245	    -set_serial 2 -days "${DAYS}" "$@"
246}
247
248geneenocsr() {
249    local OPTIND=1
250    local purpose=serverAuth
251
252    while getopts p: o
253    do
254        case $o in
255        p) purpose="$OPTARG";;
256        *) echo "Usage: $0 genee [-p EKU] cn certname cakeyname cacertname" >&2
257           return 1;;
258        esac
259    done
260
261    shift $((OPTIND - 1))
262    local cn=$1; shift
263    local cert=$1; shift
264    local cakey=$1; shift
265    local ca=$1; shift
266
267    exts=$(printf "%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \
268	    "subjectKeyIdentifier = hash" \
269	    "authorityKeyIdentifier = keyid, issuer" \
270	    "basicConstraints = CA:false" \
271	    "extendedKeyUsage = $purpose" \
272	    "subjectAltName = @alts" "DNS=${cn}")
273	cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
274	    -set_serial 2 -days "${DAYS}" "$@"
275}
276
277genss() {
278    local cn=$1; shift
279    local key=$1; shift
280    local cert=$1; shift
281
282    exts=$(printf "%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \
283	    "subjectKeyIdentifier   = hash" \
284	    "authorityKeyIdentifier = keyid, issuer" \
285	    "basicConstraints = CA:false" \
286	    "extendedKeyUsage = serverAuth" \
287	    "subjectAltName = @alts" "DNS=${cn}")
288    csr=$(req "$key" "CN = $cn") || return 1
289    echo "$csr" |
290        cert "$cert" "$exts" -signkey "${key}.pem" \
291            -set_serial 1 -days "${DAYS}" "$@"
292}
293
294gennocn() {
295    local key=$1; shift
296    local cert=$1; shift
297
298    csr=$(req_nocn "$key") || return 1
299    echo "$csr" |
300	cert "$cert" "" -signkey "${key}.pem" -set_serial 1 -days -1 "$@"
301}
302
303"$@"
304