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