|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +# Regenerate the certificate set used by test_X509_verify_cert_untrusted_inter |
| 4 | +# (tests/api/test_ossl_x509_str.c). |
| 5 | +# |
| 6 | +# The set lets the test verify an end-entity certificate together with |
| 7 | +# caller-supplied untrusted intermediates through the OpenSSL compatibility |
| 8 | +# path (X509_STORE_CTX_init "chain" argument + X509_verify_cert) and check that |
| 9 | +# such a chain is accepted only when it terminates at a trusted anchor. |
| 10 | +# |
| 11 | +# Two trust chains are produced (both end-entities use the same hostname so a |
| 12 | +# downstream hostname check is meaningful): |
| 13 | +# |
| 14 | +# single intermediate : leaf <- int-ca <- root-ca |
| 15 | +# two intermediates : leaf-deep <- int-ca2 <- int-ca <- root-ca |
| 16 | +# |
| 17 | +# Plus: |
| 18 | +# alt-ca an unrelated self-signed root (a populated but wrong |
| 19 | +# trust anchor) |
| 20 | +# int-ca-tampered int-ca with the final byte of its signatureValue |
| 21 | +# flipped (valid TBSCertificate, broken outer signature) |
| 22 | +# |
| 23 | +# The certificates intentionally omit subjectKeyIdentifier / |
| 24 | +# authorityKeyIdentifier; the test relies on this, so the script aborts at the |
| 25 | +# end if they were added back. OpenSSL 3.x adds them automatically with no |
| 26 | +# option to suppress them, so regenerate with a tool that does not (e.g. |
| 27 | +# OPENSSL=/usr/bin/openssl on macOS). |
| 28 | +# |
| 29 | +# RSA-2048 / SHA-256, ~30 year validity so the regression does not expire. |
| 30 | +# |
| 31 | +# Requires: openssl (or a compatible LibreSSL, see above) and python3 (used to |
| 32 | +# flip a signature byte for the tampered intermediate). With set -e a missing |
| 33 | +# python3 aborts regeneration partway through, leaving a half-written fixture |
| 34 | +# set; install python3 (or replace the byte-flip step) before regenerating. |
| 35 | + |
| 36 | +set -e |
| 37 | +cd "$(dirname "$0")" |
| 38 | + |
| 39 | +OPENSSL="${OPENSSL:-openssl}" |
| 40 | +DAYS=10957 |
| 41 | +RSA_BITS=2048 |
| 42 | + |
| 43 | +CA_EXT=$(mktemp) |
| 44 | +LEAF_EXT=$(mktemp) |
| 45 | +trap 'rm -f "$CA_EXT" "$LEAF_EXT" *.csr *.srl' EXIT |
| 46 | + |
| 47 | +# No pathlen so the first intermediate can still issue the second one in the |
| 48 | +# two-intermediate positive control; no key identifiers (see header). |
| 49 | +cat > "$CA_EXT" <<'EOF' |
| 50 | +basicConstraints = critical, CA:TRUE |
| 51 | +keyUsage = critical, digitalSignature, keyCertSign, cRLSign |
| 52 | +EOF |
| 53 | + |
| 54 | +cat > "$LEAF_EXT" <<'EOF' |
| 55 | +basicConstraints = critical, CA:FALSE |
| 56 | +keyUsage = critical, digitalSignature, keyEncipherment |
| 57 | +extendedKeyUsage = serverAuth |
| 58 | +subjectAltName = DNS:www.example.test |
| 59 | +EOF |
| 60 | + |
| 61 | +genkey() { "$OPENSSL" genrsa -out "$1" "$RSA_BITS" 2>/dev/null; } |
| 62 | + |
| 63 | +# Self-sign a root CA: genroot <key> <cert> <CN> |
| 64 | +genroot() { |
| 65 | + "$OPENSSL" req -x509 -new -key "$1" -sha256 -subj "/CN=$3" -days "$DAYS" \ |
| 66 | + -extensions v3 \ |
| 67 | + -config <(printf '[req]\ndistinguished_name=dn\n[dn]\n[v3]\n%s' \ |
| 68 | + "$(cat "$CA_EXT")") \ |
| 69 | + -out "$2" |
| 70 | +} |
| 71 | + |
| 72 | +# Sign a CSR: signcert <csr> <cert> <issuer-cert> <issuer-key> <extfile> |
| 73 | +signcert() { |
| 74 | + "$OPENSSL" x509 -req -in "$1" -CA "$3" -CAkey "$4" -CAcreateserial \ |
| 75 | + -sha256 -days "$DAYS" -extfile "$5" -out "$2" 2>/dev/null |
| 76 | +} |
| 77 | + |
| 78 | +# Roots ---------------------------------------------------------------------- |
| 79 | +genkey root-ca-key.pem |
| 80 | +genroot root-ca-key.pem root-ca-cert.pem "wolfSSL Untrusted-Anchor Test Root" |
| 81 | + |
| 82 | +genkey alt-ca-key.pem |
| 83 | +genroot alt-ca-key.pem alt-ca-cert.pem "wolfSSL Untrusted-Anchor Test Alt Root" |
| 84 | + |
| 85 | +# Intermediate signed by the root -------------------------------------------- |
| 86 | +genkey int-ca-key.pem |
| 87 | +"$OPENSSL" req -new -key int-ca-key.pem -sha256 \ |
| 88 | + -subj "/CN=wolfSSL Untrusted-Anchor Test Intermediate" -out int-ca.csr |
| 89 | +signcert int-ca.csr int-ca-cert.pem root-ca-cert.pem root-ca-key.pem "$CA_EXT" |
| 90 | + |
| 91 | +# Leaf signed by the intermediate (single-intermediate chain) ---------------- |
| 92 | +genkey leaf-key.pem |
| 93 | +"$OPENSSL" req -new -key leaf-key.pem -sha256 \ |
| 94 | + -subj "/CN=www.example.test" -out leaf.csr |
| 95 | +signcert leaf.csr leaf-cert.pem int-ca-cert.pem int-ca-key.pem "$LEAF_EXT" |
| 96 | + |
| 97 | +# Second-level intermediate signed by the first intermediate ----------------- |
| 98 | +genkey int-ca2-key.pem |
| 99 | +"$OPENSSL" req -new -key int-ca2-key.pem -sha256 \ |
| 100 | + -subj "/CN=wolfSSL Untrusted-Anchor Test Intermediate 2" -out int-ca2.csr |
| 101 | +signcert int-ca2.csr int-ca2-cert.pem int-ca-cert.pem int-ca-key.pem "$CA_EXT" |
| 102 | + |
| 103 | +# Leaf signed by the second-level intermediate (two-intermediate chain) ------ |
| 104 | +genkey leaf-deep-key.pem |
| 105 | +"$OPENSSL" req -new -key leaf-deep-key.pem -sha256 \ |
| 106 | + -subj "/CN=www.example.test" -out leaf-deep.csr |
| 107 | +signcert leaf-deep.csr leaf-deep-cert.pem int-ca2-cert.pem int-ca2-key.pem \ |
| 108 | + "$LEAF_EXT" |
| 109 | + |
| 110 | +# Tampered intermediate: flip the final byte of the DER (last byte of the |
| 111 | +# signatureValue) so the TBSCertificate stays valid but the outer signature no |
| 112 | +# longer verifies. |
| 113 | +"$OPENSSL" x509 -in int-ca-cert.pem -outform DER -out int-ca.der |
| 114 | +python3 - <<'PY' |
| 115 | +d = open("int-ca.der", "rb").read() |
| 116 | +d = d[:-1] + bytes([d[-1] ^ 0x01]) |
| 117 | +open("int-ca-tampered.der", "wb").write(d) |
| 118 | +PY |
| 119 | +"$OPENSSL" x509 -inform DER -in int-ca-tampered.der -out int-ca-tampered-cert.pem |
| 120 | +rm -f int-ca.der int-ca-tampered.der |
| 121 | + |
| 122 | +# Guard: these test certificates must not carry key identifiers (see header). |
| 123 | +for c in root-ca-cert.pem alt-ca-cert.pem int-ca-cert.pem int-ca2-cert.pem \ |
| 124 | + leaf-cert.pem leaf-deep-cert.pem; do |
| 125 | + if "$OPENSSL" x509 -in "$c" -noout -text \ |
| 126 | + | grep -q "Key Identifier"; then |
| 127 | + echo "ERROR: $c carries a subject/authority key identifier." >&2 |
| 128 | + echo "Use an OpenSSL/LibreSSL that does not auto-add them" >&2 |
| 129 | + echo "(e.g. OPENSSL=/usr/bin/openssl $0)." >&2 |
| 130 | + exit 1 |
| 131 | + fi |
| 132 | +done |
| 133 | + |
| 134 | +echo "Completed" |
0 commit comments