xref: /openssl/test/certs/mkcert.sh (revision da1c088f)
1#! /bin/bash
2#
3# Copyright 2016-2023 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 Apache License 2.0 (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 bcon="basicConstraints = critical,CA:true"
104    local ku="keyUsage = keyCertSign,cRLSign"
105    local skid="subjectKeyIdentifier = hash"
106    local akid="authorityKeyIdentifier = keyid"
107
108    exts=$(printf "%s\n%s\n%s\n" "$bcon" "$ku" "$skid" "$akid")
109    for eku in "$@"
110    do
111        exts=$(printf "%s\nextendedKeyUsage = %s\n" "$exts" "$eku")
112    done
113    csr=$(req "$key" "CN = $cn") || return 1
114    echo "$csr" |
115       cert "$cert" "$exts" -signkey "${key}.pem" -set_serial 1 -days "${DAYS}"
116}
117
118genca() {
119    local OPTIND=1
120    local purpose=
121
122    while getopts p:c: o
123    do
124        case $o in
125        p) purpose="$OPTARG";;
126        c) certpol="$OPTARG";;
127        *) echo "Usage: $0 genca [-p EKU][-c policyoid] cn keyname certname cakeyname cacertname" >&2
128           return 1;;
129        esac
130    done
131
132    shift $((OPTIND - 1))
133    local cn=$1; shift
134    local key=$1; shift
135    local cert=$1; shift
136    local cakey=$1; shift
137    local cacert=$1; shift
138    local bcon="basicConstraints = critical,CA:true"
139    local ku="keyUsage = keyCertSign,cRLSign"
140    local skid="subjectKeyIdentifier = hash"
141    local akid="authorityKeyIdentifier = keyid"
142
143    exts=$(printf "%s\n%s\n%s\n" "$bcon" "$ku" "$skid" "$akid")
144    if [ -n "$purpose" ]; then
145        exts=$(printf "%s\nextendedKeyUsage = %s\n" "$exts" "$purpose")
146    fi
147    if [ -n "$NC" ]; then
148        exts=$(printf "%s\nnameConstraints = %s\n" "$exts" "$NC")
149    fi
150    if [ -n "$certpol" ]; then
151        exts=$(printf "%s\ncertificatePolicies = %s\n" "$exts" "$certpol")
152    fi
153
154    csr=$(req "$key" "CN = $cn") || return 1
155    echo "$csr" |
156        cert "$cert" "$exts" -CA "${cacert}.pem" -CAkey "${cakey}.pem" \
157	    -set_serial 2 -days "${DAYS}" "$@"
158}
159
160gen_nonbc_ca() {
161    local cn=$1; shift
162    local key=$1; shift
163    local cert=$1; shift
164    local cakey=$1; shift
165    local cacert=$1; shift
166    local skid="subjectKeyIdentifier = hash"
167    local akid="authorityKeyIdentifier = keyid"
168
169    exts=$(printf "%s\n%s\n%s\n" "$skid" "$akid")
170    exts=$(printf "%s\nkeyUsage = %s\n" "$exts" "keyCertSign, cRLSign")
171    for eku in "$@"
172    do
173        exts=$(printf "%s\nextendedKeyUsage = %s\n" "$exts" "$eku")
174    done
175    csr=$(req "$key" "CN = $cn") || return 1
176    echo "$csr" |
177        cert "$cert" "$exts" -CA "${cacert}.pem" -CAkey "${cakey}.pem" \
178	    -set_serial 2 -days "${DAYS}"
179}
180
181# Usage: $0 genpc keyname certname eekeyname eecertname pcext1 pcext2 ...
182#
183# Note: takes csr on stdin, so must be used with $0 req like this:
184#
185# $0 req keyname dn | $0 genpc keyname certname eekeyname eecertname pcext ...
186genpc() {
187    local key=$1; shift
188    local cert=$1; shift
189    local cakey=$1; shift
190    local ca=$1; shift
191
192    exts=$(printf "%s\n%s\n%s\n%s\n" \
193	    "subjectKeyIdentifier = hash" \
194	    "authorityKeyIdentifier = keyid, issuer:always" \
195	    "basicConstraints = CA:false" \
196	    "proxyCertInfo = critical, @pcexts";
197           echo "[pcexts]";
198           for x in "$@"; do echo $x; done)
199    cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
200	 -set_serial 2 -days "${DAYS}"
201}
202
203geneeconfig() {
204    local key=$1; shift
205    local cert=$1; shift
206    local cakey=$1; shift
207    local ca=$1; shift
208    local conf=$1; shift
209
210    exts=$(printf "%s\n%s\n%s\n%s\n" \
211        "subjectKeyIdentifier = hash" \
212        "authorityKeyIdentifier = keyid" \
213        "basicConstraints = CA:false"; \
214        echo "$conf")
215
216    cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
217        -set_serial 2 -days "${DAYS}"
218}
219
220# Usage: $0 geneealt keyname certname cakeyname cacertname alt1 alt2 ...
221#
222# Note: takes csr on stdin, so must be used with $0 req like this:
223#
224# $0 req keyname dn | $0 geneealt keyname certname cakeyname cacertname alt ...
225geneealt() {
226    local key=$1; shift
227    local cert=$1; shift
228    local cakey=$1; shift
229    local ca=$1; shift
230
231    conf=$(echo "subjectAltName = @alts"
232           echo "[alts]";
233           for x in "$@"; do echo "$x"; done)
234
235    geneeconfig $key $cert $cakey $ca "$conf"
236}
237
238genee() {
239    local OPTIND=1
240    local purpose=serverAuth
241    local ku=
242
243    while getopts p:k: o
244    do
245        case $o in
246        p) purpose="$OPTARG";;
247        k) ku="keyUsage = $OPTARG";;
248        *) echo "Usage: $0 genee [-k KU] [-p EKU] cn keyname certname cakeyname cacertname" >&2
249           return 1;;
250        esac
251    done
252
253    shift $((OPTIND - 1))
254    local cn=$1; shift
255    local key=$1; shift
256    local cert=$1; shift
257    local cakey=$1; shift
258    local ca=$1; shift
259
260    exts=$(printf "%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \
261	    "subjectKeyIdentifier = hash" \
262	    "authorityKeyIdentifier = keyid, issuer" \
263	    "basicConstraints = CA:false" \
264            "$ku" \
265	    "extendedKeyUsage = $purpose" \
266	    "subjectAltName = @alts" "DNS=${cn}")
267    csr=$(req "$key" "CN = $cn") || return 1
268    echo "$csr" |
269	cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
270	    -set_serial 2 -days "${DAYS}" "$@"
271}
272
273geneeextra() {
274    local OPTIND=1
275    local purpose=serverAuth
276
277    while getopts p: o
278    do
279        case $o in
280        p) purpose="$OPTARG";;
281        *) echo "Usage: $0 geneeextra [-p EKU] cn keyname certname cakeyname cacertname extraext" >&2
282           return 1;;
283        esac
284    done
285
286    shift $((OPTIND - 1))
287    local cn=$1; shift
288    local key=$1; shift
289    local cert=$1; shift
290    local cakey=$1; shift
291    local ca=$1; shift
292    local extraext=$1; shift
293
294    exts=$(printf "%s\n%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \
295	    "subjectKeyIdentifier = hash" \
296	    "authorityKeyIdentifier = keyid, issuer" \
297	    "basicConstraints = CA:false" \
298	    "extendedKeyUsage = $purpose" \
299	    "subjectAltName = @alts"\
300	    "$extraext" "DNS=${cn}")
301    csr=$(req "$key" "CN = $cn") || return 1
302    echo "$csr" |
303	cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
304	    -set_serial 2 -days "${DAYS}" "$@"
305}
306
307geneenocsr() {
308    local OPTIND=1
309    local purpose=serverAuth
310
311    while getopts p: o
312    do
313        case $o in
314        p) purpose="$OPTARG";;
315        *) echo "Usage: $0 geneenocsr [-p EKU] cn certname cakeyname cacertname" >&2
316           return 1;;
317        esac
318    done
319
320    shift $((OPTIND - 1))
321    local cn=$1; shift
322    local cert=$1; shift
323    local cakey=$1; shift
324    local ca=$1; shift
325
326    exts=$(printf "%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \
327	    "subjectKeyIdentifier = hash" \
328	    "authorityKeyIdentifier = keyid, issuer" \
329	    "basicConstraints = CA:false" \
330	    "extendedKeyUsage = $purpose" \
331	    "subjectAltName = @alts" "DNS=${cn}")
332	cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
333	    -set_serial 2 -days "${DAYS}" "$@"
334}
335
336genss() {
337    local cn=$1; shift
338    local key=$1; shift
339    local cert=$1; shift
340
341    exts=$(printf "%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \
342	    "subjectKeyIdentifier   = hash" \
343	    "authorityKeyIdentifier = keyid, issuer" \
344	    "basicConstraints = CA:false" \
345	    "extendedKeyUsage = serverAuth" \
346	    "subjectAltName = @alts" "DNS=${cn}")
347    csr=$(req "$key" "CN = $cn") || return 1
348    echo "$csr" |
349        cert "$cert" "$exts" -signkey "${key}.pem" \
350            -set_serial 1 -days "${DAYS}" "$@"
351}
352
353gennocn() {
354    local key=$1; shift
355    local cert=$1; shift
356
357    csr=$(req_nocn "$key") || return 1
358    echo "$csr" |
359	cert "$cert" "" -signkey "${key}.pem" -set_serial 1 -days -1 "$@"
360}
361
362genct() {
363    local OPTIND=1
364    local purpose=serverAuth
365
366    while getopts p: o
367    do
368        case $o in
369        p) purpose="$OPTARG";;
370        *) echo "Usage: $0 genct [-p EKU] cn keyname certname cakeyname cacertname ctlogkey" >&2
371           return 1;;
372        esac
373    done
374
375    shift $((OPTIND - 1))
376    local cn=$1; shift
377    local key=$1; shift
378    local cert=$1; shift
379    local cakey=$1; shift
380    local ca=$1; shift
381    local logkey=$1; shift
382
383    exts=$(printf "%s\n%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \
384	    "subjectKeyIdentifier = hash" \
385	    "authorityKeyIdentifier = keyid, issuer" \
386	    "basicConstraints = CA:false" \
387	    "extendedKeyUsage = $purpose" \
388            "1.3.6.1.4.1.11129.2.4.3 = critical,ASN1:NULL"\
389	    "subjectAltName = @alts" "DNS=${cn}")
390    csr=$(req "$key" "CN = $cn") || return 1
391    echo "$csr" |
392	cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
393	    -set_serial 2 -days "${DAYS}" "$@"
394    cat ${cert}.pem ${ca}.pem > ${cert}-chain.pem
395    go run github.com/google/certificate-transparency-go/ctutil/sctgen \
396       --log_private_key ${logkey}.pem \
397       --timestamp="2020-01-01T00:00:00Z" \
398       --cert_chain ${cert}-chain.pem \
399       --tls_out ${cert}.tlssct
400    rm ${cert}-chain.pem
401    filesize=$(wc -c <${cert}.tlssct)
402    exts=$(printf "%s\n%s\n%s\n%s\n%s%04X%04X%s\n%s\n[alts]\n%s\n" \
403	    "subjectKeyIdentifier = hash" \
404	    "authorityKeyIdentifier = keyid, issuer" \
405	    "basicConstraints = CA:false" \
406	    "extendedKeyUsage = $purpose" \
407	    "1.3.6.1.4.1.11129.2.4.2 = ASN1:FORMAT:HEX,OCT:" $((filesize+2)) $filesize `xxd -p ${cert}.tlssct | tr -d '\n'` \
408	    "subjectAltName = @alts" "DNS=${cn}")
409    echo "$csr" |
410	cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
411	    -set_serial 2 -days "${DAYS}" "$@"
412}
413
414"$@"
415