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