1 1.1 christos #! /bin/bash 2 1.1 christos # 3 1.1.1.6 christos # Copyright 2016-2023 The OpenSSL Project Authors. All Rights Reserved. 4 1.1 christos # Copyright (c) 2016 Viktor Dukhovni <openssl-users (at] dukhovni.org>. 5 1.1 christos # All rights reserved. 6 1.1 christos # 7 1.1.1.5 christos # Licensed under the Apache License 2.0 (the "License"). You may not use 8 1.1.1.2 christos # this file except in compliance with the License. You can obtain a copy 9 1.1.1.2 christos # in the file LICENSE in the source distribution or at 10 1.1.1.2 christos # https://www.openssl.org/source/license.html 11 1.1.1.2 christos 12 1.1.1.2 christos # This file is dual-licensed and is also available under other terms. 13 1.1.1.2 christos # Please contact the author. 14 1.1 christos 15 1.1 christos # 100 years should be enough for now 16 1.1 christos if [ -z "$DAYS" ]; then 17 1.1 christos DAYS=36525 18 1.1 christos fi 19 1.1 christos 20 1.1 christos if [ -z "$OPENSSL_SIGALG" ]; then 21 1.1 christos OPENSSL_SIGALG=sha256 22 1.1 christos fi 23 1.1 christos 24 1.1 christos if [ -z "$REQMASK" ]; then 25 1.1 christos REQMASK=utf8only 26 1.1 christos fi 27 1.1 christos 28 1.1 christos stderr_onerror() { 29 1.1 christos ( 30 1.1 christos err=$("$@" >&3 2>&1) || { 31 1.1 christos printf "%s\n" "$err" >&2 32 1.1 christos exit 1 33 1.1 christos } 34 1.1 christos ) 3>&1 35 1.1 christos } 36 1.1 christos 37 1.1 christos key() { 38 1.1 christos local key=$1; shift 39 1.1 christos 40 1.1 christos local alg=rsa 41 1.1 christos if [ -n "$OPENSSL_KEYALG" ]; then 42 1.1 christos alg=$OPENSSL_KEYALG 43 1.1 christos fi 44 1.1 christos 45 1.1 christos local bits=2048 46 1.1 christos if [ -n "$OPENSSL_KEYBITS" ]; then 47 1.1 christos bits=$OPENSSL_KEYBITS 48 1.1 christos fi 49 1.1 christos 50 1.1 christos if [ ! -f "${key}.pem" ]; then 51 1.1 christos args=(-algorithm "$alg") 52 1.1 christos case $alg in 53 1.1 christos rsa) args=("${args[@]}" -pkeyopt rsa_keygen_bits:$bits );; 54 1.1 christos ec) args=("${args[@]}" -pkeyopt "ec_paramgen_curve:$bits") 55 1.1 christos args=("${args[@]}" -pkeyopt ec_param_enc:named_curve);; 56 1.1.1.2 christos dsa) args=(-paramfile "$bits");; 57 1.1.1.2 christos ed25519) ;; 58 1.1.1.2 christos ed448) ;; 59 1.1 christos *) printf "Unsupported key algorithm: %s\n" "$alg" >&2; return 1;; 60 1.1 christos esac 61 1.1 christos stderr_onerror \ 62 1.1 christos openssl genpkey "${args[@]}" -out "${key}.pem" 63 1.1 christos fi 64 1.1 christos } 65 1.1 christos 66 1.1 christos # Usage: $0 req keyname dn1 dn2 ... 67 1.1 christos req() { 68 1.1 christos local key=$1; shift 69 1.1 christos 70 1.1 christos key "$key" 71 1.1 christos local errs 72 1.1 christos 73 1.1 christos stderr_onerror \ 74 1.1 christos openssl req -new -"${OPENSSL_SIGALG}" -key "${key}.pem" \ 75 1.1 christos -config <(printf "string_mask=%s\n[req]\n%s\n%s\n[dn]\n" \ 76 1.1 christos "$REQMASK" "prompt = no" "distinguished_name = dn" 77 1.1 christos for dn in "$@"; do echo "$dn"; done) 78 1.1 christos } 79 1.1 christos 80 1.1 christos req_nocn() { 81 1.1 christos local key=$1; shift 82 1.1 christos 83 1.1 christos key "$key" 84 1.1 christos stderr_onerror \ 85 1.1 christos openssl req -new -"${OPENSSL_SIGALG}" -subj / -key "${key}.pem" \ 86 1.1 christos -config <(printf "[req]\n%s\n[dn]\nCN_default =\n" \ 87 1.1 christos "distinguished_name = dn") 88 1.1 christos } 89 1.1 christos 90 1.1 christos cert() { 91 1.1 christos local cert=$1; shift 92 1.1 christos local exts=$1; shift 93 1.1 christos 94 1.1 christos stderr_onerror \ 95 1.1 christos openssl x509 -req -"${OPENSSL_SIGALG}" -out "${cert}.pem" \ 96 1.1 christos -extfile <(printf "%s\n" "$exts") "$@" 97 1.1 christos } 98 1.1 christos 99 1.1 christos genroot() { 100 1.1 christos local cn=$1; shift 101 1.1 christos local key=$1; shift 102 1.1 christos local cert=$1; shift 103 1.1.1.5 christos local bcon="basicConstraints = critical,CA:true" 104 1.1.1.5 christos local ku="keyUsage = keyCertSign,cRLSign" 105 1.1 christos local skid="subjectKeyIdentifier = hash" 106 1.1 christos local akid="authorityKeyIdentifier = keyid" 107 1.1 christos 108 1.1.1.5 christos exts=$(printf "%s\n%s\n%s\n" "$bcon" "$ku" "$skid" "$akid") 109 1.1 christos for eku in "$@" 110 1.1 christos do 111 1.1 christos exts=$(printf "%s\nextendedKeyUsage = %s\n" "$exts" "$eku") 112 1.1 christos done 113 1.1 christos csr=$(req "$key" "CN = $cn") || return 1 114 1.1 christos echo "$csr" | 115 1.1 christos cert "$cert" "$exts" -signkey "${key}.pem" -set_serial 1 -days "${DAYS}" 116 1.1 christos } 117 1.1 christos 118 1.1 christos genca() { 119 1.1.1.4 christos local OPTIND=1 120 1.1.1.4 christos local purpose= 121 1.1.1.4 christos 122 1.1.1.6 christos while getopts p:c: o 123 1.1.1.4 christos do 124 1.1.1.4 christos case $o in 125 1.1.1.4 christos p) purpose="$OPTARG";; 126 1.1.1.6 christos c) certpol="$OPTARG";; 127 1.1.1.6 christos *) echo "Usage: $0 genca [-p EKU][-c policyoid] cn keyname certname cakeyname cacertname" >&2 128 1.1.1.4 christos return 1;; 129 1.1.1.4 christos esac 130 1.1.1.4 christos done 131 1.1.1.4 christos 132 1.1.1.4 christos shift $((OPTIND - 1)) 133 1.1 christos local cn=$1; shift 134 1.1 christos local key=$1; shift 135 1.1 christos local cert=$1; shift 136 1.1 christos local cakey=$1; shift 137 1.1 christos local cacert=$1; shift 138 1.1.1.5 christos local bcon="basicConstraints = critical,CA:true" 139 1.1.1.5 christos local ku="keyUsage = keyCertSign,cRLSign" 140 1.1 christos local skid="subjectKeyIdentifier = hash" 141 1.1 christos local akid="authorityKeyIdentifier = keyid" 142 1.1 christos 143 1.1.1.5 christos exts=$(printf "%s\n%s\n%s\n" "$bcon" "$ku" "$skid" "$akid") 144 1.1.1.4 christos if [ -n "$purpose" ]; then 145 1.1.1.4 christos exts=$(printf "%s\nextendedKeyUsage = %s\n" "$exts" "$purpose") 146 1.1.1.4 christos fi 147 1.1 christos if [ -n "$NC" ]; then 148 1.1 christos exts=$(printf "%s\nnameConstraints = %s\n" "$exts" "$NC") 149 1.1 christos fi 150 1.1.1.6 christos if [ -n "$certpol" ]; then 151 1.1.1.6 christos exts=$(printf "%s\ncertificatePolicies = %s\n" "$exts" "$certpol") 152 1.1.1.6 christos fi 153 1.1.1.6 christos 154 1.1 christos csr=$(req "$key" "CN = $cn") || return 1 155 1.1 christos echo "$csr" | 156 1.1 christos cert "$cert" "$exts" -CA "${cacert}.pem" -CAkey "${cakey}.pem" \ 157 1.1.1.4 christos -set_serial 2 -days "${DAYS}" "$@" 158 1.1 christos } 159 1.1 christos 160 1.1 christos gen_nonbc_ca() { 161 1.1 christos local cn=$1; shift 162 1.1 christos local key=$1; shift 163 1.1 christos local cert=$1; shift 164 1.1 christos local cakey=$1; shift 165 1.1 christos local cacert=$1; shift 166 1.1 christos local skid="subjectKeyIdentifier = hash" 167 1.1 christos local akid="authorityKeyIdentifier = keyid" 168 1.1 christos 169 1.1 christos exts=$(printf "%s\n%s\n%s\n" "$skid" "$akid") 170 1.1 christos exts=$(printf "%s\nkeyUsage = %s\n" "$exts" "keyCertSign, cRLSign") 171 1.1 christos for eku in "$@" 172 1.1 christos do 173 1.1 christos exts=$(printf "%s\nextendedKeyUsage = %s\n" "$exts" "$eku") 174 1.1 christos done 175 1.1 christos csr=$(req "$key" "CN = $cn") || return 1 176 1.1 christos echo "$csr" | 177 1.1 christos cert "$cert" "$exts" -CA "${cacert}.pem" -CAkey "${cakey}.pem" \ 178 1.1 christos -set_serial 2 -days "${DAYS}" 179 1.1 christos } 180 1.1 christos 181 1.1 christos # Usage: $0 genpc keyname certname eekeyname eecertname pcext1 pcext2 ... 182 1.1 christos # 183 1.1 christos # Note: takes csr on stdin, so must be used with $0 req like this: 184 1.1 christos # 185 1.1 christos # $0 req keyname dn | $0 genpc keyname certname eekeyname eecertname pcext ... 186 1.1 christos genpc() { 187 1.1 christos local key=$1; shift 188 1.1 christos local cert=$1; shift 189 1.1 christos local cakey=$1; shift 190 1.1 christos local ca=$1; shift 191 1.1 christos 192 1.1 christos exts=$(printf "%s\n%s\n%s\n%s\n" \ 193 1.1 christos "subjectKeyIdentifier = hash" \ 194 1.1 christos "authorityKeyIdentifier = keyid, issuer:always" \ 195 1.1 christos "basicConstraints = CA:false" \ 196 1.1 christos "proxyCertInfo = critical, @pcexts"; 197 1.1 christos echo "[pcexts]"; 198 1.1 christos for x in "$@"; do echo $x; done) 199 1.1 christos cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \ 200 1.1 christos -set_serial 2 -days "${DAYS}" 201 1.1 christos } 202 1.1 christos 203 1.1.1.5 christos geneeconfig() { 204 1.1.1.5 christos local key=$1; shift 205 1.1.1.5 christos local cert=$1; shift 206 1.1.1.5 christos local cakey=$1; shift 207 1.1.1.5 christos local ca=$1; shift 208 1.1.1.5 christos local conf=$1; shift 209 1.1.1.5 christos 210 1.1.1.5 christos exts=$(printf "%s\n%s\n%s\n%s\n" \ 211 1.1.1.5 christos "subjectKeyIdentifier = hash" \ 212 1.1.1.5 christos "authorityKeyIdentifier = keyid" \ 213 1.1.1.5 christos "basicConstraints = CA:false"; \ 214 1.1.1.5 christos echo "$conf") 215 1.1.1.5 christos 216 1.1.1.5 christos cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \ 217 1.1.1.5 christos -set_serial 2 -days "${DAYS}" 218 1.1.1.5 christos } 219 1.1.1.5 christos 220 1.1.1.5 christos # Usage: $0 geneealt keyname certname cakeyname cacertname alt1 alt2 ... 221 1.1 christos # 222 1.1 christos # Note: takes csr on stdin, so must be used with $0 req like this: 223 1.1 christos # 224 1.1.1.5 christos # $0 req keyname dn | $0 geneealt keyname certname cakeyname cacertname alt ... 225 1.1 christos geneealt() { 226 1.1 christos local key=$1; shift 227 1.1 christos local cert=$1; shift 228 1.1 christos local cakey=$1; shift 229 1.1 christos local ca=$1; shift 230 1.1 christos 231 1.1.1.5 christos conf=$(echo "subjectAltName = @alts" 232 1.1 christos echo "[alts]"; 233 1.1.1.5 christos for x in "$@"; do echo "$x"; done) 234 1.1.1.5 christos 235 1.1.1.5 christos geneeconfig $key $cert $cakey $ca "$conf" 236 1.1 christos } 237 1.1 christos 238 1.1 christos genee() { 239 1.1 christos local OPTIND=1 240 1.1 christos local purpose=serverAuth 241 1.1 christos 242 1.1 christos while getopts p: o 243 1.1 christos do 244 1.1 christos case $o in 245 1.1 christos p) purpose="$OPTARG";; 246 1.1 christos *) echo "Usage: $0 genee [-p EKU] cn keyname certname cakeyname cacertname" >&2 247 1.1 christos return 1;; 248 1.1 christos esac 249 1.1 christos done 250 1.1 christos 251 1.1 christos shift $((OPTIND - 1)) 252 1.1 christos local cn=$1; shift 253 1.1 christos local key=$1; shift 254 1.1 christos local cert=$1; shift 255 1.1 christos local cakey=$1; shift 256 1.1 christos local ca=$1; shift 257 1.1 christos 258 1.1 christos exts=$(printf "%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \ 259 1.1 christos "subjectKeyIdentifier = hash" \ 260 1.1 christos "authorityKeyIdentifier = keyid, issuer" \ 261 1.1 christos "basicConstraints = CA:false" \ 262 1.1 christos "extendedKeyUsage = $purpose" \ 263 1.1 christos "subjectAltName = @alts" "DNS=${cn}") 264 1.1 christos csr=$(req "$key" "CN = $cn") || return 1 265 1.1 christos echo "$csr" | 266 1.1 christos cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \ 267 1.1 christos -set_serial 2 -days "${DAYS}" "$@" 268 1.1 christos } 269 1.1 christos 270 1.1.1.5 christos geneeextra() { 271 1.1.1.5 christos local OPTIND=1 272 1.1.1.5 christos local purpose=serverAuth 273 1.1.1.5 christos 274 1.1.1.5 christos while getopts p: o 275 1.1.1.5 christos do 276 1.1.1.5 christos case $o in 277 1.1.1.5 christos p) purpose="$OPTARG";; 278 1.1.1.5 christos *) echo "Usage: $0 geneeextra [-p EKU] cn keyname certname cakeyname cacertname extraext" >&2 279 1.1.1.5 christos return 1;; 280 1.1.1.5 christos esac 281 1.1.1.5 christos done 282 1.1.1.5 christos 283 1.1.1.5 christos shift $((OPTIND - 1)) 284 1.1.1.5 christos local cn=$1; shift 285 1.1.1.5 christos local key=$1; shift 286 1.1.1.5 christos local cert=$1; shift 287 1.1.1.5 christos local cakey=$1; shift 288 1.1.1.5 christos local ca=$1; shift 289 1.1.1.5 christos local extraext=$1; shift 290 1.1.1.5 christos 291 1.1.1.5 christos exts=$(printf "%s\n%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \ 292 1.1.1.5 christos "subjectKeyIdentifier = hash" \ 293 1.1.1.5 christos "authorityKeyIdentifier = keyid, issuer" \ 294 1.1.1.5 christos "basicConstraints = CA:false" \ 295 1.1.1.5 christos "extendedKeyUsage = $purpose" \ 296 1.1.1.5 christos "subjectAltName = @alts"\ 297 1.1.1.5 christos "$extraext" "DNS=${cn}") 298 1.1.1.5 christos csr=$(req "$key" "CN = $cn") || return 1 299 1.1.1.5 christos echo "$csr" | 300 1.1.1.5 christos cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \ 301 1.1.1.5 christos -set_serial 2 -days "${DAYS}" "$@" 302 1.1.1.5 christos } 303 1.1.1.5 christos 304 1.1.1.3 christos geneenocsr() { 305 1.1.1.3 christos local OPTIND=1 306 1.1.1.3 christos local purpose=serverAuth 307 1.1.1.3 christos 308 1.1.1.3 christos while getopts p: o 309 1.1.1.3 christos do 310 1.1.1.3 christos case $o in 311 1.1.1.3 christos p) purpose="$OPTARG";; 312 1.1.1.5 christos *) echo "Usage: $0 geneenocsr [-p EKU] cn certname cakeyname cacertname" >&2 313 1.1.1.3 christos return 1;; 314 1.1.1.3 christos esac 315 1.1.1.3 christos done 316 1.1.1.3 christos 317 1.1.1.3 christos shift $((OPTIND - 1)) 318 1.1.1.3 christos local cn=$1; shift 319 1.1.1.3 christos local cert=$1; shift 320 1.1.1.3 christos local cakey=$1; shift 321 1.1.1.3 christos local ca=$1; shift 322 1.1.1.3 christos 323 1.1.1.3 christos exts=$(printf "%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \ 324 1.1.1.3 christos "subjectKeyIdentifier = hash" \ 325 1.1.1.3 christos "authorityKeyIdentifier = keyid, issuer" \ 326 1.1.1.3 christos "basicConstraints = CA:false" \ 327 1.1.1.3 christos "extendedKeyUsage = $purpose" \ 328 1.1.1.3 christos "subjectAltName = @alts" "DNS=${cn}") 329 1.1.1.3 christos cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \ 330 1.1.1.3 christos -set_serial 2 -days "${DAYS}" "$@" 331 1.1.1.3 christos } 332 1.1.1.3 christos 333 1.1 christos genss() { 334 1.1 christos local cn=$1; shift 335 1.1 christos local key=$1; shift 336 1.1 christos local cert=$1; shift 337 1.1 christos 338 1.1 christos exts=$(printf "%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \ 339 1.1 christos "subjectKeyIdentifier = hash" \ 340 1.1 christos "authorityKeyIdentifier = keyid, issuer" \ 341 1.1 christos "basicConstraints = CA:false" \ 342 1.1 christos "extendedKeyUsage = serverAuth" \ 343 1.1 christos "subjectAltName = @alts" "DNS=${cn}") 344 1.1 christos csr=$(req "$key" "CN = $cn") || return 1 345 1.1 christos echo "$csr" | 346 1.1 christos cert "$cert" "$exts" -signkey "${key}.pem" \ 347 1.1 christos -set_serial 1 -days "${DAYS}" "$@" 348 1.1 christos } 349 1.1 christos 350 1.1 christos gennocn() { 351 1.1 christos local key=$1; shift 352 1.1 christos local cert=$1; shift 353 1.1 christos 354 1.1 christos csr=$(req_nocn "$key") || return 1 355 1.1 christos echo "$csr" | 356 1.1 christos cert "$cert" "" -signkey "${key}.pem" -set_serial 1 -days -1 "$@" 357 1.1 christos } 358 1.1 christos 359 1.1.1.5 christos genct() { 360 1.1.1.5 christos local OPTIND=1 361 1.1.1.5 christos local purpose=serverAuth 362 1.1.1.5 christos 363 1.1.1.5 christos while getopts p: o 364 1.1.1.5 christos do 365 1.1.1.5 christos case $o in 366 1.1.1.5 christos p) purpose="$OPTARG";; 367 1.1.1.5 christos *) echo "Usage: $0 genct [-p EKU] cn keyname certname cakeyname cacertname ctlogkey" >&2 368 1.1.1.5 christos return 1;; 369 1.1.1.5 christos esac 370 1.1.1.5 christos done 371 1.1.1.5 christos 372 1.1.1.5 christos shift $((OPTIND - 1)) 373 1.1.1.5 christos local cn=$1; shift 374 1.1.1.5 christos local key=$1; shift 375 1.1.1.5 christos local cert=$1; shift 376 1.1.1.5 christos local cakey=$1; shift 377 1.1.1.5 christos local ca=$1; shift 378 1.1.1.5 christos local logkey=$1; shift 379 1.1.1.5 christos 380 1.1.1.5 christos exts=$(printf "%s\n%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \ 381 1.1.1.5 christos "subjectKeyIdentifier = hash" \ 382 1.1.1.5 christos "authorityKeyIdentifier = keyid, issuer" \ 383 1.1.1.5 christos "basicConstraints = CA:false" \ 384 1.1.1.5 christos "extendedKeyUsage = $purpose" \ 385 1.1.1.5 christos "1.3.6.1.4.1.11129.2.4.3 = critical,ASN1:NULL"\ 386 1.1.1.5 christos "subjectAltName = @alts" "DNS=${cn}") 387 1.1.1.5 christos csr=$(req "$key" "CN = $cn") || return 1 388 1.1.1.5 christos echo "$csr" | 389 1.1.1.5 christos cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \ 390 1.1.1.5 christos -set_serial 2 -days "${DAYS}" "$@" 391 1.1.1.5 christos cat ${cert}.pem ${ca}.pem > ${cert}-chain.pem 392 1.1.1.5 christos go run github.com/google/certificate-transparency-go/ctutil/sctgen \ 393 1.1.1.5 christos --log_private_key ${logkey}.pem \ 394 1.1.1.5 christos --timestamp="2020-01-01T00:00:00Z" \ 395 1.1.1.5 christos --cert_chain ${cert}-chain.pem \ 396 1.1.1.5 christos --tls_out ${cert}.tlssct 397 1.1.1.5 christos rm ${cert}-chain.pem 398 1.1.1.5 christos filesize=$(wc -c <${cert}.tlssct) 399 1.1.1.5 christos exts=$(printf "%s\n%s\n%s\n%s\n%s%04X%04X%s\n%s\n[alts]\n%s\n" \ 400 1.1.1.5 christos "subjectKeyIdentifier = hash" \ 401 1.1.1.5 christos "authorityKeyIdentifier = keyid, issuer" \ 402 1.1.1.5 christos "basicConstraints = CA:false" \ 403 1.1.1.5 christos "extendedKeyUsage = $purpose" \ 404 1.1.1.5 christos "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 1.1.1.5 christos "subjectAltName = @alts" "DNS=${cn}") 406 1.1.1.5 christos echo "$csr" | 407 1.1.1.5 christos cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \ 408 1.1.1.5 christos -set_serial 2 -days "${DAYS}" "$@" 409 1.1.1.5 christos } 410 1.1.1.5 christos 411 1.1 christos "$@" 412