Files
SZMCToolkit.RevitAddins/ConsoleAppTest/DigitalSigner.cs
2026-02-23 10:28:26 +08:00

120 lines
4.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}
}