Scenario 8.2: Firmare codice

Categoria: Firme digitali
Complessità: Alta
Prerequisiti: Certificato Code-Signing
Tempo stimato: 20-30 minuti


Descrizione

Questo scenario descrive la firma di codice e eseguibili con algoritmi sicuri Post-Quantum. Code Signing consente:

Formati supportati:


Workflow

flowchart LR CODE[Executable/DLL] --> HASH[Hash Authenticode] HASH --> SIGN[Firma ML-DSA + RSA] KEY[Chiave Code-Signing] --> SIGN SIGN --> TS[Aggiungere timestamp] TSA[Server TSA] --> TS TS --> OUTPUT[File firmato] style SIGN fill:#e8f5e9 style TS fill:#fff3e0


Esempio codice: Firma Authenticode

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using System.Security.Cryptography;
 
using var ctx = PqCryptoContext.Initialize();
 
// Caricare certificato e chiave Code-Signing
var codeSignCert = ctx.LoadCertificate("codesign.crt.pem");
var codeSignKey = ctx.LoadPrivateKey("codesign.key.pem", "KeyPassword!");
 
// Creare firma Authenticode
var signatureOptions = new AuthenticodeSignatureOptions
{
    Certificate = codeSignCert,
    PrivateKey = codeSignKey,
    HashAlgorithm = HashAlgorithmName.SHA256,
    TimestampUrl = "http://timestamp.digicert.com",
    TimestampHashAlgorithm = HashAlgorithmName.SHA256,
    Mode = CryptoMode.Hybrid,
    Description = "MyApp - Applicazione sicura",
    DescriptionUrl = "https://myapp.example.com"
};
 
// Firmare EXE
var inputPath = "MyApp.exe";
var outputPath = "MyApp-signed.exe";
 
ctx.SignAuthenticode(inputPath, outputPath, signatureOptions);
 
Console.WriteLine($"Codice firmato: {outputPath}");
Console.WriteLine($"  Firmatario: {codeSignCert.Subject}");
Console.WriteLine($"  Timestamp: {signatureOptions.TimestampUrl}");

Integrazione Windows SignTool

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 \"Firmato con 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 fallito: {error}");
        }
 
        Console.WriteLine($"Firmato: {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;
    }
}

Firmare script PowerShell

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

Firmare pacchetto NuGet

public class NuGetSigner
{
    public async Task SignPackage(
        string packagePath,
        X509Certificate2 cert,
        AsymmetricAlgorithm privateKey,
        string timestampUrl)
    {
        using var ctx = PqCryptoContext.Initialize();
 
        // Aprire pacchetto NuGet
        using var package = new ZipArchive(File.Open(packagePath, FileMode.Open), ZipArchiveMode.Update);
 
        // Creare .signature.p7s
        var signatureEntry = package.CreateEntry(".signature.p7s");
 
        // Calcolare hash del pacchetto (senza entry firma)
        var packageHash = ComputePackageHash(package);
 
        // Firma CMS
        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);
 
        // Aggiungere timestamp
        await AddTimestamp(signedCms, timestampUrl);
 
        // Scrivere firma
        using var signatureStream = signatureEntry.Open();
        signatureStream.Write(signedCms.Encode());
 
        Console.WriteLine($"Pacchetto NuGet firmato: {packagePath}");
    }
}

Doppia firma (Legacy + PQ)

Per periodo di transizione: entrambe le firme in parallelo

public class DualSignature
{
    public void SignWithDualSignature(string exePath, SigningCredentials credentials)
    {
        using var ctx = PqCryptoContext.Initialize();
 
        // 1. Prima firma: SHA-1 (per compatibilità Windows XP/Vista)
        ctx.SignAuthenticode(exePath, exePath, new AuthenticodeSignatureOptions
        {
            Certificate = credentials.LegacyCert,
            PrivateKey = credentials.LegacyKey,
            HashAlgorithm = HashAlgorithmName.SHA1,
            TimestampUrl = credentials.TimestampUrl,
            AppendSignature = false  // Prima firma
        });
 
        // 2. Seconda firma: SHA-256 + PQ (per sistemi moderni)
        ctx.SignAuthenticode(exePath, exePath, new AuthenticodeSignatureOptions
        {
            Certificate = credentials.PqCert,
            PrivateKey = credentials.PqKey,
            HashAlgorithm = HashAlgorithmName.SHA256,
            Mode = CryptoMode.Hybrid,
            TimestampUrl = credentials.TimestampUrl,
            AppendSignature = true  // Aggiungere seconda firma
        });
 
        Console.WriteLine("Doppia firma creata (SHA-1 + SHA-256/PQ)");
    }
}

Server timestamp

Fornitore URL Protocollo
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

IMPORTANTE: Senza timestamp la firma non è valida dopo la scadenza del certificato!


Requisiti specifici per settore

Settore Standard Requisiti
Windows Authenticode Certificato EV per SmartScreen
Automotive UNECE R156 Firma firmware, HSM
Sanità DiGAV Firma qualificata
Industria IEC 62443 Firmware PLC

Scenari correlati

Relazione Scenario Descrizione
Prerequisito 3.3 Cert Code-Signing Creare certificato
Importante 8.3 Timestamp Validità a lungo termine
Correlato 8.4 Verificare firma Verifica

« ← 8.1 Firmare documento | ↑ Panoramica firme | 8.3 Timestamp → »


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