Home | History | Annotate | Line # | Download | only in certs
      1 #! /bin/bash
      2 #
      3 # Copyright 2016-2023 The OpenSSL Project Authors. All Rights Reserved.
      4 # Copyright (c) 2016 Viktor Dukhovni <openssl-users (at] 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
     16 if [ -z "$DAYS" ]; then
     17     DAYS=36525
     18 fi
     19 
     20 if [ -z "$OPENSSL_SIGALG" ]; then
     21     OPENSSL_SIGALG=sha256
     22 fi
     23 
     24 if [ -z "$REQMASK" ]; then
     25     REQMASK=utf8only
     26 fi
     27 
     28 stderr_onerror() {
     29     (
     30         err=$("$@" >&3 2>&1) || {
     31             printf "%s\n" "$err" >&2
     32             exit 1
     33         }
     34     ) 3>&1
     35 }
     36 
     37 key() {
     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 ...
     67 req() {
     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 
     80 req_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 
     90 cert() {
     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 
     99 genroot() {
    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 
    118 genca() {
    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 
    160 gen_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 ...
    186 genpc() {
    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 
    203 geneeconfig() {
    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 ...
    225 geneealt() {
    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 
    238 genee() {
    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 
    273 geneeextra() {
    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 
    307 geneenocsr() {
    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 
    336 genss() {
    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 
    353 gennocn() {
    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 
    362 genct() {
    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