File: system\security\cryptography\pkcs\signerinfo.cs
Project: ndp\clr\src\managedlibraries\security\System.Security.csproj (System.Security)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
 
//
// SignerInfo.cs
// 
 
namespace System.Security.Cryptography.Pkcs {
    using System.Collections;
    using System.Diagnostics;
    using System.Globalization;
    using System.Runtime.InteropServices;
    using System.Runtime.Versioning;
    using System.Security.Permissions;
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    using System.Security.Cryptography.Xml;
 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public sealed class SignerInfo {
        private X509Certificate2                m_certificate;
        private SubjectIdentifier                m_signerIdentifier;
        private CryptographicAttributeObjectCollection m_signedAttributes;
        private CryptographicAttributeObjectCollection m_unsignedAttributes;
 
        private SignedCms                   m_signedCms;
        private SignerInfo                  m_parentSignerInfo;
        private byte[]                      m_encodedSignerInfo;
        [SecurityCritical]
        private SafeLocalAllocHandle        m_pbCmsgSignerInfo;
        private CAPI.CMSG_SIGNER_INFO       m_cmsgSignerInfo;
 
        //
        // Constructors.
        //
 
        private SignerInfo () {}
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        [SecurityCritical]
        internal SignerInfo (SignedCms signedCms, SafeLocalAllocHandle pbCmsgSignerInfo) {
            // Sanity check.
            Debug.Assert(signedCms != null && pbCmsgSignerInfo != null && !pbCmsgSignerInfo.IsInvalid);
 
            m_signedCms = signedCms;
            m_parentSignerInfo = null;
            m_encodedSignerInfo = null;
            m_pbCmsgSignerInfo = pbCmsgSignerInfo;
            m_cmsgSignerInfo = (CAPI.CMSG_SIGNER_INFO) Marshal.PtrToStructure(pbCmsgSignerInfo.DangerousGetHandle(), typeof(CAPI.CMSG_SIGNER_INFO));
        }
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        [SecuritySafeCritical]
        internal unsafe SignerInfo (SignedCms signedCms, SignerInfo parentSignerInfo, byte[] encodedSignerInfo) {
            // Sanity check.
            Debug.Assert(signedCms != null && encodedSignerInfo != null && encodedSignerInfo.Length > 0);
 
            uint cbCmsgSignerInfo = 0;
            SafeLocalAllocHandle pbCmsgSignerInfo = SafeLocalAllocHandle.InvalidHandle;
 
            fixed (byte * pEncodedSignerInfo = &encodedSignerInfo[0]) {
                if (!CAPI.DecodeObject(new IntPtr(CAPI.PKCS7_SIGNER_INFO),
                                       new IntPtr(pEncodedSignerInfo),
                                       (uint) encodedSignerInfo.Length,
                                       out pbCmsgSignerInfo,
                                       out cbCmsgSignerInfo))
                    throw new CryptographicException(Marshal.GetLastWin32Error());
            }
 
            m_signedCms = signedCms;
            m_parentSignerInfo = parentSignerInfo;
            m_encodedSignerInfo = (byte[]) encodedSignerInfo.Clone();
            m_pbCmsgSignerInfo = pbCmsgSignerInfo;
            m_cmsgSignerInfo = (CAPI.CMSG_SIGNER_INFO) Marshal.PtrToStructure(pbCmsgSignerInfo.DangerousGetHandle(), typeof(CAPI.CMSG_SIGNER_INFO));
        }
 
        //
        // Public APIs.
        //
 
        public int Version {
            get {
                return (int) m_cmsgSignerInfo.dwVersion;
            }
        }
 
 
        public X509Certificate2 Certificate {
            get {
                if (m_certificate == null)
                    m_certificate = PkcsUtils.FindCertificate(this.SignerIdentifier, m_signedCms.Certificates);
 
                return m_certificate;
            }
        }
 
        public SubjectIdentifier SignerIdentifier {
            [SecuritySafeCritical]
            get {
                if (m_signerIdentifier == null)
                    m_signerIdentifier = new SubjectIdentifier(m_cmsgSignerInfo);
 
                return m_signerIdentifier;
            }
        }
 
        public Oid DigestAlgorithm {
            get {
                return new Oid(m_cmsgSignerInfo.HashAlgorithm.pszObjId);
            }
        }
 
        public CryptographicAttributeObjectCollection SignedAttributes {
            [SecuritySafeCritical]
            get {
                if (m_signedAttributes == null)
                    m_signedAttributes = new CryptographicAttributeObjectCollection(m_cmsgSignerInfo.AuthAttrs);
 
                return m_signedAttributes;
            }
        }
 
        public CryptographicAttributeObjectCollection UnsignedAttributes {
            [SecuritySafeCritical]
            get {
                if (m_unsignedAttributes == null)
                    m_unsignedAttributes = new CryptographicAttributeObjectCollection(m_cmsgSignerInfo.UnauthAttrs);
 
                return m_unsignedAttributes;
            }
        }
 
        public SignerInfoCollection CounterSignerInfos {
            get {
                // We only support one level of counter signing.
                if (m_parentSignerInfo != null)
                    return new SignerInfoCollection();
 
                return new SignerInfoCollection(m_signedCms, this);
            }
        }
 
        public void ComputeCounterSignature () {
            ComputeCounterSignature(new CmsSigner(m_signedCms.Version == 2 ? SubjectIdentifierType.SubjectKeyIdentifier : SubjectIdentifierType.IssuerAndSerialNumber));
        }
 
        public void ComputeCounterSignature (CmsSigner signer) {
            // We only support one level of counter signing.
            if (m_parentSignerInfo != null)
                throw new CryptographicException(CAPI.E_NOTIMPL);
            if (signer == null)
                throw new ArgumentNullException("signer");
 
            if (signer.Certificate == null)
                signer.Certificate = PkcsUtils.SelectSignerCertificate();
 
            if (!signer.Certificate.HasPrivateKey)
                throw new CryptographicException(CAPI.NTE_NO_KEY);
 
            CounterSign(signer);
        }
 
        [SecuritySafeCritical]
        public void RemoveCounterSignature (int index) {
            // We only support one level of counter signing.
            if (m_parentSignerInfo != null)
                throw new CryptographicException(CAPI.E_NOTIMPL);
 
            RemoveCounterSignature(PkcsUtils.GetSignerIndex(m_signedCms.GetCryptMsgHandle(), this, 0), index);
 
            return;
        }
 
        [SecuritySafeCritical]
        public void RemoveCounterSignature (SignerInfo counterSignerInfo) {
            // We only support one level of counter signing.
            if (m_parentSignerInfo != null)
                throw new CryptographicException(CAPI.E_NOTIMPL);
            if (counterSignerInfo == null)
                throw new ArgumentNullException("counterSignerInfo");
 
            foreach (CryptographicAttributeObject attribute in UnsignedAttributes) {
                if (String.Compare(attribute.Oid.Value, CAPI.szOID_RSA_counterSign, StringComparison.OrdinalIgnoreCase) == 0) {
                    for (int index = 0; index < attribute.Values.Count; index++) {
                        AsnEncodedData encodedCounterSignature = (AsnEncodedData) attribute.Values[index];
                        SignerInfo counterSignerInfo2 = new SignerInfo(m_signedCms, m_parentSignerInfo, encodedCounterSignature.RawData);
 
                        if ((counterSignerInfo.SignerIdentifier.Type == SubjectIdentifierType.IssuerAndSerialNumber) &&
                            (counterSignerInfo2.SignerIdentifier.Type == SubjectIdentifierType.IssuerAndSerialNumber)) {
                            X509IssuerSerial issuerSerial1 = (X509IssuerSerial) counterSignerInfo.SignerIdentifier.Value;
                            X509IssuerSerial issuerSerial2 = (X509IssuerSerial) counterSignerInfo2.SignerIdentifier.Value;
 
                            if ((String.Compare(issuerSerial1.IssuerName, issuerSerial2.IssuerName, StringComparison.OrdinalIgnoreCase) == 0) &&
                                (String.Compare(issuerSerial1.SerialNumber, issuerSerial2.SerialNumber, StringComparison.OrdinalIgnoreCase) == 0)) {
                                RemoveCounterSignature(PkcsUtils.GetSignerIndex(m_signedCms.GetCryptMsgHandle(), this, 0), index);
                                return;
                            }
                        }
                        else if ((counterSignerInfo.SignerIdentifier.Type == SubjectIdentifierType.SubjectKeyIdentifier) &&
                                 (counterSignerInfo2.SignerIdentifier.Type == SubjectIdentifierType.SubjectKeyIdentifier)) {
                            string keyIdentifier1 = counterSignerInfo.SignerIdentifier.Value as string;
                            string keyIdentifier2 = counterSignerInfo2.SignerIdentifier.Value as string;
 
                            if (String.Compare(keyIdentifier1, keyIdentifier2, StringComparison.OrdinalIgnoreCase) == 0) {
                                RemoveCounterSignature(PkcsUtils.GetSignerIndex(m_signedCms.GetCryptMsgHandle(), this, 0), index);
                                return;
                            }
                        }
                    }
                }
            }
 
            throw new CryptographicException(CAPI.CRYPT_E_SIGNER_NOT_FOUND);
        }
 
        public void CheckSignature (bool verifySignatureOnly) {
            CheckSignature(new X509Certificate2Collection(), verifySignatureOnly);
        }
 
        public void CheckSignature (X509Certificate2Collection extraStore, bool verifySignatureOnly) {
            if (extraStore == null)
                throw new ArgumentNullException("extraStore");
 
            X509Certificate2 certificate = this.Certificate;
            if (certificate == null) {
                certificate = PkcsUtils.FindCertificate(SignerIdentifier, extraStore);
                if (certificate == null)
                    throw new CryptographicException(CAPI.CRYPT_E_SIGNER_NOT_FOUND);
            }
 
            Verify(extraStore, certificate, verifySignatureOnly);
        }
 
        [SecuritySafeCritical]
        public void CheckHash() {
 
            int cvseSize = Marshal.SizeOf(typeof(CAPI.CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA));
            CAPI.CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA cvse = new CAPI.CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA(cvseSize);
            cvse.dwSignerType  = CAPI.CMSG_VERIFY_SIGNER_NULL;
            cvse.dwSignerIndex = (uint) PkcsUtils.GetSignerIndex(m_signedCms.GetCryptMsgHandle(), this, 0);
 
            unsafe {
                if (!CAPI.CryptMsgControl(m_signedCms.GetCryptMsgHandle(),
                                            0,
                                            CAPI.CMSG_CTRL_VERIFY_SIGNATURE_EX,
                                            new IntPtr(&cvse)))
                    throw new CryptographicException(Marshal.GetLastWin32Error());
            }
        }
 
        [SecuritySafeCritical]
        public byte[] GetSignature()
        {
            byte[] ret = new byte[m_cmsgSignerInfo.EncryptedHash.cbData];
            Marshal.Copy(m_cmsgSignerInfo.EncryptedHash.pbData, ret, 0, ret.Length);
            return ret;
        }
 
        public Oid SignatureAlgorithm {
            get {
                return new Oid(m_cmsgSignerInfo.HashEncryptionAlgorithm.pszObjId);
            }
        }
 
        //
        // Internal methods.
        //
 
        internal CAPI.CMSG_SIGNER_INFO GetCmsgSignerInfo () {
            return m_cmsgSignerInfo;
        }
 
        //
        // Private methods.
        //
 
        [SecuritySafeCritical]
        private unsafe void CounterSign (CmsSigner signer) {
            // Sanity check.
            Debug.Assert(signer != null);
 
            // 
 
 
            CspParameters parameters = new CspParameters();
            if (X509Utils.GetPrivateKeyInfo(X509Utils.GetCertContext(signer.Certificate), ref parameters) == false)
                throw new CryptographicException(Marshal.GetLastWin32Error());
 
            KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
            KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(parameters, KeyContainerPermissionFlags.Open | KeyContainerPermissionFlags.Sign);
            kp.AccessEntries.Add(entry);
            kp.Demand();
 
            // Get the signer's index.
            uint index = (uint) PkcsUtils.GetSignerIndex(m_signedCms.GetCryptMsgHandle(), this, 0);
 
            // Create CMSG_SIGNER_ENCODE_INFO structure.
            SafeLocalAllocHandle pSignerEncodeInfo = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(Marshal.SizeOf(typeof(CAPI.CMSG_SIGNER_ENCODE_INFO))));
            SafeCryptProvHandle safeCryptProvHandle;
            CAPI.CMSG_SIGNER_ENCODE_INFO signerEncodeInfo = PkcsUtils.CreateSignerEncodeInfo(signer, out safeCryptProvHandle);
 
            try {
                // Marshal to unmanaged memory.
                Marshal.StructureToPtr(signerEncodeInfo, pSignerEncodeInfo.DangerousGetHandle(), false);
 
                // Counter sign.
                if (!CAPI.CryptMsgCountersign(m_signedCms.GetCryptMsgHandle(),
                                              index,
                                              1,
                                              pSignerEncodeInfo.DangerousGetHandle()))
                    throw new CryptographicException(Marshal.GetLastWin32Error());
 
                // CAPI requires that the messge be re-encoded if any unauthenticated
                // attribute has been added. So, let's re-open it to decode to work
                // around this limitation.
                m_signedCms.ReopenToDecode();
            }
            finally {
                Marshal.DestroyStructure(pSignerEncodeInfo.DangerousGetHandle(), typeof(CAPI.CMSG_SIGNER_ENCODE_INFO));
                pSignerEncodeInfo.Dispose();
 
                // and don't forget to dispose of resources allocated for the structure.
                signerEncodeInfo.Dispose();
                safeCryptProvHandle.Dispose();
            }
 
            // Finally, add certs to bag of certs.
            PkcsUtils.AddCertsToMessage(m_signedCms.GetCryptMsgHandle(), m_signedCms.Certificates, PkcsUtils.CreateBagOfCertificates(signer));
 
            return;
        }
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        [SecuritySafeCritical]
        private unsafe void Verify (X509Certificate2Collection extraStore, X509Certificate2 certificate, bool verifySignatureOnly) {
            checked {
                // We need to find out if DSS parameters inheritance is necessary. If so, we need to 
                // first build the chain to cause CAPI to inherit and set the parameters in the 
                // CERT_PUBKEY_ALG_PARA_PROP_ID extended property. Once we have the parameters in
                // the property, we then need to retrieve a copy and point to it in the CERT_INFO
                // structure.
                SafeLocalAllocHandle pbParameters = SafeLocalAllocHandle.InvalidHandle;
                CAPI.CERT_CONTEXT pCertContext = (CAPI.CERT_CONTEXT) Marshal.PtrToStructure(X509Utils.GetCertContext(certificate).DangerousGetHandle(), typeof(CAPI.CERT_CONTEXT));
 
                // Point to SubjectPublicKeyInfo field inside the CERT_INFO structure.
                IntPtr pSubjectPublicKeyInfo = new IntPtr((long) pCertContext.pCertInfo + (long) Marshal.OffsetOf(typeof(CAPI.CERT_INFO), "SubjectPublicKeyInfo"));
 
                // Point to Algorithm field inside the SubjectPublicKeyInfo field.
                IntPtr pAlgorithm = new IntPtr((long) pSubjectPublicKeyInfo + (long) Marshal.OffsetOf(typeof(CAPI.CERT_PUBLIC_KEY_INFO), "Algorithm"));
 
                // Point to Parameters field inside the Algorithm field.
                IntPtr pParameters = new IntPtr((long) pAlgorithm + (long) Marshal.OffsetOf(typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER), "Parameters"));
            
                // Retrieve the pszObjId pointer.
                IntPtr pObjId = Marshal.ReadIntPtr(pAlgorithm);
 
                // Translate the OID to AlgId value.
                CAPI.CRYPT_OID_INFO pOIDInfo = CAPI.CryptFindOIDInfo(CAPI.CRYPT_OID_INFO_OID_KEY, pObjId, CAPI.CRYPT_PUBKEY_ALG_OID_GROUP_ID);
 
                // Is this DSS?
                if (pOIDInfo.Algid == CAPI.CALG_DSS_SIGN) {
                    bool inheritParameters = false;
 
                    // This is DSS, so inherit the parameters if necessary.
                    IntPtr pcbData = new IntPtr((long) pParameters + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "cbData"));
                    IntPtr ppbData = new IntPtr((long) pParameters + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "pbData"));
                    if (Marshal.ReadInt32(pcbData) == 0) {
                        inheritParameters = true;
                    }
                    else {
                        // Need to inherit if NULL pbData or *pbData is 0x05 (NULL ASN tag).
                        if (Marshal.ReadIntPtr(ppbData) == IntPtr.Zero) {
                            inheritParameters = true;
                        }
                        else {
                            IntPtr pbData = Marshal.ReadIntPtr(ppbData);
                            if ((uint) Marshal.ReadInt32(pbData) == CAPI.ASN_TAG_NULL) {
                                inheritParameters = true;
                            }
                        }
                    }
 
                    // Do we need to copy inherited DSS parameters?
                    if (inheritParameters) {
                        // Build the chain to force CAPI to propagate the parameters to 
                        // CERT_PUBKEY_ALG_PARA_PROP_ID extended property.
                        SafeCertChainHandle pChainContext = SafeCertChainHandle.InvalidHandle;
                        X509Utils.BuildChain(new IntPtr(CAPI.HCCE_CURRENT_USER),
                                             X509Utils.GetCertContext(certificate),
                                             null,
                                             null,
                                             null,
                                             X509RevocationMode.NoCheck,
                                             X509RevocationFlag.ExcludeRoot,
                                             DateTime.Now,
                                             new TimeSpan(0, 0, 0), // default
                                             ref pChainContext);
                        pChainContext.Dispose();
 
                        // The parameter is inherited in the extended property, but not copied
                        // to CERT_INFO, so we need to do it ourselves.
                        uint cbParameters = 0;
 
                        if (!CAPI.CAPISafe.CertGetCertificateContextProperty(X509Utils.GetCertContext(certificate),
                                                                             CAPI.CERT_PUBKEY_ALG_PARA_PROP_ID,
                                                                             pbParameters,
                                                                             ref cbParameters))
                            throw new CryptographicException(Marshal.GetLastWin32Error());
 
                        if (cbParameters > 0) {
                            pbParameters = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cbParameters));
                            if (!CAPI.CAPISafe.CertGetCertificateContextProperty(X509Utils.GetCertContext(certificate),
                                                                                 CAPI.CERT_PUBKEY_ALG_PARA_PROP_ID,
                                                                                 pbParameters,
                                                                                 ref cbParameters))
                                throw new CryptographicException(Marshal.GetLastWin32Error());
 
                            Marshal.WriteInt32(pcbData, (int)cbParameters);
                            Marshal.WriteIntPtr(ppbData, pbParameters.DangerousGetHandle());
                        }
                    }
                }
 
                // Is this counter signer?
                if (m_parentSignerInfo == null) {
                    // Just plain signer.
                    if (!CAPI.CryptMsgControl(m_signedCms.GetCryptMsgHandle(),
                                              0,
                                              CAPI.CMSG_CTRL_VERIFY_SIGNATURE,
                                              pCertContext.pCertInfo)) { 
                        throw new CryptographicException(Marshal.GetLastWin32Error());
                    }
                }
                else {
                    // Counter signer, so need to first find parent signer's index.
                    int index = -1;
                    int lastWin32Error = 0;
 
                    // Since we allow the same signer to sign more than once,
                    // we must than try all signatures of the same signer.
                    while (true) {
                        try {
                            // Find index of parent signer.
                            index = PkcsUtils.GetSignerIndex(m_signedCms.GetCryptMsgHandle(), m_parentSignerInfo, index + 1);
                        }
                        catch (CryptographicException) {
                            // Did we ever find a signature of the same signer?
                            if (lastWin32Error == 0) {
                                // No. So we just re-throw, which is most likely CAPI.CRYPT_E_SIGNER_NOT_FOUND.
                                throw;
                            }
                            else {
                                // Yes. Throw previous error, which is most likely CAPI.NTE_BAD_SIGNATURE.
                                throw new CryptographicException(lastWin32Error);
                            }
                        }
 
                        // Now get the parent encoded singer info.
                        uint cbParentEncodedSignerInfo = 0;
                        SafeLocalAllocHandle pbParentEncodedSignerInfo = SafeLocalAllocHandle.InvalidHandle;
 
                        PkcsUtils.GetParam(m_signedCms.GetCryptMsgHandle(),
                                           CAPI.CMSG_ENCODED_SIGNER,
                                           (uint) index,
                                           out pbParentEncodedSignerInfo,
                                           out cbParentEncodedSignerInfo);
 
                        // Try next signer if we can't get parent of this signer.
                        if (cbParentEncodedSignerInfo == 0) {
                            lastWin32Error = CAPI.CRYPT_E_NO_SIGNER;
                            continue;
                        }
 
                        fixed (byte * pbEncodedSignerInfo = m_encodedSignerInfo) {
                            if (!CAPI.CAPISafe.CryptMsgVerifyCountersignatureEncoded(IntPtr.Zero,
                                                                                     CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING,
                                                                                     pbParentEncodedSignerInfo.DangerousGetHandle(),
                                                                                     cbParentEncodedSignerInfo,
                                                                                     new IntPtr(pbEncodedSignerInfo),
                                                                                     (uint)m_encodedSignerInfo.Length,
                                                                                     pCertContext.pCertInfo)) {
                                // Cache the error, and try next signer.
                                lastWin32Error = Marshal.GetLastWin32Error();
                                continue;
                            }
                        }
 
                        // Keep alive.
                        pbParentEncodedSignerInfo.Dispose();
 
                        // The signature is successfully verified.
                        break;
                    }
                }
 
                // Verfiy the cert if requested.
                if (!verifySignatureOnly) {
                    int hr = VerifyCertificate(certificate, extraStore);
                    if (hr != CAPI.S_OK)
                        throw new CryptographicException(hr);
                }
 
                // Keep alive.
                pbParameters.Dispose();
            }
        }
 
        [SecuritySafeCritical]
        private unsafe void RemoveCounterSignature (int parentIndex, int childIndex) {
            // Just make sure this is non-negative.
            if (parentIndex < 0)
                throw new ArgumentOutOfRangeException("parentIndex");
            if (childIndex < 0) 
                throw new ArgumentOutOfRangeException("childIndex");
 
            uint cbCmsgCmsSignerInfo = 0;
            SafeLocalAllocHandle pbCmsgCmsSignerInfo = SafeLocalAllocHandle.InvalidHandle;
 
            uint cbCmsgSignerInfo = 0;
            SafeLocalAllocHandle pbCmsgSignerInfo = SafeLocalAllocHandle.InvalidHandle;
 
            uint index = 0;
            uint cAttr = 0;
            IntPtr pAttr = IntPtr.Zero;
            SafeCryptMsgHandle hMsg = m_signedCms.GetCryptMsgHandle();
 
            if (PkcsUtils.CmsSupported()) {
                PkcsUtils.GetParam(hMsg, 
                                   CAPI.CMSG_CMS_SIGNER_INFO_PARAM, 
                                   (uint) parentIndex, 
                                   out pbCmsgCmsSignerInfo, 
                                   out cbCmsgCmsSignerInfo);
 
                CAPI.CMSG_CMS_SIGNER_INFO cmsgCmsSignerInfo = (CAPI.CMSG_CMS_SIGNER_INFO) Marshal.PtrToStructure(pbCmsgCmsSignerInfo.DangerousGetHandle(), typeof(CAPI.CMSG_CMS_SIGNER_INFO));
                cAttr = cmsgCmsSignerInfo.UnauthAttrs.cAttr;
                pAttr = new IntPtr((long) cmsgCmsSignerInfo.UnauthAttrs.rgAttr);
            }
            else {
                PkcsUtils.GetParam(hMsg,
                                   CAPI.CMSG_SIGNER_INFO_PARAM,
                                   (uint) parentIndex, 
                                   out pbCmsgSignerInfo, 
                                   out cbCmsgSignerInfo);
 
                CAPI.CMSG_SIGNER_INFO cmsgSignerInfo = (CAPI.CMSG_SIGNER_INFO) Marshal.PtrToStructure(pbCmsgSignerInfo.DangerousGetHandle(), typeof(CAPI.CMSG_SIGNER_INFO));
                cAttr = cmsgSignerInfo.UnauthAttrs.cAttr;
                pAttr = new IntPtr((long) cmsgSignerInfo.UnauthAttrs.rgAttr);
            }
 
            // Find index for counter signature attribute.
            // Note: It is not guaranteed that CAPI will keep all counter signatures
            // in one single unauthenticated attribute. So we need to find the correct
            // unauthenticated attribute containing this counter signer which is 
            // identified by index.
            for (index = 0; index < cAttr; index++) {
                checked {
                    CAPI.CRYPT_ATTRIBUTE attr = (CAPI.CRYPT_ATTRIBUTE) Marshal.PtrToStructure(pAttr, typeof(CAPI.CRYPT_ATTRIBUTE));
                    if (String.Compare(attr.pszObjId, CAPI.szOID_RSA_counterSign, StringComparison.OrdinalIgnoreCase) == 0) {
                        if (attr.cValue > 0) {
                            // Is it in this attribute?
                            if (childIndex < (int) attr.cValue) {
                                // Found the desired counter signature attribute. So, first remove the
                                // entire attribute, then remove just the counter signature from the
                                // retrieved attribute, and finally add back the modified attribute,
                                // if necessary.
                                CAPI.CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA delPara = new CAPI.CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA(Marshal.SizeOf(typeof(CAPI.CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA)));
                                delPara.dwSignerIndex = (uint) parentIndex;
                                delPara.dwUnauthAttrIndex = index;
 
                                if (!CAPI.CryptMsgControl(hMsg,
                                                          0,
                                                          CAPI.CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR,
                                                          new IntPtr(&delPara)))
                                    throw new CryptographicException(Marshal.GetLastWin32Error());
 
                                // No need to add back if only one counter signature in this attribute.
                                if (attr.cValue > 1) {
                                    try {
                                        // There were more than one counter signatures in this attribute, so 
                                        // need to add back a new counter signature attribute which includes
                                        // the remaining counter signatures.
                                        uint cbCounterSignatureValue = (uint) ((attr.cValue - 1) * Marshal.SizeOf(typeof(CAPI.CRYPTOAPI_BLOB)));
                                        SafeLocalAllocHandle pbCounterSignatureValue = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cbCounterSignatureValue));
 
                                        // Copy everything except the one being removed.
                                        CAPI.CRYPTOAPI_BLOB * pOldValue =  (CAPI.CRYPTOAPI_BLOB *) attr.rgValue;
                                        CAPI.CRYPTOAPI_BLOB * pNewValue =  (CAPI.CRYPTOAPI_BLOB *) pbCounterSignatureValue.DangerousGetHandle();
 
                                        for (int i = 0; i < (int) attr.cValue; i++, pOldValue++, pNewValue++) {
                                            if (i != childIndex) {
                                                *pNewValue = *pOldValue;
                                            }
                                        }
 
                                        // Encode the new counter signature attribute.
                                        byte[] encodedNewAttribute;
                                        CAPI.CRYPT_ATTRIBUTE newAttr = new CAPI.CRYPT_ATTRIBUTE();
                                        newAttr.pszObjId = attr.pszObjId;
                                        newAttr.cValue = attr.cValue - 1;
                                        newAttr.rgValue = pbCounterSignatureValue.DangerousGetHandle();
 
                                        SafeLocalAllocHandle pNewAttr = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(Marshal.SizeOf(typeof(CAPI.CRYPT_ATTRIBUTE))));
                                        Marshal.StructureToPtr(newAttr, pNewAttr.DangerousGetHandle(), false);
 
                                        try {
                                            if (!CAPI.EncodeObject(new IntPtr(CAPI.PKCS_ATTRIBUTE),
                                                                    pNewAttr.DangerousGetHandle(),
                                                                    out encodedNewAttribute))
                                                throw new CryptographicException(Marshal.GetLastWin32Error());
                                        }
                                        finally {
                                            Marshal.DestroyStructure(pNewAttr.DangerousGetHandle(), typeof(CAPI.CRYPT_ATTRIBUTE));
                                            pNewAttr.Dispose();
                                        }
 
                                        // Finally, add it back.
                                        fixed (byte * pbData = &encodedNewAttribute[0]) {
                                            CAPI.CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA addPara = new CAPI.CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA(Marshal.SizeOf(typeof(CAPI.CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA)));
                                            addPara.dwSignerIndex = (uint) parentIndex;
                                            addPara.blob.cbData = (uint) encodedNewAttribute.Length;
                                            addPara.blob.pbData = new IntPtr(pbData);
 
                                            if (!CAPI.CryptMsgControl(hMsg,
                                                                        0,
                                                                        CAPI.CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR,
                                                                        new IntPtr(&addPara)))
                                                throw new CryptographicException(Marshal.GetLastWin32Error());
                                        }
 
                                        // Keep alive.
                                        pbCounterSignatureValue.Dispose();
                                    }
                                    catch (CryptographicException) {
                                        // Roll back.
                                        byte[] encodedAttribute;
                                        if (CAPI.EncodeObject(new IntPtr(CAPI.PKCS_ATTRIBUTE),
                                                              pAttr,
                                                              out encodedAttribute)) {
                                            fixed (byte * pbData = &encodedAttribute[0]) {
                                                CAPI.CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA addPara = new CAPI.CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA(Marshal.SizeOf(typeof(CAPI.CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA)));
                                                addPara.dwSignerIndex = (uint) parentIndex;
                                                addPara.blob.cbData = (uint) encodedAttribute.Length;
                                                addPara.blob.pbData = new IntPtr(pbData);
                                                CAPI.CryptMsgControl(hMsg, 0, CAPI.CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR, new IntPtr(&addPara));
                                            }
                                        }
                                        throw;
                                    }
                                }
 
                                return;
                            }
 
                            childIndex -= (int) attr.cValue;
                        }
                    }
 
                    pAttr = new IntPtr((long) pAttr + (long) Marshal.SizeOf(typeof(CAPI.CRYPT_ATTRIBUTE)));
                }
            }
            
            // Keep alive.
            if (pbCmsgCmsSignerInfo != null && !pbCmsgCmsSignerInfo.IsInvalid) {
                pbCmsgCmsSignerInfo.Dispose();
            }
            if (pbCmsgSignerInfo != null && !pbCmsgSignerInfo.IsInvalid) {
                pbCmsgSignerInfo.Dispose();
            }
 
            throw new CryptographicException(CAPI.CRYPT_E_NO_SIGNER);
        }
 
        //
        // Private static.
        //
 
        [SecuritySafeCritical]
        private static unsafe int VerifyCertificate (X509Certificate2 certificate,
                                                     X509Certificate2Collection extraStore) {
            int dwErrorStatus;
            int hr = X509Utils.VerifyCertificate(X509Utils.GetCertContext(certificate),
                                                 null,
                                                 null,
                                                 X509RevocationMode.Online,
                                                 X509RevocationFlag.ExcludeRoot,
                                                 DateTime.Now,
                                                 new TimeSpan(0, 0, 0),
                                                 extraStore,
                                                 new IntPtr(CAPI.CERT_CHAIN_POLICY_BASE),
                                                 new IntPtr(&dwErrorStatus));
            if (hr != CAPI.S_OK)
                return dwErrorStatus;
 
            // Check key usages to make sure it is good for signing.
            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) {
                        hr = CAPI.CERT_E_WRONG_USAGE;
                        break;
                    }
                }
            }
 
            return hr;
        }
    }
 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public sealed class SignerInfoCollection : ICollection {
        private SignerInfo[] m_signerInfos;
 
        internal SignerInfoCollection () {
            m_signerInfos = new SignerInfo[0];
        }
 
        [SecuritySafeCritical]
        internal unsafe SignerInfoCollection (SignedCms signedCms) {
            uint dwSigners = 0;
            uint cbCount = (uint) Marshal.SizeOf(typeof(uint));
            SafeCryptMsgHandle safeCryptMsgHandle = signedCms.GetCryptMsgHandle();
 
            if (!CAPI.CAPISafe.CryptMsgGetParam(safeCryptMsgHandle,
                                                CAPI.CMSG_SIGNER_COUNT_PARAM,
                                                0,
                                                new IntPtr(&dwSigners),
                                                new IntPtr(&cbCount)))
                throw new CryptographicException(Marshal.GetLastWin32Error());
 
            SignerInfo[] signerInfos = new SignerInfo[dwSigners];
            for (int index = 0; index < dwSigners; index++) {
                uint cbCmsgSignerInfo = 0;
                if (!CAPI.CAPISafe.CryptMsgGetParam(safeCryptMsgHandle,
                                                    CAPI.CMSG_SIGNER_INFO_PARAM,
                                                    (uint)index,
                                                    IntPtr.Zero,
                                                    new IntPtr(&cbCmsgSignerInfo)))
                    throw new CryptographicException(Marshal.GetLastWin32Error());
 
                SafeLocalAllocHandle pbCmsgSignerInfo = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(cbCmsgSignerInfo));
 
                if (!CAPI.CAPISafe.CryptMsgGetParam(safeCryptMsgHandle,
                                                    CAPI.CMSG_SIGNER_INFO_PARAM,
                                                    (uint)index,
                                                    pbCmsgSignerInfo,
                                                    new IntPtr(&cbCmsgSignerInfo)))
                    throw new CryptographicException(Marshal.GetLastWin32Error());
 
                signerInfos[index] = new SignerInfo(signedCms, pbCmsgSignerInfo);
            }
 
            m_signerInfos = signerInfos;
        }
 
        [SecuritySafeCritical]
        internal SignerInfoCollection (SignedCms signedCms, SignerInfo signerInfo) {
            SignerInfo[] signerInfos = new SignerInfo[0];
 
            int count = 0;
            int index = 0;
 
            foreach (CryptographicAttributeObject attribute in signerInfo.UnsignedAttributes) {
                if (attribute.Oid.Value == CAPI.szOID_RSA_counterSign) {
                    count += attribute.Values.Count;
                }
            }
 
            signerInfos = new SignerInfo[count];
 
            foreach (CryptographicAttributeObject attribute in signerInfo.UnsignedAttributes) {
                if (attribute.Oid.Value == CAPI.szOID_RSA_counterSign) {
                    for (int i = 0; i < attribute.Values.Count; i++) {
                        AsnEncodedData encodedSignerInfo = (AsnEncodedData) attribute.Values[i];
                        signerInfos[index++] = new SignerInfo(signedCms, signerInfo, encodedSignerInfo.RawData);
                    }
                }
            }
 
            m_signerInfos = signerInfos;
        }
 
        public SignerInfo this[int index] {
            get {
                if (index < 0 || index >= m_signerInfos.Length)
                    throw new ArgumentOutOfRangeException("index", SecurityResources.GetResourceString("ArgumentOutOfRange_Index"));
 
                Debug.Assert(m_signerInfos[index] != null);
                return m_signerInfos[index];
            }
        }
 
        public int Count {
            get {
                return m_signerInfos.Length;
            }
        }
 
        public SignerInfoEnumerator GetEnumerator() {
            return new SignerInfoEnumerator(this);
        }
 
        /// <internalonly/>
        IEnumerator IEnumerable.GetEnumerator() {
            return new SignerInfoEnumerator(this);
        }
 
        public void CopyTo(Array array, int index) {
            if (array == null)
                throw new ArgumentNullException("array");
            if (array.Rank != 1)
                throw new ArgumentException(SecurityResources.GetResourceString("Arg_RankMultiDimNotSupported"));
            if (index < 0 || index >= array.Length)
                throw new ArgumentOutOfRangeException("index", SecurityResources.GetResourceString("ArgumentOutOfRange_Index"));
            if (index + this.Count > array.Length)
                throw new ArgumentException(SecurityResources.GetResourceString("Argument_InvalidOffLen"));
 
            for (int i=0; i < this.Count; i++) {
                array.SetValue(this[i], index);
                index++;
            }
        }
 
        public void CopyTo(SignerInfo[] array, int index) {
            ((ICollection)this).CopyTo(array, index);
        }
 
        public bool IsSynchronized {
            get {
                return false;
            }
        }
 
        public Object SyncRoot {
            get {
                return this;
            }
        }
    }
 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public sealed class SignerInfoEnumerator : IEnumerator {
        private SignerInfoCollection m_signerInfos;
        private int m_current;
 
        private SignerInfoEnumerator() {}
        internal SignerInfoEnumerator(SignerInfoCollection signerInfos) {
            m_signerInfos = signerInfos;
            m_current = -1;
        }
 
        public SignerInfo Current {
            get {
                return m_signerInfos[m_current];
            }
        }
 
        /// <internalonly/>
        Object IEnumerator.Current {
            get {
                return (Object) m_signerInfos[m_current];
            }
        }
 
        public bool MoveNext() {
            if (m_current == ((int) m_signerInfos.Count - 1)) {
                return false;
            }
            m_current++;
            return true;
        }
 
        public void Reset() {
            m_current = -1;
        }
    }
}