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