Как программировать код-подписать исполняемый файл с помощью PFX (Bouncy Castle или в противном случае)

Я пытаюсь определить лучший метод для подписи кода исполняемого файла с помощью Bouncy Castle, управляемого кода или не управляемого кода из С#. Поскольку CAPICOM теперь устарел, я полагаю, что один из методов SignerSign из mssign32.dll - лучший способ пойти, если это нужно сделать неуправляемым.

Этот ответ (qaru.site/questions/549461/...) кажется близким, но он создает файл .p7m, который, будучи правильным размером, не будет работать корректно ( явно переименованный в .exe перед запуском).

Решение, заданное здесь вопросом-вопросом (API/Library для замены signtool.exe), кажется многообещающим и управляемым, но, как говорит Том Кэнэм в комментариях ниже, "похоже, что это для подписания огибаемых сообщений. Authenticode - это подписание кода, что делает этот инструмент signtool, - это разные, поэтому EXE не запускается после подписания". Я получаю ту же ошибку, что и Том, когда я подписываюсь, используя либо вопрос-вопрос, либо ранее упоминавшееся решение Bouncy Castle.

Единственная опция, которую я еще не предпринял, приведен здесь (qaru.site/questions/521520/...), и, хотя она выглядит многообещающей, я не уверен, что она использует "authenticode" "подписание кода, в отличие от" огибаемого сообщения ", подписание кода. Этот ответ также имеет преимущество не использовать методы взаимодействия CAPICOM, которые теперь устарели, поэтому я предполагаю, что сегодня сообщаю о результатах, используя этот метод. Если это лучший вариант, может кто-нибудь там поговорить о различиях между SignerSign, SignerSignEx и SignerSignEx2, которые экспортируются из mssign32.dll? Я прочитал, что SignerSignEx2 следует использовать с Windows 8 и выше...

Короче говоря, я хотел бы реплицировать способность signtool.exe подписывать исполняемый файл с заданным файлом .exe,.pfx файлом и паролем следующим образом:

signtool sign /f cert.pfx /p password application.exe

Я ищу лучший вариант для программного подписи кода исполняемого файла (если это имеет значение), используя подпись подписи подлинности, и я предпочел бы использовать надувной замок или управляемый код, если это возможно, хотя я буду использовать что-то неуправляемое, если оно работает и в настоящее время не рекомендуется.

Спасибо!

1 ответ

Насколько я могу судить, SignSigner и SignSignerEx доступны с Windows XP, которая является самой старой операционной системой, которую я поддерживаю. Поскольку мне не нужно беспокоиться о публикации Windows App Store, этот ответ ограничен SignSigner и SignSignerEx, хотя импорт для SignSignerEx2 очень похож на SignSignerEx, и я не ожидал, что он вызовет какие-либо проблемы.

Следующий класс позволяет вам подписать исполняемый файл с помощью .pfx, вызвав:

SignWithCert(string appPath, string certPath, string certPassword, string timestampUrl);

Он также позволяет вам подписать исполняемый файл с сертификатом из хранилища ключей, вызвав:

SignWithThumbPrint(string appPath, string thumbprint, string timestampUrl);

Если вы хотите подписаться с помощью сертификата, установленного в хранилище ключей, вам может потребоваться обновить FindCertByThumbPrint (string thumbPrint), чтобы проверить больше хранилищ ключей, чем я хочу проверить. 99,5% времени, наши клиенты подписываются с .pfx, а не с отпечатком.

Для иллюстрации SignWithCert() использует SignerSignEx и SignerTimeStampEx, а SignWithThumbPrint() использует SignerSign и SignerTimeStamp.

Они легко меняются. SignerSignEx и SignerTimeStampEx возвращают указатель SIGNER_CONTEXT и позволяют изменять поведение функций с помощью аргумента dwFlags (если вы подписываете переносимый исполняемый файл). Действительные параметры флага перечислены в здесь. В принципе, если вы передадите 0x0 в качестве dwFlags в SignerSignEx, вывод будет идентичным только с использованием SignerSign. В моем случае, я полагаю, я буду использовать SignerSign, потому что я не считаю, что мне нужен указатель на контекст подписывающего лица по любой мыслимой причине.

В любом случае, вот класс. Это мой первый код для отправки здесь, поэтому я надеюсь, что не форматировал его.

Код работает так, как ожидалось, и исполняемый файл работает нормально и подписано, но двоичный вывод блока подписи немного отличается от двоичного вывода файла signtool.exe(в этом тесте ни один инструмент не использовал временную метку). Я объясняю это тем фактом, что signtool.exe, по-видимому, использует CAPICOM для подписи, и это использует Mssign32.dll, но в целом я очень доволен этим в начальном наборе тестов.

Обработка ошибок, очевидно, нуждается в улучшении.

Спасибо GregS и всем там, кто ранее показывал образцы кода.

Вот соответствующий материал. Я буду обновлять этот блок комментариями и улучшениями, когда я получу шанс сделать это.

Обновление 1: добавлена ​​несколько улучшенная обработка ошибок и комментарии, а также некоторая переформатировка отпечатка пальца в FindCertByThumbprint (строка отпечатка), чтобы можно было найти сертификат в Windows 8 и Windows 10 (общедоступный просмотр). Эти ОС не вернут совпадение, когда в отпечатке остались пробелы, поэтому теперь я исправляю их перед поиском.

using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace Utilities
{
 internal static class SignTool
 {
 #region Structures
 [StructLayoutAttribute(LayoutKind.Sequential)]
 struct SIGNER_SUBJECT_INFO
 {
 public **** cbSize;
 public IntPtr pdwIndex;
 public **** dwSubjectChoice;
 public SubjectChoiceUnion Union1;
 [StructLayoutAttribute(LayoutKind.Explicit)]
 internal struct SubjectChoiceUnion
 {
 [FieldOffsetAttribute(0)]
 public System.IntPtr pSignerFileInfo;
 [FieldOffsetAttribute(0)]
 public System.IntPtr pSignerBlobInfo;
 };
 }
 [StructLayoutAttribute(LayoutKind.Sequential)]
 struct SIGNER_CERT
 {
 public **** cbSize;
 public **** dwCertChoice;
 public SignerCertUnion Union1;
 [StructLayoutAttribute(LayoutKind.Explicit)]
 internal struct SignerCertUnion
 {
 [FieldOffsetAttribute(0)]
 public IntPtr pwszSpcFile;
 [FieldOffsetAttribute(0)]
 public IntPtr pCertStoreInfo;
 [FieldOffsetAttribute(0)]
 public IntPtr pSpcChainInfo;
 };
 public IntPtr hwnd;
 }
 [StructLayoutAttribute(LayoutKind.Sequential)]
 struct SIGNER_SIGNATURE_INFO
 {
 public **** cbSize;
 public **** algidHash; // ALG_ID
 public **** dwAttrChoice;
 public IntPtr pAttrAuthCode;
 public IntPtr psAuthenticated; // PCRYPT_ATTRIBUTES
 public IntPtr psUnauthenticated; // PCRYPT_ATTRIBUTES
 }
 [StructLayoutAttribute(LayoutKind.Sequential)]
 struct SIGNER_FILE_INFO
 {
 public **** cbSize;
 public IntPtr pwszFileName;
 public IntPtr hFile;
 }
 [StructLayoutAttribute(LayoutKind.Sequential)]
 struct SIGNER_CERT_STORE_INFO
 {
 public **** cbSize;
 public IntPtr pSigningCert; // CERT_CONTEXT
 public **** dwCertPolicy;
 public IntPtr hCertStore;
 }
 [StructLayoutAttribute(LayoutKind.Sequential)]
 struct SIGNER_CONTEXT
 {
 public **** cbSize;
 public **** cbBlob;
 public IntPtr pbBlob;
 }
 [StructLayoutAttribute(LayoutKind.Sequential)]
 struct SIGNER_PROVIDER_INFO
 {
 public **** cbSize;
 public IntPtr pwszProviderName;
 public **** dwProviderType;
 public **** dwKeySpec;
 public **** dwPvkChoice;
 public SignerProviderUnion Union1;
 [StructLayoutAttribute(LayoutKind.Explicit)]
 internal struct SignerProviderUnion
 {
 [FieldOffsetAttribute(0)]
 public IntPtr pwszPvkFileName;
 [FieldOffsetAttribute(0)]
 public IntPtr pwszKeyContainer;
 };
 }
 #endregion
 #region Imports
 [DllImport("Mssign32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
 private static extern int SignerSign(
 IntPtr pSubjectInfo, // SIGNER_SUBJECT_INFO
 IntPtr pSignerCert, // SIGNER_CERT
 IntPtr pSignatureInfo, // SIGNER_SIGNATURE_INFO
 IntPtr pProviderInfo, // SIGNER_PROVIDER_INFO
 string pwszHttpTimeStamp, // LPCWSTR
 IntPtr psRequest, // PCRYPT_ATTRIBUTES
 IntPtr pSipData // LPVOID 
 );
 [DllImport("Mssign32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
 private static extern int SignerSignEx(
 **** dwFlags, // DWORD
 IntPtr pSubjectInfo, // SIGNER_SUBJECT_INFO
 IntPtr pSignerCert, // SIGNER_CERT
 IntPtr pSignatureInfo, // SIGNER_SIGNATURE_INFO
 IntPtr pProviderInfo, // SIGNER_PROVIDER_INFO
 string pwszHttpTimeStamp, // LPCWSTR
 IntPtr psRequest, // PCRYPT_ATTRIBUTES
 IntPtr pSipData, // LPVOID 
 out SIGNER_CONTEXT ppSignerContext // SIGNER_CONTEXT
 );
 [DllImport("Mssign32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
 private static extern int SignerTimeStamp(
 IntPtr pSubjectInfo, // SIGNER_SUBJECT_INFO
 string pwszHttpTimeStamp, // LPCWSTR
 IntPtr psRequest, // PCRYPT_ATTRIBUTES
 IntPtr pSipData // LPVOID 
 );
 [DllImport("Mssign32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
 private static extern int SignerTimeStampEx(
 **** dwFlags, // DWORD
 IntPtr pSubjectInfo, // SIGNER_SUBJECT_INFO
 string pwszHttpTimeStamp, // LPCWSTR
 IntPtr psRequest, // PCRYPT_ATTRIBUTES
 IntPtr pSipData, // LPVOID
 out SIGNER_CONTEXT ppSignerContext // SIGNER_CONTEXT
 );
 [DllImport("Crypt32.dll", EntryPoint = "CertCreateCertificateContext", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]
 private static extern IntPtr CertCreateCertificateContext(
 int dwCertEncodingType,
 byte[] pbCertEncoded,
 int cbCertEncoded);
 #endregion
 #region public methods
 // Call SignerSignEx and SignerTimeStampEx for a given .pfx
 public static void SignWithCert(string appPath, string certPath, string certPassword, string timestampUrl)
 {
 IntPtr pSignerCert = IntPtr.Zero;
 IntPtr pSubjectInfo = IntPtr.Zero;
 IntPtr pSignatureInfo = IntPtr.Zero;
 IntPtr pProviderInfo = IntPtr.Zero;
 try
 {
 // Grab the X509Certificate from the .pfx file.
 X509Certificate2 cert = new X509Certificate2(certPath, certPassword);
 pSignerCert = CreateSignerCert(cert);
 pSubjectInfo = CreateSignerSubjectInfo(appPath);
 pSignatureInfo = CreateSignerSignatureInfo();
 pProviderInfo = GetProviderInfo(cert);
 SIGNER_CONTEXT signerContext;
 SignCode(0x0, pSubjectInfo, pSignerCert, pSignatureInfo, pProviderInfo, out signerContext);
 // Only attempt to timestamp if we've got a timestampUrl.
 if (!string.IsNullOrEmpty(timestampUrl))
 {
 TimeStampSignedCode(0x0, pSubjectInfo, timestampUrl, out signerContext);
 }
 }
 catch (CryptographicException ce)
 {
 string exception;
 // do anything with this useful information?
 switch (Marshal.GetHRForException(ce))
 {
 case -2146885623:
 exception = string.Format(@"An error occurred while attempting to load the signing certificate. ""{0}"" does not appear to contain a valid certificate.", certPath);
 break;
 case -2147024810:
 exception = string.Format(@"An error occurred while attempting to load the signing certificate. The specified password was incorrect.");
 break;
 default:
 exception = string.Format(@"An error occurred while attempting to load the signing certificate. {0}", ce.Message);
 break;
 }
 }
 catch (Exception e)
 {
 // do anything with this useful information?
 string exception = e.Message;
 }
 finally
 {
 if (pSignerCert != IntPtr.Zero)
 {
 Marshal.DestroyStructure(pSignerCert, typeof(SIGNER_CERT));
 }
 if (pSubjectInfo != IntPtr.Zero)
 {
 Marshal.DestroyStructure(pSubjectInfo, typeof(SIGNER_SUBJECT_INFO));
 }
 if (pSignatureInfo != IntPtr.Zero)
 {
 Marshal.DestroyStructure(pSignatureInfo, typeof(SIGNER_SIGNATURE_INFO));
 }
 if (pProviderInfo != IntPtr.Zero)
 {
 Marshal.DestroyStructure(pSignatureInfo, typeof(SIGNER_PROVIDER_INFO));
 }
 }
 }
 // Call SignerSign and SignerTimeStamp for a given thumbprint.
 public static void SignWithThumbprint(string appPath, string thumbprint, string timestampUrl)
 {
 IntPtr pSignerCert = IntPtr.Zero;
 IntPtr pSubjectInfo = IntPtr.Zero;
 IntPtr pSignatureInfo = IntPtr.Zero;
 IntPtr pProviderInfo = IntPtr.Zero;
 try
 {
 pSignerCert = CreateSignerCert(thumbprint);
 pSubjectInfo = CreateSignerSubjectInfo(appPath);
 pSignatureInfo = CreateSignerSignatureInfo();
 SignCode(pSubjectInfo, pSignerCert, pSignatureInfo, pProviderInfo);
 // Only attempt to timestamp if we've got a timestampUrl.
 if (!string.IsNullOrEmpty(timestampUrl))
 {
 TimeStampSignedCode(pSubjectInfo, timestampUrl);
 }
 }
 catch (CryptographicException ce)
 {
 // do anything with this useful information?
 string exception = string.Format(@"An error occurred while attempting to load the signing certificate. {0}", ce.Message);
 }
 catch (Exception e)
 {
 // do anything with this useful information?
 string exception = e.Message;
 }
 finally
 {
 if (pSignerCert != IntPtr.Zero)
 {
 Marshal.DestroyStructure(pSignerCert, typeof(SIGNER_CERT));
 }
 if (pSubjectInfo != IntPtr.Zero)
 {
 Marshal.DestroyStructure(pSubjectInfo, typeof(SIGNER_SUBJECT_INFO));
 }
 if (pSignatureInfo != IntPtr.Zero)
 {
 Marshal.DestroyStructure(pSignatureInfo, typeof(SIGNER_SIGNATURE_INFO));
 }
 }
 }
 #endregion
 #region private methods
 private static IntPtr CreateSignerSubjectInfo(string pathToAssembly)
 {
 SIGNER_SUBJECT_INFO info = new SIGNER_SUBJECT_INFO
 {
 cbSize = (****)Marshal.SizeOf(typeof(SIGNER_SUBJECT_INFO)),
 pdwIndex = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(****)))
 };
 var index = 0;
 Marshal.StructureToPtr(index, info.pdwIndex, false);
 info.dwSubjectChoice = 0x1; //SIGNER_SUBJECT_FILE
 IntPtr assemblyFilePtr = Marshal.StringToHGlobalUni(pathToAssembly);
 SIGNER_FILE_INFO fileInfo = new SIGNER_FILE_INFO
 {
 cbSize = (****)Marshal.SizeOf(typeof(SIGNER_FILE_INFO)),
 pwszFileName = assemblyFilePtr,
 hFile = IntPtr.Zero
 };
 info.Union1 = new SIGNER_SUBJECT_INFO.SubjectChoiceUnion
 {
 pSignerFileInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SIGNER_FILE_INFO)))
 };
 Marshal.StructureToPtr(fileInfo, info.Union1.pSignerFileInfo, false);
 IntPtr pSubjectInfo = Marshal.AllocHGlobal(Marshal.SizeOf(info));
 Marshal.StructureToPtr(info, pSubjectInfo, false);
 return pSubjectInfo;
 }
 private static X509Certificate2 FindCertByThumbprint(string thumbprint)
 {
 try
 {
 // Remove spaces convert to upper. Windows 10 (preview) and Windows 8 will not return a cert
 // unless it is a perfect match with no spaces and all uppercase characters.
 string thumbprintFixed = thumbprint.Replace(" ", string.Empty).ToUpperInvariant();
 // Check common store locations for the corresponding code-signing cert.
 X509Store[] stores = new X509Store[4] { new X509Store(StoreName.My, StoreLocation.CurrentUser),
 new X509Store(StoreName.My, StoreLocation.LocalMachine),
 new X509Store(StoreName.TrustedPublisher, StoreLocation.CurrentUser),
 new X509Store(StoreName.TrustedPublisher, StoreLocation.LocalMachine) };
 foreach (X509Store store in stores)
 {
 store.Open(OpenFlags.ReadOnly);
 // Find the cert!
 X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprintFixed, false);
 store.Close();
 // If we didn't find the cert, try the next store.
 if (certs.Count < 1)
 {
 continue;
 }
 // Return the cert (first one if there is more than one identical cert in the collection).
 return certs[0];
 }
 // No cert was found. Return null.
 throw new Exception(string.Format(@"A certificate matching the thumbprint: ""{0}"" could not be found. Make sure that a valid certificate matching the provided thumbprint is installed.", thumbprint));
 }
 catch (Exception e)
 {
 throw new Exception(string.Format("{0}", e.Message));
 }
 }
 private static IntPtr CreateSignerCert(X509Certificate2 cert)
 {
 SIGNER_CERT signerCert = new SIGNER_CERT
 {
 cbSize = (****)Marshal.SizeOf(typeof(SIGNER_CERT)),
 dwCertChoice = 0x2,
 Union1 = new SIGNER_CERT.SignerCertUnion
 {
 pCertStoreInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SIGNER_CERT_STORE_INFO)))
 },
 hwnd = IntPtr.Zero
 };
 const int X509_ASN_ENCODING = 0x00000001;
 const int PKCS_7_ASN_ENCODING = 0x00010000;
 IntPtr pCertContext = CertCreateCertificateContext(
 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
 cert.GetRawCertData(),
 cert.GetRawCertData().Length);
 SIGNER_CERT_STORE_INFO certStoreInfo = new SIGNER_CERT_STORE_INFO
 {
 cbSize = (****)Marshal.SizeOf(typeof(SIGNER_CERT_STORE_INFO)),
 pSigningCert = pCertContext,
 dwCertPolicy = 0x2, // SIGNER_CERT_POLICY_CHAIN
 hCertStore = IntPtr.Zero
 };
 Marshal.StructureToPtr(certStoreInfo, signerCert.Union1.pCertStoreInfo, false);
 IntPtr pSignerCert = Marshal.AllocHGlobal(Marshal.SizeOf(signerCert));
 Marshal.StructureToPtr(signerCert, pSignerCert, false);
 return pSignerCert;
 }
 private static IntPtr CreateSignerCert(string thumbprint)
 {
 SIGNER_CERT signerCert = new SIGNER_CERT
 {
 cbSize = (****)Marshal.SizeOf(typeof(SIGNER_CERT)),
 dwCertChoice = 0x2,
 Union1 = new SIGNER_CERT.SignerCertUnion
 {
 pCertStoreInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SIGNER_CERT_STORE_INFO)))
 },
 hwnd = IntPtr.Zero
 };
 const int X509_ASN_ENCODING = 0x00000001;
 const int PKCS_7_ASN_ENCODING = 0x00010000;
 X509Certificate2 cert = FindCertByThumbprint(thumbprint);
 IntPtr pCertContext = CertCreateCertificateContext(
 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
 cert.GetRawCertData(),
 cert.GetRawCertData().Length);
 SIGNER_CERT_STORE_INFO certStoreInfo = new SIGNER_CERT_STORE_INFO
 {
 cbSize = (****)Marshal.SizeOf(typeof(SIGNER_CERT_STORE_INFO)),
 pSigningCert = pCertContext,
 dwCertPolicy = 0x2, // SIGNER_CERT_POLICY_CHAIN
 hCertStore = IntPtr.Zero
 };
 Marshal.StructureToPtr(certStoreInfo, signerCert.Union1.pCertStoreInfo, false);
 IntPtr pSignerCert = Marshal.AllocHGlobal(Marshal.SizeOf(signerCert));
 Marshal.StructureToPtr(signerCert, pSignerCert, false);
 return pSignerCert;
 }
 private static IntPtr CreateSignerSignatureInfo()
 {
 SIGNER_SIGNATURE_INFO signatureInfo = new SIGNER_SIGNATURE_INFO
 {
 cbSize = (****)Marshal.SizeOf(typeof(SIGNER_SIGNATURE_INFO)),
 algidHash = 0x00008004, // CALG_SHA1
 dwAttrChoice = 0x0, // SIGNER_NO_ATTR
 pAttrAuthCode = IntPtr.Zero,
 psAuthenticated = IntPtr.Zero,
 psUnauthenticated = IntPtr.Zero
 };
 IntPtr pSignatureInfo = Marshal.AllocHGlobal(Marshal.SizeOf(signatureInfo));
 Marshal.StructureToPtr(signatureInfo, pSignatureInfo, false);
 return pSignatureInfo;
 }
 private static IntPtr GetProviderInfo(X509Certificate2 cert)
 {
 if (cert == null || !cert.HasPrivateKey)
 {
 return IntPtr.Zero;
 }
 ICspAsymmetricAlgorithm key = (ICspAsymmetricAlgorithm)cert.PrivateKey;
 const int PVK_TYPE_KEYCONTAINER = 2;
 if (key == null)
 {
 return IntPtr.Zero;
 }
 SIGNER_PROVIDER_INFO providerInfo = new SIGNER_PROVIDER_INFO
 {
 cbSize = (****)Marshal.SizeOf(typeof(SIGNER_PROVIDER_INFO)),
 pwszProviderName = Marshal.StringToHGlobalUni(key.CspKeyContainerInfo.ProviderName),
 dwProviderType = (****)key.CspKeyContainerInfo.ProviderType,
 dwPvkChoice = PVK_TYPE_KEYCONTAINER,
 Union1 = new SIGNER_PROVIDER_INFO.SignerProviderUnion
 {
 pwszKeyContainer = Marshal.StringToHGlobalUni(key.CspKeyContainerInfo.KeyContainerName)
 },
 };
 IntPtr pProviderInfo = Marshal.AllocHGlobal(Marshal.SizeOf(providerInfo));
 Marshal.StructureToPtr(providerInfo, pProviderInfo, false);
 return pProviderInfo;
 }
 // Use SignerSign
 private static void SignCode(IntPtr pSubjectInfo, IntPtr pSignerCert, IntPtr pSignatureInfo, IntPtr pProviderInfo)
 {
 int hResult = SignerSign(
 pSubjectInfo,
 pSignerCert,
 pSignatureInfo,
 pProviderInfo,
 null,
 IntPtr.Zero,
 IntPtr.Zero
 );
 if (hResult != 0)
 {
 // See if we can get anything useful. Jury still out on this one.
 Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
 }
 }
 // Use SignerSignEx
 private static void SignCode(**** dwFlags, IntPtr pSubjectInfo, IntPtr pSignerCert, IntPtr pSignatureInfo, IntPtr pProviderInfo, out SIGNER_CONTEXT signerContext)
 {
 int hResult = SignerSignEx(
 dwFlags,
 pSubjectInfo,
 pSignerCert,
 pSignatureInfo,
 pProviderInfo,
 null,
 IntPtr.Zero,
 IntPtr.Zero,
 out signerContext
 );
 if (hResult != 0)
 {
 // See if we can get anything useful. Jury still out on this one. 
 Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
 }
 }
 // Use SignerTimeStamp
 private static void TimeStampSignedCode(IntPtr pSubjectInfo, string timestampUrl)
 {
 int hResult = SignerTimeStamp(
 pSubjectInfo,
 timestampUrl,
 IntPtr.Zero,
 IntPtr.Zero
 );
 if (hResult != 0)
 {
 // We can't get anything useful from GetHRForLastWin32Error, so let throw our own.
 //Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
 throw new Exception(string.Format(@"""{0}"" could not be used at this time. If necessary, check the timestampUrl, internet connection, and try again.", timestampUrl));
 }
 }
 // Use SignerTimeStampEx
 private static void TimeStampSignedCode(**** dwFlags, IntPtr pSubjectInfo, string timestampUrl, out SIGNER_CONTEXT signerContext)
 {
 int hResult = SignerTimeStampEx(
 dwFlags,
 pSubjectInfo,
 timestampUrl,
 IntPtr.Zero,
 IntPtr.Zero,
 out signerContext
 );
 if (hResult != 0)
 {
 // We can't get anything useful from GetHRForLastWin32Error, so let throw our own.
 //Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
 throw new Exception(string.Format(@"""{0}"" could not be used at this time. If necessary, check the timestampUrl, internet connection, and try again.", timestampUrl));
 }
 }
 #endregion
 }
}

licensed under cc by-sa 3.0 with attribution.