As software delivery accelerates and DevOps becomes standard across industries, secure code signing is more important than ever. Code signing ensures the authenticity and integrity of software, preventing attackers from injecting malicious code into applications, scripts, containers, firmware, and more.
Enterprises, however, face a major challenge: how do you build a secure code signing system that protects high-value keys without slowing down development teams or requiring custom integrations?
In January 2018, NIST provided a great high-level overview of code signing, its threats, and some best practices to follow. This blog post extends on NIST’s overview with suggestions on how an enterprise can build such a system. In particular, we’ll focus on the signing aspect of a code signing system.
For additional information on this topic, see this post on automated hash validation, a technique for ensuring that the code being signed matches the code in the source code repository, and this post on preventing malware injections.
Who Needs Secure Code Signing?
If your organization produces any software, you should be signing it. And most companies—tech or not—produce software without realizing it:
Mobile apps
Internal applications and tools
PowerShell scripts, Bash scripts, and automation utilities
Excel macros
Firmware or embedded code
Deployment packages and installers
Whether software is used internally or shipped externally, unsigned code is a security liability. Attackers actively target build systems and signing workflows to insert malware, steal credentials, and compromise downstream users.
Code Signing in Real Enterprise Environments
Every enterprise is unique, with multiple teams that sign code for different reasons—and using different workflows.
DevOps Teams
Sign frequently and automatically
Emphasize performance
Often use non-production keys
Require authentication methods compatible with automation
Release Engineering / Production Teams
Sign less frequently
Use high-value production keys
Require the strongest possible authentication and oversight
Often require approvals and change control
A secure code signing system must support both extremes—fast and automated DevOps workflows and slow, tightly controlled release processes—without compromising security or introducing bottlenecks.
14 Best Practices for Secure Enterprise Code Signing
Below are the key principles for designing a modern, scalable code signing environment.
1. Use a Hardware Security Module (HSM) for All Keys
All code signing keys should be generated, stored, and used in a non–exportable manner in an approved hardware security module. This applies to all signing keys, not just the production ones.
All signing keys—production and non-production—must be:
Generated in the HSM
Stored in the HSM
Used in a non-exportable manner
Avoid renewing certificates for legacy software-based keys. Instead, migrate keys into the HSM, back them up, and replace them with properly generated HSM-resident keys.
2. Centralize the HSM
Never distribute private keys to users or build systems.
Centralizing key storage enables:
Permission control
Auditability
Revocation
Enforcement of security policies
3. Require Proxied Access to the HSM
HSMs provide excellent key protection but do not natively enforce enterprise authentication or authorization.
Introducing an HSM proxy allows you to:
Add MFA
Enforce device restrictions
Trigger approval workflows
Apply per-key policies
Centralize auditing
Simplify revocation
This proxy becomes the policy decision point for all cryptographic operations.
4. Implement Strict Access Control
Users should only access:
The keys they are authorized to use
During the timeframes they are authorized
Under the conditions defined by policy
Fine-grained, per-key access control is essential.
5. Require Strong, Flexible Authentication
A modern system must support:
MFA
OAuth2
Kerberos
SSO
Client-certificate authentication
Tamper-resistant local credentials for automation
Production keys should require MFA per signing request. Automated systems should have strict machine-to-machine authentication policies.
6. Authenticate Devices, Not Just Users
User identity alone is insufficient. Validate:
The device is enterprise-owned or approved
It has an unspoofable identity (TPM, Secure Enclave, etc.)
It meets security and patch compliance requirements
IP address checks alone are not adequate.
Device authentication should make use of tamper resistant secure enclaves (e.g., Trusted Platform Modules) wherever possible.
7. Define Per-Key Usage Policies
Policies should specify:
Allowed algorithms
Required authentication methods
Required approvals
Device restrictions
JIT access rules
Allowed geolocations
Notification rules
Key usage windows
This makes each key self-governing under centralized policy.
8. Support Approval Workflows
High-value keys should require quorum-based approvals prior to use.
Workflows should be:
Automatic
Logged
Enforced by the signing proxy
Impossible to bypass
9. Use Client-Side Hashing (Hash Signing)
To improve performance:
Compute the hash locally
Send only the hash to the signing service
Sign the hash within the HSM
This dramatically reduces network load and signing latency.
10. Adopt Reproducible Builds + Automated Hash Validation
Reproducible builds ensure the output is deterministic. Wherever possible, compile your code deterministically so that the output is always the same for the same source code.
Combined with automated hash validation:
The signing service checks the hash against your source repository
Unauthorized code modifications are detected
Attackers must compromise both source control and signing systems
This significantly increases security.
The most effective way to perform this digital hash signing check is with a technology called automated hash validation.
Using this approach, an attacker would now need to commit malicious code to the source code repository and gain access to the code signing system, instead of just gaining access to the code signing system. This is a harder attack to pull off and one that is much easier to detect since the source control’s history is tough to alter and constantly monitored by the developers.
11. Integrate with Native Signing Tools
Always sign using:
signtoolcodesignjarsignerproductsignLinux package signing tools (RPM, Debian, GPG)
Avoid reverse-engineering or custom formats. Native tooling ensures compatibility and prevents breakage during vendor updates.
12. Make All Signing Activity Auditable
Record:
Administrative actions
Signing actions
Approval events
Policy changes
Authentication details
Provide configurable notifications to approved personnel.
13. Support Multi-Tenancy
Large enterprises require:
Delegated administration
Subdomains
Hierarchical permissions
Per-team policies
Isolation between business units
A robust code signing system must scale organizationally as well as technically.
14. Ensure Cryptographic Agility
Your signing system must easily support:
SHA-2, SHA-3
RSA, ECDSA, EdDSA
Post-quantum algorithms (once finalized by NIST)
Stateful and stateless signature schemes
Now, NIST is expected to make its final recommendations for post-quantum signature algorithms within the next year (technically, XMSS and HSS/LMS have already been recommended as stateful signature algorithms).
This means that a proper secure code signing system needs to be agile with respect to the cryptographic algorithms it supports. The code signing system needs to be flexible enough to introduce new algorithms that may not fit the same model as the previous algorithms (e.g., no longer hash then sign or may require state management).
Cryptography evolves—your signing infrastructure must evolve with it.
Secure Code Signing Conclusion
Building a code signing system that:
-
Integrates with every platform
-
Supports native signing tools
-
Protects all private keys in HSMs
-
Enforces strong security controls
-
Supports approval workflows
-
Scales across the enterprise
-
Maintains cryptographic agility
…is complex and resource-intensive.
That’s why we built GaraTrust.
With GaraTrust:
-
Code signing keys stay secured inside your HSMs
-
Performance stays high using client-side hashing
-
Policies, approvals, authentication, and device checks are managed centrally
-
Integrations exist for Windows, macOS, Linux, GPG, RPM, Debian, Java, and more
If you’re interested in learning more about GaraTrust or setting up a demo, get in touch with the Garantir team.


