Scenarij 8.2: Potpisivanje koda

Kategorija: Digitalni potpisi
Složenost: ⭐⭐⭐⭐ (Visoka)
Preduvjeti: Code-Signing certifikat
Procijenjeno vrijeme: 20-30 minuta


Opis

Ovaj scenarij opisuje potpisivanje koda i izvršnih datoteka s Post-Quantum-sigurnim algoritmima. Code Signing omogućuje:

Podržani formati:


Tijek rada

flowchart LR CODE[Executable/DLL] --> HASH[Authenticode Hash] HASH --> SIGN[ML-DSA + RSA Potpis] KEY[Code-Signing ključ] --> SIGN SIGN --> TS[Dodavanje vremenske oznake] TSA[TSA Server] --> TS TS --> OUTPUT[Potpisana datoteka] style SIGN fill:#e8f5e9 style TS fill:#fff3e0


Primjer koda: Authenticode potpisivanje

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using System.Security.Cryptography;
 
using var ctx = PqCryptoContext.Initialize();
 
// Učitavanje Code-Signing certifikata i ključa
var codeSignCert = ctx.LoadCertificate("codesign.crt.pem");
var codeSignKey = ctx.LoadPrivateKey("codesign.key.pem", "KeyPassword!");
 
// Kreiranje Authenticode potpisa
var signatureOptions = new AuthenticodeSignatureOptions
{
    Certificate = codeSignCert,
    PrivateKey = codeSignKey,
    HashAlgorithm = HashAlgorithmName.SHA256,
    TimestampUrl = "http://timestamp.digicert.com",
    TimestampHashAlgorithm = HashAlgorithmName.SHA256,
    Mode = CryptoMode.Hybrid,
    Description = "MyApp - Sigurna aplikacija",
    DescriptionUrl = "https://myapp.example.com"
};
 
// Potpisivanje EXE datoteke
var inputPath = "MyApp.exe";
var outputPath = "MyApp-signed.exe";
 
ctx.SignAuthenticode(inputPath, outputPath, signatureOptions);
 
Console.WriteLine($"Kod potpisan: {outputPath}");
Console.WriteLine($"  Potpisnik: {codeSignCert.Subject}");
Console.WriteLine($"  Vremenska oznaka: {signatureOptions.TimestampUrl}");

Windows SignTool integracija

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 nije uspio: {error}");
        }
 
        Console.WriteLine($"Potpisano: {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;
    }
}

Potpisivanje PowerShell skripte

public class PowerShellSigner
{
    public void SignScript(string scriptPath, X509Certificate2 cert)
    {
        // Dodavanje PowerShell CMS potpisa
        var scriptContent = File.ReadAllText(scriptPath);
 
        // Kreiranje bloka potpisa
        var signatureBlock = CreatePowerShellSignature(scriptContent, cert);
 
        // Dodavanje potpisa skripti
        var signedContent = scriptContent + Environment.NewLine + signatureBlock;
        File.WriteAllText(scriptPath, signedContent);
 
        Console.WriteLine($"PowerShell skripta potpisana: {scriptPath}");
    }
 
    private string CreatePowerShellSignature(string content, X509Certificate2 cert)
    {
        using var ctx = PqCryptoContext.Initialize();
 
        // Hash skripte
        var hash = SHA256.HashData(Encoding.UTF8.GetBytes(content));
 
        // CMS potpis
        var contentInfo = new ContentInfo(hash);
        var signedCms = new SignedCms(contentInfo, true);
        var signer = new CmsSigner(cert);
        signedCms.ComputeSignature(signer);
 
        // Base64-kodirani blok potpisa
        var signatureBase64 = Convert.ToBase64String(signedCms.Encode());
 
        return $@"
# SIG # Begin signature block
# {signatureBase64}
# SIG # End signature block";
    }
}

Potpisivanje NuGet paketa

public class NuGetSigner
{
    public async Task SignPackage(
        string packagePath,
        X509Certificate2 cert,
        AsymmetricAlgorithm privateKey,
        string timestampUrl)
    {
        using var ctx = PqCryptoContext.Initialize();
 
        // Otvaranje NuGet paketa
        using var package = new ZipArchive(File.Open(packagePath, FileMode.Open), ZipArchiveMode.Update);
 
        // Kreiranje .signature.p7s
        var signatureEntry = package.CreateEntry(".signature.p7s");
 
        // Izračun hasha paketa (bez unosa potpisa)
        var packageHash = ComputePackageHash(package);
 
        // CMS potpis
        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);
 
        // Dodavanje vremenske oznake
        await AddTimestamp(signedCms, timestampUrl);
 
        // Pisanje potpisa
        using var signatureStream = signatureEntry.Open();
        signatureStream.Write(signedCms.Encode());
 
        Console.WriteLine($"NuGet paket potpisan: {packagePath}");
    }
}

Dual-Signature (Legacy + PQ)

Za prijelazno razdoblje: Oba potpisa paralelno

public class DualSignature
{
    public void SignWithDualSignature(string exePath, SigningCredentials credentials)
    {
        using var ctx = PqCryptoContext.Initialize();
 
        // 1. Prvi potpis: SHA-1 (za Windows XP/Vista kompatibilnost)
        ctx.SignAuthenticode(exePath, exePath, new AuthenticodeSignatureOptions
        {
            Certificate = credentials.LegacyCert,
            PrivateKey = credentials.LegacyKey,
            HashAlgorithm = HashAlgorithmName.SHA1,
            TimestampUrl = credentials.TimestampUrl,
            AppendSignature = false  // Prvi potpis
        });
 
        // 2. Drugi potpis: SHA-256 + PQ (za moderne sustave)
        ctx.SignAuthenticode(exePath, exePath, new AuthenticodeSignatureOptions
        {
            Certificate = credentials.PqCert,
            PrivateKey = credentials.PqKey,
            HashAlgorithm = HashAlgorithmName.SHA256,
            Mode = CryptoMode.Hybrid,
            TimestampUrl = credentials.TimestampUrl,
            AppendSignature = true  // Dodavanje drugog potpisa
        });
 
        Console.WriteLine("Dual-Signature kreiran (SHA-1 + SHA-256/PQ)");
    }
}

Timestamp serveri

Pružatelj URL Protokol
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

VAŽNO: Bez vremenske oznake potpis postaje nevaljan nakon isteka certifikata!


Zahtjevi specifični za industriju

Industrija Standard Zahtjevi
Windows Authenticode EV certifikat za SmartScreen
Automobilska UNECE R156 Firmware potpisivanje, HSM
Zdravstvo DiGAV Kvalificirani potpis
Industrija IEC 62443 SPS-Firmware

Povezani scenariji

Povezanost Scenarij Opis
Preduvjet 3.3 Code-Signing Cert Kreiranje certifikata
Važno 8.3 Vremenska oznaka Dugoročna valjanost
Povezano 8.4 Verifikacija potpisa Provjera

« ← 8.1 Potpisivanje dokumenata | ↑ Pregled potpisa | 8.3 Vremenska oznaka → »


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