File: system\security\cryptography\pkcs\pkcsutils.cs
Project: ndp\clr\src\managedlibraries\security\System.Security.csproj (System.Security)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
 
//
// PkcsUtils.cs
// 
 
namespace System.Security.Cryptography.Pkcs {
    using System.Collections;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    using System.Security.Cryptography.Xml;
    using System.Text;
    using System.Runtime.Versioning;
 
    internal static class PkcsUtils {
        private static volatile int m_cmsSupported = -1;
 
        private struct I_CRYPT_ATTRIBUTE {
            internal IntPtr pszObjId;
            internal uint   cValue;
            internal IntPtr rgValue;    // PCRYPT_ATTR_BLOB
        }
 
        internal static uint AlignedLength (uint length) {
            return ((length + (uint) 7) & ((uint) 0xfffffff8));
        }
 
        [SecuritySafeCritical]
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        internal static bool CmsSupported () {
            if (m_cmsSupported == -1) {
                using(SafeLibraryHandle hModule = CAPI.CAPISafe.LoadLibrary("Crypt32.dll")) {
                    if (!hModule.IsInvalid) {
                    IntPtr pFunc = CAPI.CAPISafe.GetProcAddress(hModule, "CryptMsgVerifyCountersignatureEncodedEx");
                    m_cmsSupported = pFunc == IntPtr.Zero ? 0 : 1;
                    }
                }
            }
 
            return m_cmsSupported == 0 ? false : true;
        }
 
        [SecuritySafeCritical]
        internal static RecipientInfoType GetRecipientInfoType (X509Certificate2 certificate) {
            RecipientInfoType recipientInfoType = RecipientInfoType.Unknown;
 
            if (certificate != null) {
                CAPI.CERT_CONTEXT pCertContext = (CAPI.CERT_CONTEXT) Marshal.PtrToStructure(X509Utils.GetCertContext(certificate).DangerousGetHandle(), typeof(CAPI.CERT_CONTEXT));
                CAPI.CERT_INFO certInfo = (CAPI.CERT_INFO) Marshal.PtrToStructure(pCertContext.pCertInfo, typeof(CAPI.CERT_INFO));
 
                uint algId = X509Utils.OidToAlgId(certInfo.SubjectPublicKeyInfo.Algorithm.pszObjId);
                if (algId == CAPI.CALG_RSA_KEYX)
                    recipientInfoType = RecipientInfoType.KeyTransport;
                else if (algId == CAPI.CALG_DH_SF || algId == CAPI.CALG_DH_EPHEM)
                    recipientInfoType = RecipientInfoType.KeyAgreement;
                else
                    recipientInfoType = RecipientInfoType.Unknown;
            }
 
            return recipientInfoType;
        }
 
        [SecurityCritical]
        internal static unsafe int GetMaxKeyLength (SafeCryptProvHandle safeCryptProvHandle, uint algId) {
            uint enumFlag = CAPI.CRYPT_FIRST;
            uint cbPeex = (uint) Marshal.SizeOf(typeof(CAPI.PROV_ENUMALGS_EX));
            SafeLocalAllocHandle pPeex = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(Marshal.SizeOf(typeof(CAPI.PROV_ENUMALGS_EX))));
 
            using (pPeex) {
                while (CAPI.CAPISafe.CryptGetProvParam(safeCryptProvHandle, CAPI.PP_ENUMALGS_EX, pPeex.DangerousGetHandle(), new IntPtr(&cbPeex), enumFlag)) {
                    CAPI.PROV_ENUMALGS_EX peex = (CAPI.PROV_ENUMALGS_EX) Marshal.PtrToStructure(pPeex.DangerousGetHandle(), typeof(CAPI.PROV_ENUMALGS_EX));
 
                    if (peex.aiAlgid == algId)
                        return (int) peex.dwMaxLen;
 
                    enumFlag = 0;
                }
            }
 
            throw new CryptographicException(CAPI.CRYPT_E_UNKNOWN_ALGO);
        }
 
        [SecurityCritical]
        internal static unsafe uint GetVersion (SafeCryptMsgHandle safeCryptMsgHandle) {
            uint dwVersion = 0;
            uint cbCount = (uint) Marshal.SizeOf(typeof(uint));
            if (!CAPI.CAPISafe.CryptMsgGetParam(safeCryptMsgHandle,
                                                CAPI.CMSG_VERSION_PARAM,
                                                0,
                                                new IntPtr(&dwVersion),
                                                new IntPtr(&cbCount)))
                checkErr(Marshal.GetLastWin32Error());
 
            return dwVersion;
        }
 
        [SecurityCritical]
        internal static unsafe uint GetMessageType (SafeCryptMsgHandle safeCryptMsgHandle) {
            uint dwMsgType = 0;
            uint cbMsgType = (uint) Marshal.SizeOf(typeof(uint));
            if (!CAPI.CAPISafe.CryptMsgGetParam(safeCryptMsgHandle,
                                                CAPI.CMSG_TYPE_PARAM,
                                                0,
                                                new IntPtr(&dwMsgType),
                                                new IntPtr(&cbMsgType)))
                checkErr(Marshal.GetLastWin32Error());
 
            return dwMsgType;
        }
 
        [SecurityCritical]
        internal static unsafe AlgorithmIdentifier GetAlgorithmIdentifier (SafeCryptMsgHandle safeCryptMsgHandle) {
            AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier();
 
            uint cbAlgorithm = 0;
            if (!CAPI.CAPISafe.CryptMsgGetParam(safeCryptMsgHandle,
                                                CAPI.CMSG_ENVELOPE_ALGORITHM_PARAM,
                                                0,
                                                IntPtr.Zero,
                                                new IntPtr(&cbAlgorithm)))
                checkErr(Marshal.GetLastWin32Error());
 
            if (cbAlgorithm > 0) {
                SafeLocalAllocHandle pbAlgorithm = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(cbAlgorithm));
                if (!CAPI.CAPISafe.CryptMsgGetParam(safeCryptMsgHandle,
                                                    CAPI.CMSG_ENVELOPE_ALGORITHM_PARAM,
                                                    0,
                                                    pbAlgorithm,
                                                    new IntPtr(&cbAlgorithm)))
                    checkErr(Marshal.GetLastWin32Error());
 
                CAPI.CRYPT_ALGORITHM_IDENTIFIER cryptAlgorithmIdentifier = (CAPI.CRYPT_ALGORITHM_IDENTIFIER) Marshal.PtrToStructure(pbAlgorithm.DangerousGetHandle(), typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER));
                algorithmIdentifier = new AlgorithmIdentifier(cryptAlgorithmIdentifier);
                pbAlgorithm.Dispose();
            }
 
            return algorithmIdentifier;
        }
 
        [SecurityCritical]
        internal static unsafe void GetParam (SafeCryptMsgHandle safeCryptMsgHandle,
                                              uint paramType,
                                              uint index,
                                              out SafeLocalAllocHandle pvData,
                                              out uint cbData) {
            cbData = 0;
            pvData = SafeLocalAllocHandle.InvalidHandle;
 
            fixed (uint * pcbData = &cbData) {
                if (!CAPI.CAPISafe.CryptMsgGetParam(safeCryptMsgHandle,
                                                    paramType,
                                                    index,
                                                    pvData,
                                                    new IntPtr(pcbData)))
                    checkErr(Marshal.GetLastWin32Error());
 
                if (cbData > 0) {
                    pvData = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cbData));
 
                    if (!CAPI.CAPISafe.CryptMsgGetParam(safeCryptMsgHandle,
                                                        paramType,
                                                        index,
                                                        pvData,
                                                        new IntPtr(pcbData)))
                        checkErr(Marshal.GetLastWin32Error());
                }
            }
        }
 
        [SecurityCritical]
        internal static unsafe void GetParam (SafeCryptMsgHandle safeCryptMsgHandle,
                                              uint paramType,
                                              uint index,
                                              out byte[] pvData,
                                              out uint cbData) {
            cbData = 0;
            pvData = new byte[0];
 
            fixed (uint * pcbData = &cbData) {
                if (!CAPI.CAPISafe.CryptMsgGetParam(safeCryptMsgHandle,
                                                    paramType,
                                                    index,
                                                    IntPtr.Zero,
                                                    new IntPtr(pcbData)))
                    checkErr(Marshal.GetLastWin32Error());
 
                if (cbData > 0) {
                    pvData = new byte[cbData];
 
                    fixed (byte * ppvData = &pvData[0]) {
                        if (!CAPI.CAPISafe.CryptMsgGetParam(safeCryptMsgHandle,
                                                            paramType,
                                                            index,
                                                            new IntPtr(ppvData),
                                                            new IntPtr(pcbData)))
                            checkErr(Marshal.GetLastWin32Error());
                    }
                }
            }
        }
 
        [SecurityCritical]
        internal static unsafe X509Certificate2Collection GetCertificates (SafeCryptMsgHandle safeCryptMsgHandle) {
            uint dwCount = 0;
            uint cbCount = (uint) Marshal.SizeOf(typeof(uint));
            X509Certificate2Collection certificates = new X509Certificate2Collection();
 
            if (!CAPI.CAPISafe.CryptMsgGetParam(safeCryptMsgHandle,
                                                CAPI.CMSG_CERT_COUNT_PARAM,
                                                0,
                                                new IntPtr(&dwCount),
                                                new IntPtr(&cbCount)))
                checkErr(Marshal.GetLastWin32Error());
 
            for (uint index = 0; index < dwCount; index++) {
                uint cbEncoded = 0;
                SafeLocalAllocHandle pbEncoded = SafeLocalAllocHandle.InvalidHandle;
 
                GetParam(safeCryptMsgHandle, CAPI.CMSG_CERT_PARAM, index, out pbEncoded, out cbEncoded);
                if (cbEncoded > 0) {
                    SafeCertContextHandle safeCertContextHandle = CAPI.CAPISafe.CertCreateCertificateContext(CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING,
                                                                                                             pbEncoded,
                                                                                                             cbEncoded);
                    if (safeCertContextHandle == null || safeCertContextHandle.IsInvalid)
                        throw new CryptographicException(Marshal.GetLastWin32Error());
 
                    certificates.Add(new X509Certificate2(safeCertContextHandle.DangerousGetHandle()));
                    safeCertContextHandle.Dispose();
                }
            }
 
            return certificates;
        }
 
        [SecurityCritical]
        internal static byte[] GetContent (SafeCryptMsgHandle safeCryptMsgHandle) {
            uint cbContent = 0;
            byte[] content = new byte[0];
 
            GetParam(safeCryptMsgHandle, CAPI.CMSG_CONTENT_PARAM, 0, out content, out cbContent);
 
            return content;
        }
 
        [SecurityCritical]
        internal static Oid GetContentType (SafeCryptMsgHandle safeCryptMsgHandle) {
            uint cbContentType = 0;
            byte[] contentType = new byte[0];
 
            GetParam(safeCryptMsgHandle, CAPI.CMSG_INNER_CONTENT_TYPE_PARAM, 0, out contentType, out cbContentType);
            if (contentType.Length > 0 && contentType[contentType.Length - 1] == 0) {
                byte[] temp = new byte[contentType.Length - 1];
                Array.Copy(contentType, 0, temp, 0, temp.Length);
                contentType = temp;
            }
            return new Oid(Encoding.ASCII.GetString(contentType));
        }
 
        [SecurityCritical]
        internal static byte[] GetMessage (SafeCryptMsgHandle safeCryptMsgHandle) {
            uint cbMessage = 0;
            byte[] message = new byte[0];
 
            GetParam(safeCryptMsgHandle, CAPI.CMSG_ENCODED_MESSAGE, 0, out message, out cbMessage);
            return message;
        }
 
        [SecurityCritical]
        internal static unsafe int GetSignerIndex (SafeCryptMsgHandle safeCrytpMsgHandle, SignerInfo signerInfo, int startIndex) {
            uint dwSigners = 0;
            uint cbCount = (uint) Marshal.SizeOf(typeof(uint));
 
            if (!CAPI.CAPISafe.CryptMsgGetParam(safeCrytpMsgHandle,
                                                CAPI.CMSG_SIGNER_COUNT_PARAM,
                                                0,
                                                new IntPtr(&dwSigners),
                                                new IntPtr(&cbCount)))
                checkErr(Marshal.GetLastWin32Error());
 
            for (int index = startIndex; index < (int) dwSigners; index++) {
                uint cbCmsgSignerInfo = 0;
 
                if (!CAPI.CAPISafe.CryptMsgGetParam(safeCrytpMsgHandle,
                                                    CAPI.CMSG_SIGNER_INFO_PARAM,
                                                    (uint)index,
                                                    IntPtr.Zero,
                                                    new IntPtr(&cbCmsgSignerInfo)))
                    checkErr(Marshal.GetLastWin32Error());
 
                if (cbCmsgSignerInfo > 0) {
                    SafeLocalAllocHandle pbCmsgSignerInfo = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(cbCmsgSignerInfo));
 
                    if (!CAPI.CAPISafe.CryptMsgGetParam(safeCrytpMsgHandle,
                                                        CAPI.CMSG_SIGNER_INFO_PARAM,
                                                        (uint)index,
                                                        pbCmsgSignerInfo,
                                                        new IntPtr(&cbCmsgSignerInfo)))
                        checkErr(Marshal.GetLastWin32Error());
 
                    CAPI.CMSG_SIGNER_INFO cmsgSignerInfo1 = signerInfo.GetCmsgSignerInfo();
                    CAPI.CMSG_SIGNER_INFO cmsgSignerInfo2 = (CAPI.CMSG_SIGNER_INFO) Marshal.PtrToStructure(pbCmsgSignerInfo.DangerousGetHandle(), typeof(CAPI.CMSG_SIGNER_INFO));
 
                    if (X509Utils.MemEqual((byte *) cmsgSignerInfo1.Issuer.pbData,
                                  cmsgSignerInfo1.Issuer.cbData,
                                  (byte *) cmsgSignerInfo2.Issuer.pbData,
                                  cmsgSignerInfo2.Issuer.cbData) &&
                        X509Utils.MemEqual((byte *) cmsgSignerInfo1.SerialNumber.pbData,
                                  cmsgSignerInfo1.SerialNumber.cbData,
                                  (byte *) cmsgSignerInfo2.SerialNumber.pbData,
                                  cmsgSignerInfo2.SerialNumber.cbData)) {
                        return index; // Signer's index is found.
                    }
 
                    // Keep alive.
                    pbCmsgSignerInfo.Dispose();
                }
            }
 
            throw new CryptographicException(CAPI.CRYPT_E_SIGNER_NOT_FOUND);
        }
 
        [SecurityCritical]
        internal static unsafe CryptographicAttributeObjectCollection GetUnprotectedAttributes (SafeCryptMsgHandle safeCryptMsgHandle) {
            uint cbUnprotectedAttr = 0;
            CryptographicAttributeObjectCollection attributes = new CryptographicAttributeObjectCollection();
            SafeLocalAllocHandle pbUnprotectedAttr = SafeLocalAllocHandle.InvalidHandle;
            if (!CAPI.CAPISafe.CryptMsgGetParam(safeCryptMsgHandle,
                                                CAPI.CMSG_UNPROTECTED_ATTR_PARAM,
                                                0,
                                                pbUnprotectedAttr,
                                                new IntPtr(&cbUnprotectedAttr))) {
                int lastWin32Error = Marshal.GetLastWin32Error();
                if (lastWin32Error != CAPI.CRYPT_E_ATTRIBUTES_MISSING)
                    checkErr(Marshal.GetLastWin32Error());
            }
 
            if (cbUnprotectedAttr > 0) {
                using (pbUnprotectedAttr = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cbUnprotectedAttr))) {
                    if (!CAPI.CAPISafe.CryptMsgGetParam(safeCryptMsgHandle,
                                                        CAPI.CMSG_UNPROTECTED_ATTR_PARAM,
                                                        0,
                                                        pbUnprotectedAttr,
                                                        new IntPtr(&cbUnprotectedAttr)))
                        checkErr(Marshal.GetLastWin32Error());
 
                    attributes = new CryptographicAttributeObjectCollection(pbUnprotectedAttr);
                }
            }
            return attributes;
        }
 
        [SecurityCritical]
        internal unsafe static X509IssuerSerial DecodeIssuerSerial (CAPI.CERT_ISSUER_SERIAL_NUMBER pIssuerAndSerial) {
            SafeLocalAllocHandle ptr = SafeLocalAllocHandle.InvalidHandle;
            uint cbSize = CAPI.CAPISafe.CertNameToStrW(CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING,
                                                       new IntPtr(&pIssuerAndSerial.Issuer),
                                                       CAPI.CERT_X500_NAME_STR | CAPI.CERT_NAME_STR_REVERSE_FLAG,
                                                       ptr,
                                                       0);
            if (cbSize <= 1) // The API actually return 1 when It fails; which is not what the documentation says.
                throw new CryptographicException(Marshal.GetLastWin32Error());
 
            ptr = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(checked(2 * cbSize)));
            cbSize = CAPI.CAPISafe.CertNameToStrW(CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING,
                                                  new IntPtr(&pIssuerAndSerial.Issuer),
                                                  CAPI.CERT_X500_NAME_STR | CAPI.CERT_NAME_STR_REVERSE_FLAG,
                                                  ptr,
                                                  cbSize);
            if (cbSize <= 1)
                throw new CryptographicException(Marshal.GetLastWin32Error());
 
            X509IssuerSerial issuerSerial = new X509IssuerSerial();
            issuerSerial.IssuerName = Marshal.PtrToStringUni(ptr.DangerousGetHandle());
            byte[] serial = new byte[pIssuerAndSerial.SerialNumber.cbData];
            Marshal.Copy(pIssuerAndSerial.SerialNumber.pbData, serial, 0, serial.Length);
            issuerSerial.SerialNumber = X509Utils.EncodeHexStringFromInt(serial);
 
            ptr.Dispose();
            return issuerSerial;
        }
 
        [SecuritySafeCritical]
        internal static string DecodeOctetString (byte[] encodedOctetString) {
            uint cbDecoded = 0;
            SafeLocalAllocHandle pbDecoded = null;
 
            if (!CAPI.DecodeObject(new IntPtr(CAPI.X509_OCTET_STRING),
                                   encodedOctetString,
                                   out pbDecoded,
                                   out cbDecoded))
                throw new CryptographicException(Marshal.GetLastWin32Error());
 
            if (cbDecoded == 0)
                return String.Empty;
 
            CAPI.CRYPTOAPI_BLOB decodedBlob = (CAPI.CRYPTOAPI_BLOB) Marshal.PtrToStructure(pbDecoded.DangerousGetHandle(), typeof(CAPI.CRYPTOAPI_BLOB));
            if (decodedBlob.cbData == 0)
                return String.Empty;
            string octetString = Marshal.PtrToStringUni(decodedBlob.pbData);
            pbDecoded.Dispose();
 
            return octetString;
        }
 
        [SecuritySafeCritical]
        internal static byte[] DecodeOctetBytes (byte[] encodedOctetString) {
            uint cbDecoded = 0;
 
            SafeLocalAllocHandle pbDecoded = null;
            if (!CAPI.DecodeObject(new IntPtr(CAPI.X509_OCTET_STRING),
                                   encodedOctetString,
                                   out pbDecoded,
                                   out cbDecoded))
                throw new CryptographicException(Marshal.GetLastWin32Error());
 
            if (cbDecoded == 0)
                return new byte[0];
 
            using (pbDecoded) {
                return CAPI.BlobToByteArray(pbDecoded.DangerousGetHandle());
            }
        }
 
        internal static byte[] EncodeOctetString (string octetString) {
            // Marshal data to be encoded to unmanaged memory.
            byte[] octets = new byte[2 * (octetString.Length + 1)];
            Encoding.Unicode.GetBytes(octetString, 0, octetString.Length, octets, 0);
            return EncodeOctetString(octets);
        }
 
        [SecuritySafeCritical]
        internal static unsafe byte[] EncodeOctetString (byte[] octets) {
            fixed (byte * pbOctets = octets) {
                CAPI.CRYPTOAPI_BLOB octetsBlob = new CAPI.CRYPTOAPI_BLOB();
                octetsBlob.cbData = (uint) octets.Length;
                octetsBlob.pbData = new IntPtr(pbOctets);
 
                // Encode data.
                byte[] encodedOctets = new byte[0];
                if (!CAPI.EncodeObject(new IntPtr((long) CAPI.X509_OCTET_STRING),
                                       new IntPtr((long) &octetsBlob),
                                       out encodedOctets)) {
                    throw new CryptographicException(Marshal.GetLastWin32Error());
                }
                return encodedOctets;
            }
        }
 
        internal static string DecodeObjectIdentifier (byte[] encodedObjId, int offset) {
            StringBuilder objId = new StringBuilder("");
            if (0 < (encodedObjId.Length - offset)) {
                byte b = encodedObjId[offset];
                byte c = (byte) ((uint) b / 40);
                objId.Append(c.ToString(null, null));
                objId.Append(".");
                c = (byte) ((uint) b % 40);
                objId.Append(c.ToString(null, null));
 
                ulong s = 0;
                for (int index = offset + 1; index < encodedObjId.Length; index++) {
                    c = encodedObjId[index];
                    s = (s << 7) + (ulong) (c & 0x7f);
                    if (0 == (c & 0x80)) {
                        objId.Append(".");
                        objId.Append(s.ToString(null, null));
                        s = 0;
                    }
                }
 
                // s should be 0 at this point, otherwise we have a bad ASN.
                if (0 != s) {
                    throw new CryptographicException(CAPI.CRYPT_E_BAD_ENCODE);
                }
            }
 
            return objId.ToString();
        }
 
        internal static CmsRecipientCollection SelectRecipients (SubjectIdentifierType recipientIdentifierType) {
            X509Store store = new X509Store("AddressBook");
            store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
 
            X509Certificate2Collection certificates = new X509Certificate2Collection(store.Certificates);
 
            foreach (X509Certificate2 certificate in store.Certificates) {
                if (certificate.NotBefore <= DateTime.Now && certificate.NotAfter >= DateTime.Now) {
                    bool validUsages = true;
                    foreach (X509Extension extension in certificate.Extensions) {
                        if (String.Compare(extension.Oid.Value, CAPI.szOID_KEY_USAGE, StringComparison.OrdinalIgnoreCase) == 0) {
                            X509KeyUsageExtension keyUsage = new X509KeyUsageExtension();
                            keyUsage.CopyFrom(extension);
                            if ((keyUsage.KeyUsages & X509KeyUsageFlags.KeyEncipherment) == 0 &&
                                (keyUsage.KeyUsages & X509KeyUsageFlags.KeyAgreement) == 0) {
                                validUsages = false;
                            }
                            break;
                        }
                    }
 
                    if (validUsages) {
                        certificates.Add(certificate);
                    }
                }
            }
 
            if (certificates.Count < 1)
                throw new CryptographicException(CAPI.CRYPT_E_RECIPIENT_NOT_FOUND);
 
            X509Certificate2Collection recipients = X509Certificate2UI.SelectFromCollection(certificates, null, null, X509SelectionFlag.MultiSelection);
            if (recipients.Count < 1) 
                throw new CryptographicException(CAPI.ERROR_CANCELLED);
 
            return new CmsRecipientCollection(recipientIdentifierType, recipients);
        }
 
        internal static X509Certificate2 SelectSignerCertificate () {
            X509Store store = new X509Store();
            store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly | OpenFlags.IncludeArchived);
 
            X509Certificate2Collection certificates = new X509Certificate2Collection();
 
            foreach (X509Certificate2 certificate in store.Certificates) {
                if (certificate.HasPrivateKey && certificate.NotBefore <= DateTime.Now && certificate.NotAfter >= DateTime.Now) {
                    bool validUsages = true;
                    foreach (X509Extension extension in certificate.Extensions) {
                        if (String.Compare(extension.Oid.Value, CAPI.szOID_KEY_USAGE, StringComparison.OrdinalIgnoreCase) == 0) {
                            X509KeyUsageExtension keyUsage = new X509KeyUsageExtension();
                            keyUsage.CopyFrom(extension);
                            if ((keyUsage.KeyUsages & X509KeyUsageFlags.DigitalSignature) == 0 &&
                                (keyUsage.KeyUsages & X509KeyUsageFlags.NonRepudiation) == 0) {
                                validUsages = false;
                            }
                            break;
                        }
                    }
 
                    if (validUsages) {
                        certificates.Add(certificate);
                    }
                }
            }
 
            if (certificates.Count < 1)
                throw new CryptographicException(CAPI.CRYPT_E_SIGNER_NOT_FOUND);
 
            certificates = X509Certificate2UI.SelectFromCollection(certificates, null, null, X509SelectionFlag.SingleSelection);
            if (certificates.Count < 1) 
                throw new CryptographicException(CAPI.ERROR_CANCELLED);
 
            Debug.Assert(certificates.Count == 1);
 
            return certificates[0];
        }
 
        [SecuritySafeCritical]
        internal static AsnEncodedDataCollection GetAsnEncodedDataCollection (CAPI.CRYPT_ATTRIBUTE cryptAttribute) {
            AsnEncodedDataCollection list = new AsnEncodedDataCollection();
            Oid oid = new Oid(cryptAttribute.pszObjId);
            string szOid = oid.Value;
 
            for (uint index = 0; index < cryptAttribute.cValue; index++) {
                checked {
                    IntPtr pAttributeBlob = new IntPtr((long)cryptAttribute.rgValue + (index * Marshal.SizeOf(typeof(CAPI.CRYPTOAPI_BLOB))));
                    Pkcs9AttributeObject attribute = new Pkcs9AttributeObject(oid, CAPI.BlobToByteArray(pAttributeBlob));
                    Pkcs9AttributeObject customAttribute = CryptoConfig.CreateFromName(szOid) as Pkcs9AttributeObject;
                    if (customAttribute != null) {
                        customAttribute.CopyFrom(attribute);
                        attribute = customAttribute;
                    }
                    list.Add(attribute);
                }
            }
            return list;
        }
 
        [SecurityCritical]
        internal static AsnEncodedDataCollection GetAsnEncodedDataCollection (CAPI.CRYPT_ATTRIBUTE_TYPE_VALUE cryptAttribute) {
            AsnEncodedDataCollection list = new AsnEncodedDataCollection();
            list.Add(new Pkcs9AttributeObject(new Oid(cryptAttribute.pszObjId), CAPI.BlobToByteArray(cryptAttribute.Value)));
            return list;
        }
 
        [SecurityCritical]
        internal static unsafe IntPtr CreateCryptAttributes (CryptographicAttributeObjectCollection attributes) {
            attributes = attributes.DeepCopy();
 
            // NULL if no attribute.
            if (attributes.Count == 0)
                return IntPtr.Zero;
 
            //
            // The goal here is to compute the size needed for the attributes we are passing to CMSG_SIGNER_ENCODE_INFO
            // The unmanaged memory structure we are creating here has the following layout:
            //
            // Let cAttr = number of attributes.
            //
            // This to create the array of CRYPT_ATTRIBUTE
            // for i = 0 to cAttr {
            //     CRYPT_ATTRRIBUTE[i]                           // pszObjId | cValue | rgValue
            // }
            //
            // This is to fill in the data for each entry of CRYPT_ATTRIBUTE array above.
            // for i = 0 to cAttr {
            //     objId[i]                                      // Value of the Oid, i.e "1.2.3.4"
            //     for j = 0 to CRYPT_ATTRIBUTE[i].cValue - 1 {  // Array of CRYPTOAPI_BLOB
            //        CRYPT_ATTRIBUTE[i].rgValue[j].cbData       // Data size
            //        CRYPT_ATTRIBUTE[i].rgValue[j].pbData       // Pointer to data
            //     }
            //     for j = 0 to CRYPT_ATTRIBUTE[i].cValue - 1 {  // Data for each entry of the CRYPTOAPI_BLOB array above.
            //        *CRYPT_ATTRIBUTE[i].rgValue[j].pbData      // The actual data
            //    }
            // }
 
            checked {
                uint totalLength = 0;
                uint cryptAttrSize = AlignedLength((uint) Marshal.SizeOf(typeof(I_CRYPT_ATTRIBUTE)));
                uint cryptBlobSize = AlignedLength((uint) Marshal.SizeOf(typeof(CAPI.CRYPTOAPI_BLOB)));
 
                // First compute the total serialized unmanaged memory size needed.
                // For each attribute, we add the CRYPT_ATTRIBUTE size, the size
                // needed for the ObjId, and the size needed for all the values
                // inside each attribute which is computed in inner loop.
                foreach (CryptographicAttributeObject attribute in attributes) {
                    totalLength += cryptAttrSize;  // sizeof(CRYPT_ATTRIBUTE)
                    totalLength += AlignedLength((uint) (attribute.Oid.Value.Length + 1));  // strlen(pszObjId) + 1
 
                    // For each value within the attribute, we add the CRYPT_ATTR_BLOB size and 
                    // the actual size needed for the data.
                    foreach (AsnEncodedData attributeValue in attribute.Values) {
                        totalLength += cryptBlobSize;   // Add CRYPT_ATTR_BLOB size
                        totalLength += AlignedLength((uint) attributeValue.RawData.Length); // Data size
                    }
                }
 
                // Allocate the unmanaged memory blob to hold the entire serialized CRYPT_ATTRIBUTE array.
                SafeLocalAllocHandle pCryptAttributes = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(totalLength));
 
                // Now fill up unmanaged memory with data from the managed side.
                I_CRYPT_ATTRIBUTE * pCryptAttribute = (I_CRYPT_ATTRIBUTE *) pCryptAttributes.DangerousGetHandle();
                IntPtr pAttrData = new IntPtr((long) pCryptAttributes.DangerousGetHandle() + (cryptAttrSize * attributes.Count));
 
                foreach (CryptographicAttributeObject attribute in attributes) {
                    byte * pszObjId = (byte *) pAttrData;
                    byte[] objId = new byte[attribute.Oid.Value.Length + 1];
                    CAPI.CRYPTOAPI_BLOB * pDataBlob = (CAPI.CRYPTOAPI_BLOB *) (pszObjId + AlignedLength((uint) objId.Length));
 
                    // CRYPT_ATTRIBUTE.pszObjId
                    pCryptAttribute->pszObjId = (IntPtr) pszObjId;
 
                    // CRYPT_ATTRIBUTE.cValue
                    pCryptAttribute->cValue = (uint) attribute.Values.Count;
 
                    // CRYPT_ATTRIBUTE.rgValue
                    pCryptAttribute->rgValue = (IntPtr) pDataBlob;
 
                    // ObjId - The actual dotted value of the OID.
                    Encoding.ASCII.GetBytes(attribute.Oid.Value, 0, attribute.Oid.Value.Length, objId, 0);
                    Marshal.Copy(objId, 0, pCryptAttribute->pszObjId, objId.Length);
 
                    // cValue of CRYPT_ATTR_BLOBs followed by cValue of actual data.
                    IntPtr pbEncodedData = new IntPtr((long) pDataBlob + (attribute.Values.Count * cryptBlobSize));
                    foreach (AsnEncodedData value in attribute.Values) {
                        // Retrieve encoded data.
                        byte[] encodedData = value.RawData;
 
                        // Write data
                        if (encodedData.Length > 0) {
                            // CRYPT_ATTR_BLOB.cbData
                            pDataBlob->cbData = (uint) encodedData.Length;
 
                            // CRYPT_ATTR_BLOB.pbData
                            pDataBlob->pbData = pbEncodedData;
 
                            Marshal.Copy(encodedData, 0, pbEncodedData, encodedData.Length);
                            pbEncodedData = new IntPtr((long) pbEncodedData + AlignedLength((uint) encodedData.Length));
                        }
 
                        // Advance pointer.
                        pDataBlob++;
                    }
 
                    // Advance pointers.
                    pCryptAttribute++;
                    pAttrData = pbEncodedData;
                }
 
                // Since we are returning IntPtr, we MUST supress finalizer, otherwise
                // the GC can collect the memory underneath us!!!
                GC.SuppressFinalize(pCryptAttributes);
 
                return pCryptAttributes.DangerousGetHandle();
            }
        }
 
        [SecuritySafeCritical]
        internal static CAPI.CMSG_SIGNER_ENCODE_INFO CreateSignerEncodeInfo (CmsSigner signer, out SafeCryptProvHandle hProv) {
            return CreateSignerEncodeInfo(signer, false, out hProv);
        }
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        [SecuritySafeCritical]
        internal static unsafe CAPI.CMSG_SIGNER_ENCODE_INFO CreateSignerEncodeInfo (CmsSigner signer, bool silent, out SafeCryptProvHandle hProv) {
            CAPI.CMSG_SIGNER_ENCODE_INFO cmsSignerEncodeInfo = new CAPI.CMSG_SIGNER_ENCODE_INFO(Marshal.SizeOf(typeof(CAPI.CMSG_SIGNER_ENCODE_INFO)));
 
            SafeCryptProvHandle safeCryptProvHandle = SafeCryptProvHandle.InvalidHandle;
            uint keySpec = 0;
 
            cmsSignerEncodeInfo.HashAlgorithm.pszObjId = signer.DigestAlgorithm.Value;
 
            if (0 == String.Compare(
                signer.Certificate.PublicKey.Oid.Value,
                CAPI.szOID_X957_DSA,
                StringComparison.Ordinal))
            {
                cmsSignerEncodeInfo.HashEncryptionAlgorithm.pszObjId = CAPI.szOID_X957_sha1DSA;
            }
 
            cmsSignerEncodeInfo.cAuthAttr = (uint) signer.SignedAttributes.Count;
            cmsSignerEncodeInfo.rgAuthAttr = CreateCryptAttributes(signer.SignedAttributes);
 
            cmsSignerEncodeInfo.cUnauthAttr = (uint) signer.UnsignedAttributes.Count;
            cmsSignerEncodeInfo.rgUnauthAttr = CreateCryptAttributes(signer.UnsignedAttributes);
 
            if (signer.SignerIdentifierType == SubjectIdentifierType.NoSignature) {
                cmsSignerEncodeInfo.HashEncryptionAlgorithm.pszObjId = CAPI.szOID_PKIX_NO_SIGNATURE;
                cmsSignerEncodeInfo.pCertInfo  = IntPtr.Zero;
                cmsSignerEncodeInfo.dwKeySpec  = keySpec;
 
                //  If the HashEncryptionAlgorithm is set to szOID_PKIX_NO_SIGNATURE, then,
                //  the signature value only contains the hash octets. hCryptProv must still
                //  be specified. However, since a private key isn't used the hCryptProv can be
                //  acquired using CRYPT_VERIFYCONTEXT.
                if (!CAPI.CryptAcquireContext(ref safeCryptProvHandle,
                                              null,
                                              null,
                                              CAPI.PROV_RSA_FULL,
                                                CAPI.CRYPT_VERIFYCONTEXT)) {
                        throw new CryptographicException(Marshal.GetLastWin32Error());
                }
 
                cmsSignerEncodeInfo.hCryptProv = safeCryptProvHandle.DangerousGetHandle();
                hProv = safeCryptProvHandle;
 
                // Fake up the SignerId so our server can recognize it
                // dwIdChoice
                cmsSignerEncodeInfo.SignerId.dwIdChoice = CAPI.CERT_ID_ISSUER_SERIAL_NUMBER;
 
                // Issuer
                X500DistinguishedName dummyName = new X500DistinguishedName(CAPI.DummySignerCommonName);
                dummyName.Oid = Oid.FromOidValue(CAPI.szOID_RDN_DUMMY_SIGNER, OidGroup.ExtensionOrAttribute);
                cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.Issuer.cbData = (uint)dummyName.RawData.Length;
                SafeLocalAllocHandle pbDataIssuer = 
                    CAPI.LocalAlloc(CAPI.LPTR, 
                                    new IntPtr(cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.Issuer.cbData));
                Marshal.Copy(dummyName.RawData, 0, pbDataIssuer.DangerousGetHandle(), dummyName.RawData.Length);
                cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.Issuer.pbData = pbDataIssuer.DangerousGetHandle();
                GC.SuppressFinalize(pbDataIssuer);
 
                // SerialNumber
                cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.SerialNumber.cbData = (uint)1;
                SafeLocalAllocHandle pbDataSerialNumber = 
                        CAPI.LocalAlloc(CAPI.LPTR, 
                                        new IntPtr(cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.SerialNumber.cbData));
                byte * pSerialNumber = (byte *)pbDataSerialNumber.DangerousGetHandle();
                *pSerialNumber = 0x00;
                cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.SerialNumber.pbData = 
                    pbDataSerialNumber.DangerousGetHandle();
                GC.SuppressFinalize(pbDataSerialNumber);
 
                return cmsSignerEncodeInfo;
            }
 
            SafeCertContextHandle safeCertContextHandle = X509Utils.GetCertContext(signer.Certificate);
            int hr = GetCertPrivateKey(safeCertContextHandle, out safeCryptProvHandle, out keySpec);
 
            if (hr != CAPI.S_OK)
                throw new CryptographicException(hr);
 
            cmsSignerEncodeInfo.dwKeySpec = keySpec;
            cmsSignerEncodeInfo.hCryptProv = safeCryptProvHandle.DangerousGetHandle();
            hProv = safeCryptProvHandle;
 
            CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle());
            cmsSignerEncodeInfo.pCertInfo  = pCertContext.pCertInfo;
 
            // If CMS, then fill in the Subject Key Identifier (SKI) or SignerId.
            if (signer.SignerIdentifierType == SubjectIdentifierType.SubjectKeyIdentifier) {
                uint cbData = 0;
                SafeLocalAllocHandle pbData = SafeLocalAllocHandle.InvalidHandle;
                if (!CAPI.CAPISafe.CertGetCertificateContextProperty(safeCertContextHandle,
                                                                     CAPI.CERT_KEY_IDENTIFIER_PROP_ID,
                                                                     pbData,
                                                                     ref cbData))
                    throw new CryptographicException(Marshal.GetLastWin32Error());
 
                if (cbData > 0) {
                    pbData = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cbData));
 
                    if (!CAPI.CAPISafe.CertGetCertificateContextProperty(safeCertContextHandle,
                                                                         CAPI.CERT_KEY_IDENTIFIER_PROP_ID,
                                                                         pbData,
                                                                         ref cbData))
                        throw new CryptographicException(Marshal.GetLastWin32Error());
 
                    cmsSignerEncodeInfo.SignerId.dwIdChoice = CAPI.CERT_ID_KEY_IDENTIFIER;
                    cmsSignerEncodeInfo.SignerId.Value.KeyId.cbData = cbData;
                    cmsSignerEncodeInfo.SignerId.Value.KeyId.pbData = pbData.DangerousGetHandle();
                    
                    // Since we are storing only IntPtr in CMSG_SIGNER_ENCODE_INFO.SignerId.KeyId.pbData,
                    // we MUST supress finalizer, otherwise the GC can collect the resource underneath us!!!
                    GC.SuppressFinalize(pbData);
                }
            }
 
            return cmsSignerEncodeInfo;
        }
 
        [SecurityCritical]
        internal static int GetCertPrivateKey(SafeCertContextHandle safeCertContextHandle, out SafeCryptProvHandle safeCryptProvHandle, out uint keySpec) {
            bool freeCsp = false;
 
            IntPtr hKey;
            uint cbSize = (uint)IntPtr.Size;
            safeCryptProvHandle = null;
 
            if (CAPI.CAPISafe.CertGetCertificateContextProperty(
                safeCertContextHandle,
                CAPI.CERT_NCRYPT_KEY_HANDLE_PROP_ID,
                out hKey,
                ref cbSize))
            {
                keySpec = 0;
                safeCryptProvHandle = new SafeCryptProvHandle(hKey, safeCertContextHandle);
                return CAPI.S_OK;
            }
 
            // Check to see if KEY_PROV_INFO contains "MS Base ..."
            // If so, acquire "MS Enhanced..." or "MS Strong".
            // if failed, then use CryptAcquireCertificatePrivateKey
            CspParameters parameters = new CspParameters();
            if (X509Utils.GetPrivateKeyInfo(safeCertContextHandle, ref parameters) == false)
                throw new CryptographicException(Marshal.GetLastWin32Error());
 
            if (String.Compare(parameters.ProviderName, CAPI.MS_DEF_PROV, StringComparison.OrdinalIgnoreCase) == 0)
            {
                SafeCryptProvHandle provHandle = SafeCryptProvHandle.InvalidHandle;
 
                if (CAPI.CryptAcquireContext(ref provHandle, parameters.KeyContainerName, CAPI.MS_ENHANCED_PROV, CAPI.PROV_RSA_FULL, 0) ||
                    CAPI.CryptAcquireContext(ref provHandle, parameters.KeyContainerName, CAPI.MS_STRONG_PROV, CAPI.PROV_RSA_FULL, 0))
                {
                    safeCryptProvHandle = provHandle;
                }
            }
 
            keySpec = (uint)parameters.KeyNumber;
            int hr = CAPI.S_OK;
 
            uint flags = CAPI.CRYPT_ACQUIRE_COMPARE_KEY_FLAG | CAPI.CRYPT_ACQUIRE_USE_PROV_INFO_FLAG;
            if (parameters.ProviderType == 0)
            {
                //
                // The ProviderType being 0 indicates that this cert is using a CNG key. Set the flag to tell CryptAcquireCertificatePrivateKey that it's okay to give
                // us a CNG key.
                //
                // (This should be equivalent to passing CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG. But fixing it this way restricts the code path changes
                // within Crypt32 to the cases that were already non-functional in 4.6.1. Thus, it is a "safer" way to fix it for a point release.)
                //
                flags |= CAPI.CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG;
            }
 
            if ((safeCryptProvHandle == null) || (safeCryptProvHandle.IsInvalid))
            {
                hKey = IntPtr.Zero;
 
                if (CAPI.CAPISafe.CryptAcquireCertificatePrivateKey(safeCertContextHandle,
                                                                    flags,
                                                                    IntPtr.Zero,
                                                                    ref hKey,
                                                                    ref keySpec,
                                                                    ref freeCsp))
                {
                    safeCryptProvHandle = new SafeCryptProvHandle(hKey, freeCsp);
                }
                else
                {
                    hr = Marshal.GetHRForLastWin32Error();
                }
            }
 
            return hr;
        }
 
        [SecuritySafeCritical]
        internal static X509Certificate2Collection CreateBagOfCertificates (CmsSigner signer) {
            X509Certificate2Collection certificates = new X509Certificate2Collection();
 
            //
            // First add extra bag of certs.
            //
 
            certificates.AddRange(signer.Certificates);
 
            //
            // Then include chain option.
            //
 
            if (signer.IncludeOption != X509IncludeOption.None) {
                if (signer.IncludeOption == X509IncludeOption.EndCertOnly) {
                    certificates.Add(signer.Certificate);
                }
                else {
                    int cCerts = 1;
                    X509Chain chain = new X509Chain();
                    chain.Build(signer.Certificate);
 
                    // Can't honor the option if we only have a partial chain.
                    if ((chain.ChainStatus.Length > 0) && 
                        ((chain.ChainStatus[0].Status & X509ChainStatusFlags.PartialChain) == X509ChainStatusFlags.PartialChain))
                        throw new CryptographicException(CAPI.CERT_E_CHAINING);
 
                    if (signer.IncludeOption == X509IncludeOption.WholeChain) {
                        cCerts = chain.ChainElements.Count;
                    }
                    else {
                        // Default to ExcludeRoot.
                        if (chain.ChainElements.Count > 1) {
                            cCerts = chain.ChainElements.Count - 1;
                        }
                    }
 
                    for (int i = 0; i < cCerts; i++) {
                        certificates.Add(chain.ChainElements[i].Certificate);
                    }
                }
            }
 
            return certificates;
        }
 
        [SecurityCritical]
        internal static unsafe SafeLocalAllocHandle CreateEncodedCertBlob (X509Certificate2Collection certificates) {
            SafeLocalAllocHandle certBlob = SafeLocalAllocHandle.InvalidHandle;
 
            certificates = new X509Certificate2Collection(certificates);
 
            if (certificates.Count > 0) {
                checked {
                    certBlob = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(certificates.Count * Marshal.SizeOf(typeof(CAPI.CRYPTOAPI_BLOB))));
                    CAPI.CRYPTOAPI_BLOB * pCertBlob = (CAPI.CRYPTOAPI_BLOB * ) certBlob.DangerousGetHandle();
 
                    foreach (X509Certificate2 certificate in certificates) {
                        SafeCertContextHandle safeCertContextHandle = X509Utils.GetCertContext(certificate);
                        CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle());
 
                        pCertBlob->cbData = pCertContext.cbCertEncoded;
                        pCertBlob->pbData = pCertContext.pbCertEncoded;
                        pCertBlob++;
                    }
                }
            }
 
            return certBlob;
        }
 
        [SecuritySafeCritical]
        internal static unsafe uint AddCertsToMessage (SafeCryptMsgHandle safeCryptMsgHandle, X509Certificate2Collection bagOfCerts, X509Certificate2Collection chainOfCerts) {
            uint certsAdded = 0;
 
            foreach (X509Certificate2 certificate in chainOfCerts) {
                // Skip it if already in the bag of certs.
                X509Certificate2Collection foundCerts = bagOfCerts.Find(X509FindType.FindByThumbprint, certificate.Thumbprint, false);
                if (foundCerts.Count == 0) {
                    SafeCertContextHandle safeCertContextHandle = X509Utils.GetCertContext(certificate);
                    CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle());
 
                    CAPI.CRYPTOAPI_BLOB certBlob = new CAPI.CRYPTOAPI_BLOB();
                    certBlob.cbData = pCertContext.cbCertEncoded;
                    certBlob.pbData = pCertContext.pbCertEncoded;
 
                    if (!CAPI.CryptMsgControl(safeCryptMsgHandle,
                                              0,
                                              CAPI.CMSG_CTRL_ADD_CERT,
                                              new IntPtr((long) &certBlob)))
                        throw new CryptographicException(Marshal.GetLastWin32Error());
                    certsAdded++;
                }
            }
 
            return certsAdded;
        }
 
        internal static X509Certificate2 FindCertificate (SubjectIdentifier identifier, X509Certificate2Collection certificates) {
            X509Certificate2 certificate = null;
 
            if (certificates != null && certificates.Count > 0) {
                X509Certificate2Collection filters;
                switch (identifier.Type) {
                case SubjectIdentifierType.IssuerAndSerialNumber:
                    filters = certificates.Find(X509FindType.FindByIssuerDistinguishedName, ((X509IssuerSerial) identifier.Value).IssuerName, false);
                    if (filters.Count > 0) {
                        filters = filters.Find(X509FindType.FindBySerialNumber, ((X509IssuerSerial) identifier.Value).SerialNumber, false);
                        if (filters.Count > 0)
                            certificate = filters[0];
                    }
                    break;
                case SubjectIdentifierType.SubjectKeyIdentifier:
                    filters = certificates.Find(X509FindType.FindBySubjectKeyIdentifier, identifier.Value, false);
                    if (filters.Count > 0)
                        certificate = filters[0];
 
                    break;
                }
            }
 
            return certificate;
        }
 
        private static void checkErr (int err) {
            if (CAPI.CRYPT_E_INVALID_MSG_TYPE != err)
                throw new CryptographicException(err);
        }
 
        // for Key ID signing only
        [SecuritySafeCritical]
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        internal static unsafe X509Certificate2 CreateDummyCertificate (CspParameters parameters) {
            SafeCertContextHandle handle = SafeCertContextHandle.InvalidHandle;
 
            // hProv
            SafeCryptProvHandle hProv = SafeCryptProvHandle.InvalidHandle;
            UInt32 dwFlags = 0;
            if (0 != (parameters.Flags & CspProviderFlags.UseMachineKeyStore))
            {
                dwFlags |= CAPI.CRYPT_MACHINE_KEYSET;
            }
            if (0 != (parameters.Flags & CspProviderFlags.UseDefaultKeyContainer))
            {
                dwFlags |= CAPI.CRYPT_VERIFYCONTEXT;
            }
            if (0 != (parameters.Flags & CspProviderFlags.NoPrompt))
            {
                dwFlags |= CAPI.CRYPT_SILENT;
            }
            bool rc = CAPI.CryptAcquireContext(ref hProv,
                                               parameters.KeyContainerName,
                                               parameters.ProviderName,
                                               (uint)parameters.ProviderType,
                                               dwFlags);
            if (!rc)
                throw new CryptographicException(Marshal.GetLastWin32Error());
 
            // pKeyProvInfo
            CAPI.CRYPT_KEY_PROV_INFO KeyProvInfo = new CAPI.CRYPT_KEY_PROV_INFO();
            KeyProvInfo.pwszProvName       = parameters.ProviderName;
            KeyProvInfo.pwszContainerName  = parameters.KeyContainerName;
            KeyProvInfo.dwProvType         = (uint)parameters.ProviderType;
            KeyProvInfo.dwKeySpec          = (uint)parameters.KeyNumber ;
            KeyProvInfo.dwFlags            = (uint)((parameters.Flags & CspProviderFlags.UseMachineKeyStore) == CspProviderFlags.UseMachineKeyStore ? CAPI.CRYPT_MACHINE_KEYSET : 0);
 
            SafeLocalAllocHandle pKeyProvInfo = CAPI.LocalAlloc(CAPI.LPTR, 
                                                                new IntPtr(Marshal.SizeOf(typeof(CAPI.CRYPT_KEY_PROV_INFO))));
            Marshal.StructureToPtr(KeyProvInfo, pKeyProvInfo.DangerousGetHandle(), false);
 
            // Signature
            CAPI.CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm = new CAPI.CRYPT_ALGORITHM_IDENTIFIER();
            SignatureAlgorithm.pszObjId = CAPI.szOID_OIWSEC_sha1RSASign;
 
            SafeLocalAllocHandle pSignatureAlgorithm = CAPI.LocalAlloc(CAPI.LPTR, 
                                                                new IntPtr( Marshal.SizeOf(typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER))));
            Marshal.StructureToPtr(SignatureAlgorithm, pSignatureAlgorithm.DangerousGetHandle(), false);
 
            // pSubjectIssuerBlob
            X500DistinguishedName subjectName = new X500DistinguishedName("cn=CMS Signer Dummy Certificate");
            fixed (byte * pbOctets = subjectName.RawData) {
                CAPI.CRYPTOAPI_BLOB SubjectIssuerBlob = new CAPI.CRYPTOAPI_BLOB();
                SubjectIssuerBlob.cbData = (uint)subjectName.RawData.Length;
                SubjectIssuerBlob.pbData = new IntPtr(pbOctets);
 
                handle = CAPI.CAPIUnsafe.CertCreateSelfSignCertificate(hProv,
                                                                       new IntPtr(&SubjectIssuerBlob),
                                                                       1,
                                                                       pKeyProvInfo.DangerousGetHandle(),
                                                                       pSignatureAlgorithm.DangerousGetHandle(),
                                                                       IntPtr.Zero,  //StartTime
                                                                       IntPtr.Zero,  //EndTime
                                                                       IntPtr.Zero); //Extensions
            }
 
            Marshal.DestroyStructure(pKeyProvInfo.DangerousGetHandle(), typeof(CAPI.CRYPT_KEY_PROV_INFO));
            pKeyProvInfo.Dispose();
            Marshal.DestroyStructure(pSignatureAlgorithm.DangerousGetHandle(), typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER));
            pSignatureAlgorithm.Dispose();
 
            if (handle == null || handle.IsInvalid)
                throw new CryptographicException(Marshal.GetLastWin32Error());
 
            X509Certificate2 certificate = new X509Certificate2(handle.DangerousGetHandle());
            handle.Dispose();
            return certificate;
        }
    }
}