Scenario 8.2: Sign Code

Category: Digital Signatures
Complexity: ⭐⭐⭐⭐ (High)
Prerequisites: Code signing certificate
Estimated Time: 20-30 Minutes


Description

This scenario describes signing code and executables with Post-Quantum secure algorithms. Code signing enables:

  • Authentication of the software publisher
  • Integrity protection against tampering
  • Trustworthiness for end users
  • Compliance with security policies

Supported Formats:

  • Windows Authenticode (EXE, DLL, MSI)
  • PowerShell Scripts (.ps1)
  • NuGet Packages (.nupkg)
  • Java JAR Files
  • macOS Code Signature

Workflow

flowchart LR CODE[Executable/DLL] --> HASH[Authenticode Hash] HASH --> SIGN[ML-DSA + RSA Signature] KEY[Code Signing Key] --> SIGN SIGN --> TS[Add Timestamp] TSA[TSA Server] --> TS TS --> OUTPUT[Signed File] style SIGN fill:#e8f5e9 style TS fill:#fff3e0


Code Example: Authenticode Signing

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using System.Security.Cryptography;
 
using var ctx = PqCryptoContext.Initialize();
 
// Load code signing certificate and key
var codeSignCert = ctx.LoadCertificate("codesign.crt.pem");
var codeSignKey = ctx.LoadPrivateKey("codesign.key.pem", "KeyPassword!");
 
// Create Authenticode signature
var signatureOptions = new AuthenticodeSignatureOptions
{
    Certificate = codeSignCert,
    PrivateKey = codeSignKey,
    HashAlgorithm = HashAlgorithmName.SHA256,
    TimestampUrl = "http://timestamp.digicert.com",
    TimestampHashAlgorithm = HashAlgorithmName.SHA256,
    Mode = CryptoMode.Hybrid,
    Description = "MyApp - Secure Application",
    DescriptionUrl = "https://myapp.example.com"
};
 
// Sign EXE
var inputPath = "MyApp.exe";
var outputPath = "MyApp-signed.exe";
 
ctx.SignAuthenticode(inputPath, outputPath, signatureOptions);
 
Console.WriteLine($"Code signed: {outputPath}");
Console.WriteLine($"  Signer: {codeSignCert.Subject}");
Console.WriteLine($"  Timestamp: {signatureOptions.TimestampUrl}");

Windows SignTool Integration

public class SignToolWrapper
{
    public void Sign(string filePath, string pfxPath, string password, string timestampUrl)
    {
        var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "signtool.exe",
                Arguments = $"sign " +
                    $"/fd SHA256 " +
                    $"/f \"{pfxPath}\" " +
                    $"/p \"{password}\" " +
                    $"/tr \"{timestampUrl}\" " +
                    $"/td SHA256 " +
                    $"/d \"Signed with PQ-Crypto\" " +
                    $"\"{filePath}\"",
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false
            }
        };
 
        process.Start();
        process.WaitForExit();
 
        if (process.ExitCode != 0)
        {
            var error = process.StandardError.ReadToEnd();
            throw new InvalidOperationException($"SignTool failed: {error}");
        }
 
        Console.WriteLine($"Signed: {filePath}");
    }
 
    public bool Verify(string filePath)
    {
        var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "signtool.exe",
                Arguments = $"verify /pa /v \"{filePath}\"",
                RedirectStandardOutput = true,
                UseShellExecute = false
            }
        };
 
        process.Start();
        process.WaitForExit();
 
        return process.ExitCode == 0;
    }
}

Sign PowerShell Script

public class PowerShellSigner
{
    public void SignScript(string scriptPath, X509Certificate2 cert)
    {
        // Add PowerShell CMS signature
        var scriptContent = File.ReadAllText(scriptPath);
 
        // Create signature block
        var signatureBlock = CreatePowerShellSignature(scriptContent, cert);
 
        // Append signature to script
        var signedContent = scriptContent + Environment.NewLine + signatureBlock;
        File.WriteAllText(scriptPath, signedContent);
 
        Console.WriteLine($"PowerShell script signed: {scriptPath}");
    }
 
    private string CreatePowerShellSignature(string content, X509Certificate2 cert)
    {
        using var ctx = PqCryptoContext.Initialize();
 
        // Hash the script
        var hash = SHA256.HashData(Encoding.UTF8.GetBytes(content));
 
        // CMS signature
        var contentInfo = new ContentInfo(hash);
        var signedCms = new SignedCms(contentInfo, true);
        var signer = new CmsSigner(cert);
        signedCms.ComputeSignature(signer);
 
        // Base64 encoded signature block
        var signatureBase64 = Convert.ToBase64String(signedCms.Encode());
 
        return $@"
# SIG # Begin signature block
# {signatureBase64}
# SIG # End signature block";
    }
}

Sign NuGet Package

public class NuGetSigner
{
    public async Task SignPackage(
        string packagePath,
        X509Certificate2 cert,
        AsymmetricAlgorithm privateKey,
        string timestampUrl)
    {
        using var ctx = PqCryptoContext.Initialize();
 
        // Open NuGet package
        using var package = new ZipArchive(File.Open(packagePath, FileMode.Open), ZipArchiveMode.Update);
 
        // Create .signature.p7s
        var signatureEntry = package.CreateEntry(".signature.p7s");
 
        // Calculate package hash (without signature entry)
        var packageHash = ComputePackageHash(package);
 
        // CMS signature
        var contentInfo = new ContentInfo(packageHash);
        var signedCms = new SignedCms(contentInfo, true);
        var signer = new CmsSigner(cert)
        {
            DigestAlgorithm = new Oid("2.16.840.1.101.3.4.2.1"),
            IncludeOption = X509IncludeOption.WholeChain
        };
 
        signedCms.ComputeSignature(signer, mode: CryptoMode.Hybrid);
 
        // Add timestamp
        await AddTimestamp(signedCms, timestampUrl);
 
        // Write signature
        using var signatureStream = signatureEntry.Open();
        signatureStream.Write(signedCms.Encode());
 
        Console.WriteLine($"NuGet package signed: {packagePath}");
    }
}

Dual Signature (Legacy + PQ)

For transition period: Both signatures in parallel

public class DualSignature
{
    public void SignWithDualSignature(string exePath, SigningCredentials credentials)
    {
        using var ctx = PqCryptoContext.Initialize();
 
        // 1. First signature: SHA-1 (for Windows XP/Vista compatibility)
        ctx.SignAuthenticode(exePath, exePath, new AuthenticodeSignatureOptions
        {
            Certificate = credentials.LegacyCert,
            PrivateKey = credentials.LegacyKey,
            HashAlgorithm = HashAlgorithmName.SHA1,
            TimestampUrl = credentials.TimestampUrl,
            AppendSignature = false  // First signature
        });
 
        // 2. Second signature: SHA-256 + PQ (for modern systems)
        ctx.SignAuthenticode(exePath, exePath, new AuthenticodeSignatureOptions
        {
            Certificate = credentials.PqCert,
            PrivateKey = credentials.PqKey,
            HashAlgorithm = HashAlgorithmName.SHA256,
            Mode = CryptoMode.Hybrid,
            TimestampUrl = credentials.TimestampUrl,
            AppendSignature = true  // Append second signature
        });
 
        Console.WriteLine("Dual signature created (SHA-1 + SHA-256/PQ)");
    }
}

Timestamp Servers

Provider URL Protocol
DigiCert http://timestamp.digicert.com RFC 3161
Sectigo http://timestamp.sectigo.com RFC 3161
GlobalSign http://timestamp.globalsign.com RFC 3161
SSL.com http://ts.ssl.com RFC 3161

IMPORTANT: Without a timestamp, the signature becomes invalid after the certificate expires!


Industry-Specific Requirements

Industry Standard Requirements
Windows Authenticode EV certificate for SmartScreen
Automotive UNECE R156 Firmware signing, HSM
Healthcare DiGAV Qualified signature
Industry IEC 62443 PLC firmware

Relationship Scenario Description
Prerequisite 3.3 Code Signing Certificate Create certificate
Important 8.3 Timestamp Long-term validity
Related 8.4 Verify Signature Validation

« ← 8.1 Sign Document | ↑ Signatures Overview | 8.3 Timestamp → »


Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional

Zuletzt geändert: on 2026/01/30 at 06:47 AM