All articles

What is code signing?

The Chainguard Team
DevSecOps
Key Takeaways
  • Code signing acts like a digital wax seal, ensuring software artifacts haven’t been tampered with as they move through the supply chain.

  • It verifies authenticity byte-for-byte, protecting against malicious injections, accidental changes, and ensuring artifact integrity.

  • Core elements include public/private keys, digital certificates, and hashes that enable unique, verifiable signatures.

  • Chainguard secures its zero-CVE images with Sigstore-based code signing, delivering full provenance and supply chain integrity.

In the old days, people needed to send each other messages or secrets via letters. They wanted to know that no one else had read them or tampered with the letter between point A and point B. To ensure secrecy, they imprinted a custom wax seal on each letter, unique to the sender. When the other person received the letter, they could look at the seal and ensure it hadn’t been tampered with or broken, confident that the letter was pristine and the actual message that the sender wanted them to read. 

Today, code often passes through many different pipelines, build environments, and infrastructure during the development and deployment process. For example, a Docker container image might originate on the local PC that a developer uses to write and build code. The developer might then build an image in their cloud instance and then  upload the image to a platform like Docker Hub. From there, the image would move again when users download and run it within their own environments.

Given that container images and other code artifacts change hands frequently, how can users ensure that the code they run hasn’t been manipulated somewhere along that supply chain?

The answer is code signing. By creating an immutable digital signature, engineers can verify the provenance of artifacts and establish a hardened, secure software supply chain.

What is code signing?

Code signing is the process of generating a digital signature for a software artifact — meaning a container image, source code, library, SBOM, or any other file generated during the software development process. When someone wants to verify that the software they’re using is an exact match of software produced or hosted elsewhere, they can check whether the code signature on the original code matches the signature on their downloaded or deployed version.

We’ll explain more about how this code signature is generated in a future section, but what’s important to know is that a digital signature takes into account every byte of a software artifact. So even the tiniest change in an image or file will be reflected in the generated signature. This is how you can trust that no part of the code has been tampered with. Much like the wax seal from our example above, code signing is a process that ensures code hasn’t meaningfully changed between point A and point B.

Benefits of code signing

The main reason to use code signing is that it allows you to verify that the version of software a user obtains is a byte-for-byte match of the artifact originally distributed by the software publisher or maintainers.

This is important because, again, code can change hands frequently during the development and distribution process. At each stage of the software development lifecycle, there is new risk that can be introduced into your environment. A few common problems include: 

  • Malicious Code Injections: Threat actors who have access to any environment the code passes through could insert malware into it. The result is that users who download the code will receive a tampered, malicious version – even though it appears to come from a legitimate source. But since signing takes into account every byte in a file, any injected code would inevitably change the signature.

  • Accidental code changes: Sometimes, issues like incomplete file copies or faulty read-write operations could result in code that contains errors. While this doesn’t pose a security risk, these faulty transfers could create problems in production when users try to run the code. Even accidental code changes like this will change the signature, making it evident there was an issue before it becomes a problem for your users..

Users can rule out both of these potential problems by using code signatures to confirm that the code has not changed since developers released it.

Components of code signing

Code signing uses the following components and concepts:

  • Public keys: In the context of code signing, a public key is a digital record that contains information about the organization that creates or publishes software.

  • Digital certificate: A file that includes a public key along with information about the organization that generated the key. Digital certificates are typically shared publicly using a certificate authority, such as Fulcio

  • Private keys: A private key is a digital record that is available only to the software publisher. Using a private key in conjunction with a public key, a software publisher can create a unique signature that is linked to both keys – meaning that only the owner of the private key (the software publisher) can generate the signature, while at the same time anyone who has access to the public can verify that the signature was created using the private key (and hence by the software publisher).

  • Hash: A hash is a digital identifier that is created using a hashing algorithm. Each hash is unique to the software artifact that it represents. Thus, if the artifact changes in any way, the hash will no longer match.

How does code signing work?

Leveraging the components of code signing, as described above, and a code signing tool like Sigstore, developers can generate unique signatures for their software artifacts. The basic code signing process involves these steps:

  1. Key pair generation: The software publisher creates a private key and a public key.

  2. Hash generation: The publisher creates a hash of the software artifact it wants to sign.

  3. Hash signing: By feeding its private key into a signing algorithm, the publisher generates a unique software signature that includes information about the publisher.

  4. Verification: After downloading a copy of the software, the user uses the software publisher’s public key to process the signature. As long as no changes to the software have taken place, the signatures will match.

These are the essential steps in code signing, but additional actions may take place to add context or enhance security. For example, Sigstore supports transparency logs and timestamps, which provide granular visibility into when and by whom signatures were created.

Weaknesses and limitations of code signing

While code signing is a valuable way of helping to prevent threat actors from injecting malicious code into software supply chains, attackers can still potentially undermine code signing processes using methods like the following:

  • Stealing private keys: If attackers obtain a private key, they can use it to sign malicious software in a way that makes it appear to be signed by the legitimate owner of the private key. As a result, users won’t realize that the software is not valid.

  • Injecting malicious code before signing: Attackers who obtain access to a software repository or development environment may be able to plant malware before the code is signed and published. In this case, developers may release signed code that has been compromised without realizing it.

  • Certificate impersonation: Certificate impersonation happens when attackers create public keys and digital certificates similar to those of a legitimate organization and use them to sign code. Users who don’t pay close attention to details inside the certificate may think their code has been signed by an organization they can trust.

  • Dependency abuse: Code signing only verifies the authenticity of a specific image, package, or other software resource. It doesn’t guarantee that the resource’s dependencies are also secure. This may lead to scenarios where users run a valid, signed image, but the image automatically installs or runs malicious software.

These weaknesses don’t mean that organizations shouldn’t use code signing. However, they are reminders that even when signatures match, security risks may still exist within a software supply chain.

Securing images with Chainguard

In addition to publishing minimal, zero-CVE container images that teams can use to reduce software supply chain risks, Chainguard implements modern code signing best practices with Sigstore. That means every artifact Chainguard generates (including images, packages, and SBOMs) is signed to provide end-to-end integrity with full provenance across builds, tests, and distribution.

Contact us to learn more about how Chainguard can help protect your software supply chain.

Share this article

Frequently Asked Questions

Related articles

Want to learn more about Chainguard?