title: C# implementation to get SignedCms from signed file
body: <p>I am having an implementation using C++ CRYPT32.DLL to extract a SignedCms object from a signed c# assembly dll.</p>
  
  <p><a href="https://i.stack.imgur.com/cRn0Y.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/cRn0Y.png" alt="signed c# assembly"></a></p>
  
  <p>The certificate used to sign the dll is expired but has a valid certificate chain inside. The important thing is that the certificate consists of three certificates which I all want to extract.</p>
  
  <p><a href="https://i.stack.imgur.com/tvcC1.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/tvcC1.png" alt="CertificateChain"></a></p>
  
  <pre><code>    private static readonly int CERT_QUERY_OBJECT_FILE = 0x00000001;
      private static readonly int CERT_QUERY_CONTENT_FLAG_ALL = 0x00003ffe;
      private static readonly int CERT_QUERY_FORMAT_FLAG_ALL = 0x0000000e;
      private static readonly int CMSG_ENCODED_MESSAGE = 29;
  
      [DllImport("CRYPT32.DLL", EntryPoint = "CryptQueryObject", CharSet = CharSet.Auto, SetLastError = true)]
      private static extern bool CryptQueryObject(
          int dwObjectType,
          IntPtr pvObject,
          int dwExpectedContentTypeFlags,
          int dwExpectedFormatTypeFlags,
          int dwFlags,
          IntPtr pdwMsgAndCertEncodingType,
          IntPtr pdwContentType,
          IntPtr pdwFormatType,
          IntPtr phCertStore,
          IntPtr phMsg,
          IntPtr ppvContext
      );
  
      [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
      private static extern bool CryptMsgGetParam(
          IntPtr hCryptMsg, int dwParamType, int dwIndex, IntPtr pvData, ref int pcbData );
  
      public static SignedCms GetSignedCmsFromFile(string fileName)
      {
          var pvObject = Marshal.StringToHGlobalUni(fileName);
          var phMessage = Marshal.AllocHGlobal(IntPtr.Size);
          var pvData = IntPtr.Zero;
  
          try
          {
              SignedCms signedCms = null;
              var success = CryptQueryObject(
                  CERT_QUERY_OBJECT_FILE,
                  pvObject,
                  CERT_QUERY_CONTENT_FLAG_ALL,
                  CERT_QUERY_FORMAT_FLAG_ALL,
                  0,
                  IntPtr.Zero,
                  IntPtr.Zero,
                  IntPtr.Zero,
                  IntPtr.Zero,
                  phMessage,
                  IntPtr.Zero);
  
              if (success)
              {
                  var hMessage = Marshal.ReadIntPtr(phMessage);
                  var cbData = 0;
                  success = CryptMsgGetParam(
                      hMessage,
                      CMSG_ENCODED_MESSAGE,
                      0,
                      IntPtr.Zero,
                      ref cbData);
  
                  if (success)
                  {
                      var data = new byte[cbData];
                      pvData = Marshal.AllocHGlobal(sizeof(byte) * data.Length);
  
                      success = CryptMsgGetParam(
                          hMessage,
                          CMSG_ENCODED_MESSAGE,
                          0,
                          pvData,
                          ref cbData);
  
                      if (success)
                      {
                          Marshal.Copy(pvData, data, 0, cbData);
                          signedCms = new SignedCms();
  
                          try
                          {
                              signedCms.Decode(data);
                              File.WriteAllBytes(fileName + ".export", data);
                          }
                          catch (CryptographicException)
                          {
                              signedCms = null;
                          }
                      }
                  }
              }
  
              return signedCms;
          }
          finally
          {
              if (pvObject != IntPtr.Zero)
                  Marshal.FreeHGlobal(pvObject);
  
              if (phMessage != IntPtr.Zero)
                  Marshal.FreeHGlobal(phMessage);
  
              if (pvData != IntPtr.Zero)
                  Marshal.FreeHGlobal(pvData);
          }
      }
  </code></pre>
  
  <p>The function works very well but since the crypt32.dll function is depricated I am looking for a pure C# implementation that is doing the same and provides the SignedCms object.</p>
  
  <p>The closest what I could find so far is the X509Certificate.</p>
  
  <pre><code>X509Certificate2 cert = new X509Certificate2(fileName)
  </code></pre>
  
  <p><a href="https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/nf-wincrypt-cryptqueryobject" rel="nofollow noreferrer">CryptQueryObject function</a></p>
  
  <hr>
  
  <p>Solution</p>
  
  <p>Thanks to the post below I could implement a pure C# method to extract the SignedCms.</p>
  
  <p>The only thing that is not fitting perfectly is that the startindex is off by 8 bytes and the size is 10 bytes smaller.</p>
  
  <pre><code>    public static SignedCms GetSignedCmsFromFile(string fileName)
      {
          var result = new SignedCms();
  
          uint startIndex = 0;
          uint size = 0;
  
          var reader = new PeHeaderReader(fileName);
          if (reader.Is32BitHeader)
          {
              startIndex = reader.OptionalHeader32.CertificateTable.VirtualAddress;
              size = reader.OptionalHeader32.CertificateTable.Size;
          }
          else
          {
              startIndex = reader.OptionalHeader64.CertificateTable.VirtualAddress;
              size = reader.OptionalHeader64.CertificateTable.Size;
          }
  
          //var data = File.ReadAllBytes(fileName).Skip((int)startIndex).Take((int)size).ToArray();
  
          //Somehow the start index and size are not fitting perfectly and I have to adapt them
          var data = File.ReadAllBytes(fileName).Skip((int)startIndex + 8).Take((int)size - 10).ToArray();
  
          result.Decode(data);
  
          return result;
      }
  </code></pre>