.net - example - API/Library para reemplazar signtool.exe
x509certificate2 c# example (4)
Para los google-Travellers que llegan aquí:
Este hilo del foro de MSDN dice que hay una API CryptUIWizDigitalSign en Windows. También apunta a un artículo de blog de Alejandro Campos Magencio que muestra una implementación de muestra en VB.NET.
Como parece que falta una versión de C #, convertí el código de Alejandro en C #. Tenga en cuenta que el siguiente código solo funciona con archivos (aún).
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);
}
}
}
¡Espero eso ayude!
El SDK de Windows viene con una herramienta llamada signtool.exe que le permite firmar un archivo con un certificado. Necesito hacer lo mismo pero en un servicio en segundo plano, así que estoy buscando una biblioteca (preferiblemente código administrado, pero COM lo hará) para hacer lo mismo. ¿Algunas ideas?
Encontré la respuesta. A continuación, se indica cómo usar un certificado X.509 para firmar un archivo en .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);
Aquí, el certificado es la ruta al archivo .pfx que contiene el certificado y fileToSign es el archivo para firmar.
¿No puedes simplemente guiarte a tu manera? ¿Escribe un archivo por lotes simple que le proporcione los argumentos y la entrada correctos? Eso es al menos lo que hacemos cuando vemos este problema en un servidor UNIX.
SignTool está utilizando CAPICOM, que es el contenedor COM para Crypto API. Puedes usar cualquiera de los dos. Si va con CAPICOM, puede verificar la información aquí .
Estoy teniendo el mismo problema que Sebastien. Al examinar las API, parece que esto es para firmar mensajes envueltos. Authenticode - la firma de código que signtool hace - es diferente, por lo que el EXE no se ejecuta después de la firma.
Todavía estoy buscando alternativas.