Code signing

All RADKit packages and installers available externally are cryptographically signed by Cisco.

Danger

You should NEVER install a RADKit component whose code signature you have not been able to find and verify. See below for information pertaining to the different platforms and types of installation we support.

Windows installer

The Windows EXE installer contains an embedded software signature. The verification is performed by Windows at installation time. No additional Windows configuration is necessary; in particular, it is not necessary to install additional trusted certificates for this verification to work.

To inspect the signature manually, right-click the installer EXE, select Properties, then Digital Signatures, then Details.

macOS installer

The macOS PKG installer and its payload both contain embedded software signatures, as well as a stapled notarization ticket from Apple. The verification is performed by macOS, both at installation time and at run time. No additional macOS configuration is necessary; in particular, it is not necessary to install additional trusted certificates for this verification to work.

To verify the signature manually and display the signing identity, run the following command on the PKG installer in a Terminal:

spctl --assess --ignore-cache --no-cache -vv --type install PKG_FILE

where PKG_FILE should be replaced with the path to the RADKit PKG installer, for example:

% spctl --assess --ignore-cache --no-cache -vv --type install cisco_radkit_1.6.11_macos_x86_64_signed.pkg
cisco_radkit_1.6.11_macos_x86_64_signed.pkg: accepted
source=Notarized Developer ID
origin=Developer ID Installer: Cisco Systems, Inc (9X38D433RE)

Linux installer

Starting with RADKit 1.7.0, each RADKit release includes a single .sig256 file that contains signed hashes for all distribution files in that release, including the Linux installer.

In RADKit 1.6.x releases, the Linux installer used to ship with its own specific .sig signature file (bearing the same file name + a .sig extension).

Both the .sig and .sig256 signature types must be verified before installation (see below for details).

Python wheels

Starting with RADKit 1.7.0, each RADKit release includes a single .sig256 file that contains signed hashes for all distribution files in that release, including the wheel tarballs as well as each individual wheel file (i.e. it is possible to verify individual .whl file signatures even after unpacking the tarballs or downloading those from a PyPI index).

In RADKit 1.6.x releases, the wheel tarballs used to ship with their own specific .sig signature files (bearing the same file name + a .sig extension).

Both the .sig and .sig256 signature types must be verified before installation (see below for details).

Verifying .sig256 signatures

Warning

This only applies to RADKit 1.7.0 and later. For older 1.6.x releases, please see Verifying .sig signatures (obsolete).

The RADKit .sig256 signature file is a CMS/PKCS#7 bundle containing a cryptographic signature, the signing and subordinate authority certificates needed for signature verification, and a data payload that contains the SHA-256 hashes of all the files in a given RADKit distribution.

When verifying the CMS signature, the root certificate must be provided separately, to ensure that the trust anchor comes from a different source than the signature files and the verification script.

Step 1: Get the Cisco code signing root CA certificate from Cisco

  • Browse to https://www.cisco.com/security/pki/

  • Check that the HTTPS session is secure and that you are on www.cisco.com (this is important)

  • Click the link: Cisco Root CA M2 (crcam2) - PEM (make sure you select PEM, not CER)

  • Copy/Save the entire contents to a local file named crcam2.pem

  • Place the file in the same directory as the files to be verified

This operation can be performed once and the same file can be reused for subsequent verifications.

Step 2: automated verification using radkit_verify_sig256.py

Use the simple radkit_verify_sig256.py Python 3 script that is provided with RADKit downloads. Its contents are also copied below. Make sure to read and understand the script before you run it. It will simply unpack and verify the list of signed hashes in the .sig256 file, then iterate over each file in argument and check whether it has the correct hash on disk. The path to the .sig256 file for the RADKit release must be passed as the --sig argument, and the path to the crcam2.pem file may optionally be passed as the --root argument (otherwise it is loaded from the current directory).

$ ./radkit_verify_sig256.py --sig cisco_radkit_1.7.0.sig256 cisco_radkit_1.7.0b6_pip_linux_x86.tgz
Verifying: cisco_radkit_1.7.0.sig256
Parsing checksums
5956f5e39ed8420f407eeb2f893821162bf74246b64af1ff10dc32c1c8c25c90  cisco_radkit_1.7.0b6_doc_html.tgz
19bd7a5a84c7864e21f3afdab775a4aa3add6919d602c14a785d47022cdab943  cisco_radkit_1.7.0b6_linux_x86_64.sh
e47764fb398036d4f29356fc7b9c71483855f8cc2b304a90e1abda851bb486f1  cisco_radkit_1.7.0b6_pip_linux_arm.tgz
f91563cfc0c95ed4f8b11991c772c3139f834bca07c9982971509a373a895686  cisco_radkit_1.7.0b6_pip_linux_x86.tgz
f0da6af26617368cbf5d7678fd4d3ec80ab82a3bcfb873b6440b224823983bf2  cisco_radkit_1.7.0b6_pip_macos.tgz
f1def4b2a826dc7ba55c3aa777d313c31a7c243c7185aab58f46189f5c48721d  cisco_radkit_1.7.0b6_pip_win.tgz
[...]

Verifying: cisco_radkit_1.7.0b6_pip_linux_x86.tgz
Base name: cisco_radkit_1.7.0b6_pip_linux_x86.tgz
Expected : f91563cfc0c95ed4f8b11991c772c3139f834bca07c9982971509a373a895686
Computed : f91563cfc0c95ed4f8b11991c772c3139f834bca07c9982971509a373a895686

SUCCESS: All files verified successfully

If a verification fails, the script will exit immediately with an ERROR: message. If all verifications are successful, it will print a SUCCESS: message. Please go through the entire output and double-check that there is a “Verification successful” message displayed and that the checksums actually match for each of the files that you intended to verify.

Warning

The radkit_verify_sig256.py can actually verify its own hash from the .sig256 file. Please do make sure however, that you understand the script and that it shows no sign of tampering, as self-verifying scripts cannot be fully trusted by definition.

Source code for radkit_verify_sig256.py

#!/usr/bin/env python3
# This file is part of RADKit / Lazy Maestro <radkit@cisco.com>
# Copyright (c) 2018-2025 by Cisco Systems, Inc.
# All rights reserved.

# This script verifies RADKit *.sig256 cryptographic signatures.
# It requires OpenSSL command-line tools and a Python version supported by RADKit.
# Make sure that the `openssl` CLI tools in your PATH come from a trusted source.
#
# The signature file is a CMS/PKCS#7 bundle containing the SHA-256 hashes of all files
# that are part of a given RADKit release. It contains a signature, a data payload and
# the signing and subordinate authority certs. The root cert must be provided separately.
#
# Step 1: get the Cisco code signing root CA certificate from cisco.com
# - Go to https://www.cisco.com/security/pki/certs/crcam2.pem
# - Check in your browser that the HTTPS session is secure (this is important)
# - Copy/Save the entire contents to a file named `crcam2.pem`
# - Place the file in the same directory as this script
#
# Step 2: run the script for one or more RADKit downloads
# - Each RADKit release has an accompanying `.sig256` file that can be downloaded with it
# - Place this script, the .sig256 file, and the RADKit release files you want to verify
#   together in the same directory (this also works for individual wheels files obtained
#   using "pip download" from a PyPI index)
# - Run this script with "--sig" pointing to the .sig256 file, and each file to verify as argument:
#   python3 radkit_verify_sig256.py --sig <sig256-file> <file1> <file2> ...
#
# Example:
#   python3 radkit_verify_sig256.py --sig cisco_radkit_1.7.0.sig256 \
#           cisco_radkit_1.7.0_pip_linux.tgz cisco_radkit_1.7.0_pip_macos.tgz
#
# If a verification fails, the script will exit immediately with an "ERROR" message.
# If all verifications are successful, it will print a "SUCCESS" message.
# Please go through the entire output and double-check that there is a "Verification successful"
# message displayed and that the checksums actually match for EACH of the files that you
# intended to verify.

# ruff: noqa
# fmt: off

from argparse import ArgumentParser
from pathlib import Path
from re import search
from subprocess import CalledProcessError, run

ROOT = "crcam2.pem"
DGST_REGEX = r"^(SHA2-256|SHA256)\((\S+)\)= ([0-9a-f]+)$"

def must_exist(file: Path) -> None:
    if not file.exists():
        raise RuntimeError(f"ERROR: File not found: {file}")

if __name__ == "__main__":

    parser = ArgumentParser(
        prog="radkit_verify.py",
        description="Verify RADKit cryptographic signatures",
    )
    parser.add_argument("--root", type=Path, default=Path(ROOT), help="root certificate (crcam2.pem)")
    parser.add_argument("--sig", type=Path, help="release signature file (*.sig256)", required=True)
    parser.add_argument("files", nargs="+", type=Path, help="files to verify")
    options = parser.parse_args()

    print(f"Verifying: {options.sig}")
    must_exist(options.root)
    must_exist(options.sig)
    try:
        cms_result = run(
            args=["openssl", "cms", "-verify", "-binary", "-inform", "der",
                  "-CAfile", options.root, "-in", options.sig ],
            capture_output=True,
            check=True,
        )
    except CalledProcessError:
        raise RuntimeError(f"ERROR: Signature check failed: {options.sig}") from None
    
    print("Parsing checksums")
    checksums = {}
    for line in cms_result.stdout.decode().splitlines():
        match = search(DGST_REGEX, line)
        if match is None:
            raise RuntimeError(f"ERROR: Malformed checksum entry: {line}")
        _, name, checksum = match.groups()
        checksums[name] = checksum
        print(f"  {checksum}  {name}")

    print()
    for file in options.files:
        print(f"Verifying: {file}\nBase name: {file.name}")
        must_exist(file)
        if file.name not in checksums:
            raise RuntimeError(f"ERROR: Unknown file (no known checksum): {file}")
        expected = checksums[file.name]
        print(f"Expected : {expected}")
        try:
            dgst_result = run(
                args=["openssl", "dgst", "-sha256", file],
                capture_output=True,
                check=True,
            )
        except CalledProcessError:
            raise RuntimeError(f"ERROR: Checksum computation failed: {file}") from None
        
        match = search(DGST_REGEX, dgst_result.stdout.decode().strip())
        computed = match.group(3)
        print(f"Computed : {computed}")
        if expected != computed:
            raise RuntimeError(f"ERROR: Checksum is incorrect: {file}")
        print()

    print("SUCCESS: All files verified successfully")

Verifying .sig signatures (obsolete)

Warning

This only applies to RADKit 1.6.x releases. For newer releases, please see Verifying .sig256 signatures.

Each RADKit .sig signature file is a CMS/PKCS#7 bundle containing a cryptographic signature, an empty data payload, plus the signing and subordinate authority certificates needed for verification. The root certificate must be provided separately, to ensure that the trust anchor comes from a different source than the signature files and the verification script.

Step 1: Get the Cisco code signing root CA certificate from Cisco

  • Browse to https://www.cisco.com/security/pki/

  • Check that the HTTPS session is secure and that you are on www.cisco.com (this is important)

  • Click the link: Cisco Root CA M2 (crcam2) - PEM (make sure you select PEM, not CER)

  • Copy/Save the entire contents to a local file named crcam2.pem

  • Place the file in the same directory as the files to be verified

This operation can be performed once and the same file can be reused for subsequent verifications.

Step 2, Option 1: automated verification using radkit_verify.py

Use the simple radkit_verify.py Python 3 script that is provided with RADKit downloads. Make sure to read and understand the script before you run it. It will simply iterate over each file in argument and run the command described in Option 2 below. For example:

$ python3 radkit_verify.py cisco_radkit_1.6.0_pip_linux.tgz cisco_radkit_1.6.0_pip_macos.tgz
Verifying: cisco_radkit_1.6.0_pip_linux.tgz
Signature: cisco_radkit_1.6.0_pip_linux.tgz.sig
CMS Verification successful

Verifying: cisco_radkit_1.6.0_pip_macos.tgz
Signature: cisco_radkit_1.6.0_pip_macos.tgz.sig
CMS Verification successful

SUCCESS: All files verified successfully

Note

The script expects crcam2.pem from Step 1 to be located in the current working directory.

If a verification fails, the script will exit immediately with an ERROR: message. If all verifications are successful, it will print a SUCCESS: message.

Please go through the entire output and double-check that there is a “Verification successful” message displayed for each of the files that you intended to verify.

Step 2, Option 2: manual verification using OpenSSL

Alternatively, you can use the openssl cms command to verify each signature in turn. This method does not require Python. The syntax is:

openssl cms -binary -verify -noout -inform der -CAfile crcam2.pem -in FILE.sig -content FILE >/dev/null

where FILE and FILE.sig should be replaced with the downloaded file name and its accompanying signature file. The command will return a zero status and print Verification successful on success.

Warning

You must redirect the standard output of the openssl cms command to /dev/null on Linux/macOS, or to NUL on Windows. Errors are still printed on standard error.

Source code for radkit_verify.py

#!/usr/bin/env python3
# This file is part of RADKit / Lazy Maestro <radkit@cisco.com>
# Copyright (c) 2018-2025 by Cisco Systems, Inc.
# All rights reserved.

# This script verifies RADKit *.sig cryptographic signatures.
# It requires OpenSSL command-line tools and a Python version supported by RADKit.
# Make sure that the `openssl` CLI tools in your PATH come from a trusted source.
#
# The signature file is a CMS/PKCS#7 bundle containing the signature, an empty data payload,
# plus the signing and subordinate authority certs. The root cert must be provided separately.
#
# Step 1: get the Cisco code signing root CA certificate from cisco.com
# - Go to https://www.cisco.com/security/pki/certs/crcam2.pem
# - Check in your browser that the HTTPS session is secure (this is important)
# - Copy/Save the entire contents to a file named `crcam2.pem`
# - Place the file in the same directory as this script and the files to be verified
#
# Step 2: run the script for one or more signed RADKit downloads
# - Each signed file has an accompanying `.sig` file that must be downloaded with it
# - Place this script, `<file>`, `<file>.sig` and `crcam2.pem` in the same directory
#   (`<file>` is a placeholder for the name of the actual file you are about to verify)
# - Run this script with each file to verify as argument, without the `.sig` extension:
#   python3 radkit_verify.py <file1> [<file2> ...]
#
# Example:
#   python3 radkit_verify.py cisco_radkit_1.6.0_pip_linux.tgz cisco_radkit_1.6.0_pip_macos.tgz
#
# If a verification fails, the script will exit immediately with an "ERROR" message.
# If all verifications are successful, it will print a "SUCCESS" message.
# Please go through the entire output and double-check that there is a "Verification successful"
# message displayed for EACH of the files you intended to verify.

# ruff: noqa
# fmt: off

from argparse import ArgumentParser
from pathlib import Path
from subprocess import DEVNULL, CalledProcessError, check_call

ROOT = "crcam2.pem"

def must_exist(file: Path) -> None:
    if not file.exists():
        raise RuntimeError(f"ERROR: File not found: {file}")

if __name__ == "__main__":

    parser = ArgumentParser(
        prog="radkit_verify.py",
        description="Verify RADKit *.sig cryptographic signatures",
    )
    parser.add_argument("files", nargs="+", help="Paths to files to verify", type=Path)
    options = parser.parse_args()

    must_exist(Path(ROOT))

    for file in options.files:
        sig = file.with_suffix(file.suffix + ".sig")
        print(f"Verifying: {file}\nSignature: {sig}")
        must_exist(file)
        must_exist(sig)
        try:
            check_call(
                args=["openssl", "cms", "-verify", "-binary", "-inform", "der",
                      "-CAfile", ROOT, "-in", sig, "-content", file],
                stdout=DEVNULL,
            )
        except CalledProcessError:
            raise RuntimeError(f"ERROR: Signature check failed: {file}") from None
        print()

    print("SUCCESS: All files verified successfully")