Commit | Line | Data |
---|---|---|
58d41635 MS |
1 | #!/bin/bash |
2 | # SPDX-License-Identifier: GPL-2.0 | |
3 | # | |
4 | # Copyright © 2020, Microsoft Corporation. All rights reserved. | |
5 | # | |
6 | # Author: Mickaël Salaün <mic@linux.microsoft.com> | |
7 | # | |
8 | # Compute and print the To Be Signed (TBS) hash of a certificate. This is used | |
9 | # as description of keys in the blacklist keyring to identify certificates. | |
10 | # This output should be redirected, without newline, in a file (hash0.txt) and | |
11 | # signed to create a PKCS#7 file (hash0.p7s). Both of these files can then be | |
12 | # loaded in the kernel with. | |
13 | # | |
14 | # Exemple on a workstation: | |
15 | # ./print-cert-tbs-hash.sh certificate-to-invalidate.pem > hash0.txt | |
16 | # openssl smime -sign -in hash0.txt -inkey builtin-private-key.pem \ | |
17 | # -signer builtin-certificate.pem -certfile certificate-chain.pem \ | |
18 | # -noattr -binary -outform DER -out hash0.p7s | |
19 | # | |
20 | # Exemple on a managed system: | |
21 | # keyctl padd blacklist "$(< hash0.txt)" %:.blacklist < hash0.p7s | |
22 | ||
23 | set -u -e -o pipefail | |
24 | ||
25 | CERT="${1:-}" | |
26 | BASENAME="$(basename -- "${BASH_SOURCE[0]}")" | |
27 | ||
28 | if [ $# -ne 1 ] || [ ! -f "${CERT}" ]; then | |
29 | echo "usage: ${BASENAME} <certificate>" >&2 | |
30 | exit 1 | |
31 | fi | |
32 | ||
33 | # Checks that it is indeed a certificate (PEM or DER encoded) and exclude the | |
34 | # optional PEM text header. | |
35 | if ! PEM="$(openssl x509 -inform DER -in "${CERT}" 2>/dev/null || openssl x509 -in "${CERT}")"; then | |
36 | echo "ERROR: Failed to parse certificate" >&2 | |
37 | exit 1 | |
38 | fi | |
39 | ||
40 | # TBSCertificate starts at the second entry. | |
41 | # Cf. https://tools.ietf.org/html/rfc3280#section-4.1 | |
42 | # | |
43 | # Exemple of first lines printed by openssl asn1parse: | |
44 | # 0:d=0 hl=4 l= 763 cons: SEQUENCE | |
45 | # 4:d=1 hl=4 l= 483 cons: SEQUENCE | |
46 | # 8:d=2 hl=2 l= 3 cons: cont [ 0 ] | |
47 | # 10:d=3 hl=2 l= 1 prim: INTEGER :02 | |
48 | # 13:d=2 hl=2 l= 20 prim: INTEGER :3CEB2CB8818D968AC00EEFE195F0DF9665328B7B | |
49 | # 35:d=2 hl=2 l= 13 cons: SEQUENCE | |
50 | # 37:d=3 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption | |
51 | RANGE_AND_DIGEST_RE=' | |
52 | 2s/^\s*\([0-9]\+\):d=\s*[0-9]\+\s\+hl=\s*[0-9]\+\s\+l=\s*\([0-9]\+\)\s\+cons:\s*SEQUENCE\s*$/\1 \2/p; | |
53 | 7s/^\s*[0-9]\+:d=\s*[0-9]\+\s\+hl=\s*[0-9]\+\s\+l=\s*[0-9]\+\s\+prim:\s*OBJECT\s*:\(.*\)$/\1/p; | |
54 | ' | |
55 | ||
56 | RANGE_AND_DIGEST=($(echo "${PEM}" | \ | |
57 | openssl asn1parse -in - | \ | |
58 | sed -n -e "${RANGE_AND_DIGEST_RE}")) | |
59 | ||
60 | if [ "${#RANGE_AND_DIGEST[@]}" != 3 ]; then | |
61 | echo "ERROR: Failed to parse TBSCertificate." >&2 | |
62 | exit 1 | |
63 | fi | |
64 | ||
65 | OFFSET="${RANGE_AND_DIGEST[0]}" | |
66 | END="$(( OFFSET + RANGE_AND_DIGEST[1] ))" | |
67 | DIGEST="${RANGE_AND_DIGEST[2]}" | |
68 | ||
69 | # The signature hash algorithm is used by Linux to blacklist certificates. | |
70 | # Cf. crypto/asymmetric_keys/x509_cert_parser.c:x509_note_pkey_algo() | |
71 | DIGEST_MATCH="" | |
72 | while read -r DIGEST_ITEM; do | |
73 | if [ -z "${DIGEST_ITEM}" ]; then | |
74 | break | |
75 | fi | |
76 | if echo "${DIGEST}" | grep -qiF "${DIGEST_ITEM}"; then | |
77 | DIGEST_MATCH="${DIGEST_ITEM}" | |
78 | break | |
79 | fi | |
80 | done < <(openssl list -digest-commands | tr ' ' '\n' | sort -ur) | |
81 | ||
82 | if [ -z "${DIGEST_MATCH}" ]; then | |
83 | echo "ERROR: Unknown digest algorithm: ${DIGEST}" >&2 | |
84 | exit 1 | |
85 | fi | |
86 | ||
87 | echo "${PEM}" | \ | |
88 | openssl x509 -in - -outform DER | \ | |
89 | dd "bs=1" "skip=${OFFSET}" "count=${END}" "status=none" | \ | |
90 | openssl dgst "-${DIGEST_MATCH}" - | \ | |
91 | awk '{printf "tbs:" $2}' |