using System; using System.IO; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Diagnostics; class DigitalSigner { // 对文件进行签名,并将签名保存为 .sig 文件(分离签名) public static void SignFile(string filePath, string pfxPath, string pfxPassword) { byte[] fileBytes = File.ReadAllBytes(filePath); byte[] signature; using (var cert = new X509Certificate2(pfxPath, pfxPassword)) using (RSA rsa = cert.GetRSAPrivateKey()) { if (rsa == null) throw new InvalidOperationException("证书中没有私钥。"); signature = rsa.SignData(fileBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); } File.WriteAllBytes(filePath + ".sig", signature); Console.WriteLine($"签名已保存为 {filePath}.sig"); } // 使用 signtool.exe 对 PE 文件进行 Authenticode 嵌入式签名(会出现在文件属性的 “数字签名”) // signtoolPath: 如果系统已把 signtool 放到 PATH,可传 "signtool",否则传完整路径。 // timestampUrl: 可传 null 或空字符串以跳过时间戳(不推荐)。 public static void SignFileAuthenticode(string filePath, string pfxPath, string pfxPassword, string signtoolPath = "signtool", string timestampUrl = "http://timestamp.digicert.com") { if (!File.Exists(filePath)) throw new FileNotFoundException("待签名文件不存在。", filePath); if (!File.Exists(pfxPath)) throw new FileNotFoundException("PFX 文件不存在。", pfxPath); // 构造 signtool 参数:使用 SHA256 摘要并添加 RFC3161 时间戳(/tr /td) var args = $@"sign /fd SHA256 /f ""{pfxPath}"" /p ""{pfxPassword}"""; if (!string.IsNullOrEmpty(timestampUrl)) { // 使用 RFC3161 时间戳服务器,/tr 指定时间戳 URL,/td 指定时间戳摘要算法 args += $@" /tr ""{timestampUrl}"" /td SHA256"; } args += $@" ""{filePath}"""; var psi = new ProcessStartInfo { FileName = signtoolPath, Arguments = args, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true }; using (var proc = Process.Start(psi)) { string stdout = proc.StandardOutput.ReadToEnd(); string stderr = proc.StandardError.ReadToEnd(); proc.WaitForExit(); if (proc.ExitCode != 0) { throw new InvalidOperationException($"signtool.exe 返回非零退出码 {proc.ExitCode}。\nStdout:\n{stdout}\nStderr:\n{stderr}"); } Console.WriteLine("Authenticode 签名成功。"); Console.WriteLine(stdout); } } // 验证文件签名(分离签名示例) public static bool VerifySignature(string filePath, string sigPath, string certPath) { byte[] fileBytes = File.ReadAllBytes(filePath); byte[] signature = File.ReadAllBytes(sigPath); var cert = new X509Certificate2(certPath); using (RSA rsa = cert.GetRSAPublicKey()) { return rsa.VerifyData(fileBytes, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); } } public static X509Certificate2 CreateSelfSignedCertificate(string subjectName) { var rsa = RSA.Create(2048); var request = new CertificateRequest( subjectName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); request.CertificateExtensions.Add( new X509KeyUsageExtension( X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment, critical: false)); request.CertificateExtensions.Add( new X509EnhancedKeyUsageExtension( new OidCollection { new Oid("1.3.6.1.5.5.7.3.3") }, false)); var cert = request.CreateSelfSigned( notBefore: DateTimeOffset.UtcNow.AddDays(-1), notAfter: DateTimeOffset.UtcNow.AddYears(1)); if (cert.HasPrivateKey) { return cert; } else { var certWithKey = cert.CopyWithPrivateKey(rsa); rsa.Dispose(); cert.Dispose(); return certWithKey; } } }