CryptUIWizDigitalSign locks a file if it fails

Alexnbg72 1 Reputation point
2020-07-28T11:02:20.867+00:00

Hi!

I have a problem using the CryptUIWizDigitalSign-Function in C#.

If I pass a file via CRYPTUI_WIZ_DIGITAL_SIGN_INFO-Structure that cannot be signed (i.e. Text-File), the function fails.
But if i try to delete the file after the failure, the file is locked until the process ends.
It the operation succeeds, the file is freed and everything is OK.

So the question is: how can I free the file, when the function fails?

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

namespace SignToolNet
{
  public class Program
  {
    public const int CRYPTUI_WIZ_NO_UI = 1;

    public const int CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE = 1;

    public const int CRYPTUI_WIZ_DIGITAL_SIGN_CERT = 1;

    public const int CRYPTUI_WIZ_DIGITAL_SIGN_COMMERCIAL = 1;


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

    [StructLayout(LayoutKind.Sequential)]
    public struct CRYPTUI_WIZ_DIGITAL_SIGN_EXTENDED_INFO
    {
      public int dwSize;
      public int dwAttrFlags;
      [MarshalAs(UnmanagedType.LPWStr)]
      public string pwszDescription;
      [MarshalAs(UnmanagedType.LPWStr)]
      public string pwszMoreInfoLocation;
      [MarshalAs(UnmanagedType.LPStr)]
      public string pszHashAlg;
      [MarshalAs(UnmanagedType.LPWStr)]
      public string pwszSigningCertDisplayString;
      public IntPtr hAdditionalCertStore;
      public IntPtr psAuthenticated;
      public IntPtr psUnauthenticated;
    }


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

    [DllImport("Cryptui.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern bool CryptUIWizDigitalSign(
        int dwFlags,
        IntPtr hwndParent,
        [MarshalAs(UnmanagedType.LPWStr)]
            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);

    public static void Main(string[] args)
    {
      string fileToSign = "d:\\test.txt";
      string certThumbprint = "cert";
      string timestampUrl = "http://timestamp.comodoca.com";
      try
      {
        if (!System.IO.File.Exists(fileToSign)) return;
        X509Store store = new X509Store(StoreLocation.CurrentUser);
        store.Open(OpenFlags.ReadOnly);
        var list = store.Certificates.Find(X509FindType.FindByThumbprint, certThumbprint, false);
        if (list.Count == 0) return;
        var cert = list[0];
        var digitalSignInfo = new CRYPTUI_WIZ_DIGITAL_SIGN_INFO();
        digitalSignInfo.dwSize = Marshal.SizeOf(digitalSignInfo);
        digitalSignInfo.dwSubjectChoice = CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE;
        digitalSignInfo.pwszFileName = fileToSign;
        digitalSignInfo.dwSigningCertChoice = CRYPTUI_WIZ_DIGITAL_SIGN_CERT;
        digitalSignInfo.pSigningCertContext = cert.Handle;
        digitalSignInfo.pwszTimestampURL = timestampUrl;
        digitalSignInfo.dwAdditionalCertChoice = 0;
        digitalSignInfo.pSignExtInfo = IntPtr.Zero;

        IntPtr psignContext = IntPtr.Zero;

        try
        {

          IntPtr pSignContext = IntPtr.Zero;
          bool result = CryptUIWizDigitalSign(CRYPTUI_WIZ_NO_UI, IntPtr.Zero, string.Empty, ref digitalSignInfo, ref pSignContext);


          if (pSignContext!=IntPtr.Zero && !CryptUIWizFreeDigitalSignContext(pSignContext))
            throw new Win32Exception(Marshal.GetLastWin32Error(), "CryptUIWizFreeDigitalSignContext");
          if (!result)
          {
            throw new Win32Exception(Marshal.GetLastWin32Error());
          }
        }
        finally
        {
          Marshal.FreeHGlobal(digitalSignInfo.pSignExtInfo);
        }
      }
      catch (Win32Exception ex)
      {
        Console.WriteLine(string.Format("{0} {1:x}", ex.Message, ex.NativeErrorCode));
        Environment.Exit(1);
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.Message);
        Environment.Exit(1);
      }
    }
  }
}

I am running Windows 10 1909 Build 18363.959 and Windows Server 2019 1809 Build 17763.1339.

Cheers
Alex

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,420 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Viorel 112.1K Reputation points
    2020-08-13T10:02:46.657+00:00

    If your experiments shown that the file can be deleted after closing the process, then maybe consider the next temporary workaround.

    Instead of calling CryptUIWizDigitalSign from your main application, create an additional small helper application that calls CryptUIWizDigitalSign. You can change the .exe extension to something else. Then use the Process class to start it and wait with WaitForExit. Some parameters can be passed via command-line arguments. The result can be returned via ExitCode. Then delete the bad file.

    Instead of creating the helper application, you can re-invoke your main application, passing some command-line argument to distinguish this special mode.

    0 comments No comments