Пример #1
0
        internal void AddCertificates(IEnumerable <byte[]> encodedCertificates)
        {
            foreach (var cert in encodedCertificates)
            {
                using (var hb = new HeapBlockRetainer())
                {
                    var unmanagedCert = hb.Alloc(cert.Length);
                    Marshal.Copy(cert, 0, unmanagedCert, cert.Length);
                    var blob = new CRYPT_INTEGER_BLOB()
                    {
                        cbData = (uint)cert.Length,
                        pbData = unmanagedCert
                    };

                    var unmanagedBlob = hb.Alloc(Marshal.SizeOf(blob));
                    Marshal.StructureToPtr(blob, unmanagedBlob, fDeleteOld: false);

                    if (!NativeMethods.CryptMsgControl(
                            _handle,
                            dwFlags: 0,
                            dwCtrlType: CMSG_CONTROL_TYPE.CMSG_CTRL_ADD_CERT,
                            pvCtrlPara: unmanagedBlob))
                    {
                        Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
                    }
                }
            }
        }
Пример #2
0
        internal unsafe void AddCountersignature(CmsSigner cmsSigner, CngKey privateKey)
        {
            using (var hb = new HeapBlockRetainer())
            {
                var signerInfo = NativeUtility.CreateSignerInfo(cmsSigner, privateKey, hb);

                NativeUtility.ThrowIfFailed(NativeMethods.CryptMsgCountersign(
                                                _handle,
                                                dwIndex: 0,
                                                cCountersigners: 1,
                                                rgCountersigners: signerInfo));

                AddCertificates(cmsSigner.Certificates.OfType <X509Certificate2>());
            }
        }
Пример #3
0
        internal unsafe void AddTimestamp(SignedCms timestamp)
        {
            using (var hb = new HeapBlockRetainer())
            {
                var timestampAttr    = GetCryptAttributeForData(timestamp.Encode(), Oids.SignatureTimeStampTokenAttribute, hb);
                var timestampAddAttr = CreateUnsignedAddAttribute(timestampAttr, hb);

                timestampAddAttr.cbSize = (uint)Marshal.SizeOf(timestampAddAttr);
                var unmanagedTimestampAddAttr = hb.Alloc(Marshal.SizeOf(timestampAddAttr));
                Marshal.StructureToPtr(timestampAddAttr, unmanagedTimestampAddAttr, fDeleteOld: false);

                if (!NativeMethods.CryptMsgControl(
                        _handle,
                        dwFlags: 0,
                        dwCtrlType: CMSG_CONTROL_TYPE.CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR,
                        pvCtrlPara: unmanagedTimestampAddAttr))
                {
                    Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
                }
            }
        }
Пример #4
0
        internal byte[] GetRepositoryCountersignatureSignatureValue()
        {
            using (var retainer = new HeapBlockRetainer())
            {
                var repositoryCountersignature = GetRepositoryCountersignature(retainer);

                if (repositoryCountersignature == null)
                {
                    return(null);
                }

                var countersignatureSignatureValue = new byte[repositoryCountersignature.Value.SignerInfo.EncryptedHash.cbData];

                Marshal.Copy(
                    repositoryCountersignature.Value.SignerInfo.EncryptedHash.pbData,
                    countersignatureSignatureValue,
                    startIndex: 0,
                    length: countersignatureSignatureValue.Length);

                return(countersignatureSignatureValue);
            }
        }
Пример #5
0
        internal static SignedCms NativeSign(CmsSigner cmsSigner, byte[] data, CngKey privateKey)
        {
            using (var hb = new HeapBlockRetainer())
            {
                var certificateBlobs = new BLOB[cmsSigner.Certificates.Count];

                for (var i = 0; i < cmsSigner.Certificates.Count; ++i)
                {
                    var cert    = cmsSigner.Certificates[i];
                    var context = Marshal.PtrToStructure <CERT_CONTEXT>(cert.Handle);

                    certificateBlobs[i] = new BLOB()
                    {
                        cbData = context.cbCertEncoded, pbData = context.pbCertEncoded
                    };
                }

                byte[] encodedData;
                var    signerInfo = CreateSignerInfo(cmsSigner, privateKey, hb);

                var signedInfo = new CMSG_SIGNED_ENCODE_INFO();
                signedInfo.cbSize   = Marshal.SizeOf(signedInfo);
                signedInfo.cSigners = 1;

                using (var signerInfoHandle = new SafeLocalAllocHandle(Marshal.AllocHGlobal(Marshal.SizeOf(signerInfo))))
                {
                    Marshal.StructureToPtr(signerInfo, signerInfoHandle.DangerousGetHandle(), fDeleteOld: false);

                    signedInfo.rgSigners    = signerInfoHandle.DangerousGetHandle();
                    signedInfo.cCertEncoded = certificateBlobs.Length;

                    using (var certificatesHandle = new SafeLocalAllocHandle(Marshal.AllocHGlobal(Marshal.SizeOf(certificateBlobs[0]) * certificateBlobs.Length)))
                    {
                        for (var i = 0; i < certificateBlobs.Length; ++i)
                        {
                            Marshal.StructureToPtr(certificateBlobs[i], new IntPtr(certificatesHandle.DangerousGetHandle().ToInt64() + Marshal.SizeOf(certificateBlobs[i]) * i), fDeleteOld: false);
                        }

                        signedInfo.rgCertEncoded = certificatesHandle.DangerousGetHandle();

                        var hMsg = NativeMethods.CryptMsgOpenToEncode(
                            NativeMethods.X509_ASN_ENCODING | NativeMethods.PKCS_7_ASN_ENCODING,
                            dwFlags: 0,
                            dwMsgType: NativeMethods.CMSG_SIGNED,
                            pvMsgEncodeInfo: ref signedInfo,
                            pszInnerContentObjID: null,
                            pStreamInfo: IntPtr.Zero);

                        ThrowIfFailed(!hMsg.IsInvalid);

                        ThrowIfFailed(NativeMethods.CryptMsgUpdate(
                                          hMsg,
                                          data,
                                          (uint)data.Length,
                                          fFinal: true));

                        uint valueLength = 0;

                        ThrowIfFailed(NativeMethods.CryptMsgGetParam(
                                          hMsg,
                                          CMSG_GETPARAM_TYPE.CMSG_CONTENT_PARAM,
                                          dwIndex: 0,
                                          pvData: null,
                                          pcbData: ref valueLength));

                        encodedData = new byte[(int)valueLength];

                        ThrowIfFailed(NativeMethods.CryptMsgGetParam(
                                          hMsg,
                                          CMSG_GETPARAM_TYPE.CMSG_CONTENT_PARAM,
                                          dwIndex: 0,
                                          pvData: encodedData,
                                          pcbData: ref valueLength));
                    }
                }

                var cms = new SignedCms();

                cms.Decode(encodedData);

                return(cms);
            }
        }
Пример #6
0
        private unsafe static CMSG_SIGNER_ENCODE_INFO CreateSignerInfo(
            CmsSigner cmsSigner,
            CngKey privateKey,
            HeapBlockRetainer hb)
        {
            var signerInfo = new CMSG_SIGNER_ENCODE_INFO();

            signerInfo.cbSize    = (uint)Marshal.SizeOf(signerInfo);
            signerInfo.pCertInfo = Marshal.PtrToStructure <CERT_CONTEXT>(cmsSigner.Certificate.Handle).pCertInfo;
            signerInfo.hCryptProvOrhNCryptKey = privateKey.Handle.DangerousGetHandle();
            signerInfo.HashAlgorithm.pszObjId = cmsSigner.DigestAlgorithm.Value;

            if (cmsSigner.SignerIdentifierType == SubjectIdentifierType.SubjectKeyIdentifier)
            {
                var certContextHandle = IntPtr.Zero;

                try
                {
                    certContextHandle = NativeMethods.CertDuplicateCertificateContext(cmsSigner.Certificate.Handle);

                    uint cbData = 0;
                    var  pbData = IntPtr.Zero;

                    ThrowIfFailed(NativeMethods.CertGetCertificateContextProperty(
                                      certContextHandle,
                                      NativeMethods.CERT_KEY_IDENTIFIER_PROP_ID,
                                      pbData,
                                      ref cbData));

                    if (cbData > 0)
                    {
                        pbData = hb.Alloc((int)cbData);

                        ThrowIfFailed(NativeMethods.CertGetCertificateContextProperty(
                                          certContextHandle,
                                          NativeMethods.CERT_KEY_IDENTIFIER_PROP_ID,
                                          pbData,
                                          ref cbData));

                        signerInfo.SignerId.dwIdChoice   = NativeMethods.CERT_ID_KEY_IDENTIFIER;
                        signerInfo.SignerId.KeyId.cbData = cbData;
                        signerInfo.SignerId.KeyId.pbData = pbData;
                    }
                }
                finally
                {
                    if (certContextHandle != IntPtr.Zero)
                    {
                        NativeMethods.CertFreeCertificateContext(certContextHandle);
                    }
                }
            }

            if (cmsSigner.SignedAttributes.Count != 0)
            {
                signerInfo.cAuthAttr = cmsSigner.SignedAttributes.Count;

                checked
                {
                    var attributeSize    = Marshal.SizeOf <CRYPT_ATTRIBUTE>();
                    var blobSize         = Marshal.SizeOf <CRYPT_INTEGER_BLOB>();
                    var attributesArray  = (CRYPT_ATTRIBUTE *)hb.Alloc(attributeSize * cmsSigner.SignedAttributes.Count);
                    var currentAttribute = attributesArray;

                    foreach (var attribute in cmsSigner.SignedAttributes)
                    {
                        currentAttribute->pszObjId = hb.AllocAsciiString(attribute.Oid.Value);
                        currentAttribute->cValue   = (uint)attribute.Values.Count;
                        currentAttribute->rgValue  = hb.Alloc(blobSize);

                        foreach (var value in attribute.Values)
                        {
                            var attrData = value.RawData;

                            if (attrData.Length > 0)
                            {
                                var blob = (CRYPT_INTEGER_BLOB *)currentAttribute->rgValue;

                                blob->cbData = (uint)attrData.Length;
                                blob->pbData = hb.Alloc(value.RawData.Length);

                                Marshal.Copy(attrData, 0, blob->pbData, attrData.Length);
                            }
                        }

                        currentAttribute++;
                    }

                    signerInfo.rgAuthAttr = new IntPtr(attributesArray);
                }
            }

            return(signerInfo);
        }
Пример #7
0
        internal unsafe void AddTimestamp(byte[] timeStampCms)
        {
            using (var hb = new HeapBlockRetainer())
            {
                var unmanagedTimestamp = hb.Alloc(timeStampCms.Length);
                Marshal.Copy(timeStampCms, 0, unmanagedTimestamp, timeStampCms.Length);
                var blob = new CRYPT_INTEGER_BLOB()
                {
                    cbData = (uint)timeStampCms.Length,
                    pbData = unmanagedTimestamp
                };
                var unmanagedBlob = hb.Alloc(Marshal.SizeOf(blob));
                Marshal.StructureToPtr(blob, unmanagedBlob, fDeleteOld: false);

                var attr = new CRYPT_ATTRIBUTE()
                {
                    pszObjId = hb.AllocAsciiString(Oids.SignatureTimeStampTokenAttribute),
                    cValue   = 1,
                    rgValue  = unmanagedBlob
                };
                var unmanagedAttr = hb.Alloc(Marshal.SizeOf(attr));
                Marshal.StructureToPtr(attr, unmanagedAttr, fDeleteOld: false);

                uint encodedLength = 0;
                if (!NativeMethods.CryptEncodeObjectEx(
                        dwCertEncodingType: NativeMethods.X509_ASN_ENCODING | NativeMethods.PKCS_7_ASN_ENCODING,
                        lpszStructType: new IntPtr(NativeMethods.PKCS_ATTRIBUTE),
                        pvStructInfo: unmanagedAttr,
                        dwFlags: 0,
                        pEncodePara: IntPtr.Zero,
                        pvEncoded: IntPtr.Zero,
                        pcbEncoded: ref encodedLength))
                {
                    var err = Marshal.GetLastWin32Error();
                    if (err != NativeMethods.ERROR_MORE_DATA)
                    {
                        Marshal.ThrowExceptionForHR(NativeMethods.GetHRForWin32Error(err));
                    }
                }

                var unmanagedEncoded = hb.Alloc((int)encodedLength);
                if (!NativeMethods.CryptEncodeObjectEx(
                        dwCertEncodingType: NativeMethods.X509_ASN_ENCODING | NativeMethods.PKCS_7_ASN_ENCODING,
                        lpszStructType: new IntPtr(NativeMethods.PKCS_ATTRIBUTE),
                        pvStructInfo: unmanagedAttr,
                        dwFlags: 0,
                        pEncodePara: IntPtr.Zero,
                        pvEncoded: unmanagedEncoded,
                        pcbEncoded: ref encodedLength))
                {
                    Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
                }

                var addAttr = new CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA()
                {
                    dwSignerIndex = 0,
                    BLOB          = new CRYPT_INTEGER_BLOB()
                    {
                        cbData = encodedLength,
                        pbData = unmanagedEncoded
                    }
                };
                addAttr.cbSize = (uint)Marshal.SizeOf(addAttr);
                var unmanagedAddAttr = hb.Alloc(Marshal.SizeOf(addAttr));
                Marshal.StructureToPtr(addAttr, unmanagedAddAttr, fDeleteOld: false);

                if (!NativeMethods.CryptMsgControl(
                        _handle,
                        dwFlags: 0,
                        dwCtrlType: CMSG_CONTROL_TYPE.CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR,
                        pvCtrlPara: unmanagedAddAttr))
                {
                    Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
                }
            }
        }
Пример #8
0
        private unsafe RepositoryCounterSignerInfo?GetRepositoryCountersignature(HeapBlockRetainer retainer)
        {
            const uint primarySignerInfoIndex = 0;
            uint       unsignedAttributeCount = 0;
            var        pointer = IntPtr.Zero;

            NativeUtility.ThrowIfFailed(NativeMethods.CryptMsgGetParam(
                                            _handle,
                                            CMSG_GETPARAM_TYPE.CMSG_SIGNER_UNAUTH_ATTR_PARAM,
                                            primarySignerInfoIndex,
                                            pointer,
                                            ref unsignedAttributeCount));

            if (unsignedAttributeCount == 0)
            {
                return(null);
            }

            pointer = retainer.Alloc((int)unsignedAttributeCount);

            NativeUtility.ThrowIfFailed(NativeMethods.CryptMsgGetParam(
                                            _handle,
                                            CMSG_GETPARAM_TYPE.CMSG_SIGNER_UNAUTH_ATTR_PARAM,
                                            primarySignerInfoIndex,
                                            pointer,
                                            ref unsignedAttributeCount));

            var unsignedAttributes         = MarshalUtility.PtrToStructure <CRYPT_ATTRIBUTES>(pointer);
            int sizeOfCryptAttributeString = MarshalUtility.SizeOf <CRYPT_ATTRIBUTE_STRING>();
            int sizeOfCryptIntegerBlob     = MarshalUtility.SizeOf <CRYPT_INTEGER_BLOB>();

            for (uint i = 0; i < unsignedAttributes.cAttr; ++i)
            {
                var attributePointer = new IntPtr(
                    (long)unsignedAttributes.rgAttr + (i * sizeOfCryptAttributeString));
                var attribute = MarshalUtility.PtrToStructure <CRYPT_ATTRIBUTE_STRING>(attributePointer);

                if (!string.Equals(attribute.pszObjId, Oids.Countersignature, StringComparison.Ordinal))
                {
                    continue;
                }

                for (var j = 0; j < attribute.cValue; ++j)
                {
                    var attributeValuePointer = new IntPtr(
                        (long)attribute.rgValue + (j * sizeOfCryptIntegerBlob));
                    var  attributeValue = MarshalUtility.PtrToStructure <CRYPT_INTEGER_BLOB>(attributeValuePointer);
                    uint cbSignerInfo   = 0;

                    NativeUtility.ThrowIfFailed(NativeMethods.CryptDecodeObject(
                                                    CMSG_ENCODING.Any,
                                                    new IntPtr(NativeMethods.PKCS7_SIGNER_INFO),
                                                    attributeValue.pbData,
                                                    attributeValue.cbData,
                                                    dwFlags: 0,
                                                    pvStructInfo: IntPtr.Zero,
                                                    pcbStructInfo: new IntPtr(&cbSignerInfo)));

                    var counterSignerInfoPointer = retainer.Alloc((int)cbSignerInfo);

                    NativeUtility.ThrowIfFailed(NativeMethods.CryptDecodeObject(
                                                    CMSG_ENCODING.Any,
                                                    new IntPtr(NativeMethods.PKCS7_SIGNER_INFO),
                                                    attributeValue.pbData,
                                                    attributeValue.cbData,
                                                    dwFlags: 0,
                                                    pvStructInfo: counterSignerInfoPointer,
                                                    pcbStructInfo: new IntPtr(&cbSignerInfo)));

                    var counterSignerInfo = MarshalUtility.PtrToStructure <CMSG_SIGNER_INFO>(counterSignerInfoPointer);

                    if (IsRepositoryCounterSignerInfo(counterSignerInfo))
                    {
                        return(new RepositoryCounterSignerInfo()
                        {
                            dwUnauthAttrIndex = i,
                            UnauthAttr = attribute,
                            SignerInfo = counterSignerInfo
                        });
                    }
                }
            }

            return(null);
        }
Пример #9
0
        private static unsafe CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA CreateUnsignedAddAttribute(CRYPT_ATTRIBUTE attr, HeapBlockRetainer hb)
        {
            var unmanagedAttr = hb.Alloc(Marshal.SizeOf(attr));

            Marshal.StructureToPtr(attr, unmanagedAttr, fDeleteOld: false);

            uint encodedLength = 0;

            if (!NativeMethods.CryptEncodeObjectEx(
                    CMSG_ENCODING.Any,
                    lpszStructType: new IntPtr(NativeMethods.PKCS_ATTRIBUTE),
                    pvStructInfo: unmanagedAttr,
                    dwFlags: 0,
                    pEncodePara: IntPtr.Zero,
                    pvEncoded: IntPtr.Zero,
                    pcbEncoded: ref encodedLength))
            {
                var err = Marshal.GetLastWin32Error();
                if (err != NativeMethods.ERROR_MORE_DATA)
                {
                    Marshal.ThrowExceptionForHR(NativeMethods.GetHRForWin32Error(err));
                }
            }

            var unmanagedEncoded = hb.Alloc((int)encodedLength);

            if (!NativeMethods.CryptEncodeObjectEx(
                    CMSG_ENCODING.Any,
                    lpszStructType: new IntPtr(NativeMethods.PKCS_ATTRIBUTE),
                    pvStructInfo: unmanagedAttr,
                    dwFlags: 0,
                    pEncodePara: IntPtr.Zero,
                    pvEncoded: unmanagedEncoded,
                    pcbEncoded: ref encodedLength))
            {
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
            }

            var addAttr = new CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA()
            {
                dwSignerIndex = 0,
                BLOB          = new CRYPT_INTEGER_BLOB()
                {
                    cbData = encodedLength,
                    pbData = unmanagedEncoded
                }
            };

            return(addAttr);
        }
Пример #10
0
        private static CRYPT_ATTRIBUTE GetCryptAttributeForData(byte[] data, string attributeOid, HeapBlockRetainer hb)
        {
            var unmanagedData = hb.Alloc(data.Length);

            Marshal.Copy(data, 0, unmanagedData, data.Length);
            var blob = new CRYPT_INTEGER_BLOB()
            {
                cbData = (uint)data.Length,
                pbData = unmanagedData
            };

            var unmanagedBlob = hb.Alloc(Marshal.SizeOf(blob));

            Marshal.StructureToPtr(blob, unmanagedBlob, fDeleteOld: false);

            var attr = new CRYPT_ATTRIBUTE()
            {
                pszObjId = hb.AllocAsciiString(attributeOid),
                cValue   = 1,
                rgValue  = unmanagedBlob
            };

            return(attr);
        }
Пример #11
0
        internal unsafe void AddTimestampToRepositoryCountersignature(SignedCms timestamp)
        {
            using (var hb = new HeapBlockRetainer())
            {
                var repositoryCountersignature = GetRepositoryCountersignature(hb);
                if (repositoryCountersignature == null)
                {
                    throw new SignatureException(Strings.Error_NotOneRepositoryCounterSignature);
                }

                // Remove repository countersignature from message
                var countersignatureDelAttr = new CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA()
                {
                    dwSignerIndex     = 0,
                    dwUnauthAttrIndex = repositoryCountersignature.Value.dwUnauthAttrIndex
                };

                countersignatureDelAttr.cbSize = (uint)Marshal.SizeOf(countersignatureDelAttr);
                var unmanagedCountersignatureDelAttr = hb.Alloc(Marshal.SizeOf(countersignatureDelAttr));
                Marshal.StructureToPtr(countersignatureDelAttr, unmanagedCountersignatureDelAttr, fDeleteOld: false);

                if (!NativeMethods.CryptMsgControl(
                        _handle,
                        dwFlags: 0,
                        dwCtrlType: CMSG_CONTROL_TYPE.CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR,
                        pvCtrlPara: unmanagedCountersignatureDelAttr))
                {
                    Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
                }

                // Add timestamp attribute to existing unsigned attributes
                var signerInfo      = repositoryCountersignature.Value.SignerInfo;
                var unauthAttrCount = signerInfo.UnauthAttrs.cAttr + 1;

                var sizeOfCryptAttribute = MarshalUtility.SizeOf <CRYPT_ATTRIBUTE>();
                var attributesArray      = (CRYPT_ATTRIBUTE *)hb.Alloc((int)(sizeOfCryptAttribute * unauthAttrCount));
                var currentAttribute     = attributesArray;

                // Copy existing unsigned attributes
                for (var i = 0; i < unauthAttrCount - 1; ++i)
                {
                    var existingAttributePointer = new IntPtr(
                        (long)signerInfo.UnauthAttrs.rgAttr + (i * sizeOfCryptAttribute));
                    var existingAttribute = MarshalUtility.PtrToStructure <CRYPT_ATTRIBUTE>(existingAttributePointer);

                    currentAttribute->pszObjId = existingAttribute.pszObjId;
                    currentAttribute->cValue   = existingAttribute.cValue;
                    currentAttribute->rgValue  = existingAttribute.rgValue;

                    currentAttribute++;
                }

                // Add timestamp attribute
                *currentAttribute = GetCryptAttributeForData(timestamp.Encode(), Oids.SignatureTimeStampTokenAttribute, hb);

                signerInfo.UnauthAttrs = new CRYPT_ATTRIBUTES()
                {
                    cAttr  = unauthAttrCount,
                    rgAttr = new IntPtr(attributesArray)
                };

                // Encode signer info
                var unmanagedSignerInfo = hb.Alloc(Marshal.SizeOf(signerInfo));
                Marshal.StructureToPtr(signerInfo, unmanagedSignerInfo, fDeleteOld: false);

                uint encodedLength = 0;
                if (!NativeMethods.CryptEncodeObjectEx(
                        CMSG_ENCODING.Any,
                        lpszStructType: new IntPtr(NativeMethods.PKCS7_SIGNER_INFO),
                        pvStructInfo: unmanagedSignerInfo,
                        dwFlags: 0,
                        pEncodePara: IntPtr.Zero,
                        pvEncoded: IntPtr.Zero,
                        pcbEncoded: ref encodedLength))
                {
                    var err = Marshal.GetLastWin32Error();
                    if (err != NativeMethods.ERROR_MORE_DATA)
                    {
                        Marshal.ThrowExceptionForHR(NativeMethods.GetHRForWin32Error(err));
                    }
                }

                var unmanagedEncoded = hb.Alloc((int)encodedLength);
                if (!NativeMethods.CryptEncodeObjectEx(
                        CMSG_ENCODING.Any,
                        lpszStructType: new IntPtr(NativeMethods.PKCS7_SIGNER_INFO),
                        pvStructInfo: unmanagedSignerInfo,
                        dwFlags: 0,
                        pEncodePara: IntPtr.Zero,
                        pvEncoded: unmanagedEncoded,
                        pcbEncoded: ref encodedLength))
                {
                    Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
                }

                var encondedSignerBlob = new CRYPT_INTEGER_BLOB()
                {
                    cbData = encodedLength,
                    pbData = unmanagedEncoded
                };

                var unmanagedBlob = hb.Alloc(Marshal.SizeOf(encondedSignerBlob));
                Marshal.StructureToPtr(encondedSignerBlob, unmanagedBlob, fDeleteOld: false);

                var signerInfoAttr = new CRYPT_ATTRIBUTE()
                {
                    pszObjId = hb.AllocAsciiString(Oids.Countersignature),
                    cValue   = 1,
                    rgValue  = unmanagedBlob
                };

                // Create add unauth for signer info
                var signerInfoAddAttr = CreateUnsignedAddAttribute(signerInfoAttr, hb);

                // Add repository countersignature back to message
                signerInfoAddAttr.cbSize = (uint)Marshal.SizeOf(signerInfoAddAttr);
                var unmanagedSignerInfoAddAttr = hb.Alloc(Marshal.SizeOf(signerInfoAddAttr));

                Marshal.StructureToPtr(signerInfoAddAttr, unmanagedSignerInfoAddAttr, fDeleteOld: false);

                if (!NativeMethods.CryptMsgControl(
                        _handle,
                        dwFlags: 0,
                        dwCtrlType: CMSG_CONTROL_TYPE.CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR,
                        pvCtrlPara: unmanagedSignerInfoAddAttr))
                {
                    Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
                }
            }
        }