首次提交
This commit is contained in:
119
ConsoleAppTest/DigitalSigner.cs
Normal file
119
ConsoleAppTest/DigitalSigner.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user