using System;
using System.Runtime.InteropServices;
using Vanara.InteropServices;
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;

namespace Vanara.PInvoke
{
	/// <summary>Methods and data types found in Crypt32.dll.</summary>
	public static partial class Crypt32
	{
		/// <summary>A callback function used to read from and write data to a disk when processing large messages.</summary>
		/// <param name="pvArg">The arguments specified by CMSG_STREAM_INFO.</param>
		/// <param name="pbData">A pointer to a block of processed data that is available to the application.</param>
		/// <param name="cbData">The size, in bytes, of the block of processed data at pbData.</param>
		/// <param name="fFinal">
		/// Specifies that the last block of data is being processed and that this is the last time the callback will be executed.
		/// </param>
		/// <returns><see langword="true"/> on success; <see langword="false"/> on failure.</returns>
		[PInvokeData("wincrypt.h", MSDNShortId = "a4e7f6e8-351f-4981-b223-50b65f503394")]
		public delegate bool PFN_CMSG_STREAM_OUTPUT([In] IntPtr pvArg, [In] IntPtr pbData, uint cbData, [MarshalAs(UnmanagedType.Bool)] bool fFinal);

		/// <summary>
		/// The <c>CryptGetSignerCertificateCallback</c> user supplied callback function is used with the CRYPT_VERIFY_MESSAGE_PARA
		/// structure to get and verify a message signer's certificate.
		/// </summary>
		/// <param name="pvGetArg"/>
		/// <param name="dwCertEncodingType">
		/// <para>
		/// Specifies the type of encoding used. It is always acceptable to specify both the certificate and message encoding types by
		/// combining them with a bitwise- <c>OR</c> operation as shown in the following example:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
		/// <para>Currently defined encoding types are:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>X509_ASN_ENCODING</term>
		/// </item>
		/// <item>
		/// <term>PKCS_7_ASN_ENCODING</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="pSignerId">
		/// A pointer to a CERT_INFO structure containing the issuer and serial number. Can be <c>NULL</c> if there is no content or signer.
		/// </param>
		/// <param name="hMsgCertStore">A handle to the certificate store containing all the certificates and CRLs in the signed message.</param>
		/// <returns>
		/// If a signer certificate is found, the function returns a pointer to a read-only CERT_CONTEXT. The returned <c>CERT_CONTEXT</c>
		/// was obtained either from a certificate store or was created using CertCreateCertificateContext. In either case, it must be freed
		/// using CertFreeCertificateContext. If this function fails, the return value is <c>NULL</c>.
		/// </returns>
		/// <remarks>If the message does not contain content or signers, the function is called with pSignerId set to <c>NULL</c>.</remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nc-wincrypt-pfn_crypt_get_signer_certificate
		// PFN_CRYPT_GET_SIGNER_CERTIFICATE PfnCryptGetSignerCertificate; PCCERT_CONTEXT PfnCryptGetSignerCertificate( void *pvGetArg, DWORD
		// dwCertEncodingType, PCERT_INFO pSignerId, HCERTSTORE hMsgCertStore ) {...}
		[PInvokeData("wincrypt.h", MSDNShortId = "557ebb26-cce0-4c41-b49c-769b2831cf35")]
		public delegate PCCERT_CONTEXT PFN_CRYPT_GET_SIGNER_CERTIFICATE([In, Out] IntPtr pvGetArg, CertEncodingType dwCertEncodingType, in CERT_INFO pSignerId, HCERTSTORE hMsgCertStore);

		/// <summary>Flags for various structure behaviors.</summary>
		[PInvokeData("wincrypt.h", MSDNShortId = "1601d860-6054-4650-a033-ea088655b7e4")]
		[Flags]
		public enum CryptMsgActionFlags
		{
			/// <summary>
			/// If the encoded output is to be a CMSG_SIGNED inner content of an outer cryptographic message such as a CMSG_ENVELOPED
			/// message, the CRYPT_MESSAGE_BARE_CONTENT_OUT_FLAG must be set.
			/// </summary>
			CRYPT_MESSAGE_BARE_CONTENT_OUT_FLAG = 0x00000001,

			/// <summary>CRYPT_MESSAGE_ENCAPSULATED_CONTENT_OUT_FLAG can be set to encapsulate non-data inner content into an OCTET STRING.</summary>
			CRYPT_MESSAGE_ENCAPSULATED_CONTENT_OUT_FLAG = 0x00000002,

			/// <summary>
			/// CRYPT_MESSAGE_KEYID_SIGNER_FLAG can be set to identify signers by their Key Identifier and not their Issuer and Serial Number.
			/// </summary>
			CRYPT_MESSAGE_KEYID_SIGNER_FLAG = 0x00000004,

			/// <summary>
			/// CRYPT_MESSAGE_SILENT_KEYSET_FLAG can be set to suppress any UI by the CSP. For more information about the CRYPT_SILENT flag,
			/// see CryptAcquireContext.
			/// </summary>
			CRYPT_MESSAGE_SILENT_KEYSET_FLAG = 0x00000040,
		}

		/// <summary>Message control types.</summary>
		[PInvokeData("wincrypt.h", MSDNShortId = "a990d44d-2993-429f-b817-2a834105ecef")]
		public enum CryptMsgControlType
		{
			/// <summary>A BLOB that contains the encoded bytes of attribute certificate.</summary>
			[CorrespondingType(typeof(CRYPTOAPI_BLOB))]
			CMSG_CTRL_ADD_ATTR_CERT = 14,

			/// <summary>A CRYPT_INTEGER_BLOB structure that contains the encoded bytes of the certificate to be added to the message.</summary>
			[CorrespondingType(typeof(CRYPTOAPI_BLOB))]
			CMSG_CTRL_ADD_CERT = 10,

			/// <summary>
			/// A CMSG_CMS_SIGNER_INFO structure that contains signer information. This operation differs from CMSG_CTRL_ADD_SIGNER because
			/// the signer information contains the signature.
			/// </summary>
			[CorrespondingType(typeof(CMSG_CMS_SIGNER_INFO))]
			CMSG_CTRL_ADD_CMS_SIGNER_INFO = 20,

			/// <summary>A BLOB that contains the encoded bytes of the CRL to be added to the message.</summary>
			[CorrespondingType(typeof(CRYPTOAPI_BLOB))]
			CMSG_CTRL_ADD_CRL = 12,

			/// <summary>A CMSG_SIGNER_ENCODE_INFO structure that contains the signer information to be added to the message.</summary>
			[CorrespondingType(typeof(CMSG_SIGNER_ENCODE_INFO))]
			CMSG_CTRL_ADD_SIGNER = 6,

			/// <summary>
			/// A CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA structure that contains the index of the signer and a BLOB that contains the
			/// unauthenticated attribute information to be added to the message.
			/// </summary>
			[CorrespondingType(typeof(CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA))]
			CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR = 8,

			/// <summary>
			/// A CMSG_CTRL_DECRYPT_PARA structure used to decrypt the message for the specified key transport recipient. This value is
			/// applicable to RSA recipients. This operation specifies that the CryptMsgControl function search the recipient index to
			/// obtain the key transport recipient information. If the function fails, GetLastError will return CRYPT_E_INVALID_INDEX if no
			/// key transport recipient is found.
			/// </summary>
			[CorrespondingType(typeof(CMSG_CTRL_DECRYPT_PARA))]
			CMSG_CTRL_DECRYPT = 2,

			/// <summary>The index of the attribute certificate to be removed.</summary>
			[CorrespondingType(typeof(uint))]
			CMSG_CTRL_DEL_ATTR_CERT = 15,

			/// <summary>The index of the certificate to be deleted from the message.</summary>
			[CorrespondingType(typeof(uint))]
			CMSG_CTRL_DEL_CERT = 11,

			/// <summary>The index of the CRL to be deleted from the message.</summary>
			[CorrespondingType(typeof(uint))]
			CMSG_CTRL_DEL_CRL = 13,

			/// <summary>The index of the signer to be deleted.</summary>
			[CorrespondingType(typeof(uint))]
			CMSG_CTRL_DEL_SIGNER = 7,

			/// <summary>
			/// A CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA structure that contains an index that specifies the signer and the index that
			/// specifies the signer's unauthenticated attribute to be deleted.
			/// </summary>
			[CorrespondingType(typeof(CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA))]
			CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR = 9,

			/// <summary>
			/// A CERT_STRONG_SIGN_PARA structure used to perform strong signature checking.
			/// <para>
			/// To check for a strong signature, specify this control type before calling CryptMsgGetAndVerifySigner or before calling
			/// CryptMsgControl with the following control types set:
			/// </para>
			/// <para>CMSG_CTRL_VERIFY_SIGNATURE</para>
			/// <para>CMSG_CTRL_VERIFY_SIGNATURE_EX</para>
			/// <para>
			/// After the signature is successfully verified, this function checks for a strong signature. If the signature is not strong,
			/// the operation will fail and the GetLastError value will be set to NTE_BAD_ALGID.
			/// </para>
			/// </summary>
			[CorrespondingType(typeof(CERT_STRONG_SIGN_PARA))]
			CMSG_CTRL_ENABLE_STRONG_SIGNATURE = 21,

			/// <summary>
			/// A CMSG_CTRL_KEY_AGREE_DECRYPT_PARA structure used to decrypt the message for the specified key agreement session key. Key
			/// agreement is used with Diffie-Hellman encryption/decryption.
			/// </summary>
			[CorrespondingType(typeof(CMSG_CTRL_KEY_AGREE_DECRYPT_PARA))]
			CMSG_CTRL_KEY_AGREE_DECRYPT = 17,

			/// <summary>
			/// A CMSG_CTRL_KEY_TRANS_DECRYPT_PARA structure used to decrypt the message for the specified key transport recipient. Key
			/// transport is used with RSA encryption/decryption.
			/// </summary>
			[CorrespondingType(typeof(CMSG_CTRL_KEY_TRANS_DECRYPT_PARA))]
			CMSG_CTRL_KEY_TRANS_DECRYPT = 16,

			/// <summary>
			/// A CMSG_CTRL_MAIL_LIST_DECRYPT_PARA structure used to decrypt the message for the specified recipient using a previously
			/// distributed key-encryption key (KEK).
			/// </summary>
			[CorrespondingType(typeof(CMSG_CTRL_MAIL_LIST_DECRYPT_PARA))]
			CMSG_CTRL_MAIL_LIST_DECRYPT = 18,

			/// <summary>This value is not used.</summary>
			CMSG_CTRL_VERIFY_HASH = 5,

			/// <summary>A CERT_INFO structure that identifies the signer of the message whose signature is to be verified.</summary>
			[CorrespondingType(typeof(CERT_INFO))]
			CMSG_CTRL_VERIFY_SIGNATURE = 1,

			/// <summary>
			/// A CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA structure that specifies the signer index and public key to verify the message
			/// signature. The signer public key can be a CERT_PUBLIC_KEY_INFO structure, a certificate context, or a certificate chain context.
			/// </summary>
			[CorrespondingType(typeof(CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA))]
			CMSG_CTRL_VERIFY_SIGNATURE_EX = 19,
		}

		/// <summary>Flags for message functions.</summary>
		[PInvokeData("wincrypt.h", MSDNShortId = "1c12003a-c2f3-4069-8bd6-b8f2875b0c98")]
		[Flags]
		public enum CryptMsgFlags
		{
			/// <summary>
			/// Indicates that streamed output will not have an outer ContentInfo wrapper (as defined by PKCS #7). This makes it suitable to
			/// be streamed into an enclosing message.
			/// </summary>
			CMSG_BARE_CONTENT_FLAG = 0x00000001,

			/// <summary></summary>
			CMSG_LENGTH_ONLY_FLAG = 0x00000002,

			/// <summary>Indicates that there is detached data being supplied for the subsequent calls to CryptMsgUpdate.</summary>
			CMSG_DETACHED_FLAG = 0x00000004,

			/// <summary>
			/// Authenticated attributes are forced to be included in the SignerInfo (as defined by PKCS #7) in cases where they would not
			/// otherwise be required.
			/// </summary>
			CMSG_AUTHENTICATED_ATTRIBUTES_FLAG = 0x00000008,

			/// <summary>
			/// Used to calculate the size of a DER encoding of a message to be nested inside an enveloped message. This is particularly
			/// useful when streaming is being performed.
			/// </summary>
			CMSG_CONTENTS_OCTETS_FLAG = 0x00000010,

			/// <summary></summary>
			CMSG_MAX_LENGTH_FLAG = 0x00000020,

			/// <summary>
			/// Non-Data type inner content is encapsulated within an OCTET STRING. This flag is applicable for both Signed and Enveloped messages.
			/// </summary>
			CMSG_CMS_ENCAPSULATED_CONTENT_FLAG = 0x00000040,

			/// <summary></summary>
			CMSG_SIGNED_DATA_NO_SIGN_FLAG = 0x00000080,

			/// <summary>
			/// If set, the hCryptProv that is passed to this function is released on the final CryptMsgUpdate. The handle is not released
			/// if the function fails.
			/// </summary>
			CMSG_CRYPT_RELEASE_CONTEXT_FLAG = 0x00008000,
		}

		/// <summary>Flags used by CMSG_KEY_AGREE_RECIPIENT_INFO.</summary>
		[PInvokeData("wincrypt.h")]
		public enum CryptMsgKeyOriginator
		{
			/// <summary>OriginatorCertId</summary>
			CMSG_KEY_AGREE_ORIGINATOR_CERT = 1,

			/// <summary>OriginatorPublicKeyInfo</summary>
			CMSG_KEY_AGREE_ORIGINATOR_PUBLIC_KEY = 2,
		}

		/// <summary>
		/// Indicates the parameter types of data to be retrieved.
		/// <para>
		/// For an encoded message, only the CMSG_BARE_CONTENT, CMSG_ENCODE_SIGNER, CMSG_CONTENT_PARAM and CMSG_COMPUTED_HASH_PARAM
		/// dwParamTypes are valid.
		/// </para>
		/// </summary>
		[PInvokeData("wincrypt.h", MSDNShortId = "5a05eb09-208f-4e94-abfa-c2f14c0a3164")]
		public enum CryptMsgParamType
		{
			/// <summary>
			/// Returns the message type of a decoded message of unknown type. The retrieved message type can be compared to supported types
			/// to determine whether processing can continued. For supported message types, see the dwMessageType parameter of CryptMsgOpenToDecode.
			/// </summary>
			[CorrespondingType(typeof(uint))]
			CMSG_TYPE_PARAM = 1,

			/// <summary>
			/// Returns the whole PKCS #7 message from a message opened to encode. Retrieves the inner content of a message opened to
			/// decode. If the message is enveloped, the inner type is data, and CryptMsgControl has been called to decrypt the message, the
			/// decrypted content is returned. If the inner type is not data, the encoded BLOB that requires further decoding is returned.
			/// If the message is not enveloped and the inner content is DATA, the returned data is the octets of the inner content. This
			/// type is applicable to both encode and decode.
			/// <para>For decoding, if the type is CMSG_DATA, the content's octets are returned; else, the encoded inner content is returned.</para>
			/// </summary>
			[CorrespondingType(typeof(byte[]))]
			[CorrespondingType(typeof(CRYPTOAPI_BLOB))]
			CMSG_CONTENT_PARAM = 2,

			/// <summary>
			/// Retrieves the encoded content of an encoded cryptographic message, without the outer layer of the CONTENT_INFO structure.
			/// That is, only the encoding of the PKCS #7 defined ContentInfo.content field is returned.
			/// </summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_BARE_CONTENT_PARAM = 3,

			/// <summary>Returns the inner content type of a received message. This type is not applicable to messages of type DATA.</summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_INNER_CONTENT_TYPE_PARAM = 4,

			/// <summary>Returns the number of signers of a received SIGNED message.</summary>
			[CorrespondingType(typeof(uint))]
			CMSG_SIGNER_COUNT_PARAM = 5,

			/// <summary>
			/// Returns information on a message signer. This includes the issuer and serial number of the signer's certificate and
			/// authenticated and unauthenticated attributes of the signer's certificate. To retrieve signer information on all of the
			/// signers of a message, call CryptMsgGetParam varying dwIndex from 0 to the number of signers minus one.
			/// </summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_SIGNER_INFO_PARAM = 6,

			/// <summary>
			/// Returns information on a message signer needed to identify the signer's certificate. A certificate's Issuer and SerialNumber
			/// can be used to uniquely identify a certificate for retrieval. To retrieve information for all the signers, repetitively call
			/// CryptMsgGetParam varying dwIndex from 0 to the number of signers minus one. Only the Issuer and SerialNumber fields in the
			/// CERT_INFO structure returned contain available, valid data.
			/// </summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_SIGNER_CERT_INFO_PARAM = 7,

			/// <summary>
			/// Returns the hash algorithm used by a signer of the message. To get the hash algorithm for a specified signer, call
			/// CryptMsgGetParam with dwIndex equal to that signer's index.
			/// </summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_SIGNER_HASH_ALGORITHM_PARAM = 8,

			/// <summary>
			/// Returns the authenticated attributes of a message signer. To retrieve the authenticated attributes for a specified signer,
			/// call CryptMsgGetParam with dwIndex equal to that signer's index.
			/// </summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_SIGNER_AUTH_ATTR_PARAM = 9,

			/// <summary>
			/// Returns a message signer's unauthenticated attributes. To retrieve the unauthenticated attributes for a specified signer,
			/// call CryptMsgGetParam with dwIndex equal to that signer's index.
			/// </summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_SIGNER_UNAUTH_ATTR_PARAM = 10,

			/// <summary>Returns the number of certificates in a received SIGNED or ENVELOPED message.</summary>
			[CorrespondingType(typeof(uint))]
			CMSG_CERT_COUNT_PARAM = 11,

			/// <summary>
			/// Returns a signer's certificate. To get all of the signer's certificates, call CryptMsgGetParam, varying dwIndex from 0 to
			/// the number of available certificates minus one.
			/// </summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_CERT_PARAM = 12,

			/// <summary>Returns the count of CRLs in a received, SIGNED or ENVELOPED message.</summary>
			[CorrespondingType(typeof(uint))]
			CMSG_CRL_COUNT_PARAM = 13,

			/// <summary>
			/// Returns a CRL. To get all the CRLs, call CryptMsgGetParam, varying dwIndex from 0 to the number of available CRLs minus one.
			/// </summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_CRL_PARAM = 14,

			/// <summary>Returns the encryption algorithm used to encrypt an ENVELOPED message.</summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_ENVELOPE_ALGORITHM_PARAM = 15,

			/// <summary>Returns the number of key transport recipients of an ENVELOPED received message.</summary>
			[CorrespondingType(typeof(uint))]
			CMSG_RECIPIENT_COUNT_PARAM = 17,

			/// <summary>
			/// Returns the index of the key transport recipient used to decrypt an ENVELOPED message. This value is available only after a
			/// message has been decrypted.
			/// </summary>
			[CorrespondingType(typeof(uint))]
			CMSG_RECIPIENT_INDEX_PARAM = 18,

			/// <summary>
			/// Returns certificate information about a key transport message's recipient. To get certificate information on all key
			/// transport message's recipients, repetitively call CryptMsgGetParam, varying dwIndex from 0 to the number of recipients minus
			/// one. Only the Issuer, SerialNumber, and PublicKeyAlgorithm members of the CERT_INFO structure returned are available and valid.
			/// </summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_RECIPIENT_INFO_PARAM = 19,

			/// <summary>Returns the hash algorithm used to hash the message when it was created.</summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_HASH_ALGORITHM_PARAM = 20,

			/// <summary>Returns the hash value stored in the message when it was created.</summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_HASH_DATA_PARAM = 21,

			/// <summary>Returns the hash calculated of the data in the message. This type is applicable to both encode and decode.</summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_COMPUTED_HASH_PARAM = 22,

			/// <summary>Returns the encryption algorithm used to encrypted the message.</summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_ENCRYPT_PARAM = 26,

			/// <summary>Returns the encrypted hash of a signature. Typically used for performing time-stamping.</summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_ENCRYPTED_DIGEST = 27,

			/// <summary>Returns the encoded CMSG_SIGNER_INFO signer information for a message signer.</summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_ENCODED_SIGNER = 28,

			/// <summary>
			/// Changes the contents of an already encoded message. The message must first be decoded with a call to CryptMsgOpenToDecode.
			/// Then the change to the message is made through a call to CryptMsgControl, CryptMsgCountersign, or
			/// CryptMsgCountersignEncoded. The message is then encoded again with a call to CryptMsgGetParam, specifying
			/// CMSG_ENCODED_MESSAGE to get a new encoding that reflects the changes made. This can be used, for instance, to add a
			/// time-stamp attribute to a message.
			/// </summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_ENCODED_MESSAGE = 29,

			/// <summary>Returns the version of the decoded message. For more information, see the table in the Remarks section.</summary>
			[CorrespondingType(typeof(uint))]
			CMSG_VERSION_PARAM = 30,

			/// <summary>Returns the count of the attribute certificates in a SIGNED or ENVELOPED message.</summary>
			[CorrespondingType(typeof(uint))]
			CMSG_ATTR_CERT_COUNT_PARAM = 31,

			/// <summary>
			/// Retrieves an attribute certificate. To get all the attribute certificates, call CryptMsgGetParam varying dwIndex set to 0
			/// the number of attributes minus one.
			/// </summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_ATTR_CERT_PARAM = 32,

			/// <summary>Returns the total count of all message recipients including key agreement and mail list recipients.</summary>
			[CorrespondingType(typeof(uint))]
			CMSG_CMS_RECIPIENT_COUNT_PARAM = 33,

			/// <summary>Returns the index of the key transport, key agreement, or mail list recipient used to decrypt an ENVELOPED message.</summary>
			[CorrespondingType(typeof(uint))]
			CMSG_CMS_RECIPIENT_INDEX_PARAM = 34,

			/// <summary>Returns the index of the encrypted key of a key agreement recipient used to decrypt an ENVELOPED message.</summary>
			[CorrespondingType(typeof(uint))]
			CMSG_CMS_RECIPIENT_ENCRYPTED_KEY_INDEX_PARAM = 35,

			/// <summary>
			/// Returns information about a key transport, key agreement, or mail list recipient. It is not limited to key transport message
			/// recipients. To get information on all of a message's recipients, repetitively call CryptMsgGetParam, varying dwIndex from 0
			/// to the number of recipients minus one.
			/// </summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_CMS_RECIPIENT_INFO_PARAM = 36,

			/// <summary>Returns the unprotected attributes in an enveloped message.</summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_UNPROTECTED_ATTR_PARAM = 37,

			/// <summary>
			/// Returns information on a message signer needed to identify the signer's public key. This could be a certificate's Issuer and
			/// SerialNumber, a KeyID, or a HashId. To retrieve information for all the signers, call CryptMsgGetParam varying dwIndex from
			/// 0 to the number of signers minus one.
			/// </summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_SIGNER_CERT_ID_PARAM = 38,

			/// <summary>
			/// Returns information on a message signer. This includes a signerId and authenticated and unauthenticated attributes. To
			/// retrieve signer information on all of the signers of a message, call CryptMsgGetParam varying dwIndex from 0 to the number
			/// of signers minus one.
			/// </summary>
			[CorrespondingType(typeof(byte[]))]
			CMSG_CMS_SIGNER_INFO_PARAM = 39,
		}

		/// <summary>Message signer type.</summary>
		[PInvokeData("wincrypt.h", MSDNShortId = "56b73de8-c170-46f6-b488-096475b59c15")]
		public enum CryptMsgSignerType
		{
			/// <summary>CERT_PUBLIC_KEY_INFO</summary>
			[CorrespondingType(typeof(CERT_PUBLIC_KEY_INFO))]
			CMSG_VERIFY_SIGNER_PUBKEY = 1,

			/// <summary>CERT_CONTEXT</summary>
			[CorrespondingType(typeof(CERT_CONTEXT))]
			CMSG_VERIFY_SIGNER_CERT = 2,

			/// <summary>CERT_CHAIN_CONTEXT</summary>
			[CorrespondingType(typeof(CERT_CHAIN_CONTEXT))]
			CMSG_VERIFY_SIGNER_CHAIN = 3,

			/// <summary>The CMSG verify signer null</summary>
			CMSG_VERIFY_SIGNER_NULL = 4,
		}

		/// <summary>Message types.</summary>
		[PInvokeData("wincrypt.h", MSDNShortId = "1c12003a-c2f3-4069-8bd6-b8f2875b0c98")]
		public enum CryptMsgType
		{
			/// <summary>An octet (BYTE) string.</summary>
			CMSG_DATA = 1,

			/// <summary>CMSG_SIGNED_ENCODE_INFO</summary>
			CMSG_SIGNED = 2,

			/// <summary>CMSG_ENVELOPED_ENCODE_INFO</summary>
			CMSG_ENVELOPED = 3,

			/// <summary>Not implemented.</summary>
			CMSG_SIGNED_AND_ENVELOPED = 4,

			/// <summary>CMSG_HASHED_ENCODE_INFO</summary>
			CMSG_HASHED = 5,

			/// <summary>Not implemented.</summary>
			CMSG_ENCRYPTED = 6,
		}

		/// <summary>Flags that modify the function behavior.</summary>
		[PInvokeData("wincrypt.h", MSDNShortId = "da756cd5-1dec-4d88-9c90-76dd263035eb")]
		public enum CryptMsgVerifyCounterFlags
		{
			/// <summary>
			/// Performs a strong signature check after successful signature verification. Set the pvExtra parameter to point to a
			/// CERT_STRONG_SIGN_PARA structure that contains the parameters needed to check the signature strength.. Windows 8 and Windows
			/// Server 2012: Support for this flag begins.
			/// </summary>
			CMSG_VERIFY_COUNTER_SIGN_ENABLE_STRONG_FLAG = 0x00000001
		}

		/// <summary>
		/// <para>The <c>CryptDecodeMessage</c> function decodes, decrypts, and verifies a cryptographic message.</para>
		/// <para>
		/// This function can be used when the type of cryptographic message is unknown. The dwMsgTypeFlags constants can be combined with a
		/// bitwise- <c>OR</c> operation so that the function will try to find one of the types. When one of the types is found, the
		/// function reports the type found and returns the data appropriate to that type.
		/// </para>
		/// <para>
		/// In each pass, the function cracks only a single level of encryption or encoding. For additional cracking, this function, or one
		/// of the other Simplified Message Functions, must be called again.
		/// </para>
		/// </summary>
		/// <param name="dwMsgTypeFlags">
		/// <para>
		/// Indicates the message type. Message types can be combined with the bitwise- <c>OR</c> operator. This parameter can be one of the
		/// following message types:
		/// </para>
		/// <list type="bullet">
		/// <item>
		/// <term>CMSG_DATA_FLAG</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED_FLAG</term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENVELOPED_FLAG</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED_AND_ENVELOPED_FLAG</term>
		/// </item>
		/// <item>
		/// <term>CMSG_HASHED_FLAG</term>
		/// </item>
		/// </list>
		/// <para><c>Note</c> After return, the <c>DWORD</c> pointed to by pdwMsgType is set with the type of the message.</para>
		/// </param>
		/// <param name="pDecryptPara">A pointer to a CRYPT_DECRYPT_MESSAGE_PARA structure that contains decryption parameters.</param>
		/// <param name="pVerifyPara">A pointer to a CRYPT_VERIFY_MESSAGE_PARA structure that contains verification parameters.</param>
		/// <param name="dwSignerIndex">
		/// <para>
		/// Indicates which signer, among the possible many signers of a message, is to be verified. This index can be changed in multiple
		/// calls to the function to verify additional signers.
		/// </para>
		/// <para>
		/// dwSignerIndex is set to zero for the first signer. If the function returns <c>FALSE</c>, and GetLastError returns
		/// CRYPT_E_NO_SIGNER, the previous call returned the last signer of the message. This parameter is used only with messages of types
		/// CMSG_SIGNED_AND_ENVELOPED or CMSG_SIGNED. For all other message types, it should be set to zero.
		/// </para>
		/// </param>
		/// <param name="pbEncodedBlob">A pointer to the encoded BLOB that is to be decoded.</param>
		/// <param name="cbEncodedBlob">The size, in bytes, of the encoded BLOB.</param>
		/// <param name="dwPrevInnerContentType">
		/// Only applicable when processing nested cryptographic messages. When processing an outer cryptographic message, it must be set to
		/// zero. When decoding a nested cryptographic message, it is set to the value returned at pdwInnerContentType by a previous calling
		/// of <c>CryptDecodeMessage</c> for the outer message. It can be any of the CMSG types listed in pdwMsgType. For backward
		/// compatibility, set dwPrevInnerContentType to zero.
		/// </param>
		/// <param name="pdwMsgType">
		/// <para>
		/// A pointer to a <c>DWORD</c> that specifies the message type returned. This parameter can be one of the following message types:
		/// </para>
		/// <list type="bullet">
		/// <item>
		/// <term>CMSG_DATA</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED</term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENVELOPED</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED_AND_ENVELOPED</term>
		/// </item>
		/// <item>
		/// <term>CMSG_HASHED</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="pdwInnerContentType">
		/// <para>
		/// A pointer to a <c>DWORD</c> that specifies the type of an inner message. The message type codes used for pdwMsgType are used
		/// here, also.
		/// </para>
		/// <para>If there is no cryptographic nesting, CMSG_DATA is returned.</para>
		/// </param>
		/// <param name="pbDecoded">
		/// <para>A pointer to a buffer to receive the decoded message.</para>
		/// <para>
		/// This parameter can be <c>NULL</c> if the decoded message is not required or to set the size of the decoded message for memory
		/// allocation purposes. A decoded message will not be returned if this parameter is <c>NULL</c>. For more information, see
		/// Retrieving Data of Unknown Length.
		/// </para>
		/// </param>
		/// <param name="pcbDecoded">
		/// <para>
		/// A pointer to a variable that specifies the size, in bytes, of the buffer pointed to by the pbDecoded parameter. When the
		/// function returns, this variable contains the size of the decoded message.
		/// </para>
		/// <para>
		/// <c>Note</c> When processing the data returned in the buffer, applications must use the actual size of the data returned. The
		/// actual size can be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually
		/// specified large enough to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed
		/// to by this parameter is updated to reflect the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <param name="ppXchgCert">
		/// A pointer to a pointer to a CERT_CONTEXT structure with a certificate that corresponds to the private exchange key needed to
		/// decode the message. This parameter is only set for message types CMSG_ENVELOPED and CMSG_SIGNED_AND_ENVELOPED.
		/// </param>
		/// <param name="ppSignerCert">
		/// A pointer to a pointer to a CERT_CONTEXT structure of the certificate context of the signer. This parameter is only set for
		/// message types CMSG_SIGNED and CMSG_SIGNED_AND_ENVELOPED.
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the function returns nonzero ( <c>TRUE</c>).</para>
		/// <para>If the function fails, it returns zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
		/// <para>The CryptDecryptMessage, CryptVerifyMessageSignature, or CryptVerifyMessageHash functions can be propagated to this function.</para>
		/// <para>The following error code is most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>ERROR_MORE_DATA</term>
		/// <term>
		/// If the buffer specified by the pbDecoded parameter is not large enough to hold the returned data, the function sets the
		/// ERROR_MORE_DATA code and stores the required buffer size, in bytes, in the variable pointed to by pcbDecoded.
		/// </term>
		/// </item>
		/// </list>
		/// </returns>
		/// <remarks>
		/// <para>
		/// The dwMsgTypeFlags parameter specifies the set of allowable messages. For example, to decode either SIGNED or ENVELOPED
		/// messages, set dwMsgTypeFlags to CMSG_SIGNED_FLAG | CMSG_ENVELOPED_FLAG. Either or both of the pDecryptPara or pVerifyPara
		/// parameters must be specified.
		/// </para>
		/// <para>
		/// For a successfully decoded or verified message, the certificate context pointers pointed to by ppXchgCert and ppSignerCert are
		/// updated. They must be freed by calling CertFreeCertificateContext. If the function fails, they are set to <c>NULL</c>.
		/// </para>
		/// <para>
		/// The ppXchgCert or ppSignerCert parameters can be set to <c>NULL</c> before the function is called, which indicates that the
		/// caller is not interested in getting the exchange certificate or the signer certificate context.
		/// </para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptdecodemessage BOOL CryptDecodeMessage( DWORD
		// dwMsgTypeFlags, PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, DWORD dwSignerIndex, const BYTE
		// *pbEncodedBlob, DWORD cbEncodedBlob, DWORD dwPrevInnerContentType, DWORD *pdwMsgType, DWORD *pdwInnerContentType, BYTE
		// *pbDecoded, DWORD *pcbDecoded, PCCERT_CONTEXT *ppXchgCert, PCCERT_CONTEXT *ppSignerCert );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "25ffd058-8f75-4ba5-b075-e3efc09f5d9d")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptDecodeMessage(CryptMsgType dwMsgTypeFlags, in CRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, in CRYPT_VERIFY_MESSAGE_PARA pVerifyPara,
			uint dwSignerIndex, [In] IntPtr pbEncodedBlob, uint cbEncodedBlob, CryptMsgType dwPrevInnerContentType, out CryptMsgType pdwMsgType,
			out CryptMsgType pdwInnerContentType, [Out] IntPtr pbDecoded, ref uint pcbDecoded, out SafePCCERT_CONTEXT ppXchgCert, out SafePCCERT_CONTEXT ppSignerCert);

		/// <summary>The <c>CryptDecryptAndVerifyMessageSignature</c> function decrypts a message and verifies its signature.</summary>
		/// <param name="pDecryptPara">A pointer to a CRYPT_DECRYPT_MESSAGE_PARA structure that contains decryption parameters.</param>
		/// <param name="pVerifyPara">A pointer to a CRYPT_VERIFY_MESSAGE_PARA structure that contains verification parameters.</param>
		/// <param name="dwSignerIndex">
		/// Identifies a particular signer of the message. A message can be signed by more than one signer and this function can be called
		/// multiple times changing this parameter to check for several signers. It is set to zero for the first signer. If the function
		/// returns <c>FALSE</c>, and GetLastError returns CRYPT_E_NO_SIGNER, the previous call received the last signer of the message.
		/// </param>
		/// <param name="pbEncryptedBlob">A pointer to the signed, encoded, and encrypted message to be decrypted and verified.</param>
		/// <param name="cbEncryptedBlob">The size, in bytes, of the encrypted message.</param>
		/// <param name="pbDecrypted">
		/// <para>A pointer to a buffer to receive the decrypted message.</para>
		/// <para>
		/// This parameter can be <c>NULL</c> if the decrypted message is not required or to set the size of the decrypted message for
		/// memory allocation purposes. A decrypted message will not be returned if this parameter is <c>NULL</c>. For more information, see
		/// Retrieving Data of Unknown Length.
		/// </para>
		/// </param>
		/// <param name="pcbDecrypted">
		/// <para>
		/// A pointer to a <c>DWORD</c> that specifies the size, in bytes, of the buffer pointed to by the pbDecrypted parameter. When the
		/// function returns, it contains the size of the decrypted message copied to pbDecrypted.
		/// </para>
		/// <para>
		/// <c>Note</c> When processing the data returned in the pbDecrypted buffer, applications must use the actual size of the data
		/// returned. The actual size can be slightly smaller than the size of the buffer specified in pcbDecrypted on input. On output, the
		/// variable pointed to by this parameter is set to reflect the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <param name="ppXchgCert">
		/// A pointer to a CERT_CONTEXT structure of the certificate that corresponds to the private exchange key needed to decrypt the message.
		/// </param>
		/// <param name="ppSignerCert">A pointer to a CERT_CONTEXT structure of the certificate of the signer.</param>
		/// <returns>
		/// <para>If the function succeeds, the function returns nonzero ( <c>TRUE</c>).</para>
		/// <para>If the function fails, it returns zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
		/// <para>
		/// <c>Note</c> Errors from the called functions CryptDecryptMessage and CryptVerifyMessageSignature might be propagated to this function.
		/// </para>
		/// <para>The GetLastError function returns the following error code most often.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>ERROR_MORE_DATA</term>
		/// <term>
		/// If the buffer specified by the pbDecrypted parameter is not large enough to hold the returned data, the function sets the
		/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, in the variable pointed to by pcbDecrypted.
		/// </term>
		/// </item>
		/// </list>
		/// </returns>
		/// <remarks>
		/// <para>
		/// For a successfully decrypted and verified message, the certificate context pointers pointed to by ppXchgCert and ppSignerCert
		/// are updated. They must be freed by calling CertFreeCertificateContext. If the function fails, they are set to <c>NULL</c>.
		/// </para>
		/// <para>
		/// To indicate that the caller is not interested in the exchange certificate or the signer certificate context, set the ppXchgCert
		/// and ppSignerCert parameters to <c>NULL</c>.
		/// </para>
		/// <para>Examples</para>
		/// <para>For an example that uses this function, see Example C Program: Sending and Receiving a Signed and Encrypted Message.</para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptdecryptandverifymessagesignature BOOL
		// CryptDecryptAndVerifyMessageSignature( PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, DWORD
		// dwSignerIndex, const BYTE *pbEncryptedBlob, DWORD cbEncryptedBlob, BYTE *pbDecrypted, DWORD *pcbDecrypted, PCCERT_CONTEXT
		// *ppXchgCert, PCCERT_CONTEXT *ppSignerCert );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "0864a187-617f-4a21-9809-d2dbbc54ab9c")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptDecryptAndVerifyMessageSignature(in CRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, in CRYPT_VERIFY_MESSAGE_PARA pVerifyPara, uint dwSignerIndex,
			[In] IntPtr pbEncryptedBlob, uint cbEncryptedBlob, [Out] IntPtr pbDecrypted, ref uint pcbDecrypted, out SafePCCERT_CONTEXT ppXchgCert, out SafePCCERT_CONTEXT ppSignerCert);

		/// <summary>The <c>CryptDecryptMessage</c> function decodes and decrypts a message.</summary>
		/// <param name="pDecryptPara">A pointer to a CRYPT_DECRYPT_MESSAGE_PARA structure that contains decryption parameters.</param>
		/// <param name="pbEncryptedBlob">A pointer to a buffer that contains the encoded and encrypted message to be decrypted.</param>
		/// <param name="cbEncryptedBlob">The size, in bytes, of the encoded and encrypted message.</param>
		/// <param name="pbDecrypted">
		/// <para>A pointer to a buffer that receives the decrypted message.</para>
		/// <para>
		/// To set the size of this information for memory allocation purposes, this parameter can be <c>NULL</c>. A decrypted message will
		/// not be returned if this parameter is <c>NULL</c>. For more information, see Retrieving Data of Unknown Length.
		/// </para>
		/// </param>
		/// <param name="pcbDecrypted">
		/// <para>
		/// A pointer to a <c>DWORD</c> that specifies the size, in bytes, of the buffer pointed to by the pbDecrypted parameter. When the
		/// function returns, this variable contains the size, in bytes, of the decrypted message copied to pbDecrypted.
		/// </para>
		/// <para>
		/// <c>Note</c> When processing the data returned in the pbDecrypted buffer, applications must use the actual size of the data
		/// returned. The actual size can be slightly smaller than the size of the buffer specified in pcbDecrypted on input. On input,
		/// buffer sizes are usually specified large enough to ensure that the largest possible output data will fit in the buffer. On
		/// output, the <c>DWORD</c> is updated to the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <param name="ppXchgCert">
		/// A pointer to a CERT_CONTEXT structure of a certificate that corresponds to the private exchange key needed to decrypt the
		/// message. To indicate that the function should not return the certificate context used to decrypt, set this parameter to <c>NULL</c>.
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the function returns nonzero ( <c>TRUE</c>).</para>
		/// <para>If the function fails, it returns zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
		/// <para><c>Note</c> Errors from calls to CryptImportKey and CryptDecrypt might be propagated to this function.</para>
		/// <para>The GetLastError function returns the following error codes most often.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>ERROR_MORE_DATA</term>
		/// <term>
		/// If the buffer specified by the pbDecrypted parameter is not large enough to hold the returned data, the function sets the
		/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, in the variable pointed to by pcbDecrypted.
		/// </term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>
		/// Invalid message and certificate encoding types. Currently only PKCS_7_ASN_ENCODING and X509_ASN_ENCODING_TYPE are supported.
		/// Invalid cbSize in *pDecryptPara.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNEXPECTED_MSG_TYPE</term>
		/// <term>Not an enveloped cryptographic message.</term>
		/// </item>
		/// <item>
		/// <term>NTE_BAD_ALGID</term>
		/// <term>The message was encrypted by using an unknown or unsupported algorithm.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_NO_DECRYPT_CERT</term>
		/// <term>No certificate was found having a private key property to use for decrypting.</term>
		/// </item>
		/// </list>
		/// <para>
		/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
		/// about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		/// <remarks>
		/// <para>
		/// When <c>NULL</c> is passed for pbDecrypted, and pcbDecrypted is not <c>NULL</c>, <c>NULL</c> is returned for the address passed
		/// in ppXchgCert; otherwise, a pointer to a CERT_CONTEXT is returned. For a successfully decrypted message, this pointer to a
		/// <c>CERT_CONTEXT</c> points to the certificate context used to decrypt the message. It must be freed by calling
		/// CertFreeCertificateContext. If the function fails, the value at ppXchgCert is set to <c>NULL</c>.
		/// </para>
		/// <para>Examples</para>
		/// <para>For an example that uses this function, see Example C Program: Using CryptEncryptMessage and CryptDecryptMessage.</para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptdecryptmessage BOOL CryptDecryptMessage(
		// PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, const BYTE *pbEncryptedBlob, DWORD cbEncryptedBlob, BYTE *pbDecrypted, DWORD
		// *pcbDecrypted, PCCERT_CONTEXT *ppXchgCert );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "e540b816-64e1-4c78-9020-2b221e813acc")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptDecryptMessage(in CRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, [In] IntPtr pbEncryptedBlob, uint cbEncryptedBlob, [Out] IntPtr pbDecrypted, ref uint pcbDecrypted, out SafePCCERT_CONTEXT ppXchgCert);

		/// <summary>The <c>CryptDecryptMessage</c> function decodes and decrypts a message.</summary>
		/// <param name="pDecryptPara">A pointer to a CRYPT_DECRYPT_MESSAGE_PARA structure that contains decryption parameters.</param>
		/// <param name="pbEncryptedBlob">A pointer to a buffer that contains the encoded and encrypted message to be decrypted.</param>
		/// <param name="cbEncryptedBlob">The size, in bytes, of the encoded and encrypted message.</param>
		/// <param name="pbDecrypted">
		/// <para>A pointer to a buffer that receives the decrypted message.</para>
		/// <para>
		/// To set the size of this information for memory allocation purposes, this parameter can be <c>NULL</c>. A decrypted message will
		/// not be returned if this parameter is <c>NULL</c>. For more information, see Retrieving Data of Unknown Length.
		/// </para>
		/// </param>
		/// <param name="pcbDecrypted">
		/// <para>
		/// A pointer to a <c>DWORD</c> that specifies the size, in bytes, of the buffer pointed to by the pbDecrypted parameter. When the
		/// function returns, this variable contains the size, in bytes, of the decrypted message copied to pbDecrypted.
		/// </para>
		/// <para>
		/// <c>Note</c> When processing the data returned in the pbDecrypted buffer, applications must use the actual size of the data
		/// returned. The actual size can be slightly smaller than the size of the buffer specified in pcbDecrypted on input. On input,
		/// buffer sizes are usually specified large enough to ensure that the largest possible output data will fit in the buffer. On
		/// output, the <c>DWORD</c> is updated to the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <param name="ppXchgCert">
		/// A pointer to a CERT_CONTEXT structure of a certificate that corresponds to the private exchange key needed to decrypt the
		/// message. To indicate that the function should not return the certificate context used to decrypt, set this parameter to <c>NULL</c>.
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the function returns nonzero ( <c>TRUE</c>).</para>
		/// <para>If the function fails, it returns zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
		/// <para><c>Note</c> Errors from calls to CryptImportKey and CryptDecrypt might be propagated to this function.</para>
		/// <para>The GetLastError function returns the following error codes most often.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>ERROR_MORE_DATA</term>
		/// <term>
		/// If the buffer specified by the pbDecrypted parameter is not large enough to hold the returned data, the function sets the
		/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, in the variable pointed to by pcbDecrypted.
		/// </term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>
		/// Invalid message and certificate encoding types. Currently only PKCS_7_ASN_ENCODING and X509_ASN_ENCODING_TYPE are supported.
		/// Invalid cbSize in *pDecryptPara.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNEXPECTED_MSG_TYPE</term>
		/// <term>Not an enveloped cryptographic message.</term>
		/// </item>
		/// <item>
		/// <term>NTE_BAD_ALGID</term>
		/// <term>The message was encrypted by using an unknown or unsupported algorithm.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_NO_DECRYPT_CERT</term>
		/// <term>No certificate was found having a private key property to use for decrypting.</term>
		/// </item>
		/// </list>
		/// <para>
		/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
		/// about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		/// <remarks>
		/// <para>
		/// When <c>NULL</c> is passed for pbDecrypted, and pcbDecrypted is not <c>NULL</c>, <c>NULL</c> is returned for the address passed
		/// in ppXchgCert; otherwise, a pointer to a CERT_CONTEXT is returned. For a successfully decrypted message, this pointer to a
		/// <c>CERT_CONTEXT</c> points to the certificate context used to decrypt the message. It must be freed by calling
		/// CertFreeCertificateContext. If the function fails, the value at ppXchgCert is set to <c>NULL</c>.
		/// </para>
		/// <para>Examples</para>
		/// <para>For an example that uses this function, see Example C Program: Using CryptEncryptMessage and CryptDecryptMessage.</para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptdecryptmessage BOOL CryptDecryptMessage(
		// PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, const BYTE *pbEncryptedBlob, DWORD cbEncryptedBlob, BYTE *pbDecrypted, DWORD
		// *pcbDecrypted, PCCERT_CONTEXT *ppXchgCert );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "e540b816-64e1-4c78-9020-2b221e813acc")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptDecryptMessage(in CRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, [In] IntPtr pbEncryptedBlob, uint cbEncryptedBlob, [Out] IntPtr pbDecrypted, ref uint pcbDecrypted, IntPtr ppXchgCert = default);

		/// <summary>The <c>CryptEncryptMessage</c> function encrypts and encodes a message.</summary>
		/// <param name="pEncryptPara">
		/// <para>A pointer to a CRYPT_ENCRYPT_MESSAGE_PARA structure that contains the encryption parameters.</para>
		/// <para>
		/// The <c>CryptEncryptMessage</c> function does not support the SHA2 OIDs, <c>szOID_DH_SINGLE_PASS_STDDH_SHA256_KDF</c> and <c>szOID_DH_SINGLE_PASS_STDDH_SHA384_KDF</c>.
		/// </para>
		/// </param>
		/// <param name="cRecipientCert">Number of elements in the rgpRecipientCert array.</param>
		/// <param name="rgpRecipientCert">
		/// Array of pointers to CERT_CONTEXT structures that contain the certificates of intended recipients of the message.
		/// </param>
		/// <param name="pbToBeEncrypted">A pointer to a buffer that contains the message that is to be encrypted.</param>
		/// <param name="cbToBeEncrypted">The size, in bytes, of the message that is to be encrypted.</param>
		/// <param name="pbEncryptedBlob">
		/// <para>A pointer to BLOB that contains a buffer that receives the encrypted and encoded message.</para>
		/// <para>
		/// To set the size of this information for memory allocation purposes, this parameter can be <c>NULL</c>. For more information, see
		/// Retrieving Data of Unknown Length.
		/// </para>
		/// </param>
		/// <param name="pcbEncryptedBlob">
		/// <para>
		/// A pointer to a <c>DWORD</c> that specifies the size, in bytes, of the buffer pointed to by the pbEncryptedBlob parameter. When
		/// the function returns, this variable contains the size, in bytes, of the encrypted and encoded message copied to pbEncryptedBlob.
		/// </para>
		/// <para>
		/// <c>Note</c> When processing the data returned in the buffer of the pbEncryptedBlob, applications need to use the actual size of
		/// the data returned. The actual size can be slightly smaller than the size of the buffer specified on input. (On input, buffer
		/// sizes are usually specified large enough to ensure that the largest possible output data will fit in the buffer.) On output, the
		/// variable pointed to by this parameter is updated to reflect the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the function returns nonzero ( <c>TRUE</c>).</para>
		/// <para>If the function fails, it returns zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
		/// <para>
		/// <c>Note</c> Errors from calls to CryptGenKey, CryptEncrypt, CryptImportKey, and CryptExportKey can be propagated to this function.
		/// </para>
		/// <para>The GetLastError function returns the following error codes most often.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>ERROR_MORE_DATA</term>
		/// <term>
		/// If the buffer specified by the pbEncryptedBlob parameter is not large enough to hold the returned data, the function sets the
		/// ERROR_MORE_DATA code and stores the required buffer size, in bytes, in the variable pointed to by pcbEncryptedBlob.
		/// </term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>
		/// The message encoding type is not valid. Currently only PKCS_7_ASN_ENCODING is supported. The cbSize in *pEncryptPara is not valid.
		/// </term>
		/// </item>
		/// </list>
		/// <para>
		/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
		/// about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptencryptmessage BOOL CryptEncryptMessage(
		// PCRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, DWORD cRecipientCert, PCCERT_CONTEXT [] rgpRecipientCert, const BYTE *pbToBeEncrypted,
		// DWORD cbToBeEncrypted, BYTE *pbEncryptedBlob, DWORD *pcbEncryptedBlob );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "927f2e9a-96cf-4744-bd57-420b5034d28d")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptEncryptMessage(in CRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, uint cRecipientCert, [In, MarshalAs(UnmanagedType.LPArray)] PCCERT_CONTEXT[] rgpRecipientCert,
			[In] IntPtr pbToBeEncrypted, uint cbToBeEncrypted, [Out] IntPtr pbEncryptedBlob, ref uint pcbEncryptedBlob);

		/// <summary>
		/// The <c>CryptGetMessageCertificates</c> function returns the handle of an open certificate store containing the message's
		/// certificates and CRLs. This function calls CertOpenStore using provider type CERT_STORE_PROV_PKCS7 as its lpszStoreProvider parameter.
		/// </summary>
		/// <param name="dwMsgAndCertEncodingType">
		/// <para>
		/// Specifies the encoding type used. It is always acceptable to specify both the certificate and message encoding types by
		/// combining them with a bitwise- <c>OR</c> operation as shown in the following example:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
		/// <para>Currently defined encoding types are:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>X509_ASN_ENCODING</term>
		/// </item>
		/// <item>
		/// <term>PKCS_7_ASN_ENCODING</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="hCryptProv">
		/// <para>This parameter is not used and should be set to <c>NULL</c>.</para>
		/// <para>
		/// <c>Windows Server 2003 and Windows XP:</c> Handle of the CSP passed to CertOpenStore. For more information, see
		/// CertOpenStore.Unless there is a strong reason for passing a specific cryptographic provider in hCryptProv, pass zero to cause
		/// the default RSA or DSS provider to be acquired.
		/// </para>
		/// <para>This parameter's data type is <c>HCRYPTPROV</c>.</para>
		/// </param>
		/// <param name="dwFlags">Flags passed to CertOpenStore. For more information, see CertOpenStore.</param>
		/// <param name="pbSignedBlob">A pointer to a buffered CRYPT_INTEGER_BLOB structure that contains the signed message.</param>
		/// <param name="cbSignedBlob">The size, in bytes, of the signed message.</param>
		/// <returns>
		/// <para>Returns the certificate store containing the message's certificates and CRLs. For an error, <c>NULL</c> is returned.</para>
		/// <para>The following lists the error code most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>Invalid message and certificate encoding types. Currently only PKCS_7_ASN_ENCODING and X509_ASN_ENCODING are supported.</term>
		/// </item>
		/// </list>
		/// <para>
		/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
		/// about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		/// <remarks>
		/// <para>Use GetLastError to determine the reason for any errors.</para>
		/// <para>Examples</para>
		/// <para>For an example that uses this function, see Example C Program: Setting and Getting Certificate Store Properties.</para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgetmessagecertificates HCERTSTORE
		// CryptGetMessageCertificates( DWORD dwMsgAndCertEncodingType, HCRYPTPROV_LEGACY hCryptProv, DWORD dwFlags, const BYTE
		// *pbSignedBlob, DWORD cbSignedBlob );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "d890f91f-bb45-463b-b7c0-56acc9367571")]
		public static extern SafeHCERTSTORE CryptGetMessageCertificates(CertEncodingType dwMsgAndCertEncodingType, [Optional] HCRYPTPROV hCryptProv, CertStoreFlags dwFlags, [In] IntPtr pbSignedBlob, uint cbSignedBlob);

		/// <summary>The <c>CryptGetMessageSignerCount</c> function returns the number of signers of a signed message.</summary>
		/// <param name="dwMsgEncodingType">
		/// <para>
		/// Specifies the encoding type used. It is always acceptable to specify both the certificate and message encoding types by
		/// combining them with a bitwise- <c>OR</c> operation as shown in the following example:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
		/// <para>Currently defined encoding types are:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>X509_ASN_ENCODING</term>
		/// </item>
		/// <item>
		/// <term>PKCS_7_ASN_ENCODING</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="pbSignedBlob">A pointer to a buffer containing the signed message.</param>
		/// <param name="cbSignedBlob">The size, in bytes, of the signed message.</param>
		/// <returns>
		/// <para>Returns the number of signers of a signed message, zero when there are no signers, and minus one (–1) for an error.</para>
		/// <para>For extended error information, call GetLastError. The following error code is most commonly returned.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>Invalid message encoding type. Currently only PKCS_7_ASN_ENCODING is supported.</term>
		/// </item>
		/// </list>
		/// <para>
		/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
		/// about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgetmessagesignercount LONG
		// CryptGetMessageSignerCount( DWORD dwMsgEncodingType, const BYTE *pbSignedBlob, DWORD cbSignedBlob );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "d18bda8b-b333-4b1e-8ed5-f8eff04b3168")]
		public static extern int CryptGetMessageSignerCount(CertEncodingType dwMsgEncodingType, [In] IntPtr pbSignedBlob, uint cbSignedBlob);

		/// <summary>The <c>CryptHashMessage</c> function creates a hash of the message.</summary>
		/// <param name="pHashPara">A pointer to a CRYPT_HASH_MESSAGE_PARA structure that contains the hash parameters.</param>
		/// <param name="fDetachedHash">
		/// If this parameter is set to <c>TRUE</c>, only pbComputedHash is encoded in pbHashedBlob. Otherwise, both rgpbToBeHashed and
		/// pbComputedHash are encoded.
		/// </param>
		/// <param name="cToBeHashed">
		/// The number of array elements in rgpbToBeHashed and rgcbToBeHashed. This parameter can only be one unless fDetachedHash is set to <c>TRUE</c>.
		/// </param>
		/// <param name="rgpbToBeHashed">An array of pointers to buffers that contain the contents to be hashed.</param>
		/// <param name="rgcbToBeHashed">An array of sizes, in bytes, of the buffers pointed to by rgpbToBeHashed.</param>
		/// <param name="pbHashedBlob">
		/// <para>A pointer to a buffer to receive the hashed message encoded for transmission.</para>
		/// <para>
		/// This parameter can be <c>NULL</c> if the hashed message is not needed for additional processing or to set the size of the hashed
		/// message for memory allocation purposes. A hashed message will not be returned if this parameter is <c>NULL</c>. For more
		/// information, see Retrieving Data of Unknown Length.
		/// </para>
		/// </param>
		/// <param name="pcbHashedBlob">
		/// <para>
		/// A pointer to a <c>DWORD</c> that specifies the size, in bytes, of the buffer pointed to by the pbHashedBlob parameter. When the
		/// function returns, this variable contains the size, in bytes, of the decrypted message copied to pbHashedBlob. This parameter
		/// must be the address of a <c>DWORD</c> and not <c>NULL</c> or the length of the buffer will not be returned.
		/// </para>
		/// <para>
		/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
		/// be slightly smaller than the size of the buffer specified on input. On input, buffer sizes are usually specified large enough to
		/// ensure that the largest possible output data will fit in the buffer. On output, the variable pointed to by this parameter is
		/// updated to reflect the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <param name="pbComputedHash">
		/// A pointer to a buffer to receive the newly created hash value. This parameter can be <c>NULL</c> if the newly created hash is
		/// not needed for additional processing, or to set the size of the hash for memory allocation purposes. For more information, see
		/// Retrieving Data of Unknown Length.
		/// </param>
		/// <param name="pcbComputedHash">
		/// <para>
		/// A pointer to a <c>DWORD</c> that specifies the size, in bytes, of the buffer pointed to by the pbComputedHash parameter. When
		/// the function returns, this <c>DWORD</c> contains the size, in bytes, of the newly created hash that was copied to pbComputedHash.
		/// </para>
		/// <para>
		/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
		/// be slightly smaller than the size of the buffer specified on input. On input, buffer sizes are usually specified large enough to
		/// ensure that the largest possible output data will fit in the buffer. On output, the variable pointed to by this parameter is
		/// updated to reflect the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the function returns nonzero ( <c>TRUE</c>).</para>
		/// <para>If the function fails, it returns zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
		/// <para>
		/// <c>Note</c> Errors from the called functions CryptCreateHash, CryptHashData, and CryptGetHashParam might be propagated to this function.
		/// </para>
		/// <para>The GetLastError function returns the following error codes most often.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>
		/// The message encoding type is not valid. Currently only PKCS_7_ASN_ENCODING is supported. The cbSize in *pHashPara is not valid.
		/// </term>
		/// </item>
		/// <item>
		/// <term>ERROR_MORE_DATA</term>
		/// <term>
		/// If the buffer specified by the pbHashedBlob parameter is not large enough to hold the returned data, the function sets the
		/// ERROR_MORE_DATA code and stores the required buffer size, in bytes, into the variable pointed to by pbHashedBlob.
		/// </term>
		/// </item>
		/// </list>
		/// <para>
		/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
		/// about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-crypthashmessage BOOL CryptHashMessage(
		// PCRYPT_HASH_MESSAGE_PARA pHashPara, BOOL fDetachedHash, DWORD cToBeHashed, const BYTE * [] rgpbToBeHashed, DWORD []
		// rgcbToBeHashed, BYTE *pbHashedBlob, DWORD *pcbHashedBlob, BYTE *pbComputedHash, DWORD *pcbComputedHash );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "85a04c01-fd7c-4d87-b6e1-a0f2aea45d16")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptHashMessage(in CRYPT_HASH_MESSAGE_PARA pHashPara, [MarshalAs(UnmanagedType.Bool)] bool fDetachedHash, uint cToBeHashed,
			[In] IntPtr[] rgpbToBeHashed, [In] uint[] rgcbToBeHashed, [Out] IntPtr pbHashedBlob, ref uint pcbHashedBlob, [Out] IntPtr pbComputedHash, ref uint pcbComputedHash);

		/// <summary>
		/// The <c>CryptMsgCalculateEncodedLength</c> function calculates the maximum number of bytes needed for an encoded cryptographic
		/// message given the message type, encoding parameters, and total length of the data to be encoded. Note that the result will
		/// always be greater than or equal to the actual number of bytes needed.
		/// </summary>
		/// <param name="dwMsgEncodingType">
		/// <para>
		/// Specifies the encoding type used. It is always acceptable to specify both the certificate and message encoding types by
		/// combining them with a bitwise- <c>OR</c> operation as shown in the following example:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
		/// <para>Currently defined encoding types are:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>X509_ASN_ENCODING</term>
		/// </item>
		/// <item>
		/// <term>PKCS_7_ASN_ENCODING</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="dwFlags">
		/// <para>Currently defined flags are shown in the following table.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>CMSG_BARE_CONTENT_FLAG</term>
		/// <term>
		/// Indicates that streamed output will not have an outer ContentInfo wrapper (as defined by PKCS #7). This makes it suitable to be
		/// streamed into an enclosing message.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_DETACHED_FLAG</term>
		/// <term>Indicates that there is detached data being supplied for the subsequent calls to CryptMsgUpdate.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CONTENTS_OCTETS_FLAG</term>
		/// <term>
		/// Used to calculate the size of a DER encoding of a message to be nested inside an enveloped message. This is particularly useful
		/// when streaming is being performed.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CMS_ENCAPSULATED_CONTENT_FLAG</term>
		/// <term>
		/// Non-Data type inner content is encapsulated within an OCTET STRING. This flag is applicable for both Signed and Enveloped messages.
		/// </term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="dwMsgType">
		/// <para>Currently defined message types are shown in the following table.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>CMSG_DATA</term>
		/// <term>An octet (BYTE) string.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED</term>
		/// <term>CMSG_SIGNED_ENCODE_INFO</term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENVELOPED</term>
		/// <term>CMSG_ENVELOPED_ENCODE_INFO</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED_AND_ENVELOPED</term>
		/// <term>Not implemented.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_HASHED</term>
		/// <term>CMSG_HASHED_ENCODE_INFO</term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENCRYPTED</term>
		/// <term>Not implemented.</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="pvMsgEncodeInfo">
		/// A pointer to the data to be encoded. The type of data pointed to depends on the value of dwMsgType. For details, see the
		/// dwMsgType table.
		/// </param>
		/// <param name="pszInnerContentObjID">
		/// <para>
		/// When calling <c>CryptMsgCalculateEncodedLength</c> with data provided to CryptMsgUpdate already encoded, the appropriate object
		/// identifier is passed in pszInnerContentObjID. If pszInnerContentObjID is <c>NULL</c>, the inner content type is assumed not to
		/// have been previously encoded, and is encoded as an octet string and given the type CMSG_DATA.
		/// </para>
		/// <para>When streaming is being used, pszInnerContentObjID must be either <c>NULL</c> or szOID_RSA_data.</para>
		/// <para>The following algorithm object identifiers are commonly used:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>szOID_RSA_data</term>
		/// </item>
		/// <item>
		/// <term>szOID_RSA_signedData</term>
		/// </item>
		/// <item>
		/// <term>szOID_RSA_envelopedData</term>
		/// </item>
		/// <item>
		/// <term>szOID_RSA_signEnvData</term>
		/// </item>
		/// <item>
		/// <term>szOID_RSA_digestedData</term>
		/// </item>
		/// <item>
		/// <term>szOID_RSA_encryptedData</term>
		/// </item>
		/// <item>
		/// <term>SPC_INDIRECT_DATA_OBJID</term>
		/// </item>
		/// </list>
		/// <para>
		/// A user can define new inner content usage. The user must ensure that the sender and receiver of the message agree upon the
		/// semantics associated with the object identifier.
		/// </para>
		/// </param>
		/// <param name="cbData">The size, in bytes, of the content.</param>
		/// <returns>
		/// <para>
		/// Returns the required length for an encoded cryptographic message. This length might not be the exact length but it will not be
		/// less than the required length. Zero is returned if the function fails.
		/// </para>
		/// <para>
		/// To retrieve extended error information, use the GetLastError function. The following table lists the error codes most commonly returned.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>CRYPT_E_INVALID_MSG_TYPE</term>
		/// <term>The message type is not valid.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNKNOWN_ALGO</term>
		/// <term>The cryptographic algorithm is unknown.</term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>One or more arguments are not valid.</term>
		/// </item>
		/// </list>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgcalculateencodedlength DWORD
		// CryptMsgCalculateEncodedLength( DWORD dwMsgEncodingType, DWORD dwFlags, DWORD dwMsgType, void const *pvMsgEncodeInfo, LPSTR
		// pszInnerContentObjID, DWORD cbData );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "1c12003a-c2f3-4069-8bd6-b8f2875b0c98")]
		public static extern uint CryptMsgCalculateEncodedLength(CertEncodingType dwMsgEncodingType, CryptMsgFlags dwFlags, CryptMsgType dwMsgType, [In] IntPtr pvMsgEncodeInfo,
			[Optional] SafeOID pszInnerContentObjID, uint cbData);

		/// <summary>
		/// The <c>CryptMsgClose</c> function closes a cryptographic message handle. At each call to this function, the reference count on
		/// the message is reduced by one. When the reference count reaches zero, the message is fully released.
		/// </summary>
		/// <param name="hCryptMsg">Handle of the cryptographic message to be closed.</param>
		/// <returns>
		/// <para>If the function succeeds, the function returns nonzero ( <c>TRUE</c>).</para>
		/// <para>If the function fails, it returns zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgclose BOOL CryptMsgClose( HCRYPTMSG hCryptMsg );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "2478dd60-233a-4ef3-86e9-62d2a59ab28a")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptMsgClose(HCRYPTMSG hCryptMsg);

		/// <summary>
		/// <para>
		/// The <c>CryptMsgControl</c> function performs a control operation after a message has been decoded by a final call to the
		/// CryptMsgUpdate function. The control operations provided by this function are used for decryption, signature and hash
		/// verification, and the addition and deletion of certificates, certificate revocation lists (CRLs), signers, and unauthenticated attributes.
		/// </para>
		/// <para>
		/// Important changes that affect the handling of enveloped messages have been made to CryptoAPI to support Secure/Multipurpose
		/// Internet Mail Extensions (S/MIME) email interoperability. For more information, see the Remarks for the CryptMsgOpenToEncode function.
		/// </para>
		/// </summary>
		/// <param name="hCryptMsg">A handle of a cryptographic message for which a control is to be applied.</param>
		/// <param name="dwFlags">
		/// <para>The following value is defined when the dwCtrlType parameter is one of the following:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>CMSG_CTRL_DECRYPT</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_KEY_TRANS_DECRYPT</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_KEY_AGREE_DECRYPT</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_MAIL_LIST_DECRYPT</term>
		/// </item>
		/// </list>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>CMSG_CRYPT_RELEASE_CONTEXT_FLAG</term>
		/// <term>
		/// The handle to the cryptographic provider is released on the final call to the CryptMsgClose function. This handle is not
		/// released if the CryptMsgControl function fails.
		/// </term>
		/// </item>
		/// </list>
		/// <para>If the dwCtrlType parameter does not specify a decrypt operation, set this value to zero.</para>
		/// </param>
		/// <param name="dwCtrlType">
		/// <para>
		/// The type of operation to be performed. Currently defined message control types and the type of structure that should be passed
		/// to the pvCtrlPara parameter are shown in the following table.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>CMSG_CTRL_ADD_ATTR_CERT 14 (0xE)</term>
		/// <term>A BLOB that contains the encoded bytes of attribute certificate.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_ADD_CERT 10 (0xA)</term>
		/// <term>A CRYPT_INTEGER_BLOB structure that contains the encoded bytes of the certificate to be added to the message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_ADD_CMS_SIGNER_INFO 20 (0x14)</term>
		/// <term>
		/// A CMSG_CMS_SIGNER_INFO structure that contains signer information. This operation differs from CMSG_CTRL_ADD_SIGNER because the
		/// signer information contains the signature.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_ADD_CRL 12 (0xC)</term>
		/// <term>A BLOB that contains the encoded bytes of the CRL to be added to the message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_ADD_SIGNER 6 (0x6)</term>
		/// <term>A CMSG_SIGNER_ENCODE_INFO structure that contains the signer information to be added to the message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR 8 (0x8)</term>
		/// <term>
		/// A CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA structure that contains the index of the signer and a BLOB that contains the
		/// unauthenticated attribute information to be added to the message.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_DECRYPT 2 (0x2)</term>
		/// <term>
		/// A CMSG_CTRL_DECRYPT_PARA structure used to decrypt the message for the specified key transport recipient. This value is
		/// applicable to RSA recipients. This operation specifies that the CryptMsgControl function search the recipient index to obtain
		/// the key transport recipient information. If the function fails, GetLastError will return CRYPT_E_INVALID_INDEX if no key
		/// transport recipient is found.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_DEL_ATTR_CERT 15 (0xF)</term>
		/// <term>The index of the attribute certificate to be removed.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_DEL_CERT 11 (0xB)</term>
		/// <term>The index of the certificate to be deleted from the message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_DEL_CRL 13 (0xD)</term>
		/// <term>The index of the CRL to be deleted from the message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_DEL_SIGNER 7 (0x7)</term>
		/// <term>The index of the signer to be deleted.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR 9 (0x9)</term>
		/// <term>
		/// A CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA structure that contains an index that specifies the signer and the index that specifies
		/// the signer's unauthenticated attribute to be deleted.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_ENABLE_STRONG_SIGNATURE 21 (0x15)</term>
		/// <term>
		/// A CERT_STRONG_SIGN_PARA structure used to perform strong signature checking. To check for a strong signature, specify this
		/// control type before calling CryptMsgGetAndVerifySigner or before calling CryptMsgControl with the following control types set:
		/// After the signature is successfully verified, this function checks for a strong signature. If the signature is not strong, the
		/// operation will fail and the GetLastError value will be set to NTE_BAD_ALGID.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_KEY_AGREE_DECRYPT 17 (0x11)</term>
		/// <term>
		/// A CMSG_CTRL_KEY_AGREE_DECRYPT_PARA structure used to decrypt the message for the specified key agreement session key. Key
		/// agreement is used with Diffie-Hellman encryption/decryption.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_KEY_TRANS_DECRYPT 16 (0x10)</term>
		/// <term>
		/// A CMSG_CTRL_KEY_TRANS_DECRYPT_PARA structure used to decrypt the message for the specified key transport recipient. Key
		/// transport is used with RSA encryption/decryption.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_MAIL_LIST_DECRYPT 18 (0x12)</term>
		/// <term>
		/// A CMSG_CTRL_MAIL_LIST_DECRYPT_PARA structure used to decrypt the message for the specified recipient using a previously
		/// distributed key-encryption key (KEK).
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_VERIFY_HASH 5 (0x5)</term>
		/// <term>This value is not used.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_VERIFY_SIGNATURE 1 (0x1)</term>
		/// <term>A CERT_INFO structure that identifies the signer of the message whose signature is to be verified.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_VERIFY_SIGNATURE_EX 19 (0x13)</term>
		/// <term>
		/// A CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA structure that specifies the signer index and public key to verify the message signature.
		/// The signer public key can be a CERT_PUBLIC_KEY_INFO structure, a certificate context, or a certificate chain context.
		/// </term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="pvCtrlPara">
		/// <para>A pointer to a structure determined by the value of dwCtrlType.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>dwCtrlType value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>
		/// CMSG_CTRL_DECRYPT, CMSG_CTRL_KEY_TRANS_DECRYPT, CMSG_CTRL_KEY_AGREE_DECRYPT, or CMSG_CTRL_MAIL_LIST_DECRYPT, and the streamed
		/// enveloped message is being decoded
		/// </term>
		/// <term>
		/// Decoding will be done as if the streamed content were being decrypted. If any encrypted streamed content has accumulated prior
		/// to this call, some or all of the plaintext that results from the decryption of the cipher text is passed back to the application
		/// through the callback function specified in the call to the CryptMsgOpenToDecode function.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_VERIFY_HASH</term>
		/// <term>The hash computed from the content of the message is compared against the hash contained in the message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_ADD_SIGNER</term>
		/// <term>pvCtrlPara points to a CMSG_SIGNER_ENCODE_INFO structure that contains the signer information to be added to the message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_DEL_SIGNER</term>
		/// <term>
		/// After a deletion is made, any other signer indices in use for this message are no longer valid and must be reacquired by calling
		/// the CryptMsgGetParam function.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR</term>
		/// <term>
		/// After a deletion is made, any other unauthenticated attribute indices in use for this signer are no longer valid and must be
		/// reacquired by calling the CryptMsgGetParam function.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_DEL_CERT</term>
		/// <term>
		/// After a deletion is made, any other certificate indices in use for this message are no longer valid and must be reacquired by
		/// calling the CryptMsgGetParam function.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CTRL_DEL_CRL</term>
		/// <term>
		/// After a deletion is made, any other CRL indices in use for this message are no longer valid and will need to be reacquired by
		/// calling the CryptMsgGetParam function.
		/// </term>
		/// </item>
		/// </list>
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the return value is nonzero.</para>
		/// <para>
		/// If the function fails, the return value is zero and the GetLastError function returns an Abstract Syntax Notation One (ASN.1)
		/// encoding/decoding error. For information about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// <para>
		/// When a streamed, enveloped message is being decoded, errors encountered in the application-defined callback function specified
		/// by the pStreamInfo parameter of the CryptMsgOpenToDecode function might be propagated to the <c>CryptMsgControl</c> function. If
		/// this happens, the SetLastError function is not called by the <c>CryptMsgControl</c> function after the callback function
		/// returns. This preserves any errors encountered under the control of the application. It is the responsibility of the callback
		/// function (or one of the APIs that it calls) to call the <c>SetLastError</c> function if an error occurs while the application is
		/// processing the streamed data.
		/// </para>
		/// <para>Propagated errors might be encountered from the following functions:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>CryptCreateHash</term>
		/// </item>
		/// <item>
		/// <term>CryptDecrypt</term>
		/// </item>
		/// <item>
		/// <term>CryptGetHashParam</term>
		/// </item>
		/// <item>
		/// <term>CryptGetUserKey</term>
		/// </item>
		/// <item>
		/// <term>CryptHashData</term>
		/// </item>
		/// <item>
		/// <term>CryptImportKey</term>
		/// </item>
		/// <item>
		/// <term>CryptSignHash</term>
		/// </item>
		/// <item>
		/// <term>CryptVerifySignature</term>
		/// </item>
		/// </list>
		/// <para>The following error codes are most commonly returned.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>CRYPT_E_ALREADY_DECRYPTED</term>
		/// <term>The message content has already been decrypted. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_DECRYPT.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_AUTH_ATTR_MISSING</term>
		/// <term>
		/// The message does not contain an expected authenticated attribute. This error can be returned if the dwCtrlType parameter is set
		/// to CMSG_CTRL_VERIFY_SIGNATURE.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_BAD_ENCODE</term>
		/// <term>
		/// An error was encountered while encoding or decoding. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_VERIFY_SIGNATURE.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_CONTROL_TYPE</term>
		/// <term>The control type is not valid.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_HASH_VALUE</term>
		/// <term>The hash value is incorrect.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_INVALID_INDEX</term>
		/// <term>The index value is not valid.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_INVALID_MSG_TYPE</term>
		/// <term>The message type is not valid.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_OID_FORMAT</term>
		/// <term>The object identifier is badly formatted. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_ADD_SIGNER.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_RECIPIENT_NOT_FOUND</term>
		/// <term>
		/// The enveloped data message does not contain the specified recipient. This error can be returned if the dwCtrlType parameter is
		/// set to CMSG_CTRL_DECRYPT.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_SIGNER_NOT_FOUND</term>
		/// <term>The specified signer for the message was not found. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_VERIFY_SIGNATURE.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNKNOWN_ALGO</term>
		/// <term>The cryptographic algorithm is unknown.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNEXPECTED_ENCODING</term>
		/// <term>The message is not encoded as expected. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_VERIFY_SIGNATURE.</term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>One or more arguments are not valid. This error can be returned if the dwCtrlType parameter is set to CMSG_CTRL_DECRYPT.</term>
		/// </item>
		/// <item>
		/// <term>E_OUTOFMEMORY</term>
		/// <term>Not enough memory was available to complete the operation.</term>
		/// </item>
		/// </list>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgcontrol BOOL CryptMsgControl( HCRYPTMSG
		// hCryptMsg, DWORD dwFlags, DWORD dwCtrlType, void const *pvCtrlPara );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "a990d44d-2993-429f-b817-2a834105ecef")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptMsgControl(HCRYPTMSG hCryptMsg, CryptMsgFlags dwFlags, CryptMsgControlType dwCtrlType, [In, Optional] IntPtr pvCtrlPara);

		/// <summary>
		/// The <c>CryptMsgCountersign</c> function countersigns an existing signature in a message. Countersignatures are used to sign an
		/// existing signature's encrypted hash of the message. Countersignatures can be used for various purposes including time stamping a message.
		/// </summary>
		/// <param name="hCryptMsg">Cryptographic message handle to be used.</param>
		/// <param name="dwIndex">Zero-based index of the signer in the signed or signed-and-enveloped message to be countersigned.</param>
		/// <param name="cCountersigners">Number of countersigners in the rgCountersigners array.</param>
		/// <param name="rgCountersigners">Array of countersigners' CMSG_SIGNER_ENCODE_INFO structures.</param>
		/// <returns>
		/// <para>If the function succeeds, the function returns nonzero ( <c>TRUE</c>).</para>
		/// <para>If the function fails, it returns zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
		/// <para>An error can be propagated from CryptMsgCountersignEncoded.</para>
		/// <para>The following error codes are returned most often.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>One or more arguments are not valid.</term>
		/// </item>
		/// <item>
		/// <term>E_OUTOFMEMORY</term>
		/// <term>Ran out of memory.</term>
		/// </item>
		/// <item>
		/// <term>ERROR_MORE_DATA</term>
		/// <term>The specified area is not large enough to hold the returned data.</term>
		/// </item>
		/// </list>
		/// <para>
		/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
		/// about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgcountersign BOOL CryptMsgCountersign( HCRYPTMSG
		// hCryptMsg, DWORD dwIndex, DWORD cCountersigners, PCMSG_SIGNER_ENCODE_INFO rgCountersigners );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "ebf76664-bca6-462d-b519-2b60f435d8ef")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptMsgCountersign(HCRYPTMSG hCryptMsg, uint dwIndex, uint cCountersigners, [In, MarshalAs(UnmanagedType.LPArray)] CMSG_SIGNER_ENCODE_INFO[] rgCountersigners);

		/// <summary>
		/// The <c>CryptMsgCountersignEncoded</c> function countersigns an existing PKCS #7 message signature. The pbCountersignature
		/// <c>BYTE</c> buffer it creates is a PKCS #7 encoded SignerInfo that can be used as an unauthenticated Countersignature attribute
		/// of a PKCS #9 signed-data or signed-and-enveloped-data message.
		/// </summary>
		/// <param name="dwEncodingType">
		/// <para>
		/// Specifies the encoding type used. Currently, only X509_ASN_ENCODING and PKCS_7_ASN_ENCODING are being used; however, additional
		/// encoding types may be added in the future. For either current encoding type, use:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING.</para>
		/// </param>
		/// <param name="pbSignerInfo">A pointer to the encoded SignerInfo that is to be countersigned.</param>
		/// <param name="cbSignerInfo">Count, in bytes, of the encoded SignerInfo data.</param>
		/// <param name="cCountersigners">Number of countersigners in the rgCountersigners array.</param>
		/// <param name="rgCountersigners">Array of countersigners' CMSG_SIGNER_ENCODE_INFO structures.</param>
		/// <param name="pbCountersignature">
		/// <para>A pointer to a buffer to receive an encoded PKCS #9 countersignature attribute.</para>
		/// <para>
		/// On input, this parameter can be <c>NULL</c> to set the size of this information for memory allocation purposes. For more
		/// information, see Retrieving Data of Unknown Length.
		/// </para>
		/// </param>
		/// <param name="pcbCountersignature">
		/// A pointer to a variable that specifies the size, in bytes, of the buffer pointed to by the pbCountersignature parameter. When
		/// the function returns, the variable pointed to by the pcbCountersignature parameter contains the number of bytes stored in the buffer.
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the return value is nonzero ( <c>TRUE</c>).</para>
		/// <para>If the function fails, the return value is zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
		/// <para>The following table lists the error codes most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>CRYPT_E_OID_FORMAT</term>
		/// <term>The object identifier is badly formatted.</term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>One or more arguments are not valid.</term>
		/// </item>
		/// <item>
		/// <term>E_OUTOFMEMORY</term>
		/// <term>Ran out of memory.</term>
		/// </item>
		/// </list>
		/// <para>Propagated errors might be returned from one of the following functions:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>CryptCreateHash</term>
		/// </item>
		/// <item>
		/// <term>CryptHashData</term>
		/// </item>
		/// <item>
		/// <term>CryptGetHashParam</term>
		/// </item>
		/// <item>
		/// <term>CryptSignHash</term>
		/// </item>
		/// <item>
		/// <term>CryptMsgOpenToEncode</term>
		/// </item>
		/// <item>
		/// <term>CryptMsgUpdate</term>
		/// </item>
		/// <item>
		/// <term>CryptMsgControl</term>
		/// </item>
		/// </list>
		/// <para>
		/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
		/// about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgcountersignencoded BOOL
		// CryptMsgCountersignEncoded( DWORD dwEncodingType, PBYTE pbSignerInfo, DWORD cbSignerInfo, DWORD cCountersigners,
		// PCMSG_SIGNER_ENCODE_INFO rgCountersigners, PBYTE pbCountersignature, PDWORD pcbCountersignature );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "d9fd734b-e14d-4392-ac88-5565aefbedb4")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptMsgCountersignEncoded(CertEncodingType dwEncodingType, [In] IntPtr pbSignerInfo, uint cbSignerInfo, uint cCountersigners,
			[In, MarshalAs(UnmanagedType.LPArray)] CMSG_SIGNER_ENCODE_INFO[] rgCountersigners, [Out] IntPtr pbCountersignature, ref uint pcbCountersignature);

		/// <summary>The <c>CryptMsgDuplicate</c> function duplicates a cryptographic message handle by incrementing its reference count.</summary>
		/// <param name="hCryptMsg">
		/// Handle of the cryptographic message to be duplicated. Duplication is done by incrementing the reference count of the message. A
		/// copy of the message is not made.
		/// </param>
		/// <returns>
		/// The returned handle is the same as the handle input. A copy of the message is not created. When you have finished using the
		/// duplicated message handle, decrease the reference count by calling the CryptMsgClose function.
		/// </returns>
		/// <remarks>
		/// <para>
		/// <c>CryptMsgDuplicate</c> is used to increase the reference count on an <c>HCRYPTMSG</c> handle so that multiple calls to
		/// CryptMsgClose are required to actually release the handle.
		/// </para>
		/// <para>Examples</para>
		/// <para>For an example that uses this function, see Example C Program: Encoding and Decoding a Hashed Message.</para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgduplicate HCRYPTMSG CryptMsgDuplicate( HCRYPTMSG
		// hCryptMsg );
		[DllImport(Lib.Crypt32, SetLastError = false, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "9b1142b9-0caa-4304-bfe6-1c27c6a7b782")]
		public static extern HCRYPTMSG CryptMsgDuplicate(HCRYPTMSG hCryptMsg);

		/// <summary>
		/// The <c>CryptMsgGetParam</c> function acquires a message parameter after a cryptographic message has been encoded or decoded.
		/// This function is called after the final CryptMsgUpdate call.
		/// </summary>
		/// <param name="hCryptMsg">Handle of a cryptographic message.</param>
		/// <param name="dwParamType">
		/// <para>
		/// Indicates the parameter types of data to be retrieved. The type of data to be retrieved determines the type of structure to use
		/// for pvData.
		/// </para>
		/// <para>
		/// For an encoded message, only the CMSG_BARE_CONTENT, CMSG_ENCODE_SIGNER, CMSG_CONTENT_PARAM and CMSG_COMPUTED_HASH_PARAM
		/// dwParamTypes are valid.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>CMSG_ATTR_CERT_COUNT_PARAM</term>
		/// <term>pvData data type: pointer to a DWORD Returns the count of the attribute certificates in a SIGNED or ENVELOPED message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_ATTR_CERT_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array Retrieves an attribute certificate. To get all the attribute certificates, call
		/// CryptMsgGetParam varying dwIndex set to 0 the number of attributes minus one.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_BARE_CONTENT_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array Retrieves the encoded content of an encoded cryptographic message, without the outer
		/// layer of the CONTENT_INFO structure. That is, only the encoding of the PKCS #7 defined ContentInfo.content field is returned.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CERT_COUNT_PARAM</term>
		/// <term>pvData data type: pointer to DWORD Returns the number of certificates in a received SIGNED or ENVELOPED message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CERT_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array Returns a signer's certificate. To get all of the signer's certificates, call
		/// CryptMsgGetParam, varying dwIndex from 0 to the number of available certificates minus one.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_COMPUTED_HASH_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array Returns the hash calculated of the data in the message. This type is applicable to
		/// both encode and decode.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CONTENT_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array Returns the whole PKCS #7 message from a message opened to encode. Retrieves the inner
		/// content of a message opened to decode. If the message is enveloped, the inner type is data, and CryptMsgControl has been called
		/// to decrypt the message, the decrypted content is returned. If the inner type is not data, the encoded BLOB that requires further
		/// decoding is returned. If the message is not enveloped and the inner content is DATA, the returned data is the octets of the
		/// inner content. This type is applicable to both encode and decode. For decoding, if the type is CMSG_DATA, the content's octets
		/// are returned; else, the encoded inner content is returned.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CRL_COUNT_PARAM</term>
		/// <term>pvData data type: pointer to DWORD Returns the count of CRLs in a received, SIGNED or ENVELOPED message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CRL_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array Returns a CRL. To get all the CRLs, call CryptMsgGetParam, varying dwIndex from 0 to
		/// the number of available CRLs minus one.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENCODED_MESSAGE</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array Changes the contents of an already encoded message. The message must first be decoded
		/// with a call to CryptMsgOpenToDecode. Then the change to the message is made through a call to CryptMsgControl,
		/// CryptMsgCountersign, or CryptMsgCountersignEncoded. The message is then encoded again with a call to CryptMsgGetParam,
		/// specifying CMSG_ENCODED_MESSAGE to get a new encoding that reflects the changes made. This can be used, for instance, to add a
		/// time-stamp attribute to a message.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENCODED_SIGNER</term>
		/// <term>pvData data type: pointer to a BYTE array Returns the encoded CMSG_SIGNER_INFO signer information for a message signer.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENCRYPTED_DIGEST</term>
		/// <term>pvData data type: pointer to a BYTE array Returns the encrypted hash of a signature. Typically used for performing time-stamping.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENCRYPT_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array for a CRYPT_ALGORITHM_IDENTIFIER structure. Returns the encryption algorithm used to
		/// encrypted the message.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENVELOPE_ALGORITHM_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array for a CRYPT_ALGORITHM_IDENTIFIER structure. Returns the encryption algorithm used to
		/// encrypt an ENVELOPED message.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_HASH_ALGORITHM_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array for a CRYPT_ALGORITHM_IDENTIFIER structure. Returns the hash algorithm used to hash
		/// the message when it was created.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_HASH_DATA_PARAM</term>
		/// <term>pvData data type: pointer to a BYTE array Returns the hash value stored in the message when it was created.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_INNER_CONTENT_TYPE_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array to receive a null-terminated object identifier (OID) string. Returns the inner content
		/// type of a received message. This type is not applicable to messages of type DATA.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_RECIPIENT_COUNT_PARAM</term>
		/// <term>pvData data type: pointer to a DWORD Returns the number of key transport recipients of an ENVELOPED received message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CMS_RECIPIENT_COUNT_PARAM</term>
		/// <term>
		/// pvData data type: pointer to DWORD Returns the total count of all message recipients including key agreement and mail list recipients.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_RECIPIENT_INDEX_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a DWORD Returns the index of the key transport recipient used to decrypt an ENVELOPED message. This
		/// value is available only after a message has been decrypted.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CMS_RECIPIENT_INDEX_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a DWORD Returns the index of the key transport, key agreement, or mail list recipient used to
		/// decrypt an ENVELOPED message.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CMS_RECIPIENT_ENCRYPTED_KEY_INDEX_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a DWORD Returns the index of the encrypted key of a key agreement recipient used to decrypt an
		/// ENVELOPED message.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_RECIPIENT_INFO_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array to receive a CERT_INFO structure. Returns certificate information about a key
		/// transport message's recipient. To get certificate information on all key transport message's recipients, repetitively call
		/// CryptMsgGetParam, varying dwIndex from 0 to the number of recipients minus one. Only the Issuer, SerialNumber, and
		/// PublicKeyAlgorithm members of the CERT_INFO structure returned are available and valid.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CMS_RECIPIENT_INFO_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array to receive a CMSG_CMS_RECIPIENT_INFO structure. Returns information about a key
		/// transport, key agreement, or mail list recipient. It is not limited to key transport message recipients. To get information on
		/// all of a message's recipients, repetitively call CryptMsgGetParam, varying dwIndex from 0 to the number of recipients minus one.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNER_AUTH_ATTR_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array to receive a CRYPT_ATTRIBUTES structure. Returns the authenticated attributes of a
		/// message signer. To retrieve the authenticated attributes for a specified signer, call CryptMsgGetParam with dwIndex equal to
		/// that signer's index.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNER_CERT_INFO_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array to receive the CERT_INFO structure. Returns information on a message signer needed to
		/// identify the signer's certificate. A certificate's Issuer and SerialNumber can be used to uniquely identify a certificate for
		/// retrieval. To retrieve information for all the signers, repetitively call CryptMsgGetParam varying dwIndex from 0 to the number
		/// of signers minus one. Only the Issuer and SerialNumber fields in the CERT_INFO structure returned contain available, valid data.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNER_CERT_ID_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array to receive a CERT_ID structure. Returns information on a message signer needed to
		/// identify the signer's public key. This could be a certificate's Issuer and SerialNumber, a KeyID, or a HashId. To retrieve
		/// information for all the signers, call CryptMsgGetParam varying dwIndex from 0 to the number of signers minus one.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNER_COUNT_PARAM</term>
		/// <term>pvData data type: pointer to a DWORD Returns the number of signers of a received SIGNED message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNER_HASH_ALGORITHM_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array to receive the CRYPT_ALGORITHM_IDENTIFIER structure. Returns the hash algorithm used
		/// by a signer of the message. To get the hash algorithm for a specified signer, call CryptMsgGetParam with dwIndex equal to that
		/// signer's index.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNER_INFO_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array to receive a CMSG_SIGNER_INFO structure. Returns information on a message signer. This
		/// includes the issuer and serial number of the signer's certificate and authenticated and unauthenticated attributes of the
		/// signer's certificate. To retrieve signer information on all of the signers of a message, call CryptMsgGetParam varying dwIndex
		/// from 0 to the number of signers minus one.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CMS_SIGNER_INFO_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array to receive a CMSG_CMS_SIGNER_INFO structure. Returns information on a message signer.
		/// This includes a signerId and authenticated and unauthenticated attributes. To retrieve signer information on all of the signers
		/// of a message, call CryptMsgGetParam varying dwIndex from 0 to the number of signers minus one.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNER_UNAUTH_ATTR_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array to receive a CRYPT_ATTRIBUTES structure. Returns a message signer's unauthenticated
		/// attributes. To retrieve the unauthenticated attributes for a specified signer, call CryptMsgGetParam with dwIndex equal to that
		/// signer's index.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_TYPE_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a DWORD Returns the message type of a decoded message of unknown type. The retrieved message type
		/// can be compared to supported types to determine whether processing can continued. For supported message types, see the
		/// dwMessageType parameter of CryptMsgOpenToDecode.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_UNPROTECTED_ATTR_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a BYTE array to receive a CMSG_ATTR structure. Returns the unprotected attributes in an enveloped message.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_VERSION_PARAM</term>
		/// <term>
		/// pvData data type: pointer to a DWORD Returns the version of the decoded message. For more information, see the table in the
		/// Remarks section.
		/// </term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="dwIndex">
		/// Index for the parameter being retrieved, where applicable. When a parameter is not being retrieved, this parameter is ignored
		/// and is set to zero.
		/// </param>
		/// <param name="pvData">
		/// <para>
		/// A pointer to a buffer that receives the data retrieved. The form of this data will vary depending on the value of the
		/// dwParamType parameter.
		/// </para>
		/// <para>
		/// This parameter can be <c>NULL</c> to set the size of this information for memory allocation purposes. For more information, see
		/// Retrieving Data of Unknown Length.
		/// </para>
		/// <para>
		/// When processing the data returned in this buffer, applications need to use the actual size of the data returned. The actual size
		/// can be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large
		/// enough to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this
		/// parameter is updated to reflect the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <param name="pcbData">
		/// A pointer to a variable that specifies the size, in bytes, of the buffer pointed to by the pvData parameter. When the function
		/// returns, the variable pointed to by the pcbData parameter contains the number of bytes stored in the buffer.
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the return value is nonzero (TRUE).</para>
		/// <para>If the function fails, the return value is zero (FALSE). For extended error information, call GetLastError.</para>
		/// <para>The following table lists the error codes most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>CRYPT_E_ATTRIBUTES_MISSING</term>
		/// <term>The message does not contain the requested attributes.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_INVALID_INDEX</term>
		/// <term>The index value is not valid.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_INVALID_MSG_TYPE</term>
		/// <term>The message type is not valid.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_NOT_DECRYPTED</term>
		/// <term>The message content has not been decrypted yet.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_OID_FORMAT</term>
		/// <term>The object identifier is badly formatted.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNEXPECTED_ENCODING</term>
		/// <term>The message is not encoded as expected.</term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>One or more arguments are not valid.</term>
		/// </item>
		/// <item>
		/// <term>ERROR_MORE_DATA</term>
		/// <term>The specified buffer is not large enough to hold the returned data.</term>
		/// </item>
		/// </list>
		/// <para>For dwParamType CMSG_COMPUTED_HASH_PARAM, an error can be propagated from CryptGetHashParam.</para>
		/// <para>
		/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
		/// about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		/// <remarks>
		/// <para>
		/// The following version numbers are returned by calls to <c>CryptMsgGetParam</c> with dwParamType set to CMSG_VERSION_PARAM are defined:
		/// </para>
		/// <list type="bullet">
		/// <item>
		/// <term>CMSG_SIGNED_DATA_V1</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED_DATA_V3</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED_DATA_PKCS_1_5_VERSION</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED_DATA_CMS_VERSION</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNER_INFO_V1</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNER_INFO_V3</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNER_INFO_PKCS_1_5_VERSION</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNER_INFO_CMS_VERSION</term>
		/// </item>
		/// <item>
		/// <term>CMSG_HASHED_DATA_V0</term>
		/// </item>
		/// <item>
		/// <term>CMSG_HASHED_DATA_V2</term>
		/// </item>
		/// <item>
		/// <term>CMSG_HASHED_DATA_PKCS_1_5_VERSION</term>
		/// </item>
		/// <item>
		/// <term>CMSG_HASHED_DATA_CMS_VERSION</term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENVELOPED_DATA_V0</term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENVELOPED_DATA_V2</term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENVELOPED_DATA_PKCS_1_5_VERSION</term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENVELOPED_DATA_CMS_VERSION</term>
		/// </item>
		/// </list>
		/// <para>Examples</para>
		/// <para>
		/// For an example that uses this function, see Example C Program: Signing, Encoding, Decoding, and Verifying a Message, Alternate
		/// Code for Encoding an Enveloped Message, Example C Program: Encoding an Enveloped, Signed Message, and Example C Program:
		/// Encoding and Decoding a Hashed Message.
		/// </para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsggetparam BOOL CryptMsgGetParam( HCRYPTMSG
		// hCryptMsg, DWORD dwParamType, DWORD dwIndex, void *pvData, DWORD *pcbData );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "5a05eb09-208f-4e94-abfa-c2f14c0a3164")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptMsgGetParam(HCRYPTMSG hCryptMsg, CryptMsgParamType dwParamType, uint dwIndex, [Out] IntPtr pvData, ref uint pcbData);

		/// <summary>
		/// <para>
		/// The <c>CryptMsgOpenToDecode</c> function opens a cryptographic message for decoding and returns a handle of the opened message.
		/// The message remains open until the CryptMsgClose function is called.
		/// </para>
		/// <para>
		/// Important changes that affect the handling of enveloped messages have been made to CryptoAPI to support Secure/Multipurpose
		/// Internet Mail Extensions (S/MIME) email interoperability. For details, see the Remarks section of CryptMsgOpenToEncode.
		/// </para>
		/// </summary>
		/// <param name="dwMsgEncodingType">
		/// <para>
		/// Specifies the encoding type used. It is always acceptable to specify both the certificate and message encoding types by
		/// combining them with a bitwise- <c>OR</c> operation as shown in the following example:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
		/// <para>Currently defined encoding types are:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>X509_ASN_ENCODING</term>
		/// </item>
		/// <item>
		/// <term>PKCS_7_ASN_ENCODING</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="dwFlags">
		/// <para>This parameter can be one of the following flags.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>CMSG_DETACHED_FLAG</term>
		/// <term>Indicates that the message to be decoded is detached. If this flag is not set, the message is not detached.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CRYPT_RELEASE_CONTEXT_FLAG</term>
		/// <term>
		/// If set, the hCryptProv passed to this function is released on the final CryptMsgUpdate. The handle is not released if the
		/// function fails.
		/// </term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="dwMsgType">
		/// <para>
		/// Specifies the type of message to decode. In most cases, the message type is determined from the message header and zero is
		/// passed for this parameter. In some cases, notably with Internet Explorer 3.0, messages do not have headers and the type of
		/// message to be decoded must be supplied in this function call. If the header is missing and zero is passed for this parameter,
		/// the function fails.
		/// </para>
		/// <para>This parameter can be one of the following predefined message types.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>CMSG_DATA</term>
		/// <term>The message is encoded data.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENVELOPED</term>
		/// <term>The message is an enveloped message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_HASHED</term>
		/// <term>The message is a hashed message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED</term>
		/// <term>The message is a signed message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED_AND_ENVELOPED</term>
		/// <term>The message is a signed and enveloped message.</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="hCryptProv">
		/// <para>This parameter is not used and should be set to <c>NULL</c>.</para>
		/// <para>
		/// <c>Windows Server 2003 and Windows XP:</c> Specifies a handle for the cryptographic provider to use for hashing the message. For
		/// signed messages, hCryptProv is used for signature verification.This parameter's data type is <c>HCRYPTPROV</c>.
		/// </para>
		/// <para>
		/// Unless there is a strong reason for passing in a specific cryptographic provider in hCryptProv, set this parameter to
		/// <c>NULL</c>. Passing in <c>NULL</c> causes the default RSA or DSS provider to be acquired before performing hash, signature
		/// verification, or recipient encryption operations.
		/// </para>
		/// </param>
		/// <param name="pRecipientInfo">This parameter is reserved for future use and must be <c>NULL</c>.</param>
		/// <param name="pStreamInfo">
		/// <para>When streaming is not being used, this parameter must be set to <c>NULL</c>.</para>
		/// <para>
		/// <c>Note</c> Streaming is not used with CMSG_HASHED messages. When dealing with hashed data, this parameter must be set to <c>NULL</c>.
		/// </para>
		/// <para>
		/// When streaming is being used, the pStreamInfo parameter is a pointer to a CMSG_STREAM_INFO structure that contains a pointer to
		/// a callback to be called when CryptMsgUpdate is executed or when CryptMsgControl is executed when decoding a streamed enveloped message.
		/// </para>
		/// <para>For a signed message, the callback is passed a block of the decoded bytes from the inner content of the message.</para>
		/// <para>
		/// For an enveloped message, after each call to CryptMsgUpdate, you must check to determine whether the
		/// CMSG_ENVELOPE_ALGORITHM_PARAM property is available by calling the CryptMsgGetParam function. <c>CryptMsgGetParam</c> will fail
		/// and GetLastError will return CRYPT_E_STREAM_MSG_NOT_READY until <c>CryptMsgUpdate</c> has processed enough of the message to
		/// make the CMSG_ENVELOPE_ALGORITHM_PARAM property available. When the CMSG_ENVELOPE_ALGORITHM_PARAM property is available, you can
		/// iterate through the recipients, retrieving a CERT_INFO structure for each recipient by using the <c>CryptMsgGetParam</c>
		/// function to retrieve the CMSG_RECIPIENT_INFO_PARAM property. To prevent a denial of service attack from an enveloped message
		/// that has an artificially large header block, you must keep track of the amount of memory that has been passed to the
		/// <c>CryptMsgUpdate</c> function during this process. If the amount of data exceeds an application defined limit before the
		/// CMSG_ENVELOPE_ALGORITHM_PARAM property is available, you must stop processing the message and call the CryptMsgClose function to
		/// cause the operating system to release any memory that has been allocated for the message. A suggested limit is the maximum
		/// allowable size of a message. For example, if the maximum message size is 10 MB, the limit for this test should be 10 MB.
		/// </para>
		/// <para>
		/// The CERT_INFO structure is used to find a matching certificate in a previously opened certificate store by using the
		/// CertGetSubjectCertificateFromStore function. When the correct certificate is found, the CertGetCertificateContextProperty
		/// function with a CERT_KEY_PROV_INFO_PROP_ID parameter is called to retrieve a CRYPT_KEY_PROV_INFO structure. The structure
		/// contains the information necessary to acquire the recipient's private key by calling CryptAcquireContext, using the
		/// <c>pwszContainerName</c>, <c>pwszProvName</c>, <c>dwProvType</c>, and <c>dwFlags</c> members of the <c>CRYPT_KEY_PROV_INFO</c>
		/// structure. The <c>hCryptProv</c> acquired and the <c>dwKeySpec</c> member of the <c>CRYPT_KEY_PROV_INFO</c> structure are passed
		/// to the CryptMsgControl structure as a member of the CMSG_CTRL_DECRYPT_PARA structure to permit the start of the decryption of
		/// the inner content. The streaming code will then perform the decryption as the data is input. The resulting blocks of plaintext
		/// are passed to the callback function specified by the <c>pfnStreamOutput</c> member of the CMSG_STREAM_INFO structure to handle
		/// the output of the decrypted message.
		/// </para>
		/// <para>
		/// <c>Note</c> Streamed decoding of an enveloped message queues the ciphertext in memory until CryptMsgControl is called to start
		/// the decryption. The application must initiate decrypting in a timely manner so that the data can be saved to disk or routed
		/// elsewhere before the accumulated ciphertext becomes too large and the system runs out of memory.
		/// </para>
		/// <para>
		/// In the case of a signed message enclosed in an enveloped message, the plaintext output from the streaming decode of the
		/// enveloped message can be fed into another streaming decode to process the signed message.
		/// </para>
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the function returns the handle of the opened message.</para>
		/// <para>If the function fails, it returns <c>NULL</c>. For extended error information, call GetLastError.</para>
		/// <para>The following table lists the error codes most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>One or more arguments are not valid.</term>
		/// </item>
		/// <item>
		/// <term>E_OUTOFMEMORY</term>
		/// <term>A memory allocation failure occurred.</term>
		/// </item>
		/// </list>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgopentodecode HCRYPTMSG CryptMsgOpenToDecode(
		// DWORD dwMsgEncodingType, DWORD dwFlags, DWORD dwMsgType, HCRYPTPROV_LEGACY hCryptProv, PCERT_INFO pRecipientInfo,
		// PCMSG_STREAM_INFO pStreamInfo );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "b3df6312-c866-4faa-8b89-bda67c697631")]
		public static extern SafeHCRYPTMSG CryptMsgOpenToDecode(CertEncodingType dwMsgEncodingType, CryptMsgFlags dwFlags, CryptMsgType dwMsgType, [Optional] HCRYPTPROV hCryptProv, [Optional] IntPtr pRecipientInfo, in CMSG_STREAM_INFO pStreamInfo);

		/// <summary>
		/// <para>
		/// The <c>CryptMsgOpenToDecode</c> function opens a cryptographic message for decoding and returns a handle of the opened message.
		/// The message remains open until the CryptMsgClose function is called.
		/// </para>
		/// <para>
		/// Important changes that affect the handling of enveloped messages have been made to CryptoAPI to support Secure/Multipurpose
		/// Internet Mail Extensions (S/MIME) email interoperability. For details, see the Remarks section of CryptMsgOpenToEncode.
		/// </para>
		/// </summary>
		/// <param name="dwMsgEncodingType">
		/// <para>
		/// Specifies the encoding type used. It is always acceptable to specify both the certificate and message encoding types by
		/// combining them with a bitwise- <c>OR</c> operation as shown in the following example:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
		/// <para>Currently defined encoding types are:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>X509_ASN_ENCODING</term>
		/// </item>
		/// <item>
		/// <term>PKCS_7_ASN_ENCODING</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="dwFlags">
		/// <para>This parameter can be one of the following flags.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>CMSG_DETACHED_FLAG</term>
		/// <term>Indicates that the message to be decoded is detached. If this flag is not set, the message is not detached.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_CRYPT_RELEASE_CONTEXT_FLAG</term>
		/// <term>
		/// If set, the hCryptProv passed to this function is released on the final CryptMsgUpdate. The handle is not released if the
		/// function fails.
		/// </term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="dwMsgType">
		/// <para>
		/// Specifies the type of message to decode. In most cases, the message type is determined from the message header and zero is
		/// passed for this parameter. In some cases, notably with Internet Explorer 3.0, messages do not have headers and the type of
		/// message to be decoded must be supplied in this function call. If the header is missing and zero is passed for this parameter,
		/// the function fails.
		/// </para>
		/// <para>This parameter can be one of the following predefined message types.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>CMSG_DATA</term>
		/// <term>The message is encoded data.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENVELOPED</term>
		/// <term>The message is an enveloped message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_HASHED</term>
		/// <term>The message is a hashed message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED</term>
		/// <term>The message is a signed message.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED_AND_ENVELOPED</term>
		/// <term>The message is a signed and enveloped message.</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="hCryptProv">
		/// <para>This parameter is not used and should be set to <c>NULL</c>.</para>
		/// <para>
		/// <c>Windows Server 2003 and Windows XP:</c> Specifies a handle for the cryptographic provider to use for hashing the message. For
		/// signed messages, hCryptProv is used for signature verification.This parameter's data type is <c>HCRYPTPROV</c>.
		/// </para>
		/// <para>
		/// Unless there is a strong reason for passing in a specific cryptographic provider in hCryptProv, set this parameter to
		/// <c>NULL</c>. Passing in <c>NULL</c> causes the default RSA or DSS provider to be acquired before performing hash, signature
		/// verification, or recipient encryption operations.
		/// </para>
		/// </param>
		/// <param name="pRecipientInfo">This parameter is reserved for future use and must be <c>NULL</c>.</param>
		/// <param name="pStreamInfo">
		/// <para>When streaming is not being used, this parameter must be set to <c>NULL</c>.</para>
		/// <para>
		/// <c>Note</c> Streaming is not used with CMSG_HASHED messages. When dealing with hashed data, this parameter must be set to <c>NULL</c>.
		/// </para>
		/// <para>
		/// When streaming is being used, the pStreamInfo parameter is a pointer to a CMSG_STREAM_INFO structure that contains a pointer to
		/// a callback to be called when CryptMsgUpdate is executed or when CryptMsgControl is executed when decoding a streamed enveloped message.
		/// </para>
		/// <para>For a signed message, the callback is passed a block of the decoded bytes from the inner content of the message.</para>
		/// <para>
		/// For an enveloped message, after each call to CryptMsgUpdate, you must check to determine whether the
		/// CMSG_ENVELOPE_ALGORITHM_PARAM property is available by calling the CryptMsgGetParam function. <c>CryptMsgGetParam</c> will fail
		/// and GetLastError will return CRYPT_E_STREAM_MSG_NOT_READY until <c>CryptMsgUpdate</c> has processed enough of the message to
		/// make the CMSG_ENVELOPE_ALGORITHM_PARAM property available. When the CMSG_ENVELOPE_ALGORITHM_PARAM property is available, you can
		/// iterate through the recipients, retrieving a CERT_INFO structure for each recipient by using the <c>CryptMsgGetParam</c>
		/// function to retrieve the CMSG_RECIPIENT_INFO_PARAM property. To prevent a denial of service attack from an enveloped message
		/// that has an artificially large header block, you must keep track of the amount of memory that has been passed to the
		/// <c>CryptMsgUpdate</c> function during this process. If the amount of data exceeds an application defined limit before the
		/// CMSG_ENVELOPE_ALGORITHM_PARAM property is available, you must stop processing the message and call the CryptMsgClose function to
		/// cause the operating system to release any memory that has been allocated for the message. A suggested limit is the maximum
		/// allowable size of a message. For example, if the maximum message size is 10 MB, the limit for this test should be 10 MB.
		/// </para>
		/// <para>
		/// The CERT_INFO structure is used to find a matching certificate in a previously opened certificate store by using the
		/// CertGetSubjectCertificateFromStore function. When the correct certificate is found, the CertGetCertificateContextProperty
		/// function with a CERT_KEY_PROV_INFO_PROP_ID parameter is called to retrieve a CRYPT_KEY_PROV_INFO structure. The structure
		/// contains the information necessary to acquire the recipient's private key by calling CryptAcquireContext, using the
		/// <c>pwszContainerName</c>, <c>pwszProvName</c>, <c>dwProvType</c>, and <c>dwFlags</c> members of the <c>CRYPT_KEY_PROV_INFO</c>
		/// structure. The <c>hCryptProv</c> acquired and the <c>dwKeySpec</c> member of the <c>CRYPT_KEY_PROV_INFO</c> structure are passed
		/// to the CryptMsgControl structure as a member of the CMSG_CTRL_DECRYPT_PARA structure to permit the start of the decryption of
		/// the inner content. The streaming code will then perform the decryption as the data is input. The resulting blocks of plaintext
		/// are passed to the callback function specified by the <c>pfnStreamOutput</c> member of the CMSG_STREAM_INFO structure to handle
		/// the output of the decrypted message.
		/// </para>
		/// <para>
		/// <c>Note</c> Streamed decoding of an enveloped message queues the ciphertext in memory until CryptMsgControl is called to start
		/// the decryption. The application must initiate decrypting in a timely manner so that the data can be saved to disk or routed
		/// elsewhere before the accumulated ciphertext becomes too large and the system runs out of memory.
		/// </para>
		/// <para>
		/// In the case of a signed message enclosed in an enveloped message, the plaintext output from the streaming decode of the
		/// enveloped message can be fed into another streaming decode to process the signed message.
		/// </para>
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the function returns the handle of the opened message.</para>
		/// <para>If the function fails, it returns <c>NULL</c>. For extended error information, call GetLastError.</para>
		/// <para>The following table lists the error codes most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>One or more arguments are not valid.</term>
		/// </item>
		/// <item>
		/// <term>E_OUTOFMEMORY</term>
		/// <term>A memory allocation failure occurred.</term>
		/// </item>
		/// </list>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgopentodecode HCRYPTMSG CryptMsgOpenToDecode(
		// DWORD dwMsgEncodingType, DWORD dwFlags, DWORD dwMsgType, HCRYPTPROV_LEGACY hCryptProv, PCERT_INFO pRecipientInfo,
		// PCMSG_STREAM_INFO pStreamInfo );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "b3df6312-c866-4faa-8b89-bda67c697631")]
		public static extern SafeHCRYPTMSG CryptMsgOpenToDecode(CertEncodingType dwMsgEncodingType, CryptMsgFlags dwFlags, CryptMsgType dwMsgType, HCRYPTPROV hCryptProv = default, IntPtr pRecipientInfo = default, IntPtr pStreamInfo = default);

		/// <summary>
		/// The <c>CryptMsgOpenToEncode</c> function opens a cryptographic message for encoding and returns a handle of the opened message.
		/// The message remains open until CryptMsgClose is called.
		/// </summary>
		/// <param name="dwMsgEncodingType">
		/// <para>
		/// Specifies the encoding type used. It is always acceptable to specify both the certificate and message encoding types by
		/// combining them with a bitwise- <c>OR</c> operation as shown in the following example:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
		/// <para>Currently defined encoding types are:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>X509_ASN_ENCODING</term>
		/// </item>
		/// <item>
		/// <term>PKCS_7_ASN_ENCODING</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="dwFlags">
		/// <para>Currently defined dwFlags are shown in the following table.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>CMSG_BARE_CONTENT_FLAG</term>
		/// <term>
		/// The streamed output will not have an outer ContentInfo wrapper (as defined by PKCS #7). This makes it suitable to be streamed
		/// into an enclosing message.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_DETACHED_FLAG</term>
		/// <term>There is detached data being supplied for the subsequent calls to CryptMsgUpdate.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_AUTHENTICATED_ATTRIBUTES_FLAG</term>
		/// <term>
		/// Authenticated attributes are forced to be included in the SignerInfo (as defined by PKCS #7) in cases where they would not
		/// otherwise be required.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CONTENTS_OCTETS_FLAG</term>
		/// <term>
		/// Used when calculating the size of a message that has been encoded by using Distinguished Encoding Rules (DER) and that is nested
		/// inside an enveloped message. This is particularly useful when performing streaming.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CMS_ENCAPSULATED_CONTENT_FLAG</term>
		/// <term>
		/// When set, non-data type-inner content is encapsulated within an OCTET STRING. Applicable to both signed and enveloped messages.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CRYPT_RELEASE_CONTEXT_FLAG</term>
		/// <term>
		/// If set, the hCryptProv that is passed to this function is released on the final CryptMsgUpdate. The handle is not released if
		/// the function fails.
		/// </term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="dwMsgType">
		/// <para>Indicates the message type. This must be one of the following values.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>CMSG_DATA</term>
		/// <term>This value is not used.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED</term>
		/// <term>The pvMsgEncodeInfo parameter is the address of a CMSG_SIGNED_ENCODE_INFO structure that contains the encoding information.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENVELOPED</term>
		/// <term>The pvMsgEncodeInfo parameter is the address of a CMSG_ENVELOPED_ENCODE_INFO structure that contains the encoding information.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED_AND_ENVELOPED</term>
		/// <term>This value is not currently implemented.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_HASHED</term>
		/// <term>The pvMsgEncodeInfo parameter is the address of a CMSG_HASHED_ENCODE_INFO structure that contains the encoding information.</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="pvMsgEncodeInfo">
		/// The address of a structure that contains the encoding information. The type of data depends on the value of the dwMsgType
		/// parameter. For details, see dwMsgType.
		/// </param>
		/// <param name="pszInnerContentObjID">
		/// <para>
		/// If CryptMsgCalculateEncodedLength is called and the data for CryptMsgUpdate has already been message encoded, the appropriate
		/// object identifier (OID) is passed in pszInnerContentObjID. If pszInnerContentObjID is <c>NULL</c>, then the inner content type
		/// is assumed not to have been previously encoded and is therefore encoded as an octet string and given the type CMSG_DATA.
		/// </para>
		/// <para><c>Note</c> When streaming is being used, pszInnerContentObjID must be either <c>NULL</c> or szOID_RSA_data.</para>
		/// <para>
		/// The following algorithm OIDs are commonly used. A user can define new inner content usage by ensuring that the sender and
		/// receiver of the message agree upon the semantics associated with the OID.
		/// </para>
		/// <list type="bullet">
		/// <item>
		/// <term>szOID_RSA_data</term>
		/// </item>
		/// <item>
		/// <term>szOID_RSA_signedData</term>
		/// </item>
		/// <item>
		/// <term>szOID_RSA_envelopedData</term>
		/// </item>
		/// <item>
		/// <term>szOID_RSA_signEnvData</term>
		/// </item>
		/// <item>
		/// <term>szOID_RSA_digestedData</term>
		/// </item>
		/// <item>
		/// <term>szOID_RSA_encryptedData</term>
		/// </item>
		/// <item>
		/// <term>SPC_INDIRECT_DATA_OBJID</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="pStreamInfo">
		/// <para>
		/// When streaming is being used, this parameter is the address of a CMSG_STREAM_INFO structure. The callback function specified by
		/// the <c>pfnStreamOutput</c> member of the <c>CMSG_STREAM_INFO</c> structure is called when CryptMsgUpdate is executed. The
		/// callback is passed the encoded bytes that result from the encoding. For more information about how to use the callback, see <c>CMSG_STREAM_INFO</c>.
		/// </para>
		/// <para>
		/// <c>Note</c> When streaming is being used, the application must not release any data handles that are passed in the
		/// pvMsgEncodeInfo parameter, such as the provider handle in the <c>hCryptProv</c> member of the CMSG_SIGNER_ENCODE_INFO structure,
		/// until after the message handle returned by this function is closed by using the CryptMsgClose function.
		/// </para>
		/// <para>When streaming is not being used, this parameter is set to</para>
		/// <para>NULL</para>
		/// <para>.</para>
		/// <para>
		/// Streaming is not used with the <c>CMSG_HASHED</c> message type. When dealing with hashed data, this parameter must be set to <c>NULL</c>.
		/// </para>
		/// <para>
		/// Consider the case of a signed message being enclosed in an enveloped message. The encoded output from the streamed encoding of
		/// the signed message feeds into another streaming encoding of the enveloped message. The callback for the streaming encoding calls
		/// CryptMsgUpdate to encode the enveloped message. The callback for the enveloped message receives the encoded bytes of the nested
		/// signed message.
		/// </para>
		/// </param>
		/// <returns>
		/// <para>
		/// If the function succeeds, it returns a handle to the opened message. This handle must be closed when it is no longer needed by
		/// passing it to the CryptMsgClose function.
		/// </para>
		/// <para>If this function fails, <c>NULL</c> is returned.</para>
		/// <para>To retrieve extended error information, use the GetLastError function.</para>
		/// <para>The following table lists the error codes most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>CRYPT_E_INVALID_MSG_TYPE</term>
		/// <term>The message type is not valid.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_OID_FORMAT</term>
		/// <term>The OID is badly formatted.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNKNOWN_ALGO</term>
		/// <term>The cryptographic algorithm is unknown.</term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>One or more arguments are not valid.</term>
		/// </item>
		/// <item>
		/// <term>E_OUTOFMEMORY</term>
		/// <term>There is not enough memory.</term>
		/// </item>
		/// </list>
		/// <para>In addition, if dwMsgType is CMSG_SIGNED, errors can be propagated from CryptCreateHash.</para>
		/// <para>If dwMsgType is CMSG_ENVELOPED, errors can be propagated from CryptGenKey, CryptImportKey, and CryptExportKey.</para>
		/// <para>If dwMsgType is CMSG_HASHED, errors can be propagated from CryptCreateHash.</para>
		/// </returns>
		/// <remarks>
		/// <para>
		/// For functions that perform encryption, the encrypted symmetric keys are reversed from little-endian format to big-endian format
		/// after CryptExportKey is called internally. For functions that perform decryption, the encrypted symmetric keys are reversed from
		/// big-endian format to little-endian format before CryptImportKey is called.
		/// </para>
		/// <para>CRYPT_NO_SALT is specified when symmetric keys are generated and imported with CryptGenKey and CryptImportKey.</para>
		/// <para>
		/// Messages encrypted with the RC2 encryption algorithm use KP_EFFECTIVE_KEYLEN with CryptGetKeyParam to determine the effective
		/// key length of the RC2 key importing or exporting keys.
		/// </para>
		/// <para>
		/// For messages encrypted with the RC2 encryption algorithm, encode and decode operations have been updated to handle ASN RC2
		/// parameters for the <c>ContentEncryptionAlgorithm</c> member of the CMSG_ENVELOPED_ENCODE_INFO structure.
		/// </para>
		/// <para>
		/// For messages encrypted with the RC4, DES, and 3DES encryption algorithms, encode and decode operations now handle the ASN IV
		/// octet string parameter for the <c>ContentEncryptionAlgorithm</c> member of the CMSG_ENVELOPED_ENCODE_INFO structure.
		/// </para>
		/// <para>Examples</para>
		/// <para>
		/// For examples that use this function, see Example C Program: Signing, Encoding, Decoding, and Verifying a Message, Alternate Code
		/// for Encoding an Enveloped Message, Example C Program: Encoding an Enveloped, Signed Message, and Example C Program: Encoding and
		/// Decoding a Hashed Message.
		/// </para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgopentoencode HCRYPTMSG CryptMsgOpenToEncode(
		// DWORD dwMsgEncodingType, DWORD dwFlags, DWORD dwMsgType, void const *pvMsgEncodeInfo, LPSTR pszInnerContentObjID,
		// PCMSG_STREAM_INFO pStreamInfo );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "b0d2610b-05ba-4fb6-8f38-10f970a52091")]
		public static extern SafeHCRYPTMSG CryptMsgOpenToEncode(CertEncodingType dwMsgEncodingType, CryptMsgFlags dwFlags, CryptMsgType dwMsgType, [In] IntPtr pvMsgEncodeInfo,
			[Optional] SafeOID pszInnerContentObjID, in CMSG_STREAM_INFO pStreamInfo);

		/// <summary>
		/// The <c>CryptMsgOpenToEncode</c> function opens a cryptographic message for encoding and returns a handle of the opened message.
		/// The message remains open until CryptMsgClose is called.
		/// </summary>
		/// <param name="dwMsgEncodingType">
		/// <para>
		/// Specifies the encoding type used. It is always acceptable to specify both the certificate and message encoding types by
		/// combining them with a bitwise- <c>OR</c> operation as shown in the following example:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
		/// <para>Currently defined encoding types are:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>X509_ASN_ENCODING</term>
		/// </item>
		/// <item>
		/// <term>PKCS_7_ASN_ENCODING</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="dwFlags">
		/// <para>Currently defined dwFlags are shown in the following table.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>CMSG_BARE_CONTENT_FLAG</term>
		/// <term>
		/// The streamed output will not have an outer ContentInfo wrapper (as defined by PKCS #7). This makes it suitable to be streamed
		/// into an enclosing message.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_DETACHED_FLAG</term>
		/// <term>There is detached data being supplied for the subsequent calls to CryptMsgUpdate.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_AUTHENTICATED_ATTRIBUTES_FLAG</term>
		/// <term>
		/// Authenticated attributes are forced to be included in the SignerInfo (as defined by PKCS #7) in cases where they would not
		/// otherwise be required.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CONTENTS_OCTETS_FLAG</term>
		/// <term>
		/// Used when calculating the size of a message that has been encoded by using Distinguished Encoding Rules (DER) and that is nested
		/// inside an enveloped message. This is particularly useful when performing streaming.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CMS_ENCAPSULATED_CONTENT_FLAG</term>
		/// <term>
		/// When set, non-data type-inner content is encapsulated within an OCTET STRING. Applicable to both signed and enveloped messages.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CMSG_CRYPT_RELEASE_CONTEXT_FLAG</term>
		/// <term>
		/// If set, the hCryptProv that is passed to this function is released on the final CryptMsgUpdate. The handle is not released if
		/// the function fails.
		/// </term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="dwMsgType">
		/// <para>Indicates the message type. This must be one of the following values.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>CMSG_DATA</term>
		/// <term>This value is not used.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED</term>
		/// <term>The pvMsgEncodeInfo parameter is the address of a CMSG_SIGNED_ENCODE_INFO structure that contains the encoding information.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_ENVELOPED</term>
		/// <term>The pvMsgEncodeInfo parameter is the address of a CMSG_ENVELOPED_ENCODE_INFO structure that contains the encoding information.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_SIGNED_AND_ENVELOPED</term>
		/// <term>This value is not currently implemented.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_HASHED</term>
		/// <term>The pvMsgEncodeInfo parameter is the address of a CMSG_HASHED_ENCODE_INFO structure that contains the encoding information.</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="pvMsgEncodeInfo">
		/// The address of a structure that contains the encoding information. The type of data depends on the value of the dwMsgType
		/// parameter. For details, see dwMsgType.
		/// </param>
		/// <param name="pszInnerContentObjID">
		/// <para>
		/// If CryptMsgCalculateEncodedLength is called and the data for CryptMsgUpdate has already been message encoded, the appropriate
		/// object identifier (OID) is passed in pszInnerContentObjID. If pszInnerContentObjID is <c>NULL</c>, then the inner content type
		/// is assumed not to have been previously encoded and is therefore encoded as an octet string and given the type CMSG_DATA.
		/// </para>
		/// <para><c>Note</c> When streaming is being used, pszInnerContentObjID must be either <c>NULL</c> or szOID_RSA_data.</para>
		/// <para>
		/// The following algorithm OIDs are commonly used. A user can define new inner content usage by ensuring that the sender and
		/// receiver of the message agree upon the semantics associated with the OID.
		/// </para>
		/// <list type="bullet">
		/// <item>
		/// <term>szOID_RSA_data</term>
		/// </item>
		/// <item>
		/// <term>szOID_RSA_signedData</term>
		/// </item>
		/// <item>
		/// <term>szOID_RSA_envelopedData</term>
		/// </item>
		/// <item>
		/// <term>szOID_RSA_signEnvData</term>
		/// </item>
		/// <item>
		/// <term>szOID_RSA_digestedData</term>
		/// </item>
		/// <item>
		/// <term>szOID_RSA_encryptedData</term>
		/// </item>
		/// <item>
		/// <term>SPC_INDIRECT_DATA_OBJID</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="pStreamInfo">
		/// <para>
		/// When streaming is being used, this parameter is the address of a CMSG_STREAM_INFO structure. The callback function specified by
		/// the <c>pfnStreamOutput</c> member of the <c>CMSG_STREAM_INFO</c> structure is called when CryptMsgUpdate is executed. The
		/// callback is passed the encoded bytes that result from the encoding. For more information about how to use the callback, see <c>CMSG_STREAM_INFO</c>.
		/// </para>
		/// <para>
		/// <c>Note</c> When streaming is being used, the application must not release any data handles that are passed in the
		/// pvMsgEncodeInfo parameter, such as the provider handle in the <c>hCryptProv</c> member of the CMSG_SIGNER_ENCODE_INFO structure,
		/// until after the message handle returned by this function is closed by using the CryptMsgClose function.
		/// </para>
		/// <para>When streaming is not being used, this parameter is set to</para>
		/// <para>NULL</para>
		/// <para>.</para>
		/// <para>
		/// Streaming is not used with the <c>CMSG_HASHED</c> message type. When dealing with hashed data, this parameter must be set to <c>NULL</c>.
		/// </para>
		/// <para>
		/// Consider the case of a signed message being enclosed in an enveloped message. The encoded output from the streamed encoding of
		/// the signed message feeds into another streaming encoding of the enveloped message. The callback for the streaming encoding calls
		/// CryptMsgUpdate to encode the enveloped message. The callback for the enveloped message receives the encoded bytes of the nested
		/// signed message.
		/// </para>
		/// </param>
		/// <returns>
		/// <para>
		/// If the function succeeds, it returns a handle to the opened message. This handle must be closed when it is no longer needed by
		/// passing it to the CryptMsgClose function.
		/// </para>
		/// <para>If this function fails, <c>NULL</c> is returned.</para>
		/// <para>To retrieve extended error information, use the GetLastError function.</para>
		/// <para>The following table lists the error codes most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>CRYPT_E_INVALID_MSG_TYPE</term>
		/// <term>The message type is not valid.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_OID_FORMAT</term>
		/// <term>The OID is badly formatted.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNKNOWN_ALGO</term>
		/// <term>The cryptographic algorithm is unknown.</term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>One or more arguments are not valid.</term>
		/// </item>
		/// <item>
		/// <term>E_OUTOFMEMORY</term>
		/// <term>There is not enough memory.</term>
		/// </item>
		/// </list>
		/// <para>In addition, if dwMsgType is CMSG_SIGNED, errors can be propagated from CryptCreateHash.</para>
		/// <para>If dwMsgType is CMSG_ENVELOPED, errors can be propagated from CryptGenKey, CryptImportKey, and CryptExportKey.</para>
		/// <para>If dwMsgType is CMSG_HASHED, errors can be propagated from CryptCreateHash.</para>
		/// </returns>
		/// <remarks>
		/// <para>
		/// For functions that perform encryption, the encrypted symmetric keys are reversed from little-endian format to big-endian format
		/// after CryptExportKey is called internally. For functions that perform decryption, the encrypted symmetric keys are reversed from
		/// big-endian format to little-endian format before CryptImportKey is called.
		/// </para>
		/// <para>CRYPT_NO_SALT is specified when symmetric keys are generated and imported with CryptGenKey and CryptImportKey.</para>
		/// <para>
		/// Messages encrypted with the RC2 encryption algorithm use KP_EFFECTIVE_KEYLEN with CryptGetKeyParam to determine the effective
		/// key length of the RC2 key importing or exporting keys.
		/// </para>
		/// <para>
		/// For messages encrypted with the RC2 encryption algorithm, encode and decode operations have been updated to handle ASN RC2
		/// parameters for the <c>ContentEncryptionAlgorithm</c> member of the CMSG_ENVELOPED_ENCODE_INFO structure.
		/// </para>
		/// <para>
		/// For messages encrypted with the RC4, DES, and 3DES encryption algorithms, encode and decode operations now handle the ASN IV
		/// octet string parameter for the <c>ContentEncryptionAlgorithm</c> member of the CMSG_ENVELOPED_ENCODE_INFO structure.
		/// </para>
		/// <para>Examples</para>
		/// <para>
		/// For examples that use this function, see Example C Program: Signing, Encoding, Decoding, and Verifying a Message, Alternate Code
		/// for Encoding an Enveloped Message, Example C Program: Encoding an Enveloped, Signed Message, and Example C Program: Encoding and
		/// Decoding a Hashed Message.
		/// </para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgopentoencode HCRYPTMSG CryptMsgOpenToEncode(
		// DWORD dwMsgEncodingType, DWORD dwFlags, DWORD dwMsgType, void const *pvMsgEncodeInfo, LPSTR pszInnerContentObjID,
		// PCMSG_STREAM_INFO pStreamInfo );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "b0d2610b-05ba-4fb6-8f38-10f970a52091")]
		public static extern SafeHCRYPTMSG CryptMsgOpenToEncode(CertEncodingType dwMsgEncodingType, CryptMsgFlags dwFlags, CryptMsgType dwMsgType, [In] IntPtr pvMsgEncodeInfo,
			[Optional] SafeOID pszInnerContentObjID, IntPtr pStreamInfo = default);

		/// <summary>
		/// The <c>CryptMsgUpdate</c> function adds contents to a cryptographic message. The use of this function allows messages to be
		/// constructed piece by piece through repetitive calls of <c>CryptMsgUpdate</c>. The added message content is either encoded or
		/// decoded depending on whether the message was opened with CryptMsgOpenToEncode or CryptMsgOpenToDecode.
		/// </summary>
		/// <param name="hCryptMsg">Cryptographic message handle of the message to be updated.</param>
		/// <param name="pbData">A pointer to the buffer holding the data to be encoded or decoded.</param>
		/// <param name="cbData">Number of bytes of data in the pbData buffer.</param>
		/// <param name="fFinal">
		/// <para>
		/// Indicates that the last block of data for encoding or decoding is being processed. Correct usage of this flag is dependent upon
		/// whether the message being processed has detached data. The inclusion of detached data in a message is indicated by setting
		/// dwFlags to CMSG_DETACHED_FLAG in the call to the function that opened the message.
		/// </para>
		/// <para>
		/// If CMSG_DETACHED_FLAG was not set and the message was opened using either CryptMsgOpenToDecode or CryptMsgOpenToEncode, fFinal
		/// is set to <c>TRUE</c>, and <c>CryptMsgUpdate</c> is only called once.
		/// </para>
		/// <para>
		/// If the CMSG_DETACHED_FLAG flag was set and a message is opened using CryptMsgOpenToEncode, fFinal is set to <c>TRUE</c> only on
		/// the last call to <c>CryptMsgUpdate</c>.
		/// </para>
		/// <para>
		/// If the CMSG_DETACHED_FLAG flag was set and a message is opened using CryptMsgOpenToDecode, fFinal is set to <c>TRUE</c> when the
		/// header is processed by a single call to <c>CryptMsgUpdate</c>. It is set to <c>FALSE</c> while processing the detached data in
		/// subsequent calls to <c>CryptMsgUpdate</c> until the last detached data block is to be processed. On the last call to
		/// <c>CryptMsgUpdate</c>, it is set to <c>TRUE</c>.
		/// </para>
		/// <para>
		/// When detached data is decoded, the header and the content of a message are contained in different BLOBs. Each BLOB requires that
		/// fFinal be set to <c>TRUE</c> when the last call to the function is made for that BLOB.
		/// </para>
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the return value is nonzero ( <c>TRUE</c>).</para>
		/// <para>If the function fails, the return value is zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
		/// <para>
		/// Errors encountered in the application defined callback function specified by pStreamInfo in CryptMsgOpenToDecode and
		/// CryptMsgOpenToEncode might be propagated to <c>CryptMsgUpdate</c> if streaming is used. If this happens, SetLastError is not
		/// called by <c>CryptMsgUpdate</c> after the callback function returns, which preserves any errors encountered under the control of
		/// the application. It is the responsibility of the callback function (or one of the APIs that it calls) to call
		/// <c>SetLastError</c> if an error occurs while the application is processing the streamed data.
		/// </para>
		/// <para>The following table lists the error codes most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>CRYPT_E_INVALID_MSG_TYPE</term>
		/// <term>The message type is not valid.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_MSG_ERROR</term>
		/// <term>An error was encountered doing a cryptographic operation.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_OID_FORMAT</term>
		/// <term>The object identifier is badly formatted.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNEXPECTED_ENCODING</term>
		/// <term>The message is not encoded as expected.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNKNOWN_ALGO</term>
		/// <term>The cryptographic algorithm is unknown.</term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>One or more arguments are not valid.</term>
		/// </item>
		/// <item>
		/// <term>E_OUTOFMEMORY</term>
		/// <term>Ran out of memory.</term>
		/// </item>
		/// </list>
		/// <para>Propagated errors might be encountered from any of the following functions:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>CryptHashData</term>
		/// </item>
		/// <item>
		/// <term>CryptGetHashParam</term>
		/// </item>
		/// <item>
		/// <term>CryptSignHash</term>
		/// </item>
		/// <item>
		/// <term>CryptGetKeyParam</term>
		/// </item>
		/// <item>
		/// <term>CryptEncrypt</term>
		/// </item>
		/// <item>
		/// <term>CryptCreateHash</term>
		/// </item>
		/// </list>
		/// <para>
		/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
		/// about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgupdate BOOL CryptMsgUpdate( HCRYPTMSG hCryptMsg,
		// const BYTE *pbData, DWORD cbData, BOOL fFinal );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "d27d75f0-1646-4926-b375-59e52b00326c")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptMsgUpdate(HCRYPTMSG hCryptMsg, [In] IntPtr pbData, uint cbData, [MarshalAs(UnmanagedType.Bool)] bool fFinal);

		/// <summary>
		/// The <c>CryptMsgVerifyCountersignatureEncoded</c> function verifies a countersignature in terms of the SignerInfo structure (as
		/// defined by PKCS #7).
		/// </summary>
		/// <param name="hCryptProv">
		/// <para>This parameter is not used and should be set to <c>NULL</c>.</para>
		/// <para>
		/// <c>Windows Server 2003 and Windows XP:</c><c>NULL</c> or the handle of the cryptographic provider to use to hash the
		/// encryptedDigest field of pbSignerInfo.This parameter's data type is <c>HCRYPTPROV</c>.
		/// </para>
		/// <para>
		/// Unless there is a strong reason for passing in a specific cryptographic provider in hCryptProv, pass <c>NULL</c> to cause the
		/// default RSA or DSS provider to be used.
		/// </para>
		/// </param>
		/// <param name="dwEncodingType">
		/// <para>
		/// Specifies the encoding type used. Currently, only X509_ASN_ENCODING and PKCS_7_ASN_ENCODING are being used; however, additional
		/// encoding types may be added in the future. For either current encoding type, use:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING.</para>
		/// </param>
		/// <param name="pbSignerInfo">A pointer to the encoded BLOB that contains the signer of the contents of a message to be countersigned.</param>
		/// <param name="cbSignerInfo">Count, in bytes, of the encoded BLOB for the signer of the contents.</param>
		/// <param name="pbSignerInfoCountersignature">A pointer to the encoded BLOB containing the countersigner information.</param>
		/// <param name="cbSignerInfoCountersignature">Count, in bytes, of the encoded BLOB for the countersigner of the message.</param>
		/// <param name="pciCountersigner">
		/// A pointer to a CERT_INFO that includes with the issuer and serial number of the countersigner. For more information, see Remarks.
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the return value is nonzero ( <c>TRUE</c>).</para>
		/// <para>If the function fails, the return value is zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
		/// <para>The following table lists the error codes most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>CRYPT_E_AUTH_ATTR_MISSING</term>
		/// <term>The message does not contain an expected authenticated attribute.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_HASH_VALUE</term>
		/// <term>The hash value is not correct.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNEXPECTED_ENCODING</term>
		/// <term>The message is not encoded as expected.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNKNOWN_ALGO</term>
		/// <term>The cryptographic algorithm is unknown.</term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>One or more arguments are not valid.</term>
		/// </item>
		/// <item>
		/// <term>E_OUTOFMEMORY</term>
		/// <term>Ran out of memory.</term>
		/// </item>
		/// </list>
		/// <para>Propagated errors from the following functions might be returned.</para>
		/// <list type="bullet">
		/// <item>
		/// <term>CryptHashData</term>
		/// </item>
		/// <item>
		/// <term>CryptGetHashParam</term>
		/// </item>
		/// <item>
		/// <term>CryptImportKey</term>
		/// </item>
		/// <item>
		/// <term>CryptVerifySignature</term>
		/// </item>
		/// <item>
		/// <term>CryptCreateHash</term>
		/// </item>
		/// </list>
		/// <para>
		/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
		/// about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		/// <remarks>
		/// <para>
		/// Countersigner verification is done using the PKCS #7 <c>SIGNERINFO</c> structure. The signature must contain the encrypted hash
		/// of the encryptedDigest field of pbSignerInfo.
		/// </para>
		/// <para>
		/// The issuer and serial number of the countersigner must match the countersigner information from pbSignerInfoCountersignature.
		/// The only fields referenced from pciCountersigner are SerialNumber, Issuer, and SubjectPublicKeyInfo. The SubjectPublicKeyInfo is
		/// used to access the public key that is then used to encrypt the hash from the pciCountersigner so compare it with the hash from
		/// the pbSignerInfo.
		/// </para>
		/// <para>Examples</para>
		/// <para>For an example that uses this function, see Example C Program: Encoding and Decoding a CounterSigned Message.</para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgverifycountersignatureencoded BOOL
		// CryptMsgVerifyCountersignatureEncoded( HCRYPTPROV_LEGACY hCryptProv, DWORD dwEncodingType, PBYTE pbSignerInfo, DWORD
		// cbSignerInfo, PBYTE pbSignerInfoCountersignature, DWORD cbSignerInfoCountersignature, PCERT_INFO pciCountersigner );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "b0332360-a737-4b48-b592-0c55d493a02d")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptMsgVerifyCountersignatureEncoded([Optional] HCRYPTPROV hCryptProv, CertEncodingType dwEncodingType, [In] IntPtr pbSignerInfo, uint cbSignerInfo,
			[In] IntPtr pbSignerInfoCountersignature, uint cbSignerInfoCountersignature, in CERT_INFO pciCountersigner);

		/// <summary>
		/// The <c>CryptMsgVerifyCountersignatureEncodedEx</c> function verifies that the pbSignerInfoCounterSignature parameter contains
		/// the encrypted hash of the <c>encryptedDigest</c> field of the pbSignerInfo parameter structure. The signer can be a
		/// CERT_PUBLIC_KEY_INFO structure, a certificate context, or a chain context.
		/// </summary>
		/// <param name="hCryptProv">
		/// <para>This parameter is not used and should be set to <c>NULL</c>.</para>
		/// <para>
		/// <c>Windows Server 2003 and Windows XP:</c><c>NULL</c> or the handle of the cryptographic provider to use to hash the
		/// encryptedDigest field of pbSignerInfo.This parameter's data type is <c>HCRYPTPROV</c>.
		/// </para>
		/// <para>
		/// Unless there is a strong reason for passing in a specific cryptographic provider in hCryptProv, pass <c>NULL</c> to cause the
		/// default RSA or DSS provider to be used.
		/// </para>
		/// </param>
		/// <param name="dwEncodingType">
		/// <para>
		/// The encoding type used. Currently, only X509_ASN_ENCODING and PKCS_7_ASN_ENCODING are being used; however, additional encoding
		/// types may be added in the future. For either current encoding type, use:
		/// </para>
		/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING.</para>
		/// </param>
		/// <param name="pbSignerInfo">A pointer to the encoded BLOB that contains the signer of the contents of a message to be countersigned.</param>
		/// <param name="cbSignerInfo">The count, in bytes, of the encoded BLOB for the signer of the contents.</param>
		/// <param name="pbSignerInfoCountersignature">A pointer to the encoded BLOB containing the countersigner information.</param>
		/// <param name="cbSignerInfoCountersignature">The count, in bytes, of the encoded BLOB for the countersigner of the message.</param>
		/// <param name="dwSignerType">
		/// <para>
		/// The structure that contains the signer information. The following table shows the predefined values and the structures indicated.
		/// </para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>CMSG_VERIFY_SIGNER_PUBKEY</term>
		/// <term>pvSigner is a pointer to a CERT_PUBLIC_KEY_INFO structure.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_VERIFY_SIGNER_CERT</term>
		/// <term>pvSigner is a pointer to a CERT_CONTEXT structure.</term>
		/// </item>
		/// <item>
		/// <term>CMSG_VERIFY_SIGNER_CHAIN</term>
		/// <term>pvSigner is a pointer to a CERT_CHAIN_CONTEXT structure.</term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="pvSigner">
		/// A pointer to a CERT_PUBLIC_KEY_INFO structure, a certificate context, or a chain context depending on the value of dwSignerType.
		/// </param>
		/// <param name="dwFlags">
		/// <para>Flags that modify the function behavior. This can be zero or the following value.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Value</term>
		/// <term>Meaning</term>
		/// </listheader>
		/// <item>
		/// <term>CMSG_VERIFY_COUNTER_SIGN_ENABLE_STRONG_FLAG 0x00000001</term>
		/// <term>
		/// Performs a strong signature check after successful signature verification. Set the pvExtra parameter to point to a
		/// CERT_STRONG_SIGN_PARA structure that contains the parameters needed to check the signature strength.. Windows 8 and Windows
		/// Server 2012: Support for this flag begins.
		/// </term>
		/// </item>
		/// </list>
		/// </param>
		/// <param name="pvExtra">
		/// If you set the dwFlags parameter to <c>CMSG_VERIFY_COUNTER_SIGN_ENABLE_STRONG_FLAG</c>, set this parameter (pvExtra) to point to
		/// a CERT_STRONG_SIGN_PARA structure that contains the parameters used to check the signature strength.
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the return value is nonzero ( <c>TRUE</c>).</para>
		/// <para>If the function fails, the return value is zero ( <c>FALSE</c>). For extended error information, call GetLastError.</para>
		/// <para>The following error codes are most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>CRYPT_E_AUTH_ATTR_MISSING</term>
		/// <term>The message does not contain an expected authenticated attribute.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_HASH_VALUE</term>
		/// <term>The hash value is not correct.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNEXPECTED_ENCODING</term>
		/// <term>The message is not encoded as expected.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNKNOWN_ALGO</term>
		/// <term>The cryptographic algorithm is unknown.</term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>One or more arguments are not valid.</term>
		/// </item>
		/// <item>
		/// <term>E_OUTOFMEMORY</term>
		/// <term>Ran out of memory.</term>
		/// </item>
		/// </list>
		/// <para>Propagated errors from the following functions might be returned.</para>
		/// <list type="bullet">
		/// <item>
		/// <term>CryptHashData</term>
		/// </item>
		/// <item>
		/// <term>CryptGetHashParam</term>
		/// </item>
		/// <item>
		/// <term>CryptImportKey</term>
		/// </item>
		/// <item>
		/// <term>CryptVerifySignature</term>
		/// </item>
		/// <item>
		/// <term>CryptCreateHash</term>
		/// </item>
		/// </list>
		/// <para>
		/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
		/// about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		/// <remarks>
		/// <para>
		/// Countersigner verification is done using the PKCS #7 <c>SIGNERINFO</c> structure. The signature must contain the encrypted hash
		/// of the encryptedDigest field of pbSignerInfo.
		/// </para>
		/// <para>
		/// The issuer and serial number of the countersigner must match the countersigner information from pbSignerInfoCountersignature.
		/// The only fields referenced from pciCountersigner are SerialNumber, Issuer, and SubjectPublicKeyInfo. The SubjectPublicKeyInfo is
		/// used to access the public key that is then used to encrypt the hash from the pciCountersigner so compare it with the hash from
		/// the pbSignerInfo.
		/// </para>
		/// <para>Examples</para>
		/// <para>For an example that uses this function, see Example C Program: Encoding and Decoding a CounterSigned Message.</para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptmsgverifycountersignatureencodedex BOOL
		// CryptMsgVerifyCountersignatureEncodedEx( HCRYPTPROV_LEGACY hCryptProv, DWORD dwEncodingType, PBYTE pbSignerInfo, DWORD
		// cbSignerInfo, PBYTE pbSignerInfoCountersignature, DWORD cbSignerInfoCountersignature, DWORD dwSignerType, void *pvSigner, DWORD
		// dwFlags, void *pvExtra );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "da756cd5-1dec-4d88-9c90-76dd263035eb")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptMsgVerifyCountersignatureEncodedEx([Optional] HCRYPTPROV hCryptProv, CertEncodingType dwEncodingType, [In] IntPtr pbSignerInfo, uint cbSignerInfo,
			[In] IntPtr pbSignerInfoCountersignature, uint cbSignerInfoCountersignature, CryptMsgSignerType dwSignerType, IntPtr pvSigner, CryptMsgVerifyCounterFlags dwFlags, [Optional] IntPtr pvExtra);

		/// <summary>
		/// The <c>CryptSignAndEncryptMessage</c> function creates a hash of the specified content, signs the hash, encrypts the content,
		/// hashes the encrypted contents and the signed hash, and then encodes both the encrypted content and the signed hash. The result
		/// is the same as if the hash were first signed and then encrypted.
		/// </summary>
		/// <param name="pSignPara">A pointer to a CRYPT_SIGN_MESSAGE_PARA structure that contains the signature parameters.</param>
		/// <param name="pEncryptPara">A pointer to a CRYPT_ENCRYPT_MESSAGE_PARA structure containing encryption parameters.</param>
		/// <param name="cRecipientCert">Number of array elements in rgpRecipientCert.</param>
		/// <param name="rgpRecipientCert">
		/// Array of pointers to CERT_CONTEXT structures. Each structure is the certificate of an intended recipients of the message.
		/// </param>
		/// <param name="pbToBeSignedAndEncrypted">A pointer to a buffer containing the content to be signed and encrypted.</param>
		/// <param name="cbToBeSignedAndEncrypted">The size, in bytes, of the pbToBeSignedAndEncrypted buffer.</param>
		/// <param name="pbSignedAndEncryptedBlob">
		/// <para>A pointer to a buffer to receive the encrypted and encoded message.</para>
		/// <para>
		/// This parameter can be <c>NULL</c> to set the size of this information for memory allocation purposes. For more information, see
		/// Retrieving Data of Unknown Length.
		/// </para>
		/// </param>
		/// <param name="pcbSignedAndEncryptedBlob">
		/// <para>
		/// A pointer to <c>DWORD</c> specifying the size, in bytes, of the buffer pointed to by pbSignedAndEncryptedBlob. When the function
		/// returns, this variable contains the size, in bytes, of the signed and encrypted message copied to *pbSignedAndEncryptedBlob.
		/// </para>
		/// <para>
		/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
		/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
		/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
		/// updated to reflect the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the return value is nonzero (TRUE).</para>
		/// <para>If the function fails, the return value is zero (FALSE).</para>
		/// <para>For extended error information, call GetLastError.</para>
		/// <para>The following lists the error code most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>ERROR_MORE_DATA</term>
		/// <term>
		/// If the buffer specified by the pbSignedAndEncryptedBlob parameter is not large enough to hold the returned data, the function
		/// sets the ERROR_MORE_DATA code, and stores the required buffer size, in bytes, into the variable pointed to by pcbSignedAndEncryptedBlob.
		/// </term>
		/// </item>
		/// </list>
		/// <para><c>Note</c> Errors from the called functions CryptSignMessage and CryptEncryptMessage might be propagated to this function.</para>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptsignandencryptmessage BOOL
		// CryptSignAndEncryptMessage( PCRYPT_SIGN_MESSAGE_PARA pSignPara, PCRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, DWORD cRecipientCert,
		// PCCERT_CONTEXT [] rgpRecipientCert, const BYTE *pbToBeSignedAndEncrypted, DWORD cbToBeSignedAndEncrypted, BYTE
		// *pbSignedAndEncryptedBlob, DWORD *pcbSignedAndEncryptedBlob );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "0ab234f2-a681-463f-8ba8-b23b05cf2626")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptSignAndEncryptMessage(in CRYPT_SIGN_MESSAGE_PARA pSignPara, in CRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, uint cRecipientCert,
			[In, MarshalAs(UnmanagedType.LPArray)] PCCERT_CONTEXT[] rgpRecipientCert, [In] IntPtr pbToBeSignedAndEncrypted, uint cbToBeSignedAndEncrypted,
			[Out] IntPtr pbSignedAndEncryptedBlob, ref uint pcbSignedAndEncryptedBlob);

		/// <summary>
		/// The <c>CryptSignMessage</c> function creates a hash of the specified content, signs the hash, and then encodes both the original
		/// message content and the signed hash.
		/// </summary>
		/// <param name="pSignPara">A pointer to CRYPT_SIGN_MESSAGE_PARA structure containing the signature parameters.</param>
		/// <param name="fDetachedSignature">
		/// <c>TRUE</c> if this is to be a detached signature. Otherwise, <c>FALSE</c>. If this parameter is set to <c>TRUE</c>, only the
		/// signed hash is encoded in pbSignedBlob. Otherwise, both rgpbToBeSigned and the signed hash are encoded.
		/// </param>
		/// <param name="cToBeSigned">
		/// Count of the number of array elements in rgpbToBeSigned and rgpbToBeSigned. This parameter must be set to one unless
		/// fDetachedSignature is set to <c>TRUE</c>.
		/// </param>
		/// <param name="rgpbToBeSigned">Array of pointers to buffers that contain the contents to be signed.</param>
		/// <param name="rgcbToBeSigned">Array of sizes, in bytes, of the content buffers pointed to in rgpbToBeSigned.</param>
		/// <param name="pbSignedBlob">
		/// <para>
		/// A pointer to a buffer to receive the encoded signed hash, if fDetachedSignature is <c>TRUE</c>, or to both the encoded content
		/// and signed hash if fDetachedSignature is <c>FALSE</c>.
		/// </para>
		/// <para>
		/// This parameter can be <c>NULL</c> to set the size of this information for memory allocation purposes. For more information, see
		/// Retrieving Data of Unknown Length.
		/// </para>
		/// </param>
		/// <param name="pcbSignedBlob">
		/// <para>
		/// A pointer to a <c>DWORD</c> specifying the size, in bytes, of the pbSignedBlob buffer. When the function returns, this variable
		/// contains the size, in bytes, of the signed and encoded message.
		/// </para>
		/// <para>
		/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
		/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
		/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
		/// updated to reflect the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the return value is nonzero ( <c>TRUE</c>).</para>
		/// <para>If the function fails, the return value is zero ( <c>FALSE</c>).</para>
		/// <para>For extended error information, call GetLastError.</para>
		/// <para>The following lists the error codes most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>ERROR_MORE_DATA</term>
		/// <term>
		/// If the buffer specified by the pbSignedBlob parameter is not large enough to hold the returned data, the function sets the
		/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, into the variable pointed to by pcbSignedBlob.
		/// </term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>
		/// The message encoding type is not valid. Currently only PKCS_7_ASN_ENCODING is supported. The cbSize in *pSignPara is not valid.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_NO_KEY_PROPERTY</term>
		/// <term>The pSigningCert in *pSignPara does not have a CERT_KEY_PROV_INFO_PROP_ID or CERT_KEY_CONTEXT_PROP_ID property.</term>
		/// </item>
		/// </list>
		/// <para>
		/// <c>Note</c> Errors from the called functions CryptCreateHash, CryptHashData, and CryptSignHash might be propagated to this function.
		/// </para>
		/// <para>
		/// If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For information
		/// about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptsignmessage BOOL CryptSignMessage(
		// PCRYPT_SIGN_MESSAGE_PARA pSignPara, BOOL fDetachedSignature, DWORD cToBeSigned, const BYTE * [] rgpbToBeSigned, DWORD []
		// rgcbToBeSigned, BYTE *pbSignedBlob, DWORD *pcbSignedBlob );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "f14f7c7b-14ac-40a7-9a49-d1a899ecc52a")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptSignMessage(in CRYPT_SIGN_MESSAGE_PARA pSignPara, [MarshalAs(UnmanagedType.Bool)] bool fDetachedSignature, uint cToBeSigned,
			[In, MarshalAs(UnmanagedType.LPArray)] IntPtr[] rgpbToBeSigned, [In] uint[] rgcbToBeSigned, [Out] IntPtr pbSignedBlob, ref uint pcbSignedBlob);

		/// <summary>
		/// The <c>CryptSignMessageWithKey</c> function signs a message by using a CSP's private key specified in the parameters. A
		/// placeholder <c>SignerId</c> is created and stored in the message.
		/// </summary>
		/// <param name="pSignPara">A pointer to a CRYPT_KEY_SIGN_MESSAGE_PARA structure that contains the signature parameters.</param>
		/// <param name="pbToBeSigned">A pointer to a buffer array that contains the message to be signed.</param>
		/// <param name="cbToBeSigned">The number of array elements in the pbToBeSigned buffer array.</param>
		/// <param name="pbSignedBlob">
		/// <para>A pointer to a buffer to receive the encoded signed message.</para>
		/// <para>
		/// This parameter can be <c>NULL</c> to set the size of this information for memory allocation purposes. For more information, see
		/// Retrieving Data of Unknown Length.
		/// </para>
		/// </param>
		/// <param name="pcbSignedBlob">
		/// <para>
		/// A pointer to a <c>DWORD</c> value that indicates the size, in bytes, of the pbSignedBlob buffer. When the function returns, this
		/// variable contains the size, in bytes, of the signed and encoded message.
		/// </para>
		/// <para>
		/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
		/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
		/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
		/// updated to reflect the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the return value is nonzero (TRUE).</para>
		/// <para>If the function fails, the return value is zero (FALSE).</para>
		/// <para>For extended error information, call GetLastError.</para>
		/// <para>The following lists the error codes most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>ERROR_MORE_DATA</term>
		/// <term>
		/// If the buffer specified by the pbSignedBlob parameter is not large enough to hold the returned data, the function sets the
		/// ERROR_MORE_DATA code and stores the required buffer size, in bytes, into the variable pointed to by pcbSignedBlob.
		/// </term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>
		/// The message encoding type is not valid. Currently only PKCS_7_ASN_ENCODING is supported. The cbSize in *pSignPara is not valid.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_NO_KEY_PROPERTY</term>
		/// <term>The pSigningCert in *pSignPara does not have a CERT_KEY_PROV_INFO_PROP_ID or CERT_KEY_CONTEXT_PROP_ID property.</term>
		/// </item>
		/// </list>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptsignmessagewithkey BOOL CryptSignMessageWithKey(
		// PCRYPT_KEY_SIGN_MESSAGE_PARA pSignPara, const BYTE *pbToBeSigned, DWORD cbToBeSigned, BYTE *pbSignedBlob, DWORD *pcbSignedBlob );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "d31024bf-7022-440b-8134-a02578510357")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptSignMessageWithKey(in CRYPT_KEY_SIGN_MESSAGE_PARA pSignPara, [In] IntPtr pbToBeSigned, uint cbToBeSigned, [Out] IntPtr pbSignedBlob, ref uint pcbSignedBlob);

		/// <summary>The <c>CryptVerifyDetachedMessageHash</c> function verifies a detached hash.</summary>
		/// <param name="pHashPara">A pointer to a CRYPT_HASH_MESSAGE_PARA structure containing the hash parameters.</param>
		/// <param name="pbDetachedHashBlob">A pointer to the encoded, detached hash.</param>
		/// <param name="cbDetachedHashBlob">The size, in bytes, of the detached hash.</param>
		/// <param name="cToBeHashed">Number of elements in the rgpbToBeHashed and rgcbToBeHashed arrays.</param>
		/// <param name="rgpbToBeHashed">Array of pointers to content buffers to be hashed.</param>
		/// <param name="rgcbToBeHashed">
		/// Array of sizes, in bytes, for the content buffers pointed to by the elements of the rgcbToBeHashed array.
		/// </param>
		/// <param name="pbComputedHash">
		/// <para>A pointer to a buffer to receive the computed hash.</para>
		/// <para>
		/// This parameter can be <c>NULL</c> if the newly created hash is not needed for additional processing, or to set the size of the
		/// hash for memory allocation purposes. For more information, see Retrieving Data of Unknown Length.
		/// </para>
		/// </param>
		/// <param name="pcbComputedHash">
		/// <para>
		/// A pointer to a <c>DWORD</c> specifying the size, in bytes, of the pbComputedHash buffer. When the function returns, this
		/// <c>DWORD</c> contains the size, in bytes, of the created hash. The hash will not be returned if this parameter is <c>NULL</c>.
		/// </para>
		/// <para>
		/// <c>Note</c> When processing the data returned , applications must use the actual size of the data returned. The actual size can
		/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
		/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
		/// updated to reflect the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the return value is nonzero (TRUE).</para>
		/// <para>If the function fails, the return value is zero (FALSE).</para>
		/// <para>For extended error information, call GetLastError.</para>
		/// <para>The following lists the error codes most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>CRYPT_E_UNEXPECTED_MSG_TYPE</term>
		/// <term>Not a hashed cryptographic message.</term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>
		/// The message encoding type is not valid. Currently only PKCS_7_ASN_ENCODING is supported. The cbSize in *pHashPara is not valid.
		/// </term>
		/// </item>
		/// <item>
		/// <term>ERROR_MORE_DATA</term>
		/// <term>
		/// If the buffer specified by the pbComputedHash parameter is not large enough to hold the returned data, the function sets the
		/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, into the variable pointed to by pcbComputedHash.
		/// </term>
		/// </item>
		/// </list>
		/// <para>
		/// <c>Note</c> Errors from the called functions CryptCreateHash, CryptHashData, and CryptGetHashParam might be propagated to this
		/// function. If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For
		/// information about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptverifydetachedmessagehash BOOL
		// CryptVerifyDetachedMessageHash( PCRYPT_HASH_MESSAGE_PARA pHashPara, BYTE *pbDetachedHashBlob, DWORD cbDetachedHashBlob, DWORD
		// cToBeHashed, const BYTE * [] rgpbToBeHashed, DWORD [] rgcbToBeHashed, BYTE *pbComputedHash, DWORD *pcbComputedHash );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "b529b9e2-9798-4548-a44f-c330524a3e6b")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptVerifyDetachedMessageHash(in CRYPT_HASH_MESSAGE_PARA pHashPara, [In] IntPtr pbDetachedHashBlob, uint cbDetachedHashBlob,
			uint cToBeHashed, [In, MarshalAs(UnmanagedType.LPArray)] IntPtr[] rgpbToBeHashed, [In] uint[] rgcbToBeHashed, [Out] IntPtr pbComputedHash, ref uint pcbComputedHash);

		/// <summary>
		/// The <c>CryptVerifyDetachedMessageSignature</c> function verifies a signed message containing a detached signature or signatures.
		/// </summary>
		/// <param name="pVerifyPara">A pointer to a CRYPT_VERIFY_MESSAGE_PARA structure containing the verification parameters.</param>
		/// <param name="dwSignerIndex">
		/// Index of the signature to be verified. A message might have several signers and this function can be called repeatedly, changing
		/// dwSignerIndex to verify other signatures. If the function returns FALSE, and GetLastError returns CRYPT_E_NO_SIGNER, the
		/// previous call received the last signer of the message.
		/// </param>
		/// <param name="pbDetachedSignBlob">A pointer to a BLOB containing the encoded message signatures.</param>
		/// <param name="cbDetachedSignBlob">The size, in bytes, of the detached signature.</param>
		/// <param name="cToBeSigned">Number of array elements in rgpbToBeSigned and rgcbToBeSigned.</param>
		/// <param name="rgpbToBeSigned">Array of pointers to buffers containing the contents to be hashed.</param>
		/// <param name="rgcbToBeSigned">Array of sizes, in bytes, for the content buffers pointed to in rgpbToBeSigned.</param>
		/// <param name="ppSignerCert">
		/// A pointer to a pointer to a CERT_CONTEXT structure of a signer certificate. When you have finished using the certificate
		/// context, free it by calling the CertFreeCertificateContext function. A pointer to a <c>CERT_CONTEXT</c> structure will not be
		/// returned if this parameter is <c>NULL</c>.
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the return value is nonzero (TRUE).</para>
		/// <para>If the function fails, the return value is zero ( <c>FALSE</c>).</para>
		/// <para>For extended error information, call GetLastError.</para>
		/// <para>The following lists the error codes most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>
		/// Invalid message and certificate encoding types. Currently only PKCS_7_ASN_ENCODING and X509_ASN_ENCODING_TYPE are supported.
		/// Invalid cbSize in *pVerifyPara.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNEXPECTED_MSG_TYPE</term>
		/// <term>Not a signed cryptographic message.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_NO_SIGNER</term>
		/// <term>The message does not have any signers or a signer for the specified dwSignerIndex.</term>
		/// </item>
		/// <item>
		/// <term>NTE_BAD_ALGID</term>
		/// <term>The message was hashed and signed by using an unknown or unsupported algorithm.</term>
		/// </item>
		/// <item>
		/// <term>NTE_BAD_SIGNATURE</term>
		/// <term>The message's signature was not verified.</term>
		/// </item>
		/// </list>
		/// <para>
		/// <c>Note</c> Errors from the called functions CryptCreateHash, CryptHashData, CryptVerifySignature, and CryptImportKey might be
		/// propagated to this function.If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1)
		/// encoding/decoding error. For information about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptverifydetachedmessagesignature BOOL
		// CryptVerifyDetachedMessageSignature( PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, DWORD dwSignerIndex, const BYTE *pbDetachedSignBlob,
		// DWORD cbDetachedSignBlob, DWORD cToBeSigned, const BYTE * [] rgpbToBeSigned, DWORD [] rgcbToBeSigned, PCCERT_CONTEXT
		// *ppSignerCert );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "d437f6bf-eb56-4d29-bb91-eb8487e50219")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptVerifyDetachedMessageSignature(in CRYPT_VERIFY_MESSAGE_PARA pVerifyPara, uint dwSignerIndex, [In] IntPtr pbDetachedSignBlob, uint cbDetachedSignBlob,
			uint cToBeSigned, [In, MarshalAs(UnmanagedType.LPArray)] IntPtr[] rgpbToBeSigned, [In] uint[] rgcbToBeSigned, out SafePCCERT_CONTEXT ppSignerCert);

		/// <summary>The <c>CryptVerifyMessageHash</c> function verifies the hash of specified content.</summary>
		/// <param name="pHashPara">A pointer to a CRYPT_HASH_MESSAGE_PARA structure containing hash parameters.</param>
		/// <param name="pbHashedBlob">A pointer to a buffer containing original content and its hash.</param>
		/// <param name="cbHashedBlob">The size, in bytes, of the original hash buffer.</param>
		/// <param name="pbToBeHashed">
		/// <para>A pointer to a buffer to receive the original content that was hashed.</para>
		/// <para>
		/// This parameter can be <c>NULL</c> if the original content is not needed for additional processing, or to set the size of the
		/// original content for memory allocation purposes. For more information, see Retrieving Data of Unknown Length.
		/// </para>
		/// </param>
		/// <param name="pcbToBeHashed">
		/// <para>
		/// A pointer to a <c>DWORD</c> specifying the size, in bytes, of the pbToBeHashed buffer. When the function returns, this variable
		/// contains the size, in bytes, of the original content copied to pbToBeHashed. The original content will not be returned if this
		/// parameter is <c>NULL</c>.
		/// </para>
		/// <para>
		/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
		/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
		/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
		/// updated to reflect the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <param name="pbComputedHash">
		/// A pointer to a buffer to receive the computed hash. This parameter can be <c>NULL</c> if the created hash is not needed for
		/// additional processing, or to set the size of the original content for memory allocation purposes. For more information, see
		/// Retrieving Data of Unknown Length.
		/// </param>
		/// <param name="pcbComputedHash">
		/// <para>
		/// A pointer to a <c>DWORD</c> specifying the size, in bytes, of the pbComputedHash buffer. When the function returns, this
		/// variable contains the size, in bytes, of the created hash. The hash is not returned if this parameter is <c>NULL</c>.
		/// </para>
		/// <para>
		/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
		/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
		/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
		/// updated to reflect the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the return value is nonzero (TRUE).</para>
		/// <para>If the function fails, the return value is zero (FALSE).</para>
		/// <para>For extended error information, call GetLastError.</para>
		/// <para>The following lists the error codes most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>CRYPT_E_UNEXPECTED_MSG_TYPE</term>
		/// <term>Not a hashed cryptographic message.</term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>
		/// The message encoding type is not valid. Currently only PKCS_7_ASN_ENCODING is supported. The cbSize in *pHashPara is not valid.
		/// </term>
		/// </item>
		/// <item>
		/// <term>ERROR_MORE_DATA</term>
		/// <term>
		/// If the buffer specified by the pbToBeHashed parameter is not large enough to hold the returned data, the function sets the
		/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, into the variable pointed to by pcbToBeHashed.
		/// </term>
		/// </item>
		/// </list>
		/// <para>
		/// <c>Note</c> Errors from the called functions CryptCreateHash, CryptHashData, and CryptGetHashParam might be propagated to this
		/// function. If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1) encoding/decoding error. For
		/// information about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptverifymessagehash BOOL CryptVerifyMessageHash(
		// PCRYPT_HASH_MESSAGE_PARA pHashPara, BYTE *pbHashedBlob, DWORD cbHashedBlob, BYTE *pbToBeHashed, DWORD *pcbToBeHashed, BYTE
		// *pbComputedHash, DWORD *pcbComputedHash );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "3b5185b9-e24b-4302-a60c-74ccbd19077c")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptVerifyMessageHash(in CRYPT_HASH_MESSAGE_PARA pHashPara, [In] IntPtr pbHashedBlob, uint cbHashedBlob, [Out] IntPtr pbToBeHashed,
			ref uint pcbToBeHashed, [Out] IntPtr pbComputedHash, ref uint pcbComputedHash);

		/// <summary>
		/// <para>The <c>CryptVerifyMessageSignature</c> function verifies a signed message's signature.</para>
		/// <para>
		/// This function should not be used to verify the signature of a detached message. You should use the
		/// CryptVerifyDetachedMessageSignature function to verify the signature of a detached message.
		/// </para>
		/// </summary>
		/// <param name="pVerifyPara">A pointer to a CRYPT_VERIFY_MESSAGE_PARA structure that contains verification parameters.</param>
		/// <param name="dwSignerIndex">
		/// The index of the desired signature. There can be more than one signature. <c>CryptVerifyMessageSignature</c> can be called
		/// repeatedly, incrementing dwSignerIndex each time. Set this parameter to zero for the first signer, or if there is only one
		/// signer. If the function returns <c>FALSE</c>, and GetLastError returns CRYPT_E_NO_SIGNER, the previous call processed the last
		/// signer of the message.
		/// </param>
		/// <param name="pbSignedBlob">A pointer to a buffer that contains the signed message.</param>
		/// <param name="cbSignedBlob">The size, in bytes, of the signed message buffer.</param>
		/// <param name="pbDecoded">
		/// <para>A pointer to a buffer to receive the decoded message.</para>
		/// <para>
		/// This parameter can be <c>NULL</c> if the decoded message is not needed for additional processing or to set the size of the
		/// message for memory allocation purposes. For more information, see Retrieving Data of Unknown Length.
		/// </para>
		/// </param>
		/// <param name="pcbDecoded">
		/// <para>
		/// A pointer to a <c>DWORD</c> value that specifies the size, in bytes, of the pbDecoded buffer. When the function returns, this
		/// <c>DWORD</c> contains the size, in bytes, of the decoded message. The decoded message will not be returned if this parameter is <c>NULL</c>.
		/// </para>
		/// <para>
		/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
		/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
		/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
		/// updated to reflect the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <param name="ppSignerCert">
		/// The address of a CERT_CONTEXT structure pointer that receives the certificate of the signer. When you have finished using this
		/// structure, free it by passing this pointer to the CertFreeCertificateContext function. This parameter can be <c>NULL</c> if the
		/// signer's certificate is not needed.
		/// </param>
		/// <returns>
		/// <para>
		/// If the function succeeds, the function returns nonzero. This does not necessarily mean that the signature was verified. In the
		/// case of a detached message, the variable pointed to by pcbDecoded will contain zero. In this case, this function will return
		/// nonzero, but the signature is not verified. To verify the signature of a detached message, use the
		/// CryptVerifyDetachedMessageSignature function.
		/// </para>
		/// <para>If the function fails, it returns zero. For extended error information, call GetLastError.</para>
		/// <para>The following table shows the error codes most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>ERROR_MORE_DATA</term>
		/// <term>
		/// If the buffer specified by the pbDecoded parameter is not large enough to hold the returned data, the function sets the
		/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, in the variable pointed to by pcbDecoded.
		/// </term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>
		/// Invalid message and certificate encoding types. Currently only PKCS_7_ASN_ENCODING and X509_ASN_ENCODING_TYPE are supported.
		/// Invalid cbSize in *pVerifyPara.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNEXPECTED_MSG_TYPE</term>
		/// <term>Not a signed cryptographic message.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_NO_SIGNER</term>
		/// <term>The message does not have any signers or a signer for the specified dwSignerIndex.</term>
		/// </item>
		/// <item>
		/// <term>NTE_BAD_ALGID</term>
		/// <term>The message was hashed and signed by using an unknown or unsupported algorithm.</term>
		/// </item>
		/// <item>
		/// <term>NTE_BAD_SIGNATURE</term>
		/// <term>The message's signature was not verified.</term>
		/// </item>
		/// </list>
		/// <para>
		/// <c>Note</c> Errors from the called functions CryptCreateHash, CryptHashData, CryptVerifySignature, and CryptImportKey can be
		/// propagated to this function. If the function fails, GetLastError may return an Abstract Syntax Notation One (ASN.1)
		/// encoding/decoding error. For information about these errors, see ASN.1 Encoding/Decoding Return Values.
		/// </para>
		/// </returns>
		/// <remarks>
		/// <para>
		/// For a verified signer and message, ppSignerCert is updated with the CERT_CONTEXT of the signer. It must be freed by calling
		/// CertFreeCertificateContext. Otherwise, ppSignerCert is set to <c>NULL</c>.
		/// </para>
		/// <para>For a message that contains only certificates and CRLs, pcbDecoded is set to <c>NULL</c>.</para>
		/// <para>Examples</para>
		/// <para>For an example that uses this function, see Example C Program: Signing a Message and Verifying a Message Signature.</para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptverifymessagesignature BOOL
		// CryptVerifyMessageSignature( PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, DWORD dwSignerIndex, const BYTE *pbSignedBlob, DWORD
		// cbSignedBlob, BYTE *pbDecoded, DWORD *pcbDecoded, PCCERT_CONTEXT *ppSignerCert );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "03411e7a-b097-4059-a198-3d412ae40e38")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptVerifyMessageSignature(in CRYPT_VERIFY_MESSAGE_PARA pVerifyPara, uint dwSignerIndex, [In] IntPtr pbSignedBlob, uint cbSignedBlob,
			[Out] IntPtr pbDecoded, ref uint pcbDecoded, out SafePCCERT_CONTEXT ppSignerCert);

		/// <summary>
		/// The <c>CryptVerifyMessageSignatureWithKey</c> function verifies a signed message's signature by using specified public key information.
		/// </summary>
		/// <param name="pVerifyPara">A pointer to a CRYPT_KEY_VERIFY_MESSAGE_PARA structure that contains verification parameters.</param>
		/// <param name="pPublicKeyInfo">
		/// A pointer to a CERT_PUBLIC_KEY_INFO structure that contains the public key that is used to verify the signed message. If
		/// <c>NULL</c>, the signature is not verified.
		/// </param>
		/// <param name="pbSignedBlob">A pointer to a buffer that contains the signed message.</param>
		/// <param name="cbSignedBlob">The size, in bytes, of the signed message buffer.</param>
		/// <param name="pbDecoded">
		/// <para>A pointer to a buffer to receive the decoded message.</para>
		/// <para>
		/// This parameter can be <c>NULL</c> if the decoded message is not needed for additional processing or to set the size of the
		/// message for memory allocation purposes. For more information, see Retrieving Data of Unknown Length.
		/// </para>
		/// </param>
		/// <param name="pcbDecoded">
		/// <para>
		/// A pointer to a <c>DWORD</c> value that specifies the size, in bytes, of the pbDecoded buffer. When the function returns, this
		/// <c>DWORD</c> contains the size, in bytes, of the decoded message. The decoded message will not be returned if this parameter is <c>NULL</c>.
		/// </para>
		/// <para>
		/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
		/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
		/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
		/// updated to reflect the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the function returns nonzero.</para>
		/// <para>If the function fails, it returns zero. For extended error information, call GetLastError.</para>
		/// <para>The following table shows the error codes most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>ERROR_MORE_DATA</term>
		/// <term>
		/// If the buffer specified by the pbDecoded parameter is not large enough to hold the returned data, the function sets the
		/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, in the variable pointed to by pcbDecoded.
		/// </term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>
		/// Invalid message and certificate encoding types. Currently only PKCS_7_ASN_ENCODING and X509_ASN_ENCODING_TYPE are supported.
		/// Invalid cbSize in *pVerifyPara.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNEXPECTED_MSG_TYPE</term>
		/// <term>Not a signed cryptographic message.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_NO_SIGNER</term>
		/// <term>The message does not have any signers or a signer for the specified dwSignerIndex.</term>
		/// </item>
		/// <item>
		/// <term>NTE_BAD_ALGID</term>
		/// <term>The message was hashed and signed by using an unknown or unsupported algorithm.</term>
		/// </item>
		/// <item>
		/// <term>NTE_BAD_SIGNATURE</term>
		/// <term>The message's signature was not verified.</term>
		/// </item>
		/// </list>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptverifymessagesignaturewithkey BOOL
		// CryptVerifyMessageSignatureWithKey( PCRYPT_KEY_VERIFY_MESSAGE_PARA pVerifyPara, PCERT_PUBLIC_KEY_INFO pPublicKeyInfo, const BYTE
		// *pbSignedBlob, DWORD cbSignedBlob, BYTE *pbDecoded, DWORD *pcbDecoded );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "6fe0f9ee-1838-4eb7-8254-05b878eb8f56")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptVerifyMessageSignatureWithKey(in CRYPT_KEY_VERIFY_MESSAGE_PARA pVerifyPara, in CERT_PUBLIC_KEY_INFO pPublicKeyInfo, [In] IntPtr pbSignedBlob,
			uint cbSignedBlob, [Out] IntPtr pbDecoded, ref uint pcbDecoded);

		/// <summary>
		/// The <c>CryptVerifyMessageSignatureWithKey</c> function verifies a signed message's signature by using specified public key information.
		/// </summary>
		/// <param name="pVerifyPara">A pointer to a CRYPT_KEY_VERIFY_MESSAGE_PARA structure that contains verification parameters.</param>
		/// <param name="pPublicKeyInfo">
		/// A pointer to a CERT_PUBLIC_KEY_INFO structure that contains the public key that is used to verify the signed message. If
		/// <c>NULL</c>, the signature is not verified.
		/// </param>
		/// <param name="pbSignedBlob">A pointer to a buffer that contains the signed message.</param>
		/// <param name="cbSignedBlob">The size, in bytes, of the signed message buffer.</param>
		/// <param name="pbDecoded">
		/// <para>A pointer to a buffer to receive the decoded message.</para>
		/// <para>
		/// This parameter can be <c>NULL</c> if the decoded message is not needed for additional processing or to set the size of the
		/// message for memory allocation purposes. For more information, see Retrieving Data of Unknown Length.
		/// </para>
		/// </param>
		/// <param name="pcbDecoded">
		/// <para>
		/// A pointer to a <c>DWORD</c> value that specifies the size, in bytes, of the pbDecoded buffer. When the function returns, this
		/// <c>DWORD</c> contains the size, in bytes, of the decoded message. The decoded message will not be returned if this parameter is <c>NULL</c>.
		/// </para>
		/// <para>
		/// <c>Note</c> When processing the data returned, applications must use the actual size of the data returned. The actual size can
		/// be slightly smaller than the size of the buffer specified on input. (On input, buffer sizes are usually specified large enough
		/// to ensure that the largest possible output data will fit in the buffer.) On output, the variable pointed to by this parameter is
		/// updated to reflect the actual size of the data copied to the buffer.
		/// </para>
		/// </param>
		/// <returns>
		/// <para>If the function succeeds, the function returns nonzero.</para>
		/// <para>If the function fails, it returns zero. For extended error information, call GetLastError.</para>
		/// <para>The following table shows the error codes most commonly returned by the GetLastError function.</para>
		/// <list type="table">
		/// <listheader>
		/// <term>Return code</term>
		/// <term>Description</term>
		/// </listheader>
		/// <item>
		/// <term>ERROR_MORE_DATA</term>
		/// <term>
		/// If the buffer specified by the pbDecoded parameter is not large enough to hold the returned data, the function sets the
		/// ERROR_MORE_DATA code, and stores the required buffer size, in bytes, in the variable pointed to by pcbDecoded.
		/// </term>
		/// </item>
		/// <item>
		/// <term>E_INVALIDARG</term>
		/// <term>
		/// Invalid message and certificate encoding types. Currently only PKCS_7_ASN_ENCODING and X509_ASN_ENCODING_TYPE are supported.
		/// Invalid cbSize in *pVerifyPara.
		/// </term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_UNEXPECTED_MSG_TYPE</term>
		/// <term>Not a signed cryptographic message.</term>
		/// </item>
		/// <item>
		/// <term>CRYPT_E_NO_SIGNER</term>
		/// <term>The message does not have any signers or a signer for the specified dwSignerIndex.</term>
		/// </item>
		/// <item>
		/// <term>NTE_BAD_ALGID</term>
		/// <term>The message was hashed and signed by using an unknown or unsupported algorithm.</term>
		/// </item>
		/// <item>
		/// <term>NTE_BAD_SIGNATURE</term>
		/// <term>The message's signature was not verified.</term>
		/// </item>
		/// </list>
		/// </returns>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptverifymessagesignaturewithkey BOOL
		// CryptVerifyMessageSignatureWithKey( PCRYPT_KEY_VERIFY_MESSAGE_PARA pVerifyPara, PCERT_PUBLIC_KEY_INFO pPublicKeyInfo, const BYTE
		// *pbSignedBlob, DWORD cbSignedBlob, BYTE *pbDecoded, DWORD *pcbDecoded );
		[DllImport(Lib.Crypt32, SetLastError = true, ExactSpelling = true)]
		[PInvokeData("wincrypt.h", MSDNShortId = "6fe0f9ee-1838-4eb7-8254-05b878eb8f56")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CryptVerifyMessageSignatureWithKey(in CRYPT_KEY_VERIFY_MESSAGE_PARA pVerifyPara, [In, Optional] IntPtr pPublicKeyInfo, [In] IntPtr pbSignedBlob,
			uint cbSignedBlob, [Out] IntPtr pbDecoded, ref uint pcbDecoded);

		/// <summary>
		/// The <c>CMSG_CMS_SIGNER_INFO</c> structure contains the content of the defined SignerInfo in signed or signed and enveloped
		/// messages. In decoding a received message, CryptMsgGetParam is called for each signer to get a <c>CMSG_CMS_SIGNER_INFO</c> structure.
		/// </summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_cms_signer_info typedef struct _CMSG_CMS_SIGNER_INFO
		// { DWORD dwVersion; CERT_ID SignerId; CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm; CRYPT_ALGORITHM_IDENTIFIER
		// HashEncryptionAlgorithm; CRYPT_DATA_BLOB EncryptedHash; CRYPT_ATTRIBUTES AuthAttrs; CRYPT_ATTRIBUTES UnauthAttrs; }
		// CMSG_CMS_SIGNER_INFO, *PCMSG_CMS_SIGNER_INFO;
		[PInvokeData("wincrypt.h", MSDNShortId = "177323ef-4e26-4681-a474-1a99fb6900af")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CMSG_CMS_SIGNER_INFO
		{
			/// <summary>The version of this structure.</summary>
			public uint dwVersion;

			/// <summary>A CERT_ID structure that identifies the signer's certificate.</summary>
			public CERT_ID SignerId;

			/// <summary>A CRYPT_ALGORITHM_IDENTIFIER structure that specifies the algorithm used in generating the hash of a message.</summary>
			public CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;

			/// <summary>A CRYPT_ALGORITHM_IDENTIFIER structure that specifies the algorithm used to encrypt the hash.</summary>
			public CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm;

			/// <summary>A CRYPT_DATA_BLOB structure that contains the encrypted hash of the message, the signature.</summary>
			public CRYPTOAPI_BLOB EncryptedHash;

			/// <summary>A CRYPT_ATTRIBUTES structure that contains authenticated attributes of the signer.</summary>
			public CRYPT_ATTRIBUTES AuthAttrs;

			/// <summary>A CRYPT_ATTRIBUTES structure that contains unauthenticated attributes of the signer.</summary>
			public CRYPT_ATTRIBUTES UnauthAttrs;
		}

		/// <summary>
		/// The <c>CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA</c> structure is used to add an unauthenticated attribute to a signer of a signed
		/// message. This structure is passed to CryptMsgControl if the dwCtrlType parameter is set to <c>CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR</c>.
		/// </summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_ctrl_add_signer_unauth_attr_para typedef struct
		// _CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA { DWORD cbSize; DWORD dwSignerIndex; CRYPT_DATA_BLOB blob; }
		// CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA, *PCMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA;
		[PInvokeData("wincrypt.h", MSDNShortId = "5e347a50-942e-4278-a9ae-ad4c30c55c6b")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA
		{
			/// <summary>Size of this structure in bytes.</summary>
			public uint cbSize;

			/// <summary>
			/// Index of the signer in the <c>rgSigners</c> array of pointers of CMSG_SIGNER_ENCODE_INFO structures in a signed message's
			/// CMSG_SIGNED_ENCODE_INFO structure. The unauthenticated attribute is to be added to this signer's information.
			/// </summary>
			public uint dwSignerIndex;

			/// <summary/>
			public CRYPTOAPI_BLOB blob;
		}

		/// <summary>
		/// <para>
		/// The <c>CMSG_CTRL_DECRYPT_PARA</c> structure contains information used to decrypt an enveloped message for a key transport
		/// recipient. This structure is passed to CryptMsgControl if the dwCtrlType parameter is CMSG_CTRL_DECRYPT.
		/// </para>
		/// <para>
		/// For information about how CryptoAPI supports Secure/Multipurpose Internet Mail Extensions (S/MIME) email interoperability, see
		/// the Remarks section of CryptMsgOpenToEncode.
		/// </para>
		/// </summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_ctrl_decrypt_para typedef struct
		// _CMSG_CTRL_DECRYPT_PARA { DWORD cbSize; union { HCRYPTPROV hCryptProv; NCRYPT_KEY_HANDLE hNCryptKey; } DUMMYUNIONNAME; DWORD
		// dwKeySpec; DWORD dwRecipientIndex; } CMSG_CTRL_DECRYPT_PARA, *PCMSG_CTRL_DECRYPT_PARA;
		[PInvokeData("wincrypt.h", MSDNShortId = "eb9b1daa-b04f-419a-88e3-7c772f9e62eb")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CMSG_CTRL_DECRYPT_PARA
		{
			/// <summary>The size, in bytes, of this structure.</summary>
			public uint cbSize;

			/// <summary/>
			public CMSG_CTRL_DECRYPT_PARA_HANDLES Handle;

			/// <summary/>
			[StructLayout(LayoutKind.Explicit)]
			public struct CMSG_CTRL_DECRYPT_PARA_HANDLES
			{
				/// <summary>
				/// Cryptographic service provider (CSP) handle. The CNG function NCryptIsKeyHandle is called to determine the union choice.
				/// </summary>
				[FieldOffset(0)]
				public HCRYPTPROV hCryptProv;

				/// <summary>
				/// A handle to the CNG Cryptographic service provider (CSP). The CNG function, NCryptIsKeyHandle, is called to determine
				/// the union choice. New encrypt algorithms are only supported in CNG functions. The CNG function, NCryptTranslateHandle,
				/// will be called to convert the CryptoAPI hCryptProv choice where necessary. We recommend that applications pass, to the
				/// hNCryptKey member, the CNG CSP handle that is returned from the NCryptOpenKey function.
				/// </summary>
				[FieldOffset(0)]
				public NCrypt.NCRYPT_KEY_HANDLE hNCryptKey;
			}

			/// <summary>
			/// <para>The private key to be used. This member is not used when the hNCryptKey member is used.</para>
			/// <para>The following <c>dwKeySpec</c> values are defined for the default provider.</para>
			/// <list type="table">
			/// <listheader>
			/// <term>Value</term>
			/// <term>Meaning</term>
			/// </listheader>
			/// <item>
			/// <term>AT_KEYEXCHANGE</term>
			/// <term>Keys used to encrypt and decrypt session keys.</term>
			/// </item>
			/// <item>
			/// <term>AT_SIGNATURE</term>
			/// <term>Keys used to create and verify digital signatures.</term>
			/// </item>
			/// </list>
			/// <para>If <c>dwKeySpec</c> is zero, the default AT_KEYEXCHANGE is used.</para>
			/// </summary>
			public CertKeySpec dwKeySpec;

			/// <summary>Index of the recipient in the message associated with the <c>hCryptProv</c> private key.</summary>
			public uint dwRecipientIndex;
		}

		/// <summary>
		/// The <c>CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA</c> structure is used to delete an unauthenticated attribute of a signer of a
		/// signed message. This structure is passed to CryptMsgControl if the dwCrlType parameter is <c>CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR</c>.
		/// </summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_ctrl_del_signer_unauth_attr_para typedef struct
		// _CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA { DWORD cbSize; DWORD dwSignerIndex; DWORD dwUnauthAttrIndex; }
		// CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA, *PCMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA;
		[PInvokeData("wincrypt.h", MSDNShortId = "729fbbe0-40c6-41e7-851f-6f93f47e8f4d")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA
		{
			/// <summary>Size of this structure in bytes.</summary>
			public uint cbSize;

			/// <summary>
			/// Index of the signer in the <c>rgSigners</c> array of pointers to CMSG_SIGNER_ENCODE_INFO structures in a signed message's
			/// CMSG_SIGNED_ENCODE_INFO structure. The unauthenticated attribute for this signer is deleted.
			/// </summary>
			public uint dwSignerIndex;

			/// <summary>
			/// Index of the element in the <c>rgUnauthAttr</c> array of the CMSG_SIGNER_ENCODE_INFO structure holding the unauthenticated
			/// attribute to be removed.
			/// </summary>
			public uint dwUnauthAttrIndex;
		}

		/// <summary>The <c>CMSG_CTRL_KEY_AGREE_DECRYPT_PARA</c> structure contains information about a key agreement recipient.</summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_ctrl_key_agree_decrypt_para typedef struct
		// _CMSG_CTRL_KEY_AGREE_DECRYPT_PARA { DWORD cbSize; union { HCRYPTPROV hCryptProv; NCRYPT_KEY_HANDLE hNCryptKey; } DUMMYUNIONNAME;
		// DWORD dwKeySpec; PCMSG_KEY_AGREE_RECIPIENT_INFO pKeyAgree; DWORD dwRecipientIndex; DWORD dwRecipientEncryptedKeyIndex;
		// CRYPT_BIT_BLOB OriginatorPublicKey; } CMSG_CTRL_KEY_AGREE_DECRYPT_PARA, *PCMSG_CTRL_KEY_AGREE_DECRYPT_PARA;
		[PInvokeData("wincrypt.h", MSDNShortId = "14e58281-4315-4ece-8ea8-92765cffd212")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CMSG_CTRL_KEY_AGREE_DECRYPT_PARA
		{
			/// <summary>The size, in bytes, of this data structure.</summary>
			public uint cbSize;

			/// <summary/>
			public CMSG_CTRL_KEY_AGREE_DECRYPT_PARA_HANDLES Handle;

			/// <summary/>
			[StructLayout(LayoutKind.Explicit)]
			public struct CMSG_CTRL_KEY_AGREE_DECRYPT_PARA_HANDLES
			{
				/// <summary>
				/// A handle to the cryptographic service provider (CSP) used to do the recipient key encryption and export. If <c>NULL</c>,
				/// the provider specified in CMSG_ENVELOPED_ENCODE_INFO is used. The CNG function NCryptIsKeyHandle is called to determine
				/// the union choice.
				/// </summary>
				[FieldOffset(0)]
				public HCRYPTPROV hCryptProv;

				/// <summary>
				/// A handle to the CNG CSP used to do the recipient key encryption and export. The CNG function NCryptIsKeyHandle is called
				/// to determine the union choice. New encrypt algorithms are only supported in CNG functions. The CNG function
				/// NCryptTranslateHandle will be called to convert the CryptoAPI CSP hCryptProv choice where necessary. We recommend that
				/// applications pass, to the hNCryptKey member, the CNG CSP handle that is returned from the NCryptOpenKey function.
				/// </summary>
				[FieldOffset(0)]
				public NCrypt.NCRYPT_KEY_HANDLE hNCryptKey;
			}

			/// <summary>
			/// Specifies the encrypted key. The encrypted key is the result of encrypting the content-encryption key. This member is not
			/// used when the hNCryptKey member is used.
			/// </summary>
			public CertKeySpec dwKeySpec;

			/// <summary>A pointer to a CMSG_KEY_AGREE_RECIPIENT_INFO structure.</summary>
			public IntPtr pKeyAgree;

			/// <summary>Indicates a specific recipient in an array of recipients.</summary>
			public uint dwRecipientIndex;

			/// <summary>Indicates a specific encrypted key in an array of encrypted keys.</summary>
			public uint dwRecipientEncryptedKeyIndex;

			/// <summary>A CRYPT_BIT_BLOB structure that contains the sender's public key information.</summary>
			public CRYPTOAPI_BLOB OriginatorPublicKey;
		}

		/// <summary>The <c>CMSG_CTRL_KEY_TRANS_DECRYPT_PARA</c> structure contains information about a key transport message recipient.</summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_ctrl_key_trans_decrypt_para typedef struct
		// _CMSG_CTRL_KEY_TRANS_DECRYPT_PARA { DWORD cbSize; union { HCRYPTPROV hCryptProv; NCRYPT_KEY_HANDLE hNCryptKey; } DUMMYUNIONNAME;
		// DWORD dwKeySpec; PCMSG_KEY_TRANS_RECIPIENT_INFO pKeyTrans; DWORD dwRecipientIndex; } CMSG_CTRL_KEY_TRANS_DECRYPT_PARA, *PCMSG_CTRL_KEY_TRANS_DECRYPT_PARA;
		[PInvokeData("wincrypt.h", MSDNShortId = "5f3387c9-4ff0-42a0-8fc7-67d3bb8b6bef")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CMSG_CTRL_KEY_TRANS_DECRYPT_PARA
		{
			/// <summary>The size, in bytes, of this data structure.</summary>
			public uint cbSize;

			/// <summary/>
			public CMSG_CTRL_KEY_TRANS_DECRYPT_PARA_HANDLES Handle;

			/// <summary/>
			[StructLayout(LayoutKind.Explicit)]
			public struct CMSG_CTRL_KEY_TRANS_DECRYPT_PARA_HANDLES
			{
				/// <summary>
				/// A handle to the cryptographic service provider (CSP) used to do the recipient key encryption and export. If <c>NULL</c>,
				/// the provider specified in CMSG_ENVELOPED_ENCODE_INFO is used. The CNG function NCryptIsKeyHandle is called to determine
				/// the union choice.
				/// </summary>
				[FieldOffset(0)]
				public HCRYPTPROV hCryptProv;

				/// <summary>
				/// A handle to the CNG CSP used to do the recipient key encryption and export. The CNG function NCryptIsKeyHandle is called
				/// to determine the union choice. New encrypt algorithms are only supported in CNG functions. The CNG function
				/// NCryptTranslateHandle will be called to convert the CryptoAPI CSP hCryptProv choice where necessary. We recommend that
				/// applications pass, to the hNCryptKey member, the CNG CSP handle that is returned from the NCryptOpenKey function.
				/// </summary>
				[FieldOffset(0)]
				public NCrypt.NCRYPT_KEY_HANDLE hNCryptKey;
			}

			/// <summary>
			/// Specifies the encrypted key. The encrypted key is the result of encrypting the content-encryption key for a specific
			/// recipient by using that recipient's public key. This member is not used when the hNCryptKey member is used.
			/// </summary>
			public uint dwKeySpec;

			/// <summary>A pointer to a CMSG_KEY_TRANS_RECIPIENT_INFO structure.</summary>
			public IntPtr pKeyTrans;

			/// <summary>Indicates a specific recipient in any array of recipients.</summary>
			public uint dwRecipientIndex;
		}

		/// <summary>The <c>CMSG_CTRL_MAIL_LIST_DECRYPT_PARA</c> structure contains information on a mail list message recipient.</summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_ctrl_mail_list_decrypt_para typedef struct
		// _CMSG_CTRL_MAIL_LIST_DECRYPT_PARA { DWORD cbSize; HCRYPTPROV hCryptProv; PCMSG_MAIL_LIST_RECIPIENT_INFO pMailList; DWORD
		// dwRecipientIndex; DWORD dwKeyChoice; union { HCRYPTKEY hKeyEncryptionKey; void *pvKeyEncryptionKey; } DUMMYUNIONNAME; }
		// CMSG_CTRL_MAIL_LIST_DECRYPT_PARA, *PCMSG_CTRL_MAIL_LIST_DECRYPT_PARA;
		[PInvokeData("wincrypt.h", MSDNShortId = "30735e01-db6b-40fc-b4c8-cdc24e73defa")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CMSG_CTRL_MAIL_LIST_DECRYPT_PARA
		{
			/// <summary>The size, in bytes, of this data structure.</summary>
			public uint cbSize;

			/// <summary>
			/// The provider used to do the recipient key encryption and export. If <c>hCryptProv</c> is <c>NULL</c>, the provider specified
			/// in CMSG_ENVELOPED_ENCODE_INFO is used.
			/// </summary>
			public HCRYPTPROV hCryptProv;

			/// <summary>A pointer to a CMSG_MAIL_LIST_RECIPIENT_INFO structure.</summary>
			public IntPtr pMailList;

			/// <summary>Indicates a specific recipient in any array of recipients.</summary>
			public uint dwRecipientIndex;

			/// <summary>
			/// Indicates the member of the following union that will be used. Currently only CMSG_MAIL_LIST_HANDLE_KEY_CHOICE is defined.
			/// </summary>
			public uint dwKeyChoice;

			/// <summary>Handle of the key encryption key. Used with <c>dwKeyChoice</c> set to CMSG_MAIL_LIST_HANDLE_KEY_CHOICE.</summary>
			public HCRYPTKEY hKeyEncryptionKey;
		}

		/// <summary>
		/// The <c>CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA</c> structure contains information used to verify a message signature. It contains the
		/// signer index and signer public key. The signer public key can be the signer's CERT_PUBLIC_KEY_INFO structure, certificate
		/// context, or chain context.
		/// </summary>
		/// <remarks>
		/// If <c>dwSignerType</c> is CMSG_VERIFY_SIGNER_NULL, the signature is expected to contain only the unencrypted hash octets.
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_ctrl_verify_signature_ex_para typedef struct
		// _CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA { DWORD cbSize; HCRYPTPROV_LEGACY hCryptProv; DWORD dwSignerIndex; DWORD dwSignerType; void
		// *pvSigner; } CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA, *PCMSG_CTRL_VERIFY_SIGNATURE_EX_PARA;
		[PInvokeData("wincrypt.h", MSDNShortId = "56b73de8-c170-46f6-b488-096475b59c15")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA
		{
			/// <summary>The size, in bytes, of this structure.</summary>
			public uint cbSize;

			/// <summary>
			/// <para>This member is not used and should be set to <c>NULL</c>.</para>
			/// <para>
			/// <c>Windows Server 2003 and Windows XP:</c> A handle to the cryptographic provider used to verify the signature. If
			/// <c>NULL</c>, the cryptographic provider specified in CryptMsgOpenToDecode is used. If the hCryptProv in
			/// <c>CryptMsgOpenToDecode</c> is also <c>NULL</c>, the default provider according to the signer's public key object identifier
			/// (OID) is used.This member's data type is <c>HCRYPTPROV</c>.
			/// </para>
			/// </summary>
			public HCRYPTPROV hCryptProv;

			/// <summary>The index of the signer in the message.</summary>
			public uint dwSignerIndex;

			/// <summary>
			/// <para>
			/// The structure that contains the signer information. The following table shows the predefined values and the structures indicated.
			/// </para>
			/// <list type="table">
			/// <listheader>
			/// <term>Value</term>
			/// <term>Meaning</term>
			/// </listheader>
			/// <item>
			/// <term>CMSG_VERIFY_SIGNER_PUBKEY</term>
			/// <term>CERT_PUBLIC_KEY_INFO</term>
			/// </item>
			/// <item>
			/// <term>CMSG_VERIFY_SIGNER_CERT</term>
			/// <term>CERT_CONTEXT</term>
			/// </item>
			/// <item>
			/// <term>CMSG_VERIFY_SIGNER_CHAIN</term>
			/// <term>CERT_CHAIN_CONTEXT</term>
			/// </item>
			/// <item>
			/// <term>CMSG_VERIFY_SIGNER_NULL</term>
			/// <term>NULL</term>
			/// </item>
			/// </list>
			/// </summary>
			public CryptMsgSignerType dwSignerType;

			/// <summary>
			/// A pointer to a CERT_PUBLIC_KEY_INFO structure, a certificate context, a chain context, or <c>NULL</c> depending on the value
			/// of <c>dwSignerType</c>.
			/// </summary>
			public IntPtr pvSigner;
		}

		/// <summary>The <c>CMSG_KEY_AGREE_RECIPIENT_INFO</c> structure contains information used for key agreement algorithms.</summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_key_agree_recipient_info typedef struct
		// _CMSG_KEY_AGREE_RECIPIENT_INFO { DWORD dwVersion; DWORD dwOriginatorChoice; union { CERT_ID OriginatorCertId;
		// CERT_PUBLIC_KEY_INFO OriginatorPublicKeyInfo; } DUMMYUNIONNAME; CRYPT_DATA_BLOB UserKeyingMaterial; CRYPT_ALGORITHM_IDENTIFIER
		// KeyEncryptionAlgorithm; DWORD cRecipientEncryptedKeys; PCMSG_RECIPIENT_ENCRYPTED_KEY_INFO *rgpRecipientEncryptedKeys; }
		// CMSG_KEY_AGREE_RECIPIENT_INFO, *PCMSG_KEY_AGREE_RECIPIENT_INFO;
		[PInvokeData("wincrypt.h", MSDNShortId = "d29d04d6-065e-4bb7-843b-f563643eeb4c")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CMSG_KEY_AGREE_RECIPIENT_INFO
		{
			/// <summary>A <c>DWORD</c> that indicates the version of the structure. Always set to three.</summary>
			public uint dwVersion;

			/// <summary>A <c>DWORD</c> that indicates the key identifier to use.</summary>
			public CryptMsgKeyOriginator dwOriginatorChoice;

			/// <summary/>
			public CMSG_KEY_AGREE_RECIPIENT_INFO_UNION Originator;

			/// <summary/>
			[StructLayout(LayoutKind.Explicit)]
			public struct CMSG_KEY_AGREE_RECIPIENT_INFO_UNION
			{
				/// <summary>A CERT_ID that identifies the public key of the message originator.</summary>
				[FieldOffset(0)]
				public CERT_ID OriginatorCertId;

				/// <summary>A CERT_PUBLIC_KEY_INFO structure that contains the public key of the message originator.</summary>
				[FieldOffset(0)]
				public CERT_PUBLIC_KEY_INFO OriginatorPublicKeyInfo;
			}

			/// <summary>
			/// A CRYPT_DATA_BLOB that indicates that a different key is generated each time the same two parties generate a pair of keys.
			/// The sender provides the bits of this BLOB with some key agreement algorithms. This member can be <c>NULL</c>.
			/// </summary>
			public CRYPTOAPI_BLOB UserKeyingMaterial;

			/// <summary>
			/// A CRYPT_ALGORITHM_IDENTIFIER that identifies the key-encryption algorithm and any associated parameters used to encrypt the
			/// content encryption key.
			/// </summary>
			public CRYPT_ALGORITHM_IDENTIFIER KeyEncryptionAlgorithm;

			/// <summary>The number of elements in the <c>rgpRecipientEncryptedKeys</c> array.</summary>
			public uint cRecipientEncryptedKeys;

			/// <summary>
			/// The address of an array of CMSG_RECIPIENT_ENCRYPTED_KEY_INFO structures that contains information about the key recipients.
			/// The <c>cRecipientEncryptedKeys</c> member contains the number of elements in this array.
			/// </summary>
			public IntPtr rgpRecipientEncryptedKeys;
		}

		/// <summary>The <c>CMSG_KEY_TRANS_RECIPIENT_INFO</c> structure contains information used in key transport algorithms.</summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_key_trans_recipient_info typedef struct
		// _CMSG_KEY_TRANS_RECIPIENT_INFO { DWORD dwVersion; CERT_ID RecipientId; CRYPT_ALGORITHM_IDENTIFIER KeyEncryptionAlgorithm;
		// CRYPT_DATA_BLOB EncryptedKey; } CMSG_KEY_TRANS_RECIPIENT_INFO, *PCMSG_KEY_TRANS_RECIPIENT_INFO;
		[PInvokeData("wincrypt.h", MSDNShortId = "956b0646-50a5-46d1-aa9a-91194c35d2b2")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CMSG_KEY_TRANS_RECIPIENT_INFO
		{
			/// <summary>
			/// Indicates the version of the structure. If <c>RecipientId</c> uses the ISSUER_SERIAL_NUMBER to identify the recipient,
			/// <c>dwVersion</c> is set to zero. If <c>RecipientId</c> uses KEYID, <c>dwVersion</c> is set to two.
			/// </summary>
			public uint dwVersion;

			/// <summary>
			/// A CERT_ID that identifies the recipient. Currently, only ISSUER_SERIAL_NUMBER or KEYID choices in the <c>CERT_ID</c> are valid.
			/// </summary>
			public CERT_ID RecipientId;

			/// <summary>
			/// A CRYPT_ALGORITHM_IDENTIFIER that identifies the key-encryption algorithm and any associated parameters used to encrypt the
			/// content encryption key.
			/// </summary>
			public CRYPT_ALGORITHM_IDENTIFIER KeyEncryptionAlgorithm;

			/// <summary>A CRYPT_DATA_BLOB that contains the bytes of the encrypted session key.</summary>
			public CRYPTOAPI_BLOB EncryptedKey;
		}

		/// <summary>
		/// The <c>CMSG_MAIL_LIST_RECIPIENT_INFO</c> structure contains information used for previously distributed symmetric key-encryption
		/// keys (KEK).
		/// </summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_mail_list_recipient_info typedef struct
		// _CMSG_MAIL_LIST_RECIPIENT_INFO { DWORD dwVersion; CRYPT_DATA_BLOB KeyId; CRYPT_ALGORITHM_IDENTIFIER KeyEncryptionAlgorithm;
		// CRYPT_DATA_BLOB EncryptedKey; FILETIME Date; PCRYPT_ATTRIBUTE_TYPE_VALUE pOtherAttr; } CMSG_MAIL_LIST_RECIPIENT_INFO, *PCMSG_MAIL_LIST_RECIPIENT_INFO;
		[PInvokeData("wincrypt.h", MSDNShortId = "e0946278-75e9-4990-af81-d9e61da9724b")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CMSG_MAIL_LIST_RECIPIENT_INFO
		{
			/// <summary>Indicates the version of the structure. This member is always four.</summary>
			public uint dwVersion;

			/// <summary>
			/// A CRYPT_DATA_BLOB structure that identifies a symmetric key-encryption key previously distributed to the sender and one or
			/// more recipients.
			/// </summary>
			public CRYPTOAPI_BLOB KeyId;

			/// <summary>
			/// CRYPT_ALGORITHM_IDENTIFIER that identifies the key-encryption algorithm and any associated parameters used to encrypt the
			/// content encryption key.
			/// </summary>
			public CRYPT_ALGORITHM_IDENTIFIER KeyEncryptionAlgorithm;

			/// <summary>A CRYPT_DATA_BLOB structure that contains the encrypted content encryption key.</summary>
			public CRYPTOAPI_BLOB EncryptedKey;

			/// <summary>Optional. When present, this member specifies a single key-encryption key from a previously distributed set.</summary>
			public FILETIME Date;

			/// <summary>Optional pointer to a CRYPT_ATTRIBUTE_TYPE_VALUE structure containing additional information.</summary>
			public IntPtr pOtherAttr;
		}

		/// <summary>
		/// The <c>CMSG_RECIPIENT_ENCRYPTED_KEY_INFO</c> structure contains information used for an individual key agreement recipient.
		/// </summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_recipient_encrypted_key_info typedef struct
		// _CMSG_RECIPIENT_ENCRYPTED_KEY_INFO { CERT_ID RecipientId; CRYPT_DATA_BLOB EncryptedKey; FILETIME Date;
		// PCRYPT_ATTRIBUTE_TYPE_VALUE pOtherAttr; } CMSG_RECIPIENT_ENCRYPTED_KEY_INFO, *PCMSG_RECIPIENT_ENCRYPTED_KEY_INFO;
		[PInvokeData("wincrypt.h", MSDNShortId = "1921f9b6-86d9-47a0-a36e-e20d481382a3")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CMSG_RECIPIENT_ENCRYPTED_KEY_INFO
		{
			/// <summary>
			/// CERT_ID structure identifying the recipient. Currently, only the ISSUER_SERIAL_NUMBER or KEYID choices in the <c>CERT_ID</c>
			/// structure are valid.
			/// </summary>
			public CERT_ID RecipientId;

			/// <summary>A CRYPT_DATA_BLOB structure that contains the encrypted content encryption key.</summary>
			public CRYPTOAPI_BLOB EncryptedKey;

			/// <summary>
			/// Optional. When present, this member specifies which of the recipient's previously distributed UKMs was used by the sender.
			/// Only applicable to KEYID choice in the <c>RecipientId</c> CERT_ID structure.
			/// </summary>
			public FILETIME Date;

			/// <summary>
			/// Optional pointer to a CRYPT_ATTRIBUTE_TYPE_VALUE structure containing additional information. Only applicable to KEYID
			/// choice in the <c>RecipientId</c> CERT_ID structure.
			/// </summary>
			public IntPtr pOtherAttr;
		}

		/// <summary>
		/// The <c>CMSG_SIGNER_ENCODE_INFO</c> structure contains signer information. It is passed to CryptMsgCountersign,
		/// CryptMsgCountersignEncoded, and optionally to CryptMsgOpenToEncode as a member of the CMSG_SIGNED_ENCODE_INFO structure, if the
		/// dwMsgType parameter is CMSG_SIGNED.
		/// </summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_signer_encode_info typedef struct
		// _CMSG_SIGNER_ENCODE_INFO { DWORD cbSize; PCERT_INFO pCertInfo; union { HCRYPTPROV hCryptProv; NCRYPT_KEY_HANDLE hNCryptKey;
		// BCRYPT_KEY_HANDLE hBCryptKey; } DUMMYUNIONNAME; DWORD dwKeySpec; CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm; void *pvHashAuxInfo;
		// DWORD cAuthAttr; PCRYPT_ATTRIBUTE rgAuthAttr; DWORD cUnauthAttr; PCRYPT_ATTRIBUTE rgUnauthAttr; CERT_ID SignerId;
		// CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm; void *pvHashEncryptionAuxInfo; } CMSG_SIGNER_ENCODE_INFO, *PCMSG_SIGNER_ENCODE_INFO;
		[PInvokeData("wincrypt.h", MSDNShortId = "f599226d-ddd7-455f-b650-74b91674d8f9")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CMSG_SIGNER_ENCODE_INFO
		{
			/// <summary>The size, in bytes, of this structure.</summary>
			public uint cbSize;

			/// <summary>
			/// <para>A pointer to a CERT_INFO structure that contains the</para>
			/// <para><c>Issuer</c>, <c>SerialNumber</c>, and <c>SubjectPublicKeyInfo</c> members.</para>
			/// <para>
			/// The <c>pbData</c> members of the <c>Issuer</c> and <c>SerialNumber</c> structures combined uniquely identify a certificate.
			/// The <c>Algorithm</c> member of the <c>SubjectPublicKeyInfo</c> structure specifies the hash encryption algorithm used.
			/// </para>
			/// </summary>
			public IntPtr pCertInfo;

			/// <summary/>
			public CMSG_SIGNER_ENCODE_INFO_HANDLES Handle;

			/// <summary/>
			[StructLayout(LayoutKind.Explicit)]
			public struct CMSG_SIGNER_ENCODE_INFO_HANDLES
			{
				/// <summary>
				/// A handle to the cryptographic service provider (CSP). If <c>HashEncryptionAlgorithm</c> is set to
				/// szOID_PKIX_NO_SIGNATURE, this handle can be the handle of a CSP acquired by using the dwFlags parameter set to
				/// <c>CRYPT_VERIFYCONTEXT</c>. The CNG function NCryptIsKeyHandle is called to determine the union choice.
				/// </summary>
				[FieldOffset(0)]
				public HCRYPTPROV hCryptProv;

				/// <summary>
				/// A handle to the CNG CSP. The CNG function NCryptIsKeyHandle is called to determine the union choice. New encrypt
				/// algorithms are only supported in CNG functions. The CNG function NCryptTranslateHandle will be called to convert the
				/// CryptoAPI hCryptProv choice where necessary. We recommend that applications pass, to the hNCryptKey member, the CNG CSP
				/// handle that is returned from the NCryptOpenKey function.
				/// </summary>
				[FieldOffset(0)]
				public NCrypt.NCRYPT_KEY_HANDLE hNCryptKey;

				/// <summary/>
				[FieldOffset(0)]
				public BCrypt.BCRYPT_KEY_HANDLE hBCryptKey;
			}

			/// <summary>
			/// <para>Specifies the private key to be used. This member is not used when the hNCryptKey member is used.</para>
			/// <para>If <c>dwKeySpec</c> is zero, then the default AT_KEYEXCHANGE value is used.</para>
			/// <para>The following <c>dwKeySpec</c> values are defined for the default provider.</para>
			/// <list type="table">
			/// <listheader>
			/// <term>Value</term>
			/// <term>Meaning</term>
			/// </listheader>
			/// <item>
			/// <term>AT_KEYEXCHANGE</term>
			/// <term>Keys used to encrypt/decrypt session keys.</term>
			/// </item>
			/// <item>
			/// <term>AT_SIGNATURE</term>
			/// <term>Keys used to create and verify digital signatures.</term>
			/// </item>
			/// </list>
			/// </summary>
			public CertKeySpec dwKeySpec;

			/// <summary>A CRYPT_ALGORITHM_IDENTIFIER structure that specifies the hash algorithm.</summary>
			public CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;

			/// <summary>Not used. This member must be set to <c>NULL</c>.</summary>
			public IntPtr pvHashAuxInfo;

			/// <summary>
			/// The number of elements in the <c>rgAuthAttr</c> array. If no authenticated attributes are present in <c>rgAuthAttr</c>, then
			/// <c>cAuthAttr</c> is zero.
			/// </summary>
			public uint cAuthAttr;

			/// <summary>
			/// <para>An array of pointers to CRYPT_ATTRIBUTE structures, each of which contains authenticated attribute information.</para>
			/// <para>
			/// The PKCS #9 standard dictates that if there are any attributes, there must be at least two: the content type object
			/// identifier (OID) and the hash of the message. These attributes are automatically added by the system.
			/// </para>
			/// </summary>
			public IntPtr rgAuthAttr;

			/// <summary>
			/// The number of elements in the <c>rgUnauthAttr</c> array. If there are no unauthenticated attributes, <c>cUnauthAttr</c> is zero.
			/// </summary>
			public uint cUnauthAttr;

			/// <summary>
			/// An array of pointers to CRYPT_ATTRIBUTE structures, each of which contains unauthenticated attribute information.
			/// Unauthenticated attributes can contain countersignatures, among other uses.
			/// </summary>
			public IntPtr rgUnauthAttr;

			/// <summary>
			/// A CERT_ID structure that contains a unique identifier of the signer's certificate. This member can optionally be used with
			/// PKCS #7 with Cryptographic Message Syntax (CMS). If this member is not <c>NULL</c> and its <c>dwIdChoice</c> member is not
			/// zero, it is used to identify the certificate instead of the <c>Issuer</c> and <c>SerialNumber</c> members of the CERT_INFO
			/// structure pointed to by <c>pCertInfo</c>. CMS supports the KEY_IDENTIFIER and ISSUER_SERIAL_NUMBER CERT_ID structures. PKCS
			/// version 1.5 supports only the ISSUER_SERIAL_NUMBER CERT_ID choice. This member is used with CMS for PKCS #7 processing and
			/// can be used only if CMSG_SIGNER_ENCODE_INFO_HAS_CMS_FIELDS is defined.
			/// </summary>
			public CERT_ID SignerId;

			/// <summary>
			/// <para>
			/// A CRYPT_ALGORITHM_IDENTIFIER structure optionally used with PKCS #7 with CMS. If this member is not <c>NULL</c>, the
			/// algorithm identified is used instead of the SubjectPublicKeyInfo.Algorithm algorithm. If this member is set to
			/// szOID_PKIX_NO_SIGNATURE, the signature value contains only the hash octets.
			/// </para>
			/// <para>
			/// For RSA, the hash encryption algorithm is normally the same as the public key algorithm. For DSA, the hash encryption
			/// algorithm is normally a DSS signature algorithm.
			/// </para>
			/// <para>
			/// This member is used with CMS for PKCS #7 processing and can be used only if CMSG_SIGNER_ENCODE_INFO_HAS_CMS_FIELDS is defined.
			/// </para>
			/// </summary>
			public CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm;

			/// <summary>
			/// This member is not used. This member must be set to <c>NULL</c> if it is present in the data structure. This member is
			/// present only if CMSG_SIGNER_ENCODE_INFO_HAS_CMS_FIELDS is defined.
			/// </summary>
			public IntPtr pvHashEncryptionAuxInfo;
		}

		/// <summary>
		/// <para>
		/// The <c>CMSG_STREAM_INFO</c> structure is used to enable stream processing of data rather than single block processing. Stream
		/// processing is most often used when processing large messages. Stream-processed messages can originate from any serialized source
		/// such as a file on a hard disk, a server, or a CD ROM.
		/// </para>
		/// <para>This structure is passed to the CryptMsgOpenToEncode and CryptMsgOpenToDecode functions.</para>
		/// </summary>
		/// <remarks>
		/// <para>
		/// Messages can be so large that processing them all at once by storing the whole message in memory can be difficult, if not
		/// impossible. It is possible to process large messages without encountering memory limitations by streaming the data that is to be
		/// processed into manageable sized blocks. The low-level message functions can be used with streaming to encode or decode a
		/// message. Any level of nesting of messages is supported when streaming to encode and streaming to decode.
		/// </para>
		/// <para>
		/// The input message to be processed as a stream feeds into CryptMsgUpdate one block at a time, with the application determining
		/// the size of the block. As the streamed message is processed for encoding or decoding, the resulting output data is passed back
		/// to the application through an application-specified callback function that is specified by the <c>pfnStreamOutput</c> member.
		/// </para>
		/// <para>
		/// No assumptions can be made about the block size of the output data because the size can vary for several reasons, such as the
		/// jitter in output block size caused by the block size for the encryption algorithm when processing an enveloped message, or when
		/// blocks that contain the message header and the SignerInfo as defined by PKCS # 7 are processed.
		/// </para>
		/// <para>
		/// The size of the output block is passed to the callback function in its cbData parameter. The use of output data is determined in
		/// the calling application. Typically, output from stream processing will not be persisted in memory as a whole due to memory
		/// limitations; rather, it will be serialized to a disk or server file.
		/// </para>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cmsg_stream_info typedef struct _CMSG_STREAM_INFO { DWORD
		// cbContent; PFN_CMSG_STREAM_OUTPUT pfnStreamOutput; void *pvArg; } CMSG_STREAM_INFO, *PCMSG_STREAM_INFO;
		[PInvokeData("wincrypt.h", MSDNShortId = "a4e7f6e8-351f-4981-b223-50b65f503394")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CMSG_STREAM_INFO
		{
			/// <summary>
			/// Specifies the size, in bytes, of the content. Normal Distinguished Encoding Rules (DER) encoding is used unless
			/// <c>CMSG_INDEFINITE_LENGTH</c>(0xFFFFFFFF) is passed, indicating that the application is not specifying the content length.
			/// This forces the use of indefinite-length Basic Encoding Rules (BER) encoding.
			/// </summary>
			public uint cbContent;

			/// <summary>
			/// <para>The address of a callback function used to read from and write data to a disk when processing large messages.</para>
			/// <para>The callback function must have the following signature and parameters:</para>
			/// </summary>
			public PFN_CMSG_STREAM_OUTPUT pfnStreamOutput;

			/// <summary>
			/// A pointer to the argument to pass to the callback function. Typically, this is used for state data that includes the handle
			/// to a more deeply nested message (when decoding) or a less deeply nested message (when encoding).
			/// </summary>
			public IntPtr pvArg;
		}

		/// <summary>The <c>CRYPT_DECRYPT_MESSAGE_PARA</c> structure contains information for decrypting messages.</summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-crypt_decrypt_message_para typedef struct
		// _CRYPT_DECRYPT_MESSAGE_PARA { DWORD cbSize; DWORD dwMsgAndCertEncodingType; DWORD cCertStore; HCERTSTORE *rghCertStore; DWORD
		// dwFlags; } CRYPT_DECRYPT_MESSAGE_PARA, *PCRYPT_DECRYPT_MESSAGE_PARA;
		[PInvokeData("wincrypt.h", MSDNShortId = "67e136cd-12e3-4a31-9d8b-b53e1129e940")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CRYPT_DECRYPT_MESSAGE_PARA
		{
			/// <summary>Size of this structure in bytes.</summary>
			public uint cbSize;

			/// <summary>
			/// <para>
			/// Type of encoding used. It is always acceptable to specify both the certificate and message encoding types by combining them
			/// with a bitwise- <c>OR</c> operation as shown in the following example:
			/// </para>
			/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
			/// <para>Currently defined encoding types are:</para>
			/// <list type="bullet">
			/// <item>
			/// <term>X509_ASN_ENCODING</term>
			/// </item>
			/// <item>
			/// <term>PKCS_7_ASN_ENCODING</term>
			/// </item>
			/// </list>
			/// </summary>
			public CertEncodingType dwMsgAndCertEncodingType;

			/// <summary>Number of elements in the <c>rghCertStore</c> array.</summary>
			public uint cCertStore;

			/// <summary>
			/// <para>Array of certificate store handles.</para>
			/// <para>
			/// These certificate store handles are used to obtain the certificate context to use for decrypting a message. For more
			/// information, see the decryption functions CryptDecryptMessage, and CryptDecryptAndVerifyMessageSignature. An encrypted
			/// message can have one or more recipients. The recipients are identified by a unique certificate identifier, often the hash of
			/// the certificate issuer and serial number. The certificate stores are searched to find a certificate context corresponding to
			/// the unique identifier.
			/// </para>
			/// <para>
			/// Recipients can also be identified by their KeyId. Both Key Agreement (Diffie-Hellman) and Key Transport (RSA) recipients are supported.
			/// </para>
			/// <para>
			/// Only certificate contexts in the store with one of the following properties, CERT_KEY_PROV_INFO_PROP_ID, or
			/// CERT_KEY_CONTEXT_PROP_ID can be used. These properties specify the location of a needed private exchange key.
			/// </para>
			/// </summary>
			public IntPtr rghCertStore;

			/// <summary>
			/// The CRYPT_MESSAGE_SILENT_KEYSET_FLAG can be set to suppress any UI by the CSP. For more information about the CRYPT_SILENT
			/// flag, see CryptAcquireContext.
			/// </summary>
			public CryptMsgActionFlags dwFlags;
		}

		/// <summary>The <c>CRYPT_ENCRYPT_MESSAGE_PARA</c> structure contains information used to encrypt messages.</summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-crypt_encrypt_message_para typedef struct
		// _CRYPT_ENCRYPT_MESSAGE_PARA { DWORD cbSize; DWORD dwMsgEncodingType; HCRYPTPROV_LEGACY hCryptProv; CRYPT_ALGORITHM_IDENTIFIER
		// ContentEncryptionAlgorithm; void *pvEncryptionAuxInfo; DWORD dwFlags; DWORD dwInnerContentType; } CRYPT_ENCRYPT_MESSAGE_PARA, *PCRYPT_ENCRYPT_MESSAGE_PARA;
		[PInvokeData("wincrypt.h", MSDNShortId = "c683c515-3061-48e3-a64a-2798bd1245b0")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CRYPT_ENCRYPT_MESSAGE_PARA
		{
			/// <summary>The size, in bytes, of this structure.</summary>
			public uint cbSize;

			/// <summary>
			/// <para>
			/// The type of encoding used. It is always acceptable to specify both the certificate and message encoding types by combining
			/// them with a bitwise- <c>OR</c> operation as shown in the following example:
			/// </para>
			/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
			/// <para>Currently defined encoding types are:</para>
			/// <list type="bullet">
			/// <item>
			/// <term>X509_ASN_ENCODING</term>
			/// </item>
			/// <item>
			/// <term>PKCS_7_ASN_ENCODING</term>
			/// </item>
			/// </list>
			/// </summary>
			public CertEncodingType dwMsgEncodingType;

			/// <summary>
			/// <para>This member is not used and should be set to <c>NULL</c>.</para>
			/// <para>
			/// <c>Windows Server 2003 and Windows XP:</c> The handle to the cryptographic service provider (CSP) to be used for encryption.
			/// The CSP identified by <c>hCryptProv</c> is used to do content encryption, recipient key encryption, and recipient key
			/// export. Its private key is not used.
			/// </para>
			/// <para>
			/// Unless there is a strong reason for passing in a specific cryptographic provider in <c>hCryptProv</c>, pass zero to use the
			/// default RSA or DSS provider.
			/// </para>
			/// <para>This member's data type is <c>HCRYPTPROV</c>.</para>
			/// </summary>
			public HCRYPTPROV hCryptProv;

			/// <summary>
			/// <para>
			/// A CRYPT_ALGORITHM_IDENTIFIER structure that contains the object identifier (OID) of the encryption algorithm to use. The CSP
			/// specified by the <c>hCryptProv</c> must support this encryption algorithm.
			/// </para>
			/// <para>
			/// The <c>szOID_OIWSEC_desCBC</c> (CALG_DES) and <c>szOID_RSA_DES_EDE3_CBC</c> (CALG_3DES) encryption algorithms require the
			/// <c>Parameters</c> member of this structure to contain an encoded eight-byte initialization vector (IV). If the <c>cbData</c>
			/// member of the <c>Parameters</c> member is zero, an Abstract Syntax Notation One (ASN.1)-encoded OCTET STRING that contains
			/// the IV is generated using CryptGenRandom. For more information about the KP_IV parameter, see CryptSetKeyParam.
			/// </para>
			/// <para>
			/// The <c>szOID_NIST_AES128_CBC</c> (BCRYPT_AES_ALGORITHM, 128 bit), <c>szOID_NIST_AES192_CBC</c> (BCRYPT_AES_ALGORITHM, 192
			/// bit), and <c>szOID_NIST_AES256_CBC</c> (BCRYPT_AES_ALGORITHM, 256 bit) encryption algorithms require the <c>Parameters</c>
			/// member of this structure to contain an encoded sixteen-byte initialization vector (IV). If the <c>cbData</c> member of the
			/// Parameters member is zero, an Abstract Syntax Notation One (ASN.1)-encoded OCTET STRING that contains the IV is generated.
			/// </para>
			/// <para>
			/// The <c>szOID_RSA_RC2CBC</c> (CALG_RC2) algorithm requires the <c>pbData</c> member of the <c>Parameters</c> member of this
			/// structure to be a CRYPT_RC2_CBC_PARAMETERS structure. If the <c>cbData</c> member of the <c>Parameters</c> member is zero,
			/// an ASN.1-encoded <c>CRYPT_RC2_CBC_PARAMETERS</c> structure that contains the IV is generated as the <c>pbData</c> member.
			/// This generated <c>pbData</c> uses the default <c>dwVersion</c> that corresponds to the 40-bit key length. To override the
			/// default 40-bit key length, <c>pvEncryptionAuxInfo</c> can be set to point to a CMSG_RC2_AUX_INFO structure that contains a
			/// key bit length.
			/// </para>
			/// <para>
			/// <c>Note</c> When a message is decrypted, if it has an initialization vector parameter, the cryptographic message functions
			/// call CryptSetKeyParam with the initialization vector before decrypting.
			/// </para>
			/// </summary>
			public CRYPT_ALGORITHM_IDENTIFIER ContentEncryptionAlgorithm;

			/// <summary>
			/// <para>
			/// A pointer to a CMSG_RC2_AUX_INFO structure for RC2 encryption or a CMSG_SP3_COMPATIBLE_AUX_INFO structure for SP3-compatible
			/// encryption. For other than RC2 or SP3-compatible encryption, this member must be set to <c>NULL</c>.
			/// </para>
			/// <para>
			/// If the <c>ContentEncryptionAlgorithm</c> member contains <c>szOID_RSA_RC4</c>, this member points to a CMSG_RC4_AUX_INFO
			/// structure that specifies the number of salt bytes to be included.
			/// </para>
			/// </summary>
			public IntPtr pvEncryptionAuxInfo;

			/// <summary>
			/// <para>
			/// Normally set to zero. However, if the encoded output is to be a CMSG_ENVELOPED inner content of an outer cryptographic
			/// message, such as a CMSG_SIGNED message, the CRYPT_MESSAGE_BARE_CONTENT_OUT_FLAG must be set. If it is not set, content will
			/// be encoded as an inner content type of CMSG_DATA.
			/// </para>
			/// <para>
			/// CRYPT_MESSAGE_ENCAPSULATED_CONTENT_OUT_FLAG can be set to encapsulate non-data inner content within an OCTET STRING before encrypting.
			/// </para>
			/// <para>
			/// CRYPT_MESSAGE_KEYID_RECIPIENT_FLAG can be set to identify recipients by their Key Identifier and not their Issuer and Serial Number.
			/// </para>
			/// </summary>
			public CryptMsgActionFlags dwFlags;

			/// <summary>
			/// Normally set to zero. The <c>dwInnerContentType</c> member must be set to set the cryptographic message types if the input
			/// to be encrypted is the encoded output of another cryptographic message such as CMSG_SIGNED.
			/// </summary>
			public CryptMsgType dwInnerContentType;
		}

		/// <summary>The <c>CRYPT_HASH_MESSAGE_PARA</c> structure contains data for hashing messages.</summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-crypt_hash_message_para typedef struct
		// _CRYPT_HASH_MESSAGE_PARA { DWORD cbSize; DWORD dwMsgEncodingType; HCRYPTPROV_LEGACY hCryptProv; CRYPT_ALGORITHM_IDENTIFIER
		// HashAlgorithm; void *pvHashAuxInfo; } CRYPT_HASH_MESSAGE_PARA, *PCRYPT_HASH_MESSAGE_PARA;
		[PInvokeData("wincrypt.h", MSDNShortId = "60415136-3ac0-4fab-bdbf-faa16e8e43e1")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CRYPT_HASH_MESSAGE_PARA
		{
			/// <summary>Size of this structure in bytes.</summary>
			public uint cbSize;

			/// <summary>
			/// <para>
			/// Type of encoding used. It is always acceptable to specify both the certificate and message encoding types by combining them
			/// with a bitwise- <c>OR</c> operation as shown in the following example:
			/// </para>
			/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
			/// <para>Currently defined encoding types are:</para>
			/// <list type="bullet">
			/// <item>
			/// <term>X509_ASN_ENCODING</term>
			/// </item>
			/// <item>
			/// <term>PKCS_7_ASN_ENCODING</term>
			/// </item>
			/// </list>
			/// </summary>
			public CertEncodingType dwMsgEncodingType;

			/// <summary>
			/// <para>This member is not used and should be set to <c>NULL</c>.</para>
			/// <para>
			/// <c>Windows Server 2003 and Windows XP:</c> A handle to the cryptographic service provider (CSP) to be used.Unless there is a
			/// strong reason for passing in a specific cryptographic provider in <c>hCryptProv</c>, pass zero to use the default RSA or DSS provider.
			/// </para>
			/// <para>This member's data type is <c>HCRYPTPROV</c>.</para>
			/// </summary>
			public HCRYPTPROV hCryptProv;

			/// <summary>CRYPT_ALGORITHM_IDENTIFIER containing the algorithm for generating the hash of the message.</summary>
			public CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;

			/// <summary>Not currently used, and must be set to <c>NULL</c>.</summary>
			public IntPtr pvHashAuxInfo;
		}

		/// <summary>
		/// The <c>CRYPT_KEY_SIGN_MESSAGE_PARA</c> structure contains information about the cryptographic service provider (CSP) and
		/// algorithms used to sign a message.
		/// </summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-crypt_key_sign_message_para typedef struct
		// _CRYPT_KEY_SIGN_MESSAGE_PARA { DWORD cbSize; DWORD dwMsgAndCertEncodingType; union { HCRYPTPROV hCryptProv; NCRYPT_KEY_HANDLE
		// hNCryptKey; } DUMMYUNIONNAME; DWORD dwKeySpec; CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm; void *pvHashAuxInfo;
		// CRYPT_ALGORITHM_IDENTIFIER PubKeyAlgorithm; } CRYPT_KEY_SIGN_MESSAGE_PARA, *PCRYPT_KEY_SIGN_MESSAGE_PARA;
		[PInvokeData("wincrypt.h", MSDNShortId = "d5426ad6-2181-42ce-99f2-cc6cc83e20a8")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CRYPT_KEY_SIGN_MESSAGE_PARA
		{
			/// <summary>The size, in bytes, of this data structure.</summary>
			public uint cbSize;

			/// <summary>
			/// <para>
			/// Specifies the type of message and certificate encoding used. This can be a combination of one or more of the following values.
			/// </para>
			/// <list type="table">
			/// <listheader>
			/// <term>Value</term>
			/// <term>Meaning</term>
			/// </listheader>
			/// <item>
			/// <term>X509_ASN_ENCODING</term>
			/// <term>Specifies X.509 certificate encoding.</term>
			/// </item>
			/// <item>
			/// <term>PKCS_7_ASN_ENCODING</term>
			/// <term>Specifies PKCS 7 message encoding.</term>
			/// </item>
			/// </list>
			/// </summary>
			public CertEncodingType dwMsgAndCertEncodingType;

			/// <summary/>
			public CRYPT_KEY_SIGN_MESSAGE_PARA_HANDLE Handle;

			/// <summary/>
			[StructLayout(LayoutKind.Explicit)]
			public struct CRYPT_KEY_SIGN_MESSAGE_PARA_HANDLE
			{
				/// <summary>
				/// The handle of the CSP to use to sign the message. The CryptAcquireContext function is called to obtain this handle.
				/// </summary>
				[FieldOffset(0)]
				public HCRYPTPROV hCryptProv;

				/// <summary>
				/// The handle of the Cryptography API: Next Generation (CNG) CSP to use to sign the message. CNG signature algorithms are
				/// only supported in CNG functions.
				/// </summary>
				[FieldOffset(0)]
				public NCrypt.NCRYPT_KEY_HANDLE hNCryptKey;
			}

			/// <summary>
			/// <para>
			/// Identifies the type of private key to use to sign the message. This must be one of the following values. This member is
			/// ignored if a CNG key is passed in the hNCryptKey member.
			/// </para>
			/// <list type="table">
			/// <listheader>
			/// <term>Value</term>
			/// <term>Meaning</term>
			/// </listheader>
			/// <item>
			/// <term>AT_KEYEXCHANGE</term>
			/// <term>Use the key exchange key.</term>
			/// </item>
			/// <item>
			/// <term>AT_SIGNATURE</term>
			/// <term>Use the digital signature key.</term>
			/// </item>
			/// </list>
			/// </summary>
			public CertKeySpec dwKeySpec;

			/// <summary>
			/// A CRYPT_ALGORITHM_IDENTIFIER structure that specifies the algorithm to use to generate the hash of the message. This must be
			/// a hash algorithm.
			/// </summary>
			public CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;

			/// <summary>This member is not used and must be set to <c>NULL</c>.</summary>
			public IntPtr pvHashAuxInfo;

			/// <summary>
			/// A CRYPT_ALGORITHM_IDENTIFIER structure that specifies the algorithm to use to sign the message. This must be either a public
			/// key or a signature algorithm.
			/// </summary>
			public CRYPT_ALGORITHM_IDENTIFIER PubKeyAlgorithm;
		}

		/// <summary>
		/// The <c>CRYPT_KEY_VERIFY_MESSAGE_PARA</c> structure contains information needed to verify signed messages without a certificate
		/// for the signer.
		/// </summary>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-crypt_key_verify_message_para typedef struct
		// _CRYPT_KEY_VERIFY_MESSAGE_PARA { DWORD cbSize; DWORD dwMsgEncodingType; HCRYPTPROV_LEGACY hCryptProv; }
		// CRYPT_KEY_VERIFY_MESSAGE_PARA, *PCRYPT_KEY_VERIFY_MESSAGE_PARA;
		[PInvokeData("wincrypt.h", MSDNShortId = "4e0178fb-1f9f-4ee4-9a83-f37cf71d35ff")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CRYPT_KEY_VERIFY_MESSAGE_PARA
		{
			/// <summary>The size, in bytes, of this structure.</summary>
			public uint cbSize;

			/// <summary>
			/// <para>
			/// Type of encoding used. It is always acceptable to specify both the certificate and message encoding types by combining them
			/// with a bitwise- <c>OR</c> operation as shown in the following example:
			/// </para>
			/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
			/// <para>Currently defined encoding types are:</para>
			/// <list type="bullet">
			/// <item>
			/// <term>X509_ASN_ENCODING</term>
			/// </item>
			/// <item>
			/// <term>PKCS_7_ASN_ENCODING</term>
			/// </item>
			/// </list>
			/// </summary>
			public CertEncodingType dwMsgEncodingType;

			/// <summary>
			/// <para>This member is not used and should be set to <c>NULL</c>.</para>
			/// <para>
			/// <c>Windows Server 2003 and Windows XP:</c> A handle to the cryptographic service provider (CSP) to be used to verify a
			/// signed message. The CSP identified by this handle is used for hashing and for signature verification.Unless there is a
			/// strong reason for using a specific cryptographic provider, set this member to zero to use the default RSA or DSS provider.
			/// </para>
			/// <para>This member's data type is <c>HCRYPTPROV</c>.</para>
			/// </summary>
			public HCRYPTPROV hCryptProv;
		}

		/// <summary>
		/// The <c>CRYPT_SIGN_MESSAGE_PARA</c> structure contains information for signing messages using a specified signing certificate context.
		/// </summary>
		/// <remarks>
		/// The <c>HashEncryptionAlgorithm</c> and <c>pvHashEncryptionAuxInfo</c> members can only be used if
		/// CRYPT_SIGN_MESSAGE_PARA_HAS_CMS_FIELDS is defined.
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-crypt_sign_message_para 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_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm; void *pvHashEncryptionAuxInfo; } CRYPT_SIGN_MESSAGE_PARA, *PCRYPT_SIGN_MESSAGE_PARA;
		[PInvokeData("wincrypt.h", MSDNShortId = "1601d860-6054-4650-a033-ea088655b7e4")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CRYPT_SIGN_MESSAGE_PARA
		{
			/// <summary>Size of this structure in bytes.</summary>
			public uint cbSize;

			/// <summary>
			/// <para>
			/// Type of encoding used. It is always acceptable to specify both the certificate and message encoding types by combining them
			/// with a bitwise- <c>OR</c> operation as shown in the following example:
			/// </para>
			/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
			/// <para>Currently defined encoding types are:</para>
			/// <list type="bullet">
			/// <item>
			/// <term>X509_ASN_ENCODING</term>
			/// </item>
			/// <item>
			/// <term>PKCS_7_ASN_ENCODING</term>
			/// </item>
			/// </list>
			/// </summary>
			public CertEncodingType dwMsgEncodingType;

			/// <summary>
			/// <para>A pointer to the CERT_CONTEXT to be used in the signing.</para>
			/// <para>
			/// Either the CERT_KEY_PROV_INFO_PROP_ID, or CERT_KEY_CONTEXT_PROP_ID property must be set for the context to provide access to
			/// the private signature key.
			/// </para>
			/// </summary>
			public PCCERT_CONTEXT pSigningCert;

			/// <summary>CRYPT_ALGORITHM_IDENTIFIER containing the hashing algorithm used to hash the data to be signed.</summary>
			public CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;

			/// <summary>Not currently used, and must be set to <c>NULL</c>.</summary>
			public IntPtr pvHashAuxInfo;

			/// <summary>
			/// Number of elements in the <c>rgpMsgCert</c> array of CERT_CONTEXT structures. If set to zero no certificates are included in
			/// the signed message.
			/// </summary>
			public uint cMsgCert;

			/// <summary>
			/// Array of pointers to CERT_CONTEXT structures to be included in the signed message. If the <c>pSigningCert</c> is to be
			/// included, a pointer to it must be in the <c>rgpMsgCert</c> array.
			/// </summary>
			public IntPtr rgpMsgCert;

			/// <summary>
			/// Number of elements in the <c>rgpMsgCrl</c> array of pointers to CRL_CONTEXT structures. If set to zero, no
			/// <c>CRL_CONTEXT</c> structures are included in the signed message.
			/// </summary>
			public uint cMsgCrl;

			/// <summary>Array of pointers to CRL_CONTEXT structures to be included in the signed message.</summary>
			public IntPtr rgpMsgCrl;

			/// <summary>
			/// Number of elements in the <c>rgAuthAttr</c> array. If no authenticated attributes are present in <c>rgAuthAttr</c>, this
			/// member is set to zero.
			/// </summary>
			public uint cAuthAttr;

			/// <summary>
			/// Array of pointers to CRYPT_ATTRIBUTE structures, each holding authenticated attribute information. If there are
			/// authenticated attributes present, the PKCS #9 standard dictates that there must be at least two attributes present, the
			/// content type object identifier (OID), and the hash of the message itself. These attributes are automatically added by the system.
			/// </summary>
			public IntPtr rgAuthAttr;

			/// <summary>
			/// Number of elements in the <c>rgUnauthAttr</c> array. If no unauthenticated attributes are present in <c>rgUnauthAttr</c>,
			/// this member is zero.
			/// </summary>
			public uint cUnauthAttr;

			/// <summary>
			/// Array of pointers to CRYPT_ATTRIBUTE structures each holding an unauthenticated attribute information. Unauthenticated
			/// attributes can be used to contain countersignatures, among other uses.
			/// </summary>
			public IntPtr rgUnauthAttr;

			/// <summary>
			/// <para>
			/// Normally zero. If the encoded output is to be a CMSG_SIGNED inner content of an outer cryptographic message such as a
			/// CMSG_ENVELOPED message, the CRYPT_MESSAGE_BARE_CONTENT_OUT_FLAG must be set. If it is not set, the message will be encoded
			/// as an inner content type of CMSG_DATA.
			/// </para>
			/// <para>
			/// CRYPT_MESSAGE_ENCAPSULATED_CONTENT_OUT_FLAG can be set to encapsulate non-data inner content into an OCTET STRING.
			/// CRYPT_MESSAGE_KEYID_SIGNER_FLAG can be set to identify signers by their Key Identifier and not their Issuer and Serial Number.
			/// </para>
			/// <para>
			/// CRYPT_MESSAGE_SILENT_KEYSET_FLAG can be set to suppress any UI by the CSP. For more information about the CRYPT_SILENT flag,
			/// see CryptAcquireContext.
			/// </para>
			/// </summary>
			public CryptMsgActionFlags dwFlags;

			/// <summary>
			/// Normally zero. Set to the encoding type of the input message if that input to be signed is the encoded output of another
			/// cryptographic message.
			/// </summary>
			public uint dwInnerContentType;

			/// <summary>
			/// A CRYPT_ALGORITHM_IDENTIFIER. If present and not <c>NULL</c>, it is used instead of the signer's certificate
			/// <c>PublicKeyInfo.Algorithm</c> member. Note that for RSA, the hash encryption algorithm is normally the same as the public
			/// key algorithm. For DSA, the hash encryption algorithm is normally a DSS signature algorithm. This member can only be used if
			/// CRYPT_SIGN_MESSAGE_PARA_HAS_CMS_FIELDS is defined.
			/// </summary>
			[MarshalAs(UnmanagedType.FunctionPtr)]
			public CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm;

			/// <summary>
			/// Currently not used and must be set to <c>NULL</c>. This member can only be used if CRYPT_SIGN_MESSAGE_PARA_HAS_CMS_FIELDS is defined.
			/// </summary>
			public IntPtr pvHashEncryptionAuxInfo;
		}

		/// <summary>The <c>CRYPT_VERIFY_MESSAGE_PARA</c> structure contains information needed to verify signed messages.</summary>
		/// <remarks>
		/// <para>This structure is passed to the following functions:</para>
		/// <list type="bullet">
		/// <item>
		/// <term>CryptDecodeMessage</term>
		/// </item>
		/// <item>
		/// <term>CryptDecryptAndVerifyMessageSignature</term>
		/// </item>
		/// <item>
		/// <term>CryptVerifyDetachedMessageSignature</term>
		/// </item>
		/// <item>
		/// <term>CryptVerifyMessageSignature</term>
		/// </item>
		/// </list>
		/// </remarks>
		// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-crypt_verify_message_para typedef struct
		// _CRYPT_VERIFY_MESSAGE_PARA { DWORD cbSize; DWORD dwMsgAndCertEncodingType; HCRYPTPROV_LEGACY hCryptProv;
		// PFN_CRYPT_GET_SIGNER_CERTIFICATE pfnGetSignerCertificate; void *pvGetArg; PCCERT_STRONG_SIGN_PARA pStrongSignPara; }
		// CRYPT_VERIFY_MESSAGE_PARA, *PCRYPT_VERIFY_MESSAGE_PARA;
		[PInvokeData("wincrypt.h", MSDNShortId = "bbd56b5e-2bbe-420f-8842-1be50dca779f")]
		[StructLayout(LayoutKind.Sequential)]
		public struct CRYPT_VERIFY_MESSAGE_PARA
		{
			/// <summary>Size of this structure in bytes.</summary>
			public uint cbSize;

			/// <summary>
			/// <para>
			/// Type of encoding used. It is always acceptable to specify both the certificate and message encoding types by combining them
			/// with a bitwise- <c>OR</c> operation as shown in the following example:
			/// </para>
			/// <para>X509_ASN_ENCODING | PKCS_7_ASN_ENCODING</para>
			/// <para>Currently defined encoding types are:</para>
			/// <list type="bullet">
			/// <item>
			/// <term>X509_ASN_ENCODING</term>
			/// </item>
			/// <item>
			/// <term>PKCS_7_ASN_ENCODING</term>
			/// </item>
			/// </list>
			/// </summary>
			public CertEncodingType dwMsgAndCertEncodingType;

			/// <summary>
			/// <para>This member is not used and should be set to <c>NULL</c>.</para>
			/// <para>
			/// <c>Windows Server 2003 and Windows XP:</c> A handle to the cryptographic service provider to be used to verify a signed
			/// message. The CSP identified by this handle is used for hashing and for signature verification.Unless there is a strong
			/// reason for using a specific cryptographic provider, set to zero to use the default RSA or DSS provider.
			/// </para>
			/// <para>This member's data type is <c>HCRYPTPROV</c>.</para>
			/// </summary>
			public HCRYPTPROV hCryptProv;

			/// <summary>
			/// <para>
			/// A pointer to the callback function used to get the signer's certificate context. If <c>NULL</c>, the default callback is
			/// used. The default callback tries to get the signer certificate context from the message's certificate store.
			/// </para>
			/// <para>
			/// An application defined–callback function that gets the signer's certificate can be used in place of the default. It is
			/// passed the certificate identifier of the signer (its issuer and serial number) and a handle to its cryptographic signed
			/// message's certificate store.
			/// </para>
			/// <para>See CryptGetSignerCertificateCallback for the callback functions signature and arguments.</para>
			/// </summary>
			public PFN_CRYPT_GET_SIGNER_CERTIFICATE pfnGetSignerCertificate;

			/// <summary>Argument to pass to the callback function. Typically, this gets and verifies the message signer's certificate.</summary>
			public IntPtr pvGetArg;

			/// <summary>
			/// <para>
			/// Optional pointer to a CERT_STRONG_SIGN_PARA structure that contains parameters used for strong signing. If you set this
			/// member and the function successfully verifies the signature, the function will then check for a strong signature. If the
			/// signature is not strong, the operation will fail and set the GetLastError value to <c>NTE_BAD_ALGID</c>.
			/// </para>
			/// <para>
			/// <c>Note</c> You can use the <c>pStrongSignPara</c> member only if <c>CRYPT_VERIFY_MESSAGE_PARA_HAS_EXTRA_FIELDS</c> is
			/// defined by using the <c>#define</c> directive before including Wincrypt.h. If
			/// <c>CRYPT_VERIFY_MESSAGE_PARA_HAS_EXTRA_FIELDS</c> is defined, you must zero all unused fields.
			/// </para>
			/// <para>Windows 8 and Windows Server 2012:</para>
			/// <para>Support for this member begins.</para>
			/// </summary>
			public IntPtr pStrongSignPara;
		}

		/// <summary>Provides a <see cref="SafeHandle"/> for <see cref="HCRYPTMSG"/> that is disposed using <see cref="CryptMsgClose"/>.</summary>
		public class SafeHCRYPTMSG : SafeHANDLE
		{
			/// <summary>Initializes a new instance of the <see cref="SafeHCRYPTMSG"/> class and assigns an existing handle.</summary>
			/// <param name="preexistingHandle">An <see cref="IntPtr"/> object that represents the pre-existing handle to use.</param>
			/// <param name="ownsHandle">
			/// <see langword="true"/> to reliably release the handle during the finalization phase; otherwise, <see langword="false"/> (not recommended).
			/// </param>
			public SafeHCRYPTMSG(IntPtr preexistingHandle, bool ownsHandle = true) : base(preexistingHandle, ownsHandle) { }

			/// <summary>Initializes a new instance of the <see cref="SafeHCRYPTMSG"/> class.</summary>
			private SafeHCRYPTMSG() : base() { }

			/// <summary>Performs an implicit conversion from <see cref="SafeHCRYPTMSG"/> to <see cref="HCRYPTMSG"/>.</summary>
			/// <param name="h">The safe handle instance.</param>
			/// <returns>The result of the conversion.</returns>
			public static implicit operator HCRYPTMSG(SafeHCRYPTMSG h) => h.handle;

			/// <inheritdoc/>
			protected override bool InternalReleaseHandle() => CryptMsgClose(handle);
		}
	}
}