Decrypt my World

Cryptography, Security, Debugging and more!

How to sign a message and verify a message signature (C#)

How to sign a message and verify a message signature (C#)

  • Comments 1

Hi all, welcome back,

Today we'll do some more P/Invoke with CryptoAPI and C#. The following sample is a conversion to C# of the C++ sample in Example C Program: Signing a Message and Verifying a Message Signature:

<SAMPLE file="Class1.cs">

using System;
using System.Text;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
  class Class1
  {
    [STAThread]
    static void Main(string[] args)
    {
      // Parameters.
      //
      String sSignerName = "ALEJANDRO CAMPOS MAGENCIO";
      String sMessage = "CryptoAPI is a good way to handle security";

      // Variables.
      //
      Byte[] pbMessage = null;
      Int32 cbMessage = 0;
      IntPtr[] MessageArray = null;
      Int32[] MessageSizeArray = null;
      IntPtr hStoreHandle = IntPtr.Zero;   
      IntPtr pSignerCert = IntPtr.Zero;
      Crypto.CRYPT_SIGN_MESSAGE_PARA SigParams;
      Boolean res = false;
      Int32 cbSignedMessageBlob = 0;
      Byte[] pbSignedMessageBlob = null;
      Crypto.CRYPT_VERIFY_MESSAGE_PARA VerifyParams;
      Int32 cbDecodedMessageBlob = 0;
      Byte[] pbDecodedMessageBlob = null;

      try 
      {
        // Begin processing. Display the original message.
        //
        Console.WriteLine("-------------------------------------");
        Console.WriteLine("MESSAGE TO SIGN:\n");
        Console.WriteLine(sMessage + "\n\n");

        // Size of message.
        //
        pbMessage = (new UnicodeEncoding()).GetBytes(sMessage);
        cbMessage = pbMessage.Length;

        // Create the MessageArray and the MessageSizeArray.
        //        
        MessageArray = new IntPtr[1];
        MessageArray[0] = Marshal.AllocHGlobal(pbMessage.Length);
        Marshal.Copy(pbMessage, 0, MessageArray[0], pbMessage.Length);
        MessageSizeArray = new Int32[1];
        MessageSizeArray[0] = cbMessage;

        // Open a certificate store.
        //
        hStoreHandle = Crypto.CertOpenStore(
          Crypto.CERT_STORE_PROV_SYSTEM,
          0,
          IntPtr.Zero,
          Crypto.CERT_SYSTEM_STORE_CURRENT_USER,
          Crypto.CERT_PERSONAL_STORE_NAME
        );
        if (hStoreHandle == IntPtr.Zero)
        {
          throw new Exception("CertOpenStore error", new Win32Exception(Marshal.GetLastWin32Error()));
        }

        // Get a pointer to the signer's certificate.
        // This certificate must have access to the signer's private key.
        pSignerCert = Crypto.CertFindCertificateInStore(
          hStoreHandle,
          Crypto.MY_TYPE,
          0,
          Crypto.CERT_FIND_SUBJECT_STR,
          sSignerName,
          IntPtr.Zero
        );
        if (pSignerCert == IntPtr.Zero)
        {
          throw new Exception("CertFindCertificateInStore error", new Win32Exception(Marshal.GetLastWin32Error()));
        }

        // Initialize the signature structure.
        //
        SigParams = new Crypto.CRYPT_SIGN_MESSAGE_PARA();
        SigParams.cbSize = Marshal.SizeOf(SigParams);
        SigParams.dwMsgEncodingType = Crypto.MY_TYPE;
        SigParams.pSigningCert = pSignerCert;
        SigParams.HashAlgorithm.pszObjId = Crypto.szOID_OIWSEC_sha1;
        SigParams.HashAlgorithm.Parameters.pbData = IntPtr.Zero;
        SigParams.HashAlgorithm.Parameters.cbData = 0;
        SigParams.pvHashAuxInfo = IntPtr.Zero;
        SigParams.cMsgCert = 1;

        GCHandle GC = GCHandle.Alloc(pSignerCert, GCHandleType.Pinned);
        SigParams.rgpMsgCert = GC.AddrOfPinnedObject();
        GC.Free();

        SigParams.cMsgCrl = 0;
        SigParams.rgpMsgCrl = IntPtr.Zero;
        SigParams.cAuthAttr = 0;
        SigParams.rgAuthAttr = IntPtr.Zero;
        SigParams.cUnauthAttr = 0;
        SigParams.rgUnauthAttr = IntPtr.Zero;
        SigParams.dwFlags = 0;
        SigParams.dwInnerContentType = 0;

        // With two calls to CryptSignMessage, sign the message.
        // First, get the size of the output signed BLOB.
        //
        res = Crypto.CryptSignMessage(
          ref SigParams,      // Signature parameters
          false,          // Not detached
          1,            // Number of messages
          MessageArray,      // Messages to be signed
          MessageSizeArray,    // Size of messages
          null,          // Buffer for signed message
          ref cbSignedMessageBlob  // Size of buffer
        );
        if (res == false)
        {
          throw new Exception("CryptSignMessage error", new Win32Exception(Marshal.GetLastWin32Error()));
        }

        // Allocate memory for the signed BLOB.
        //
        pbSignedMessageBlob = new Byte[cbSignedMessageBlob];

        // Get the SignedMessageBlob.
        //
        res = Crypto.CryptSignMessage(
          ref SigParams,      // Signature parameters
          false,          // Not detached
          1,            // Number of messages
          MessageArray,      // Messages to be signed
          MessageSizeArray,    // Size of messages
          pbSignedMessageBlob,  // Buffer for signed message
          ref cbSignedMessageBlob // Size of buffer
        );
        if (res == false)
        {
          throw new Exception("CryptSignMessage error", new Win32Exception(Marshal.GetLastWin32Error()));
        }

        // pbSignedMessageBlob points to the signed BLOB. Display the signature.
        //
        Console.WriteLine("-------------------------------------");
        Console.WriteLine("SIGNATURE:\n");
        Console.WriteLine(Convert.ToBase64String(pbSignedMessageBlob) + "\n\n");

        //  Verify the message signature. Usually, this
        //  would be done in a separate program. 
        //

        //  Initialize the VerifyParams data structure.
        //
        VerifyParams = new Crypto.CRYPT_VERIFY_MESSAGE_PARA();
        VerifyParams.cbSize = Marshal.SizeOf(VerifyParams);
        VerifyParams.dwMsgAndCertEncodingType = Crypto.MY_TYPE;
        VerifyParams.hCryptProv = IntPtr.Zero;
        VerifyParams.pfnGetSignerCertificate = IntPtr.Zero;
        VerifyParams.pvGetArg = IntPtr.Zero;

        // With two calls to CryptVerifyMessageSignature, verify and decode
        // the signed message.
        // First, call CryptVerifyMessageSignature to get the length of the
        // buffer needed to hold the decoded message.
        //
        res = Crypto.CryptVerifyMessageSignature(
          ref VerifyParams,      // Verify parameters.
          0,              // Signer index.
          pbSignedMessageBlob,    // Pointer to signed BLOB.
          cbSignedMessageBlob,    // Size of signed BLOB.
          null,            // Buffer for decoded message.
          ref cbDecodedMessageBlob,  // Size of buffer.
          IntPtr.Zero          // Pointer to signer certificate.
        );
        if (res == false)
        {
          throw new Exception("CryptVerifyMessageSignature error", new Win32Exception(Marshal.GetLastWin32Error()));
        }

        //   Allocate memory for the buffer.
        //
        pbDecodedMessageBlob = new Byte[cbDecodedMessageBlob];

        //  Call CryptVerifyMessageSignature again to copy the message into
        //  the buffer.
        //
        res = Crypto.CryptVerifyMessageSignature(
          ref VerifyParams,      // Verify parameters.
          0,              // Signer index.
          pbSignedMessageBlob,    // Pointer to signed BLOB.
          cbSignedMessageBlob,    // Size of signed BLOB.
          pbDecodedMessageBlob,    // Buffer for decoded message.
          ref cbDecodedMessageBlob,  // Size of buffer.
          IntPtr.Zero          // Pointer to signer certificate.
        );
        if (res == false)
        {
          throw new Exception("CryptVerifyMessageSignature error", new Win32Exception(Marshal.GetLastWin32Error()));
        } 
        else 
        {
          // Display attached message to signature.
          //
          Console.WriteLine("-------------------------------------");
          Console.WriteLine("SIGNATURE VERIFIED!!!\n\n");

          Console.WriteLine("-------------------------------------");
          Console.WriteLine("ATTACHED MESSAGE:\n");
          Console.WriteLine((new UnicodeEncoding()).GetString(pbDecodedMessageBlob) + "\n\n");
        }
      }
      catch (Exception ex)
      {
        // Any errors? Show them.
        //
        if (ex.InnerException == null) 
        {
          Console.WriteLine(ex.Message + "\n\n");
        }
        else 
        {
          Console.WriteLine(ex.Message + " --> " + ex.InnerException.Message + "\n\n");
        }
      }
      finally
      {
        // Clean up and free memory.
        //
        if (MessageArray[0] != IntPtr.Zero) 
        {
          Marshal.FreeHGlobal(MessageArray[0]);
        }
        if (pSignerCert != IntPtr.Zero)
        {
          Crypto.CertFreeCertificateContext(pSignerCert);
        }
        if (hStoreHandle != IntPtr.Zero) 
        {
          Crypto.CertCloseStore(
            hStoreHandle, 
            Crypto.CERT_CLOSE_STORE_CHECK_FLAG
          );
        }
      }

      Console.WriteLine("<<Press ENTER to continue>>" + "\n");
      Console.ReadLine();
    }
  }
}

</SAMPLE>

<SAMPLE file="Crypto.cs">

using System;
using System.Runtime.InteropServices;

public class Crypto
{
  #region CONSTS

  // #define CERT_PERSONAL_STORE_NAME      L"My"
  public const string CERT_PERSONAL_STORE_NAME = "My";

  // #define CERT_COMPARE_NAME   2
  public const Int32 CERT_COMPARE_NAME = 2;

  // #define CERT_INFO_SUBJECT_FLAG  7
  public const Int32 CERT_INFO_SUBJECT_FLAG = 7;

  // #define CERT_COMPARE_SHIFT        16
  public const Int32 CERT_COMPARE_SHIFT = 16;

  // #define CERT_FIND_SUBJECT_NAME    (CERT_COMPARE_NAME << CERT_COMPARE_SHIFT | CERT_INFO_SUBJECT_FLAG)
  public const Int32 CERT_FIND_SUBJECT_NAME = 
    (CERT_COMPARE_NAME << CERT_COMPARE_SHIFT) | CERT_INFO_SUBJECT_FLAG;

  // #define CERT_COMPARE_NAME_STR_W     8
  public const Int32 CERT_COMPARE_NAME_STR_W = 8;

  // #define CERT_FIND_SUBJECT_STR_W     //   (CERT_COMPARE_NAME_STR_W << CERT_COMPARE_SHIFT | CERT_INFO_SUBJECT_FLAG)
  public const Int32 CERT_FIND_SUBJECT_STR_W = 
    (CERT_COMPARE_NAME_STR_W << CERT_COMPARE_SHIFT) | CERT_INFO_SUBJECT_FLAG;

  // #define CERT_FIND_SUBJECT_STR CERT_FIND_SUBJECT_STR_W
  public const Int32 CERT_FIND_SUBJECT_STR = CERT_FIND_SUBJECT_STR_W;

  // #define CERT_STORE_PROV_SYSTEM_W      ((LPCSTR) 10)
  public const Int32 CERT_STORE_PROV_SYSTEM_W = 10;

  // #define CERT_STORE_PROV_SYSTEM        CERT_STORE_PROV_SYSTEM_W
  public const Int32 CERT_STORE_PROV_SYSTEM = CERT_STORE_PROV_SYSTEM_W;
  
  // #define CERT_SYSTEM_STORE_CURRENT_USER_ID     1
  public const Int32 CERT_SYSTEM_STORE_CURRENT_USER_ID = 1;

  // #define CERT_SYSTEM_STORE_LOCATION_SHIFT      16
  public const Int32 CERT_SYSTEM_STORE_LOCATION_SHIFT = 16;

  // #define CERT_SYSTEM_STORE_CURRENT_USER          //   (CERT_SYSTEM_STORE_CURRENT_USER_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT)
  public const Int32 CERT_SYSTEM_STORE_CURRENT_USER = 
    CERT_SYSTEM_STORE_CURRENT_USER_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT;

  // #define CERT_CLOSE_STORE_CHECK_FLAG       0x00000002
  public const Int32 CERT_CLOSE_STORE_CHECK_FLAG = 0x00000002;

  // #define ALG_CLASS_HASH                  (4 << 13)
  // #define ALG_TYPE_ANY                    (0)
  // #define ALG_SID_SHA1                    4
  // #define CALG_SHA1               (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA1)
  public const Int32 CALG_SHA1 = (4 << 13) | 4;

  // #define ALG_CLASS_SIGNATURE             (1 << 13)
  // #define ALG_TYPE_RSA                    (2 << 9)
  // #define ALG_SID_RSA_ANY                 0
  // #define CALG_RSA_SIGN           (ALG_CLASS_SIGNATURE | ALG_TYPE_RSA | ALG_SID_RSA_ANY)
  public const Int32 CALG_RSA_SIGN = (1 << 13) | (2 << 9);

  // #define PROV_RSA_FULL           1
  public const Int32 PROV_RSA_FULL = 0x00000001;

  // #define CRYPT_VERIFYCONTEXT     0xF0000000
  public const UInt32 CRYPT_VERIFYCONTEXT = 0xF0000000; //No private key access required

  // #define X509_ASN_ENCODING           0x00000001
  public const Int32 X509_ASN_ENCODING = 0x00000001;

  // #define PKCS_7_ASN_ENCODING         0x00010000
  public const Int32 PKCS_7_ASN_ENCODING = 0x00010000;

  // #define MY_TYPE       (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
  public const Int32 MY_TYPE = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;

  // #define HP_HASHVAL              0x0002
  public const Int32 HP_HASHVAL = 0x00000002;

  // #define HP_HASHSIZE             0x0004
  public const Int32 HP_HASHSIZE = 0x00000004;

  // #define PUBLICKEYBLOBEX 0xA
  public const Int32 PUBLICKEYBLOBEX = 0x0A;

  // #define PUBLICKEYBLOB           0x6
  public const Int32 PUBLICKEYBLOB = 0x06;

  // #define CUR_BLOB_VERSION 0x02
  public const Int32 CUR_BLOB_VERSION = 0x02;

  // #define CRYPT_EXPORTABLE        0x00000001
  public const Int32 CRYPT_EXPORTABLE = 0x00000001;

  // #define szOID_RSA_MD5           "1.2.840.113549.2.5"
  public const String szOID_RSA_MD5 = "1.2.840.113549.2.5";

  // #define szOID_RSA_MD5RSA        "1.2.840.113549.1.1.4"
  public const String szOID_RSA_MD5RSA = "1.2.840.113549.1.1.4";

  // #define szOID_OIWSEC_sha1       "1.3.14.3.2.26"
  public const String szOID_OIWSEC_sha1 = "1.3.14.3.2.26";

  #endregion

  #region STRUCTS

  // typedef struct _PUBLICKEYSTRUC 
  // {
  //    BYTE bType;  
  //    BYTE bVersion;  
  //    WORD reserved;  
  //    ALG_ID aiKeyAlg;
  // } BLOBHEADER, PUBLICKEYSTRUC;
  [StructLayout(LayoutKind.Sequential)]
  public struct PUBLICKEYSTRUC
  {
    public Byte bType;
    public Byte bVersion;
    public Int16 reserved;
    public Int32 aiKeyAlg;
  }

  // typedef struct _RSAPUBKEY 
  // {
  //    DWORD magic;
  //    DWORD bitlen;
  //    DWORD pubexp;
  // } RSAPUBKEY;
  [StructLayout(LayoutKind.Sequential)]
  public struct RSAPUBKEY 
  {
    public Int32 magic;
    public Int32 bitlen;
    public Int32 pubexp;
  }

  // typedef struct _CRYPTOAPI_BLOB 
  // {
  //    DWORD   cbData;
  //    BYTE    *pbData;
  // } CRYPT_HASH_BLOB, CRYPT_INTEGER_BLOB, 
  //   CRYPT_OBJID_BLOB, CERT_NAME_BLOB;
  [StructLayout(LayoutKind.Sequential)]
  public struct CRYPTOAPI_BLOB
  {
    public Int32 cbData;
    public IntPtr pbData;
  }

  // typedef struct _CRYPT_ALGORITHM_IDENTIFIER 
  // {
  //    LPSTR pszObjId;
  //    CRYPT_OBJID_BLOB Parameters;
  // } CRYPT_ALGORITHM_IDENTIFIER;
  [StructLayout(LayoutKind.Sequential)]
  public struct CRYPT_ALGORITHM_IDENTIFIER
  {
    [MarshalAs(UnmanagedType.LPStr)]public String pszObjId;
    public CRYPTOAPI_BLOB Parameters;
  }

  // typedef struct _CRYPT_SIGN_MESSAGE_PARA 
  // {
  //    DWORD cbSize;
  //    DWORD dwMsgEncodingType;
  //    PCCERT_CONTEXT pSigningCert;
  //    CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
  //    void *pvHashAuxInfo;
  //    DWORD cMsgCert;
  //    PCCERT_CONTEXT *rgpMsgCert;
  //    DWORD cMsgCrl;
  //    PCCRL_CONTEXT *rgpMsgCrl;
  //    DWORD cAuthAttr;
  //    PCRYPT_ATTRIBUTE rgAuthAttr;
  //    DWORD cUnauthAttr;
  //    PCRYPT_ATTRIBUTE rgUnauthAttr;
  //    DWORD dwFlags;
  //    DWORD dwInnerContentType;
  // } CRYPT_SIGN_MESSAGE_PARA;
  [StructLayout(LayoutKind.Sequential)]
  public struct CRYPT_SIGN_MESSAGE_PARA 
  {
    public Int32 cbSize;
    public Int32 dwMsgEncodingType;
    public IntPtr pSigningCert;
    public CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
    public IntPtr pvHashAuxInfo;
    public Int32 cMsgCert;
    public IntPtr rgpMsgCert;
    public Int32 cMsgCrl;
    public IntPtr rgpMsgCrl;
    public Int32 cAuthAttr;
    public IntPtr rgAuthAttr;
    public Int32 cUnauthAttr;
    public IntPtr rgUnauthAttr;
    public Int32 dwFlags;
    public Int32 dwInnerContentType;
  }

  // typedef struct _CRYPT_VERIFY_MESSAGE_PARA 
  // {
  //    DWORD cbSize;
  //    DWORD dwMsgAndCertEncodingType;
  //    HCRYPTPROV hCryptProv;
  //    PFN_CRYPT_GET_SIGNER_CERTIFICATE pfnGetSignerCertificate;
  //    void *pvGetArg;
  // } CRYPT_VERIFY_MESSAGE_PARA;
  [StructLayout(LayoutKind.Sequential)]
  public struct CRYPT_VERIFY_MESSAGE_PARA 
  {
    public Int32 cbSize;
    public Int32 dwMsgAndCertEncodingType;
    public IntPtr hCryptProv;
    public IntPtr pfnGetSignerCertificate;
    public IntPtr pvGetArg;
  }

  #endregion

  #region FUNCTIONS (IMPORTS)

  // HCERTSTORE WINAPI CertOpenStore(
  //    LPCSTR lpszStoreProvider,
  //    DWORD dwMsgAndCertEncodingType,
  //    HCRYPTPROV hCryptProv,
  //    DWORD dwFlags,
  //    const void* pvPara
  // );
  [DllImport("Crypt32.dll", CharSet=CharSet.Auto, SetLastError=true)]
  public static extern IntPtr CertOpenStore(
    Int32 lpszStoreProvider,
    Int32 dwMsgAndCertEncodingType,
    IntPtr hCryptProv, 
    Int32 dwFlags, 
    String pvPara
  ); 

  // HCERTSTORE WINAPI CertOpenSystemStore(
  //    HCRYPTPROV hprov,
  //    LPTCSTR szSubsystemProtocol
  // );
  [DllImport("Crypt32.dll", CharSet=CharSet.Auto, SetLastError=true)]
  public static extern IntPtr CertOpenSystemStore(
    IntPtr hprov, 
    String szSubsystemProtocol
  );

  // BOOL WINAPI CertCloseStore(
  //    HCERTSTORE hCertStore,
  //    DWORD dwFlags
  // );
  [DllImport("Crypt32.dll", SetLastError=true)]
  public static extern Boolean CertCloseStore( 
    IntPtr hCertStore,
    Int32 dwFlags
  );     

  // BOOL WINAPI CryptAcquireContext(
  //    HCRYPTPROV* phProv,
  //    LPCTSTR pszContainer,
  //    LPCTSTR pszProvider,
  //    DWORD dwProvType,
  //    DWORD dwFlags
  // );
  [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
  public static extern bool CryptAcquireContext(
    ref IntPtr hProv,
    String pszContainer,
    String pszProvider,
    Int32 dwProvType,
    Int32 dwFlags
  );

  // BOOL WINAPI CryptCreateHash(
  //    HCRYPTPROV hProv,
  //    ALG_ID Algid,
  //    HCRYPTKEY hKey,
  //    DWORD dwFlags,
  //    HCRYPTHASH* phHash
  // );
  [DllImport("advapi32.dll", SetLastError=true)]
  public static extern bool CryptCreateHash(
    IntPtr hProv,
    Int32 Algid,
    IntPtr hKey,
    Int32 dwFlags,
    ref IntPtr phHash
  );

  // BOOL WINAPI CryptGetHashParam(
  //    HCRYPTHASH hHash,
  //    DWORD dwParam,
  //    BYTE* pbData,
  //    DWORD* pdwDataLen,
  //    DWORD dwFlags
  // );
  [DllImport("advapi32.dll", SetLastError=true)]
  public static extern bool CryptGetHashParam(
    IntPtr hHash,
    Int32 dwParam,
    ref Int32 pbData,
    ref Int32 pdwDataLen,
    Int32 dwFlags
  );

  // BOOL WINAPI CryptSetHashParam(
  //    HCRYPTHASH hHash,
  //    DWORD dwParam,
  //    BYTE* pbData,
  //    DWORD dwFlags
  // );
  [DllImport("advapi32.dll", SetLastError=true)]
  public static extern bool CryptSetHashParam(
    IntPtr hHash,
    Int32 dwParam,
    Byte[] pbData,
    Int32 dwFlags
  );

  // BOOL WINAPI CryptImportPublicKeyInfo(
  //    HCRYPTPROV hCryptProv,
  //    DWORD dwCertEncodingType,
  //    PCERT_PUBLIC_KEY_INFO pInfo,
  //    HCRYPTKEY* phKey
  // );
  [DllImport("crypt32.dll", CharSet=CharSet.Auto, SetLastError=true)]
  public static extern bool CryptImportPublicKeyInfo(
    IntPtr hCryptProv,
    Int32 dwCertEncodingType,
    IntPtr pInfo,
    ref IntPtr phKey
  );

  // BOOL WINAPI CryptImportKey(
  //    HCRYPTPROV hProv,
  //    BYTE* pbData,
  //    DWORD dwDataLen,
  //    HCRYPTKEY hPubKey,
  //    DWORD dwFlags,
  //    HCRYPTKEY* phKey
  // );
  [DllImport("advapi32.dll", SetLastError=true)]
  public static extern bool CryptImportKey(
    IntPtr hProv,
    Byte[] pbData,
    Int32 dwDataLen,
    IntPtr hPubKey,
    Int32 dwFlags,
    ref IntPtr phKey
  );

  // BOOL WINAPI CryptVerifySignature(
  //    HCRYPTHASH hHash,
  //    BYTE* pbSignature,
  //    DWORD dwSigLen,
  //    HCRYPTKEY hPubKey,
  //    LPCTSTR sDescription,
  //    DWORD dwFlags
  // );
  [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
  public static extern bool CryptVerifySignature(
    IntPtr hHash,
    Byte[] pbSignature,
    Int32 dwSigLen,
    IntPtr hPubKey,
    String sDescription,
    Int32 dwFlags
  );

  // BOOL WINAPI CryptDestroyKey(
  //    HCRYPTKEY hKey
  // );
  [DllImport("advapi32.dll", SetLastError=true)]
  public static extern bool CryptDestroyKey(
    IntPtr hKey
  );

  // BOOL WINAPI CryptDestroyHash(
  //    HCRYPTHASH hHash
  // );
  [DllImport("advapi32.dll", SetLastError=true)]
  public static extern bool CryptDestroyHash(
    IntPtr hHash
  );

  // BOOL WINAPI CryptReleaseContext(
  //    HCRYPTPROV hProv,
  //    DWORD dwFlags
  // );
  [DllImport("advapi32.dll", SetLastError=true)]
  public static extern bool CryptReleaseContext(
    IntPtr hProv,
    Int32 dwFlags
  );

  // BOOL WINAPI CryptGenKey(
  //    HCRYPTPROV hProv,
  //    ALG_ID Algid,
  //    DWORD dwFlags,
  //    HCRYPTKEY* phKey
  // );  
  [DllImport("advapi32.dll", SetLastError=true)]
  public static extern bool CryptGenKey(
    IntPtr hProv,
    Int32 Algid,
    Int32 dwFlags,
    ref IntPtr phKey
  );

  // BOOL WINAPI CryptExportKey(
  //    HCRYPTKEY hKey,
  //    HCRYPTKEY hExpKey,
  //    DWORD dwBlobType,
  //    DWORD dwFlags,
  //    BYTE* pbData,
  //    DWORD* pdwDataLen
  // );
  [DllImport("advapi32.dll", SetLastError=true)]
  public static extern bool CryptExportKey(
    IntPtr hKey,
    IntPtr hExpKey,
    Int32 dwBlobType,
    Int32 dwFlags,
    Byte[] pbData,
    ref Int32 pdwDataLen
  );

  // PCCERT_CONTEXT WINAPI CertFindCertificateInStore(
  //    HCERTSTORE hCertStore,
  //    DWORD dwCertEncodingType,
  //    DWORD dwFindFlags,
  //    DWORD dwFindType,
  //    const void* pvFindPara,
  //    PCCERT_CONTEXT pPrevCertContext
  // );
  [DllImport("Crypt32.dll", CharSet=CharSet.Auto, SetLastError=true)]
  public static extern IntPtr CertFindCertificateInStore(
    IntPtr hCertStore, 
    Int32 dwCertEncodingType,
    Int32 dwFindFlags,
    Int32 dwFindType,
    String pvFindPara,
    IntPtr pPrevCertContext
  );

  // BOOL WINAPI CertFreeCertificateContext(
  //    PCCERT_CONTEXT pCertContext
  // );
  [DllImport("Crypt32.dll", SetLastError=true)]
  public static extern Boolean CertFreeCertificateContext(
    IntPtr pCertContext
  );

  // BOOL WINAPI CryptSignMessage(
  //    PCRYPT_SIGN_MESSAGE_PARA pSignPara,
  //    BOOL fDetachedSignature,
  //    DWORD cToBeSigned,
  //    const BYTE* rgpbToBeSigned[],
  //    DWORD rgcbToBeSigned[],
  //    BYTE* pbSignedBlob,
  //    DWORD* pcbSignedBlob
  // );
  [DllImport("Crypt32.dll", SetLastError=true)]
  public static extern Boolean CryptSignMessage (
    ref CRYPT_SIGN_MESSAGE_PARA pSignPara,
    Boolean fDetachedSignature,
    Int32 cToBeSigned,
    IntPtr[] rgpbToBeSigned,
    Int32[] rgcbToBeSigned,
    Byte[] pbSignedBlob,
    ref Int32 pcbSignedBlob
  );

  // BOOL WINAPI CryptVerifyMessageSignature(
  //    PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara,
  //    DWORD dwSignerIndex,
  //    const BYTE* pbSignedBlob,
  //    DWORD cbSignedBlob,
  //    BYTE* pbDecoded,
  //    DWORD* pcbDecoded,
  //    PCCERT_CONTEXT* ppSignerCert
  // );
  [DllImport("Crypt32.dll", SetLastError=true)]
  public static extern Boolean CryptVerifyMessageSignature (
    ref CRYPT_VERIFY_MESSAGE_PARA pVerifyPara,
    Int32 dwSignerIndex,
    Byte[] pbSignedBlob,
    Int32 cbSignedBlob,
    Byte[] pbDecoded,
    ref Int32 pcbDecoded,
    IntPtr ppSignerCert
  );

  #endregion 

  #region FUNTIONS

  // Helper function to convert struts & classes to byte array
  public static byte[] RawSerialize(object anything) 
  { 
    int rawsize = Marshal.SizeOf(anything); 
    IntPtr buffer = Marshal.AllocHGlobal(rawsize); 
    Marshal.StructureToPtr(anything, buffer, false); 
    byte[] rawdatas = new byte[rawsize]; 
    Marshal.Copy(buffer, rawdatas, 0, rawsize); 
    Marshal.FreeHGlobal(buffer); 
    return rawdatas; 
  } 

  #endregion

}

</SAMPLE>

 

Note: I know there are easier ways now in .NET to handle certificate stores (X509Store) and certificates (X509Certificate2), but I decided to include the P/Invoke declarations in case you need them and for illustration purposes.

I hope this helps.

Cheers,

 

Alex (Alejandro Campos Magencio)

  • Hi Alex,

    Your example has been good so far for me. I am able to successfully produce a PKCS7 message from the code you have posted.

    Now, next is that I want to add authenticated attribute to this PKCS7 message. Unfortunately, I found that you have not discussed it in the example. I have written some code to achieve it myself but I am getting access violation on CryptSignMessage API.

    I think I am not correctly marshaling the data. Can you help me point it out?

    Here is the code which I have written to add authenticated attribute.

    //-------------------------------------------------------

               // Specify authenticated attributes.

               SigParams.cAuthAttr = (uint)_signed_attributes.Count;

               SigParams.rgAuthAttr = Marshal.AllocHGlobal(_signed_attributes.Count * Marshal.SizeOf(typeof(NativeWrapper.CRYPT_ATTRIBUTE)));

               int j = 0;

               foreach (KeyValuePair<string, byte[]> att in _signed_attributes)

               {

                   // Prepare Blob from ByteArray.

                   NativeWrapper.CRYPTOAPI_BLOB blob = new NativeWrapper.CRYPTOAPI_BLOB();

                   blob.cbData = (uint)att.Value.Length;

                   blob.pbData = Marshal.AllocHGlobal(att.Value.Length);

                   Marshal.Copy(att.Value, 0, blob.pbData, att.Value.Length);

                   // Prepare CRYPT_ATTRIBUTE structure.

                   NativeWrapper.CRYPT_ATTRIBUTE authatt = new NativeWrapper.CRYPT_ATTRIBUTE();

                   authatt.pszObjId = "1.2.840.113583.1.1.8";

                   authatt.cValue = 1;

                   authatt.rgValue = Marshal.AllocHGlobal(authatt.cValue * Marshal.SizeOf(typeof(NativeWrapper.CRYPTOAPI_BLOB)));

                   Marshal.StructureToPtr(blob, authatt.rgValue, false);

                   // Marshal CRYPT_ATTRIBUTE structure to the SigParams.rgAuthAttr array.

                   IntPtr pauthatt = new IntPtr(SigParams.rgAuthAttr.ToInt32() +

                                            j * Marshal.SizeOf(typeof (NativeWrapper.CRYPT_ATTRIBUTE)));

                   Marshal.StructureToPtr(authatt, pauthatt, false);

                   ++j;

               }

               //-------------------------------------------------------

    Thanks and Regards

Page 1 of 1 (1 items)
Leave a Comment
  • Please add 2 and 6 and type the answer here:
  • Post
Translate This Page