Skip to content

Commit

Permalink
Do not test for installer signing
Browse files Browse the repository at this point in the history
  • Loading branch information
marcoesters committed Sep 6, 2024
1 parent 5f5b2e4 commit 261fbe2
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 133 deletions.
94 changes: 36 additions & 58 deletions scripts/create_self_signed_certificates_macos.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash

set +e
set -e

if [[ -z "${ROOT_DIR}" ]]; then
ROOT_DIR=$(mktemp -d)
Expand All @@ -15,67 +15,45 @@ if [[ "${openssl_lib}" == "OpenSSL" ]] && [[ "${openssl_version}" == 3.* ]]; the
legacy=-legacy
fi

APPLICATION_ROOT="application"
APPLICATION_SIGNING_ID=${APPLICATION_SIGNING_ID:-${APPLICATION_ROOT}}
INSTALLER_ROOT="installer"
INSTALLER_SIGNING_ID=${INSTALLER_SIGNING_ID:-${INSTALLER_ROOT}}

KEYCHAIN_PATH="${KEYCHAIN_PATH:-"${ROOT_DIR}/constructor.keychain"}"
if [[ -n "${ON_CI}" ]]; then
CERT_KEYCHAIN="/Library/Keychains/System.keychain"
else
CERT_KEYCHAIN=${KEYCHAIN_PATH}
fi

security create-keychain -p "${KEYCHAIN_PASSWORD}" "${KEYCHAIN_PATH}"
security set-keychain-settings -lut 3600 "${KEYCHAIN_PATH}"
security unlock-keychain -p "${KEYCHAIN_PASSWORD}" "${KEYCHAIN_PATH}"

for context in ${APPLICATION_ROOT} ${INSTALLER_ROOT}; do
if [[ "${context}" == "${APPLICATION_ROOT}" ]]; then
keyusage="codeSigning"
certtype="1.2.840.113635.100.6.1.13"
commonname="${APPLICATION_SIGNING_ID}"
password="${APPLICATION_SIGNING_PASSWORD}"
else
keyusage="1.2.840.113635.100.4.13"
certtype="1.2.840.113635.100.6.1.14"
commonname="${INSTALLER_SIGNING_ID}"
password="${INSTALLER_SIGNING_PASSWORD}"
fi

keyfile="${ROOT_DIR}/${context}.key"
p12file="${ROOT_DIR}/${context}.p12"
crtfile="${ROOT_DIR}/${context}.crt"
pemfile="${ROOT_DIR}/${INSTALLER_ROOT}.pem"

openssl genrsa -out "${keyfile}" 2048
openssl req -x509 -new -key "${keyfile}"\
-out "${crtfile}"\
-sha256\
-days 1\
-subj "/C=XX/ST=State/L=City/O=Company/OU=Org/CN=${commonname}/[email protected]"\
-addext "basicConstraints=critical,CA:FALSE"\
-addext "extendedKeyUsage=critical,${keyusage}"\
-addext "keyUsage=critical,digitalSignature"\
-addext "${certtype}=critical,DER:0500"

# shellcheck disable=SC2086
openssl pkcs12 -export\
-out "${p12file}"\
-inkey "${keyfile}"\
-in "${crtfile}"\
-passout pass:"${password}"\
${legacy}

security import "${p12file}" -P "${password}" -t cert -f pkcs12 -k "${KEYCHAIN_PATH}" -A
# shellcheck disable=SC2086
openssl pkcs12 -in "${p12file}" -clcerts -nokeys -out "${pemfile}" ${legacy} -password pass:"${password}"

# Output to verify installer signatures
fingerprint=$(openssl x509 -in "${pemfile}" -noout -fingerprint -sha256 | cut -f2 -d'=' | sed 's/://g')
echo "SHA256 ${commonname} = ${fingerprint}"
if [[ "${context}" == "installer" ]]; then
security add-trusted-cert -d -p basic -k "${CERT_KEYCHAIN}" "${pemfile}"
fi
done
# Originally, this code contained code for creating certificates for installer signing:
# https://github.com/conda/constructor/blob/555eccb19ab4c3ed8cf5384bf66348b6d9613fd1/scripts/create_self_signed_certificates_macos.sh
# However, installer certificates must be trusted. Adding a trusted certificate to any
# keychain requires authentication, which is interactive and causes the run to hang.
APPLICATION_ROOT="application"
keyusage="codeSigning"
certtype="1.2.840.113635.100.6.1.13"
commonname="${APPLICATION_SIGNING_ID}"
password="${APPLICATION_SIGNING_PASSWORD}"
keyfile="${ROOT_DIR}/application.key"
p12file="${ROOT_DIR}/application.p12"
crtfile="${ROOT_DIR}/application.crt"

openssl genrsa -out "${keyfile}" 2048
openssl req -x509 -new -key "${keyfile}"\
-out "${crtfile}"\
-sha256\
-days 1\
-subj "/C=XX/ST=State/L=City/O=Company/OU=Org/CN=${commonname}/[email protected]"\
-addext "basicConstraints=critical,CA:FALSE"\
-addext "extendedKeyUsage=critical,${keyusage}"\
-addext "keyUsage=critical,digitalSignature"\
-addext "${certtype}=critical,DER:0500"

# shellcheck disable=SC2086
openssl pkcs12 -export\
-out "${p12file}"\
-inkey "${keyfile}"\
-in "${crtfile}"\
-passout pass:"${password}"\
${legacy}

security import "${p12file}" -P "${password}" -t cert -f pkcs12 -k "${KEYCHAIN_PATH}" -A
# shellcheck disable=SC2046
security list-keychains -d user -s "${KEYCHAIN_PATH}" $(security list-keychains -d user | xargs)
53 changes: 5 additions & 48 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,80 +1,37 @@
import os
import subprocess
from pathlib import Path

import pytest

REPO_DIR = Path(__file__).parent.parent
ON_CI = os.environ.get("CI")


@pytest.fixture
def self_signed_certificate_macos(tmp_path):
def self_signed_application_certificate_macos(tmp_path):
p = subprocess.run(
["security", "default-keychain"],
["security", "list-keychains", "-d", "user"],
capture_output=True,
text=True,
check=True,
)
default_keychain = p.stdout.strip(' "\n')
current_keychains = [keychain.strip(' "') for keychain in p.stdout.split("\n") if keychain]
cert_root = tmp_path / "certs"
cert_root.mkdir(parents=True, exist_ok=True)
signing_identity = "testinstaller"
signing_identity_password = "1234"
notarization_identity = "testapplication"
notarization_identity_password = "5678"
# Installer certificates must be trusted to be found in the keychain.
# Users will be asked for authentication.
# On GitHub runners, the system keychain does not require authentication,
# which is why it is unsed on the CI.
keychain_path = str(cert_root / "constructor.keychain")
keychain_password = "abcd"
env = {
"APPLICATION_SIGNING_ID": notarization_identity,
"APPLICATION_SIGNING_PASSWORD": notarization_identity_password,
"INSTALLER_SIGNING_ID": signing_identity,
"INSTALLER_SIGNING_PASSWORD": signing_identity_password,
"KEYCHAIN_PASSWORD": keychain_password,
"ROOT_DIR": str(cert_root),
}
if ON_CI:
env["ON_CI"] = "1"
p = subprocess.run(
["bash", REPO_DIR / "scripts" / "create_self_signed_certificates_macos.sh"],
env=env,
capture_output=True,
text=True,
check=True,
)
cert_data = {
"signing_identity": {
"name": signing_identity,
"sha256": "",
},
"notarization_identity": {
"name": notarization_identity,
"sha256": "",
},
}
for line in p.stdout.split("\n"):
if not line.startswith("SHA256"):
continue
identifier, sha256 = line.rsplit("=", 1)
if signing_identity in identifier:
cert_data["signing_identity"]["sha256"] = sha256.strip()
elif notarization_identity in identifier:
cert_data["notarization_identity"]["sha256"] = sha256.strip()
subprocess.run(
["security", "default-keychain", "-s", keychain_path],
capture_output=True,
text=True,
check=True,
)
yield cert_data
yield notarization_identity
# Clean up
subprocess.run(
["security", "default-keychain", "-s", default_keychain],
capture_output=True,
text=True,
check=True,
)
subprocess.run(["security", "list-keychains", "-d", "user", "-s", *current_keychains])
30 changes: 3 additions & 27 deletions tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,44 +509,20 @@ def test_example_osxpkg_extra_pages(tmp_path):

@pytest.mark.skipif(sys.platform != "darwin", reason="macOS only")
@pytest.mark.skipif(not shutil.which("xcodebuild"), reason="requires xcodebuild")
@pytest.mark.skipif("TEST_MACOS_SIGNING" not in os.environ, reason="TEST_MACOS_SIGNING not set")
def test_macos_signing(tmp_path, self_signed_certificate_macos):
@pytest.mark.skipif("CI" not in os.environ, reason="CI only")
def test_macos_signing(tmp_path, self_signed_application_certificate_macos):
try:
subprocess.run(["xcodebuild", "--help"], check=True, capture_output=True)
except subprocess.CalledProcessError:
pytest.skip("xcodebuild requires XCode to compile extra pages.")
notarization_identity = self_signed_certificate_macos["notarization_identity"]
signing_identity = self_signed_certificate_macos["signing_identity"]
input_path = tmp_path / "input"
recipe_path = _example_path("osxpkg_extra_pages")
shutil.copytree(str(recipe_path), str(input_path))
with open(input_path / "construct.yaml", "a") as f:
f.write(f"notarization_identity_name: {notarization_identity['name']}\n")
f.write(f"signing_identity_name: {signing_identity['name']}\n")
f.write(f"notarization_identity_name: {self_signed_application_certificate_macos}\n")
output_path = tmp_path / "output"
installer, install_dir = next(create_installer(input_path, output_path))

# Check installer signature
p = subprocess.run(
["pkgutil", "--check-signature", installer],
check=True,
capture_output=True,
text=True,
)
installer_sha256 = ""
lines = p.stdout.split("\n")
nlines = len(lines)
assert nlines > 4
for i in range(nlines - 4):
line = lines[i].strip()
if signing_identity["name"] in line and "SHA256" in lines[i + 2]:
i += 3
while i < nlines and line:
installer_sha256 += lines[i].replace(" ", "")
i += 1
break
assert installer_sha256 == signing_identity["sha256"]

# Check component signatures
expanded_path = output_path / "expanded"
# expand-full is an undocumented option that extracts all archives,
Expand Down

0 comments on commit 261fbe2

Please sign in to comment.