• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
242    while getopts p: o
243    do
244        case $o in
245        p) purpose="$OPTARG";;
246        *) echo "Usage: $0 genee [-p EKU] cn keyname certname cakeyname cacertname" >&2
247           return 1;;
248        esac
249    done
250
251    shift $((OPTIND - 1))
252    local cn=$1; shift
253    local key=$1; shift
254    local cert=$1; shift
255    local cakey=$1; shift
256    local ca=$1; shift
257
258    exts=$(printf "%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \
259	    "subjectKeyIdentifier = hash" \
260	    "authorityKeyIdentifier = keyid, issuer" \
261	    "basicConstraints = CA:false" \
262	    "extendedKeyUsage = $purpose" \
263	    "subjectAltName = @alts" "DNS=${cn}")
264    csr=$(req "$key" "CN = $cn") || return 1
265    echo "$csr" |
266	cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
267	    -set_serial 2 -days "${DAYS}" "$@"
268}
269
270geneeextra() {
271    local OPTIND=1
272    local purpose=serverAuth
273
274    while getopts p: o
275    do
276        case $o in
277        p) purpose="$OPTARG";;
278        *) echo "Usage: $0 geneeextra [-p EKU] cn keyname certname cakeyname cacertname extraext" >&2
279           return 1;;
280        esac
281    done
282
283    shift $((OPTIND - 1))
284    local cn=$1; shift
285    local key=$1; shift
286    local cert=$1; shift
287    local cakey=$1; shift
288    local ca=$1; shift
289    local extraext=$1; shift
290
291    exts=$(printf "%s\n%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \
292	    "subjectKeyIdentifier = hash" \
293	    "authorityKeyIdentifier = keyid, issuer" \
294	    "basicConstraints = CA:false" \
295	    "extendedKeyUsage = $purpose" \
296	    "subjectAltName = @alts"\
297	    "$extraext" "DNS=${cn}")
298    csr=$(req "$key" "CN = $cn") || return 1
299    echo "$csr" |
300	cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
301	    -set_serial 2 -days "${DAYS}" "$@"
302}
303
304geneenocsr() {
305    local OPTIND=1
306    local purpose=serverAuth
307
308    while getopts p: o
309    do
310        case $o in
311        p) purpose="$OPTARG";;
312        *) echo "Usage: $0 geneenocsr [-p EKU] cn certname cakeyname cacertname" >&2
313           return 1;;
314        esac
315    done
316
317    shift $((OPTIND - 1))
318    local cn=$1; shift
319    local cert=$1; shift
320    local cakey=$1; shift
321    local ca=$1; shift
322
323    exts=$(printf "%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \
324	    "subjectKeyIdentifier = hash" \
325	    "authorityKeyIdentifier = keyid, issuer" \
326	    "basicConstraints = CA:false" \
327	    "extendedKeyUsage = $purpose" \
328	    "subjectAltName = @alts" "DNS=${cn}")
329	cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
330	    -set_serial 2 -days "${DAYS}" "$@"
331}
332
333genss() {
334    local cn=$1; shift
335    local key=$1; shift
336    local cert=$1; shift
337
338    exts=$(printf "%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \
339	    "subjectKeyIdentifier   = hash" \
340	    "authorityKeyIdentifier = keyid, issuer" \
341	    "basicConstraints = CA:false" \
342	    "extendedKeyUsage = serverAuth" \
343	    "subjectAltName = @alts" "DNS=${cn}")
344    csr=$(req "$key" "CN = $cn") || return 1
345    echo "$csr" |
346        cert "$cert" "$exts" -signkey "${key}.pem" \
347            -set_serial 1 -days "${DAYS}" "$@"
348}
349
350gennocn() {
351    local key=$1; shift
352    local cert=$1; shift
353
354    csr=$(req_nocn "$key") || return 1
355    echo "$csr" |
356	cert "$cert" "" -signkey "${key}.pem" -set_serial 1 -days -1 "$@"
357}
358
359genct() {
360    local OPTIND=1
361    local purpose=serverAuth
362
363    while getopts p: o
364    do
365        case $o in
366        p) purpose="$OPTARG";;
367        *) echo "Usage: $0 genct [-p EKU] cn keyname certname cakeyname cacertname ctlogkey" >&2
368           return 1;;
369        esac
370    done
371
372    shift $((OPTIND - 1))
373    local cn=$1; shift
374    local key=$1; shift
375    local cert=$1; shift
376    local cakey=$1; shift
377    local ca=$1; shift
378    local logkey=$1; shift
379
380    exts=$(printf "%s\n%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \
381	    "subjectKeyIdentifier = hash" \
382	    "authorityKeyIdentifier = keyid, issuer" \
383	    "basicConstraints = CA:false" \
384	    "extendedKeyUsage = $purpose" \
385            "1.3.6.1.4.1.11129.2.4.3 = critical,ASN1:NULL"\
386	    "subjectAltName = @alts" "DNS=${cn}")
387    csr=$(req "$key" "CN = $cn") || return 1
388    echo "$csr" |
389	cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
390	    -set_serial 2 -days "${DAYS}" "$@"
391    cat ${cert}.pem ${ca}.pem > ${cert}-chain.pem
392    go run github.com/google/certificate-transparency-go/ctutil/sctgen \
393       --log_private_key ${logkey}.pem \
394       --timestamp="2020-01-01T00:00:00Z" \
395       --cert_chain ${cert}-chain.pem \
396       --tls_out ${cert}.tlssct
397    rm ${cert}-chain.pem
398    filesize=$(wc -c <${cert}.tlssct)
399    exts=$(printf "%s\n%s\n%s\n%s\n%s%04X%04X%s\n%s\n[alts]\n%s\n" \
400	    "subjectKeyIdentifier = hash" \
401	    "authorityKeyIdentifier = keyid, issuer" \
402	    "basicConstraints = CA:false" \
403	    "extendedKeyUsage = $purpose" \
404	    "1.3.6.1.4.1.11129.2.4.2 = ASN1:FORMAT:HEX,OCT:" $((filesize+2)) $filesize `xxd -p ${cert}.tlssct | tr -d '\n'` \
405	    "subjectAltName = @alts" "DNS=${cn}")
406    echo "$csr" |
407	cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
408	    -set_serial 2 -days "${DAYS}" "$@"
409}
410
411"$@"
412