dotnet/aspnetcore · critical · Exception

Signature verification failed: {result.stderr.decode('utf-8'

Error message

Signature verification failed: {result.stderr.decode('utf-8')}

What it means

fetch_release_file downloads Release and Release.gpg, then runs gpgv to verify the detached signature against the (optionally) specified keyring. If gpgv returns non-zero, a plain Exception is raised containing the decoded stderr. This is the GPG signature gate that anchors the whole checksum chain when --force-check-gpg is enabled.

Source

Thrown at eng/common/cross/install-debs.py:135

    release_gpg_url = f"{mirror}/dists/{suite}/Release.gpg"

    with tempfile.NamedTemporaryFile() as release_file, tempfile.NamedTemporaryFile() as release_gpg_file:
        await download_file(session, release_url, release_file.name)
        await download_file(session, release_gpg_url, release_gpg_file.name)

        print("Verifying signature of Release with Release.gpg.")
        # Use gpgv rather than gpg for verification. gpgv verifies a detached
        # signature against a fixed keyring without involving gpg-agent or
        # keyboxd, which makes it robust on hosts running GnuPG 2.4+ (e.g. Azure
        # Linux) where "gpg --keyring" routes through keyboxd and can fail.
        verify_command = ["gpgv"]
        if keyring:
            verify_command += ["--keyring", keyring]
        verify_command += [release_gpg_file.name, release_file.name]
        result = subprocess.run(verify_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

        if result.returncode != 0:
            raise Exception(f"Signature verification failed: {result.stderr.decode('utf-8')}")

        print("Signature verified successfully.")

        with open(release_file.name) as f:
            return f.read()

def parse_release_file(content, path):
    """Parses the Release file and returns sha256 checksum of the specified path."""

    # data looks like this:
    # <checksum>  <size>  <path>
    matches = re.findall(r'^ (\S*) +(\S*) +(\S*)$', content, re.MULTILINE)

    for entry in matches:
        # the file has both md5 and sha256 checksums, we want sha256 which has a length of 64
        if entry[2] == path and len(entry[0]) == 64:
            return entry[0]

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Install the correct keyring package for your suite: debian-ports-archive-keyring (Debian ports), debian-archive-keyring (Debian mainstream), or ubuntu-keyring (Ubuntu).
  2. Pass the correct --keyring path (e.g., /usr/share/keyrings/debian-archive-keyring.gpg).
  3. Ensure gpgv is installed and on PATH (apt-get install gnupg).
  4. If you intentionally skip trust, drop --force-check-gpg / pass --skipsigcheck to build-rootfs.sh — but understand you lose integrity verification.
  5. Update the keyring package if the signing key has rotated since your container image was built.

Example fix

# before
python3 install-debs.py --force-check-gpg --keyring /tmp/wrong.gpg \
  --suite sid --mirror http://deb.debian.org/debian ...
# Signature verification failed: Can't check signature: public key not found

# after
sudo apt-get install -y debian-archive-keyring
python3 install-debs.py --force-check-gpg \
  --keyring /usr/share/keyrings/debian-archive-keyring.gpg \
  --suite bookworm --mirror http://deb.debian.org/debian ...
Defensive patterns

Strategy: validation

Validate before calling

# Verify gpgv and the keyring are usable before the build
import shutil, subprocess
if not shutil.which('gpgv'):
    raise SystemExit('gpgv not installed: apt-get install gnupg')
probe = subprocess.run(['gpgv', '--keyring', keyring, '/dev/null', '/dev/null'],
                       capture_output=True)
if probe.returncode not in (0, 2):
    raise SystemExit(f'Keyring {keyring} unusable: {probe.stderr.decode()}')

Try / catch

try:
    release_content = await fetch_release_file(session, mirror, suite, keyring)
except Exception as e:
    if 'Signature verification failed' in str(e):
        if 'No public key' in str(e) or 'public key not found' in str(e):
            raise SystemExit('Install the suite\'s keyring package (e.g., debian-archive-keyring) and re-run')
        raise

Prevention

When it happens

Trigger: Invoked from fetch_and_decompress only when check_sig is True. gpgv fails because: the key for the suite is not in the keyring, the Release file was tampered, the Release.gpg is missing/corrupt, an expired signing key, or gpgv itself is not installed/misconfigured. The non-zero returncode triggers the exception.

Common situations: Wrong or empty --keyring file for the suite (Debian vs Ubuntu vs ports use different keyrings); keyring package not installed (debian-ports-archive-keyring, ubuntu-keyring); the signing key has been rotated/expired since the keyring was last updated; building in a minimal container without gpgv.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/86b3ba2daf045fd9. Report an issue: GitHub.