views:

51

answers:

1

I am trying to digitally sign files using the CryptUIWizDigitalSign function from a .NET 2.0 application compiled to AnyCPU. The call works fine when running on x86 but fails on x64, it also works on an x64 OS when compiled to x86. Any idea on how to better marshall or call from x64?

The Win32exception returned is "Error encountered during digital signing of the file ..." with a native error code of -2146762749.

The relevant portion of the code are:

[StructLayout(LayoutKind.Sequential)]
 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;
 [MarshalAs(UnmanagedType.LPWStr)]
 public string pwszTimestampURL;
 public Int32 dwAdditionalCertChoice;
 public IntPtr pSignExtInfo;
}

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

CRYPTUI_WIZ_DIGITAL_SIGN_INFO digitalSignInfo = new CRYPTUI_WIZ_DIGITAL_SIGN_INFO();
digitalSignInfo = new CRYPTUI_WIZ_DIGITAL_SIGN_INFO();
digitalSignInfo.dwSize = Marshal.SizeOf(digitalSignInfo);
digitalSignInfo.dwSubjectChoice = 1;
digitalSignInfo.dwSigningCertChoice = 1;
digitalSignInfo.pSigningCertContext = pSigningCertContext;
digitalSignInfo.pwszTimestampURL = timestampUrl;
digitalSignInfo.dwAdditionalCertChoice = 0;
digitalSignInfo.pSignExtInfo = IntPtr.Zero;
digitalSignInfo.pwszFileName = filepath;
CryptUIWizDigitalSign(1, IntPtr.Zero, null, ref digitalSignInfo, ref pSignContext));

And here is how the SigningCertContext is retrieved (minus various error handling)

public IntPtr GetCertContext(String pfxfilename, String pswd)
 IntPtr hMemStore = IntPtr.Zero;
 IntPtr hCertCntxt = IntPtr.Zero;
 IntPtr pProvInfo = IntPtr.Zero;
 uint provinfosize = 0;
 try {                
  byte[] pfxdata = PfxUtility.GetFileBytes(pfxfilename);
  CRYPT_DATA_BLOB ppfx = new CRYPT_DATA_BLOB();
  ppfx.cbData = pfxdata.Length;
  ppfx.pbData = Marshal.AllocHGlobal(pfxdata.Length);
  Marshal.Copy(pfxdata, 0, ppfx.pbData, pfxdata.Length);
  hMemStore = Win32.PFXImportCertStore(ref ppfx, pswd, CRYPT_USER_KEYSET);
  pswd = null;
  if (hMemStore != IntPtr.Zero) {
   Marshal.FreeHGlobal(ppfx.pbData);
   while ((hCertCntxt = Win32.CertEnumCertificatesInStore(hMemStore, hCertCntxt)) != IntPtr.Zero) {
    if (Win32.CertGetCertificateContextProperty(hCertCntxt, CERT_KEY_PROV_INFO_PROP_ID, IntPtr.Zero, ref provinfosize))
     pProvInfo = Marshal.AllocHGlobal((int)provinfosize);
    else
     continue;
    if (Win32.CertGetCertificateContextProperty(hCertCntxt, CERT_KEY_PROV_INFO_PROP_ID, pProvInfo, ref provinfosize))
     break;
   }               
  }
finally {
 if (pProvInfo != IntPtr.Zero)
  Marshal.FreeHGlobal(pProvInfo);
 if (hMemStore != IntPtr.Zero)
  Win32.CertCloseStore(hMemStore, 0);
}
 return hCertCntxt;
}
A: 

When applications are compiled with AnyCPU target, they will load as 32-bit on 32 bit OS, and 64-bit on 64 bit OS. You cannot load a 32-bit DLL from a 64-bit process.

You have said that it "works" when compiling to x86. Can you just do this? This leads me to believe that the cryptui.dll in your search path is a 32-bit DLL.

maxwellb