Dot-Net

用於替換 signtool.exe 的 API/庫

  • November 17, 2017

Windows SDK 附帶一個名為 signtool.exe 的工具,可讓您使用證書對文件進行簽名。我需要在後台服務中做同樣的事情,所以我正在尋找一個庫(最好是託管程式碼,但 COM 會做)來做同樣的事情。有任何想法嗎?

找到了答案。以下是如何使用 X.509 證書在 .NET 中對文件進行簽名:

CmsSigner signer = new CmsSigner();
signer.Certificate = new X509Certificate2(certificate);

SignedCms content = new SignedCms(new ContentInfo(File.ReadAllBytes(fileToSign)));
content.ComputeSignature(signer, true);
byte[] signedFile = content.Encode();

string signedFileName = fileToSign + ".signed";
File.WriteAllBytes(signedFileName, signedFile);

Console.WriteLine("Signed file: " + signedFileName);

這裡,certificate是包含證書的 .pfx 文件的路徑,而fileToSign是要簽名的文件。

SignTool 正在使用 CAPICOM,它是 Crypto API 的 COM 包裝器。您可以使用任何一種。如果您使用 CAPICOM,您可以在此處查看資訊。

對於到達這裡的Google旅行者:

這個 MSDN 論壇文章說 Windows 中有一個CryptUIWizDigitalSign API。它還指向Alejandro Campos Magencio 的一篇部落格文章,該文章展示了 VB.NET 中的範例實現。

由於似乎缺少 C# 版本,我將 Alejandro 的程式碼轉換為 C#。請注意,以下程式碼僅適用於文件(尚未)。

using System;
using System.Security.Cryptography.X509Certificates;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace ConsoleApp1
{
   /// <summary>
   /// Provides code signing functionality via Windows COM Cryptui.dll.
   /// </summary>
   class Signer
   {

       public const Int32 CRYPTUI_WIZ_NO_UI = 1;
       public const Int32 CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE = 1;
       public const Int32 CRYPTUI_WIZ_DIGITAL_SIGN_CERT = 1;

       public struct CRYPTUI_WIZ_DIGITAL_SIGN_INFO
       {
           public Int32 dwSize;
           public Int32 dwSubjectChoice;
           [MarshalAs(UnmanagedType.LPWStr)]
           public string pwszFileName;
           public Int32 dwSigningCertChoice;
           public IntPtr pSigningCertContext;
           public string pwszTimestampURL;
           public Int32 dwAdditionalCertChoice;
           public IntPtr pSignExtInfo;
       }

       [StructLayout(LayoutKind.Sequential)]
       public struct CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT
       {
           public Int32 dwSize;
           public Int32 cbBlob;
           public IntPtr pbBlob;
       }

       [DllImport("Cryptui.dll", CharSet = CharSet.Unicode, SetLastError = true)]
       public static extern bool CryptUIWizDigitalSign(Int32 dwFlags, IntPtr hwndParent, string pwszWizardTitle, ref CRYPTUI_WIZ_DIGITAL_SIGN_INFO pDigitalSignInfo, ref IntPtr ppSignContext);

       [DllImport("Cryptui.dll", CharSet = CharSet.Auto, SetLastError = true)]
       public static extern bool CryptUIWizFreeDigitalSignContext(IntPtr pSignContext);

       /// <summary>
       /// Signs the executable at the given path with the given code signing certificate.
       /// </summary>
       /// <example>
       ///    string certPath = @"C:\certs\CodeSigningTestCert.pfx";
       ///    string exePath = @"C:\temp\ConsoleApp2ToBeSigned.exe";
       ///    string certPwd = "myGreatSecurePassword";
       ///    
       ///    try
       ///    {
       ///        string resultingSignature = Signer.SignExecutable(certPath, exePath, certPwd);
       ///    }
       ///    catch (Win32Exception ex)
       ///    {
       ///        Console.WriteLine(ex.Message + ", Native error code: " + ex.NativeErrorCode.ToString());
       ///    }
       ///    catch (Exception ex)
       ///    {
       ///        // Any unexpected errors?
       ///        Console.WriteLine(ex.Message);
       ///    }
       /// 
       /// </example>
       /// <param name="certPath">The absolute path to the PFX file to be used for signing the exe file.</param>
       /// <param name="exePath">The absolute path to the executable to be signed.</param>
       /// <param name="certPwd">The password for the PFX file.</param>
       public static string SignExecutable(string certPath, string exePath, string certPwd)
       {
           X509Certificate2 cert = default(X509Certificate2);

           CRYPTUI_WIZ_DIGITAL_SIGN_INFO digitalSignInfo = default(CRYPTUI_WIZ_DIGITAL_SIGN_INFO);
           CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT signContext = default(CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT);

           IntPtr pSignContext = default(IntPtr);
           IntPtr pSigningCertContext = default(IntPtr);

           // Get certificate context
           cert = new X509Certificate2(certPath, certPwd);
           pSigningCertContext = cert.Handle;

           // Prepare signing info: exe and cert
           digitalSignInfo = new CRYPTUI_WIZ_DIGITAL_SIGN_INFO();
           digitalSignInfo.dwSize = Marshal.SizeOf(digitalSignInfo);
           digitalSignInfo.dwSubjectChoice = CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE;
           digitalSignInfo.pwszFileName = exePath;
           digitalSignInfo.dwSigningCertChoice = CRYPTUI_WIZ_DIGITAL_SIGN_CERT;
           digitalSignInfo.pSigningCertContext = pSigningCertContext;
           digitalSignInfo.pwszTimestampURL = null;
           digitalSignInfo.dwAdditionalCertChoice = 0;
           digitalSignInfo.pSignExtInfo = IntPtr.Zero;

           // Sign exe
           if ((!CryptUIWizDigitalSign(CRYPTUI_WIZ_NO_UI, IntPtr.Zero, null, ref digitalSignInfo, ref pSignContext)))
               throw new Win32Exception(Marshal.GetLastWin32Error(), "CryptUIWizDigitalSign");

           // Get the blob with the signature
           signContext = (CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT)Marshal.PtrToStructure(pSignContext, typeof(CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT));
           byte[] blob = new byte[signContext.cbBlob + 1];
           Marshal.Copy(signContext.pbBlob, blob, 0, signContext.cbBlob);

           // Free blob memory
           if ((!CryptUIWizFreeDigitalSignContext(pSignContext)))
               throw new Win32Exception(Marshal.GetLastWin32Error(), "CryptUIWizFreeDigitalSignContext");

           return System.Text.Encoding.Default.GetString(blob);
       }
   }
}

希望能幫助到你!

引用自:https://stackoverflow.com/questions/510773