add certificate pinning capabilities
This commit is contained in:
parent
fd265f0023
commit
e4d31848fd
5 changed files with 168 additions and 37 deletions
68
README.md
68
README.md
|
|
@ -54,6 +54,8 @@ APK signing
|
||||||
pin: ${{ secrets.HSM_USER_PIN }}
|
pin: ${{ secrets.HSM_USER_PIN }}
|
||||||
apk: unsigned/app-release-unsigned.apk
|
apk: unsigned/app-release-unsigned.apk
|
||||||
apk_output: dist/app.apk
|
apk_output: dist/app.apk
|
||||||
|
# The app's signing certificate (see below): any other fails the step
|
||||||
|
apk_cert_sha256: '<sha256 printed by make-cert>'
|
||||||
# Optional: detached signatures of everything in dist/, the signed APK
|
# Optional: detached signatures of everything in dist/, the signed APK
|
||||||
# included
|
# included
|
||||||
files: dist/*
|
files: dist/*
|
||||||
|
|
@ -63,38 +65,58 @@ APK signing
|
||||||
The APK must be zipaligned (AGP's release output already is).
|
The APK must be zipaligned (AGP's release output already is).
|
||||||
|
|
||||||
An APK signature carries a certificate, and Java (which `apksigner` runs on)
|
An APK signature carries a certificate, and Java (which `apksigner` runs on)
|
||||||
only offers a PKCS#11 key that has a certificate on the token. **Once, before
|
only offers a PKCS#11 key that has a certificate on the token. Whatever
|
||||||
the first APK is signed**, make a self-signed certificate for the key and store
|
certificate the token holds for the key is used, and a token holds one per key,
|
||||||
it beside the key. On the HSM host, with the HSM powered, from a clone of this
|
so **every APK signed with a key carries the same certificate**: one signing
|
||||||
repository:
|
identity for all of them, which is the usual arrangement for an Android
|
||||||
|
developer. Give it a generic subject (you or your organization, not an app),
|
||||||
|
and use a separate key if an app ever needs an identity of its own.
|
||||||
|
|
||||||
|
The certificate is public, so it is no secret where it is made; what matters is
|
||||||
|
that it is chosen once: it is the identity of every app signed with it, on
|
||||||
|
every device, and an APK signed with a different certificate is refused as an
|
||||||
|
update (users uninstall and reinstall).
|
||||||
|
|
||||||
|
To give the key a certificate, on the HSM host, with the HSM powered, from a
|
||||||
|
clone of this repository (the certificate is assembled in the container and
|
||||||
|
signed on the token, so the key never leaves it):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker build -t hsm-signer signer
|
docker build -t hsm-signer signer
|
||||||
read -rs PKCS11_PIN && export PKCS11_PIN
|
read -rs PKCS11_PIN && export PKCS11_PIN
|
||||||
docker run --rm -v /run/pcscd/pcscd.comm:/run/pcscd/pcscd.comm:ro \
|
docker run --rm -v /run/pcscd/pcscd.comm:/run/pcscd/pcscd.comm:ro \
|
||||||
-e PKCS11_PIN hsm-signer make-cert '/CN=My App/O=example.org'
|
-e PKCS11_PIN hsm-signer make-cert '/CN=Your Name/O=example.org'
|
||||||
```
|
```
|
||||||
|
|
||||||
It prints the certificate; keep a copy. The certificate is the app's identity
|
If the key already has a certificate, `make-cert` shows it and stops. Add
|
||||||
on every device from then on: replacing it means users uninstall and reinstall.
|
`-e REPLACE_CERT=1` to replace it (keep the PEM it showed, if anything else
|
||||||
|
uses that certificate). The new certificate is read back from the token to
|
||||||
|
check it, and printed as PEM, with its `sha256 (apk_cert_sha256)`. Keep the PEM
|
||||||
|
(it is public: publish it beside the public key, or keep it in each app's
|
||||||
|
repository) and put the sha256 in each app's workflow as `apk_cert_sha256`, so
|
||||||
|
a certificate replaced on the token later fails the build instead of silently
|
||||||
|
changing the apps' identity.
|
||||||
|
|
||||||
Its 30-year validity does not matter for APKs, whose certificate dates Android
|
Its 30-year validity does not matter for APKs, whose certificate dates Android
|
||||||
does not check. Running the same container with `list` instead of `make-cert`
|
does not check, but is long enough for Google Play, which wants validity past
|
||||||
|
2033-10-22. Running the same container with `list` instead of `make-cert`
|
||||||
shows what Java sees on the token.
|
shows what Java sees on the token.
|
||||||
|
|
||||||
Inputs
|
Inputs
|
||||||
------
|
------
|
||||||
|
|
||||||
| input | default | |
|
| input | default | |
|
||||||
|----------------|---------|--------------------------------------------------------------------------|
|
|-------------------|---------|--------------------------------------------------------------------------|
|
||||||
| `files` | | files to make detached signatures of |
|
| `files` | | files to make detached signatures of |
|
||||||
| `apk` | | unsigned APK to sign |
|
| `apk` | | unsigned APK to sign |
|
||||||
| `apk_output` | | where to write the signed APK; required with `apk` |
|
| `apk_output` | | where to write the signed APK; required with `apk` |
|
||||||
| `pin` | | the HSM's user PIN (required) |
|
| `pin` | | the HSM's user PIN (required) |
|
||||||
| `key_id` | `03` | the key's CKA_ID, in hex |
|
| `key_id` | `03` | the key's CKA_ID, in hex |
|
||||||
| `slot_index` | `0` | PKCS#11 slot, by position in the slot list |
|
| `slot_index` | `0` | PKCS#11 slot, by position in the slot list |
|
||||||
| `key_alias` | | APK only: which key, when the token has more than one with a certificate |
|
| `key_alias` | | APK only: which key, when the token has more than one with a certificate |
|
||||||
| `public_key` | | URL of the PEM public key; set it to log detached signatures to sigstore |
|
| `apk_cert_sha256` | | APK only: the certificate the APK must be signed with; any other fails |
|
||||||
| `uhub_control` | `false` | power cycle the HSM on a smart USB hub (below) |
|
| `public_key` | | URL of the PEM public key; set it to log detached signatures to sigstore |
|
||||||
|
| `uhub_control` | `false` | power cycle the HSM on a smart USB hub (below) |
|
||||||
|
|
||||||
At least one of `files` and `apk` is required.
|
At least one of `files` and `apk` is required.
|
||||||
|
|
||||||
|
|
@ -122,9 +144,11 @@ host's Debian release (`BASE` in [signer/Dockerfile](signer/Dockerfile)).
|
||||||
|
|
||||||
[signer/test.sh](signer/test.sh) runs the signer end to end against SoftHSM,
|
[signer/test.sh](signer/test.sh) runs the signer end to end against SoftHSM,
|
||||||
with docker or podman: a token with a bare key, a detached signature checked
|
with docker or podman: a token with a bare key, a detached signature checked
|
||||||
with openssl against the token's public key and, given an APK, `make-cert`,
|
with openssl against the token's public key and, given an APK, `make-cert`
|
||||||
APK signing, `apksigner verify`, and a check that the APK's signing key is the
|
(including its refusal to replace a certificate, and a replacement), APK
|
||||||
token's:
|
signing pinned to the current certificate (and refused when pinned to the
|
||||||
|
replaced one), `apksigner verify`, and a check that the APK's signing key is
|
||||||
|
the token's:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
signer/test.sh # detached only
|
signer/test.sh # detached only
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,10 @@ inputs:
|
||||||
description: 'APK only: the key alias, when the token holds more than one key with a certificate'
|
description: 'APK only: the key alias, when the token holds more than one key with a certificate'
|
||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
|
apk_cert_sha256:
|
||||||
|
description: 'APK only: SHA-256 of the certificate the APK must be signed with (the app identity). Signing with any other certificate fails the step. Recommended once the certificate is chosen'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
public_key:
|
public_key:
|
||||||
description: 'URL to PEM format public key. Specify only if uploading detached signatures to sigstore'
|
description: 'URL to PEM format public key. Specify only if uploading detached signatures to sigstore'
|
||||||
required: false
|
required: false
|
||||||
|
|
|
||||||
|
|
@ -243,7 +243,7 @@ die() {
|
||||||
run_signer() {
|
run_signer() {
|
||||||
container="$(docker create \
|
container="$(docker create \
|
||||||
-v /run/pcscd/pcscd.comm:/run/pcscd/pcscd.comm:ro \
|
-v /run/pcscd/pcscd.comm:/run/pcscd/pcscd.comm:ro \
|
||||||
-e PKCS11_PIN -e PKCS11_SLOT_INDEX -e KEY_ID -e KEY_ALIAS \
|
-e PKCS11_PIN -e PKCS11_SLOT_INDEX -e KEY_ID -e KEY_ALIAS -e APK_CERT_SHA256 \
|
||||||
"${signer}" "$1" "/home/user/$4" "/home/user/$5")" ||
|
"${signer}" "$1" "/home/user/$4" "/home/user/$5")" ||
|
||||||
die "could not create the signer container"
|
die "could not create the signer container"
|
||||||
docker cp "$2" "${container}:/home/user/$4" || die "could not copy $2 into the signer"
|
docker cp "$2" "${container}:/home/user/$4" || die "could not copy $2 into the signer"
|
||||||
|
|
@ -297,6 +297,7 @@ export PKCS11_PIN="${INPUT_PIN:-}"
|
||||||
export PKCS11_SLOT_INDEX="${INPUT_SLOT_INDEX:-0}"
|
export PKCS11_SLOT_INDEX="${INPUT_SLOT_INDEX:-0}"
|
||||||
export KEY_ID="${INPUT_KEY_ID:-03}"
|
export KEY_ID="${INPUT_KEY_ID:-03}"
|
||||||
export KEY_ALIAS="${INPUT_KEY_ALIAS:-}"
|
export KEY_ALIAS="${INPUT_KEY_ALIAS:-}"
|
||||||
|
export APK_CERT_SHA256="${INPUT_APK_CERT_SHA256:-}"
|
||||||
|
|
||||||
if [ "${INPUT_UHUB_CONTROL:-false}" != "false" ]; then
|
if [ "${INPUT_UHUB_CONTROL:-false}" != "false" ]; then
|
||||||
uhub_on
|
uhub_on
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,10 @@
|
||||||
# hsm-sign detached IN OUT a detached signature of IN (RSA PKCS#1 v1.5 over
|
# hsm-sign detached IN OUT a detached signature of IN (RSA PKCS#1 v1.5 over
|
||||||
# SHA-256), written to OUT
|
# SHA-256), written to OUT
|
||||||
# hsm-sign apk IN.apk OUT.apk sign an APK (signature schemes v2 and v3), then verify
|
# hsm-sign apk IN.apk OUT.apk sign an APK (signature schemes v2 and v3), then verify
|
||||||
# hsm-sign make-cert SUBJECT one-time setup for apk: a self-signed certificate for
|
# hsm-sign make-cert SUBJECT setup for apk: a self-signed certificate for the key,
|
||||||
# the key, written to the token beside it, and printed
|
# signed on the token, written to it beside the key,
|
||||||
# as PEM
|
# read back, and printed as PEM. Refuses if the key
|
||||||
|
# already has a certificate, unless REPLACE_CERT=1
|
||||||
# hsm-sign list what Java sees on the token, for diagnosing an alias
|
# hsm-sign list what Java sees on the token, for diagnosing an alias
|
||||||
#
|
#
|
||||||
# Environment:
|
# Environment:
|
||||||
|
|
@ -16,12 +17,15 @@
|
||||||
# KEY_ID the key's CKA_ID in hex; default 03
|
# KEY_ID the key's CKA_ID in hex; default 03
|
||||||
# KEY_LABEL the label make-cert gives the certificate; default apk-cert
|
# KEY_LABEL the label make-cert gives the certificate; default apk-cert
|
||||||
# KEY_ALIAS which key apksigner uses, when the token holds more than one
|
# KEY_ALIAS which key apksigner uses, when the token holds more than one
|
||||||
|
# APK_CERT_SHA256 apk: the certificate the APK must be signed with (the SHA-256 of
|
||||||
|
# its DER, as make-cert and apk print it); any other is an error
|
||||||
|
# REPLACE_CERT make-cert: 1 to replace an existing certificate
|
||||||
#
|
#
|
||||||
# Why apk needs a certificate on the token: an APK signature embeds the signer's
|
# Why apk needs a certificate on the token: an APK signature embeds the signer's
|
||||||
# certificate, and Java's PKCS#11 key store only offers a private key that has a
|
# certificate, and Java's PKCS#11 key store only offers a private key that has a
|
||||||
# certificate with the same CKA_ID. A key used only for detached signatures has none
|
# certificate with the same CKA_ID. Whatever certificate is there is used, whether
|
||||||
# until make-cert writes one. Android pins an app to its signing certificate for good,
|
# make-cert wrote it or not. Android pins an app to its signing certificate for good,
|
||||||
# so make-cert runs once and its certificate is kept.
|
# so it is chosen once and kept (and pinned with APK_CERT_SHA256).
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
die() {
|
die() {
|
||||||
|
|
@ -33,11 +37,33 @@ die() {
|
||||||
module="${PKCS11_MODULE:-$(find /usr/lib -name opensc-pkcs11.so | head -n 1)}"
|
module="${PKCS11_MODULE:-$(find /usr/lib -name opensc-pkcs11.so | head -n 1)}"
|
||||||
[ -f "$module" ] || die "no PKCS#11 module at '$module'"
|
[ -f "$module" ] || die "no PKCS#11 module at '$module'"
|
||||||
slot_index="${PKCS11_SLOT_INDEX:-0}"
|
slot_index="${PKCS11_SLOT_INDEX:-0}"
|
||||||
key_id="${KEY_ID:-03}"
|
# Lower case and an even number of digits, as pkcs11-tool lists ids and as the pkcs11:
|
||||||
|
# URI below needs them.
|
||||||
|
key_id="$(printf '%s' "${KEY_ID:-03}" | tr 'A-F' 'a-f')"
|
||||||
|
[ $((${#key_id} % 2)) -eq 0 ] || key_id="0${key_id}"
|
||||||
work="$(mktemp -d)"
|
work="$(mktemp -d)"
|
||||||
trap 'rm -rf "$work"' EXIT
|
trap 'rm -rf "$work"' EXIT
|
||||||
chmod 700 "$work"
|
chmod 700 "$work"
|
||||||
|
|
||||||
|
# The certificate for the key, in DER, to $1; fails if there is none. Certificates are
|
||||||
|
# public objects: no login.
|
||||||
|
read_cert() {
|
||||||
|
pkcs11-tool --module "$module" --slot-index "$slot_index" \
|
||||||
|
--read-object --type cert --id "$key_id" --output-file "$1" >/dev/null 2>&1 && [ -s "$1" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
# How many certificates the token holds for the key.
|
||||||
|
cert_count() {
|
||||||
|
pkcs11-tool --module "$module" --slot-index "$slot_index" --list-objects --type cert 2>/dev/null |
|
||||||
|
grep -cE "^ *ID: *${key_id}\$" || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# A certificate (DER, $1) as a person and as apk_cert_sha256 want to see it.
|
||||||
|
describe_cert() {
|
||||||
|
openssl x509 -inform DER -in "$1" -noout -subject -startdate -enddate
|
||||||
|
echo "sha256 (apk_cert_sha256): $(sha256sum "$1" | cut -d' ' -f1)"
|
||||||
|
}
|
||||||
|
|
||||||
# SunPKCS11's configuration: which library, which slot.
|
# SunPKCS11's configuration: which library, which slot.
|
||||||
cat > "$work/pkcs11.cfg" <<EOF
|
cat > "$work/pkcs11.cfg" <<EOF
|
||||||
name = HSM
|
name = HSM
|
||||||
|
|
@ -84,12 +110,42 @@ case "${1:-}" in
|
||||||
$alias_args \
|
$alias_args \
|
||||||
--in "$1" --out "$2"
|
--in "$1" --out "$2"
|
||||||
# Proof, in the log, of what was signed and by whom.
|
# Proof, in the log, of what was signed and by whom.
|
||||||
apksigner verify --verbose --print-certs "$2"
|
verified="$(apksigner verify --verbose --print-certs "$2")"
|
||||||
|
echo "$verified"
|
||||||
|
signers="$(echo "$verified" | sed -n 's/^Number of signers: //p')"
|
||||||
|
[ "$signers" = 1 ] || die "expected 1 signer, apksigner reports '${signers}'"
|
||||||
|
got="$(echo "$verified" | sed -n 's/^Signer #1 certificate SHA-256 digest: //p')"
|
||||||
|
echo "hsm-sign: signed with the certificate whose sha256 (apk_cert_sha256) is ${got}"
|
||||||
|
# The certificate is the app's identity: with APK_CERT_SHA256 set, an APK signed
|
||||||
|
# with any other certificate (one replaced on the token, a second key) is an error,
|
||||||
|
# not an update every installed copy refuses.
|
||||||
|
if [ -n "${APK_CERT_SHA256:-}" ]; then
|
||||||
|
want="$(printf '%s' "${APK_CERT_SHA256}" | tr -d ': ' | tr 'A-F' 'a-f')"
|
||||||
|
if [ "$got" != "$want" ]; then
|
||||||
|
rm -f "$2"
|
||||||
|
die "the APK was signed with certificate ${got}, not the expected ${want} (APK_CERT_SHA256)"
|
||||||
|
fi
|
||||||
|
echo "hsm-sign: that is the expected certificate"
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
make-cert)
|
make-cert)
|
||||||
[ $# -eq 2 ] || die "usage: hsm-sign make-cert '/CN=...'"
|
[ $# -eq 2 ] || die "usage: hsm-sign make-cert '/CN=...'"
|
||||||
subject="$2"
|
subject="$2"
|
||||||
|
# The certificate is the app's identity on every device that installs an APK signed
|
||||||
|
# with it, so an existing one is not replaced by accident: it is shown, and kept,
|
||||||
|
# unless REPLACE_CERT=1.
|
||||||
|
if read_cert "$work/old.der"; then
|
||||||
|
echo "hsm-sign: key ${key_id} already has a certificate:" >&2
|
||||||
|
describe_cert "$work/old.der" >&2
|
||||||
|
openssl x509 -inform DER -in "$work/old.der" >&2
|
||||||
|
[ "${REPLACE_CERT:-}" = 1 ] ||
|
||||||
|
die "not replacing it. Set REPLACE_CERT=1 to replace it (keep the PEM above if anything uses it)"
|
||||||
|
echo "hsm-sign: REPLACE_CERT=1: replacing it" >&2
|
||||||
|
replacing=1
|
||||||
|
else
|
||||||
|
replacing=0
|
||||||
|
fi
|
||||||
# OpenSSL's PKCS#11 engine, pointed at the same module, signs the certificate with
|
# OpenSSL's PKCS#11 engine, pointed at the same module, signs the certificate with
|
||||||
# the HSM's key. The PIN is in a file only this process can read, not an argument.
|
# the HSM's key. The PIN is in a file only this process can read, not an argument.
|
||||||
cat > "$work/openssl.cnf" <<EOF
|
cat > "$work/openssl.cnf" <<EOF
|
||||||
|
|
@ -109,13 +165,30 @@ distinguished_name = dn
|
||||||
EOF
|
EOF
|
||||||
uri_id="$(printf '%s' "$key_id" | sed 's/../%&/g')"
|
uri_id="$(printf '%s' "$key_id" | sed 's/../%&/g')"
|
||||||
# Thirty years: the certificate's dates are not checked for APK signatures, and
|
# Thirty years: the certificate's dates are not checked for APK signatures, and
|
||||||
# replacing it would mean a new app identity on every device.
|
# replacing it would mean a new app identity on every device. Also long enough for
|
||||||
|
# Google Play, which wants validity past 2033-10-22.
|
||||||
OPENSSL_CONF="$work/openssl.cnf" openssl req -new -x509 -sha256 -days 10950 \
|
OPENSSL_CONF="$work/openssl.cnf" openssl req -new -x509 -sha256 -days 10950 \
|
||||||
-subj "$subject" -engine pkcs11 -keyform engine \
|
-subj "$subject" -engine pkcs11 -keyform engine \
|
||||||
-key "pkcs11:id=${uri_id};type=private" -out "$work/cert.pem"
|
-key "pkcs11:id=${uri_id};type=private" -out "$work/cert.pem"
|
||||||
openssl x509 -in "$work/cert.pem" -outform DER -out "$work/cert.der"
|
openssl x509 -in "$work/cert.pem" -outform DER -out "$work/cert.der"
|
||||||
|
if [ "$replacing" = 1 ]; then
|
||||||
|
# Removed first, so tokens that keep several objects with one id do not end up
|
||||||
|
# with two certificates. Cards with one certificate per key may refuse; the
|
||||||
|
# write below then replaces it, and the check after it says whether that worked.
|
||||||
|
pkcs11-tool --module "$module" --slot-index "$slot_index" --login --pin env:PKCS11_PIN \
|
||||||
|
--delete-object --type cert --id "$key_id" >&2 ||
|
||||||
|
echo "hsm-sign: could not delete the old certificate; writing over it" >&2
|
||||||
|
fi
|
||||||
pkcs11-tool --module "$module" --slot-index "$slot_index" --login --pin env:PKCS11_PIN \
|
pkcs11-tool --module "$module" --slot-index "$slot_index" --login --pin env:PKCS11_PIN \
|
||||||
--write-object "$work/cert.der" --type cert --id "$key_id" --label "${KEY_LABEL:-apk-cert}"
|
--write-object "$work/cert.der" --type cert --id "$key_id" --label "${KEY_LABEL:-apk-cert}" >&2
|
||||||
|
# Read back: exactly one certificate for the key, and it is the new one.
|
||||||
|
count="$(cert_count)"
|
||||||
|
[ "$count" = 1 ] || die "the token has ${count} certificates for key ${key_id} after writing, expected 1"
|
||||||
|
read_cert "$work/check.der" || die "could not read the certificate back"
|
||||||
|
cmp -s "$work/cert.der" "$work/check.der" ||
|
||||||
|
die "the certificate read back is not the one written"
|
||||||
|
echo "hsm-sign: written and read back:" >&2
|
||||||
|
describe_cert "$work/cert.der" >&2
|
||||||
cat "$work/cert.pem"
|
cat "$work/cert.pem"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,19 +65,48 @@ fi
|
||||||
openssl dgst -sha256 -verify "$HOME/pub.pem" -signature /out/artifact.sig artifact
|
openssl dgst -sha256 -verify "$HOME/pub.pem" -signature /out/artifact.sig artifact
|
||||||
|
|
||||||
[ -f in.apk ] || exit 0
|
[ -f in.apk ] || exit 0
|
||||||
|
certsha() { openssl x509 -in "$1" -outform DER | sha256sum | cut -d" " -f1; }
|
||||||
|
certs() { pkcs11-tool --module "$PKCS11_MODULE" --list-objects --type cert 2>/dev/null | grep -cE "^ *ID: *$KEY_ID\$" || true; }
|
||||||
|
|
||||||
echo "== make-cert"
|
echo "== make-cert"
|
||||||
hsm-sign make-cert "/CN=hsm-sign test signer" > /out/cert.pem
|
hsm-sign make-cert "/CN=hsm-sign test signer" > /out/cert.pem
|
||||||
openssl x509 -in /out/cert.pem -noout -subject -fingerprint -sha256
|
openssl x509 -in /out/cert.pem -noout -subject -fingerprint -sha256
|
||||||
echo "== apk"
|
first="$(certsha /out/cert.pem)"
|
||||||
hsm-sign apk in.apk /out/signed.apk
|
echo "== make-cert again, without REPLACE_CERT: must refuse"
|
||||||
|
if hsm-sign make-cert "/CN=another signer" > /out/refused.pem 2> /out/refused.err; then
|
||||||
|
echo "make-cert replaced a certificate without REPLACE_CERT=1"; exit 1
|
||||||
|
fi
|
||||||
|
grep -q "already has a certificate" /out/refused.err
|
||||||
|
[ "$(certs)" = 1 ]
|
||||||
|
echo "refused, and the token still has one certificate"
|
||||||
|
echo "== make-cert with REPLACE_CERT=1"
|
||||||
|
REPLACE_CERT=1 hsm-sign make-cert "/CN=hsm-sign test signer 2" > /out/cert2.pem
|
||||||
|
second="$(certsha /out/cert2.pem)"
|
||||||
|
[ "$first" != "$second" ]
|
||||||
|
[ "$(certs)" = 1 ]
|
||||||
|
echo "replaced, and the token has one certificate: $second"
|
||||||
|
|
||||||
|
echo "== apk, pinned to the current certificate"
|
||||||
|
APK_CERT_SHA256="$second" hsm-sign apk in.apk /out/signed.apk
|
||||||
|
echo "== apk, pinned to the replaced certificate: must fail"
|
||||||
|
if APK_CERT_SHA256="$first" hsm-sign apk in.apk /out/wrong.apk > /out/wrong.log 2>&1; then
|
||||||
|
echo "apk signed with a certificate other than the pinned one"; exit 1
|
||||||
|
fi
|
||||||
|
grep -q "not the expected" /out/wrong.log
|
||||||
|
[ ! -e /out/wrong.apk ]
|
||||||
|
echo "refused, and no APK left behind"
|
||||||
echo "== independent verify"
|
echo "== independent verify"
|
||||||
apksigner verify --verbose /out/signed.apk
|
apksigner verify --verbose /out/signed.apk
|
||||||
# The APK is signed by the key, not merely by a certificate naming it: the public key
|
# The APK is signed by the key, not merely by a certificate naming it: the public key
|
||||||
# apksigner reports is the token'"'"'s.
|
# apksigner reports is the token'"'"'s, and the certificate is the current one.
|
||||||
got="$(apksigner verify --verbose --print-certs /out/signed.apk | sed -n "s/^Signer #1 public key SHA-256 digest: //p")"
|
printed="$(apksigner verify --verbose --print-certs /out/signed.apk)"
|
||||||
|
got="$(echo "$printed" | sed -n "s/^Signer #1 public key SHA-256 digest: //p")"
|
||||||
|
gotcert="$(echo "$printed" | sed -n "s/^Signer #1 certificate SHA-256 digest: //p")"
|
||||||
echo "token public key sha256: $want"
|
echo "token public key sha256: $want"
|
||||||
echo "APK signer key sha256: $got"
|
echo "APK signer key sha256: $got"
|
||||||
|
echo "APK certificate sha256: $gotcert"
|
||||||
[ "$want" = "$got" ]
|
[ "$want" = "$got" ]
|
||||||
|
[ "$gotcert" = "$second" ]
|
||||||
'
|
'
|
||||||
echo "test.sh: detached signature verified"
|
echo "test.sh: detached signature verified"
|
||||||
if [ -n "$apk_in" ]; then
|
if [ -n "$apk_in" ]; then
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue