public static SafeCryptMsgHandle CreateCryptMsgHandleToEncode(CmsRecipientCollection recipients, Oid innerContentType, AlgorithmIdentifier contentEncryptionAlgorithm, X509Certificate2Collection originatorCerts, CryptographicAttributeObjectCollection unprotectedAttributes)
            {
                using (HeapBlockRetainer hb = new HeapBlockRetainer())
                {
                    // Deep copy the CmsRecipients (and especially their underlying X509Certificate2 objects). This will prevent malicious callers from altering them or disposing them while we're performing 
                    // unsafe memory crawling inside them.
                    recipients = recipients.DeepCopy();

                    // We must keep all the certificates inside recipients alive until the call to CryptMsgOpenToEncode() finishes. The CMSG_ENVELOPED_ENCODE_INFO* structure we passed to it
                    // contains direct pointers to memory owned by the CERT_INFO memory block whose lifetime is that of the certificate. 
                    hb.KeepAlive(recipients);
                    unsafe
                    {
                        CMSG_ENVELOPED_ENCODE_INFO* pEnvelopedEncodeInfo = CreateCmsEnvelopedEncodeInfo(recipients, contentEncryptionAlgorithm, originatorCerts, unprotectedAttributes, hb);
                        SafeCryptMsgHandle hCryptMsg = Interop.Crypt32.CryptMsgOpenToEncode(MsgEncodingType.All, 0, CryptMsgType.CMSG_ENVELOPED, pEnvelopedEncodeInfo, innerContentType.Value, IntPtr.Zero);
                        if (hCryptMsg == null || hCryptMsg.IsInvalid)
                            throw Marshal.GetLastWin32Error().ToCryptographicException();

                        return hCryptMsg;
                    }
                }
            }
Beispiel #2
0
        private static unsafe void SetCmsRecipientParams(CmsRecipientCollection           recipients, 
                                                         X509Certificate2Collection      certificates, 
                                                         CryptographicAttributeObjectCollection unprotectedAttributes,
                                                         AlgorithmIdentifier              contentEncryptionAlgorithm,
                                                         ref CMSG_ENCRYPT_PARAM           encryptParam) {
            checked {
                recipients = recipients.DeepCopy();
                certificates = new X509Certificate2Collection(certificates);

                int index = 0;
                uint[] recipientInfoTypes = new uint[recipients.Count];
                int cKeyAgree = 0;
                int reiSize = recipients.Count * Marshal.SizeOf(typeof(CAPI.CMSG_RECIPIENT_ENCODE_INFO));
                int totalSize = reiSize;

                for (index = 0; index < recipients.Count; index++) {
                    recipientInfoTypes[index] = (uint) PkcsUtils.GetRecipientInfoType(recipients[index].Certificate);

                    if (recipientInfoTypes[index] == CAPI.CMSG_KEY_TRANS_RECIPIENT) {
                        totalSize += Marshal.SizeOf(typeof(CAPI.CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO));
                    }
                    else if (recipientInfoTypes[index] == CAPI.CMSG_KEY_AGREE_RECIPIENT) {
                        cKeyAgree++;
                        totalSize += Marshal.SizeOf(typeof(CAPI.CMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO));
                    }
                    else {
                        throw new CryptographicException(CAPI.CRYPT_E_UNKNOWN_ALGO);
                    }
                }

                encryptParam.rgpRecipients = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(totalSize));
                encryptParam.rgCertEncoded = SafeLocalAllocHandle.InvalidHandle;
                encryptParam.rgUnprotectedAttr = SafeLocalAllocHandle.InvalidHandle;
                encryptParam.rgSubjectKeyIdentifier = new SafeLocalAllocHandle[recipients.Count];
                encryptParam.rgszObjId = new SafeLocalAllocHandle[recipients.Count];

                if (cKeyAgree > 0) {
                    encryptParam.rgszKeyWrapObjId = new SafeLocalAllocHandle[cKeyAgree];
                    encryptParam.rgKeyWrapAuxInfo = new SafeLocalAllocHandle[cKeyAgree];
                    encryptParam.rgEphemeralIdentifier = new SafeLocalAllocHandle[cKeyAgree];
                    encryptParam.rgszEphemeralObjId = new SafeLocalAllocHandle[cKeyAgree];
                    encryptParam.rgUserKeyingMaterial = new SafeLocalAllocHandle[cKeyAgree];
                    encryptParam.prgpEncryptedKey = new SafeLocalAllocHandle[cKeyAgree];
                    encryptParam.rgpEncryptedKey = new SafeLocalAllocHandle[cKeyAgree];
                }

                // Create encode certs array.
                if (certificates.Count > 0) {
                    encryptParam.rgCertEncoded = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(certificates.Count * Marshal.SizeOf(typeof(CAPI.CRYPTOAPI_BLOB))));
                    for (index = 0; index < certificates.Count; index++) {
                        CAPI.CERT_CONTEXT pCertContext = (CAPI.CERT_CONTEXT) Marshal.PtrToStructure(X509Utils.GetCertContext(certificates[index]).DangerousGetHandle(), typeof(CAPI.CERT_CONTEXT));
                        CAPI.CRYPTOAPI_BLOB * pBlob = (CAPI.CRYPTOAPI_BLOB *) new IntPtr((long) encryptParam.rgCertEncoded.DangerousGetHandle() + 
                                                                                            (index * Marshal.SizeOf(typeof(CAPI.CRYPTOAPI_BLOB))));
                        pBlob->cbData = pCertContext.cbCertEncoded;
                        pBlob->pbData = pCertContext.pbCertEncoded;
                    }
                }

                // Create unprotected attributes array.
                if (unprotectedAttributes.Count > 0) {
                    encryptParam.rgUnprotectedAttr = new SafeLocalAllocHandle(PkcsUtils.CreateCryptAttributes(unprotectedAttributes));
                }

                // pKeyInfo = CMSG_ENVELOPED_ENCODE_INFO.rgCmsRecipients
                cKeyAgree = 0;
                IntPtr pKeyInfo = new IntPtr((long) encryptParam.rgpRecipients.DangerousGetHandle() + reiSize);
                for (index = 0; index < recipients.Count; index++) {
                    CmsRecipient recipient = recipients[index];
                    X509Certificate2 certificate = recipient.Certificate;
                    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));

                    CAPI.CMSG_RECIPIENT_ENCODE_INFO * pEncodeInfo = (CAPI.CMSG_RECIPIENT_ENCODE_INFO *) new IntPtr((long) encryptParam.rgpRecipients.DangerousGetHandle() + 
                                                                                                                    (index * Marshal.SizeOf(typeof(CAPI.CMSG_RECIPIENT_ENCODE_INFO))));
                    // CMSG_RECIPIENT_ENCODE_INFO.dwRecipientChoice
                    pEncodeInfo->dwRecipientChoice = (uint) recipientInfoTypes[index];

                    // CMSG_RECIPIENT_ENCODE_INFO.pRecipientInfo (pKeyTrans or pKeyAgree)
                    pEncodeInfo->pRecipientInfo = pKeyInfo;

                    if (recipientInfoTypes[index] == CAPI.CMSG_KEY_TRANS_RECIPIENT) {
                        // Fill in CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO.

                        // cbSize
                        IntPtr pcbSize = new IntPtr((long) pKeyInfo + (long) Marshal.OffsetOf(typeof(CAPI.CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO), "cbSize"));
                        Marshal.WriteInt32(pcbSize, Marshal.SizeOf(typeof(CAPI.CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO)));

                        // KeyEncryptionAlgorithm
                        IntPtr pKeyEncryptionAlgorithm = new IntPtr((long) pKeyInfo + (long) Marshal.OffsetOf(typeof(CAPI.CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO), "KeyEncryptionAlgorithm"));

                        byte[] objId = Encoding.ASCII.GetBytes(certInfo.SubjectPublicKeyInfo.Algorithm.pszObjId);
                        encryptParam.rgszObjId[index] = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(objId.Length + 1));
                        Marshal.Copy(objId, 0, encryptParam.rgszObjId[index].DangerousGetHandle(), objId.Length);

                        // KeyEncryptionAlgorithm.pszObjId
                        IntPtr pszObjId = new IntPtr((long) pKeyEncryptionAlgorithm + (long) Marshal.OffsetOf(typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER), "pszObjId"));
                        Marshal.WriteIntPtr(pszObjId, encryptParam.rgszObjId[index].DangerousGetHandle());

                        // KeyEncryptionAlgorithm.Parameters
                        IntPtr pParameters = new IntPtr((long) pKeyEncryptionAlgorithm + (long) Marshal.OffsetOf(typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER), "Parameters"));

                        // KeyEncryptionAlgorithm.Parameters.cbData
                        IntPtr pcbData = new IntPtr((long) pParameters + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "cbData"));
                        Marshal.WriteInt32(pcbData, (int) certInfo.SubjectPublicKeyInfo.Algorithm.Parameters.cbData);

                        // KeyEncryptionAlgorithm.Parameters.pbData
                        IntPtr ppbData = new IntPtr((long) pParameters + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "pbData"));
                        Marshal.WriteIntPtr(ppbData, certInfo.SubjectPublicKeyInfo.Algorithm.Parameters.pbData);

                        // Skip pvKeyEncryptionAuxInfo
                        // Skip hCryptProv

                        // RecipientPublicKey
                        IntPtr pRecipientPublicKey = new IntPtr((long) pKeyInfo + (long) Marshal.OffsetOf(typeof(CAPI.CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO), "RecipientPublicKey"));

                        // RecipientPublicKey.cbData
                        pcbData = new IntPtr((long) pRecipientPublicKey + (long) Marshal.OffsetOf(typeof(CAPI.CRYPT_BIT_BLOB), "cbData"));
                        Marshal.WriteInt32(pcbData, (int) certInfo.SubjectPublicKeyInfo.PublicKey.cbData);

                        // RecipientPublicKey.pbData
                        ppbData = new IntPtr((long) pRecipientPublicKey + (long) Marshal.OffsetOf(typeof(CAPI.CRYPT_BIT_BLOB), "pbData"));
                        Marshal.WriteIntPtr(ppbData, certInfo.SubjectPublicKeyInfo.PublicKey.pbData);

                        // RecipientPublicKey.cUnusedBits
                        IntPtr pcUnusedBIts = new IntPtr((long) pRecipientPublicKey + (long) Marshal.OffsetOf(typeof(CAPI.CRYPT_BIT_BLOB), "cUnusedBits"));
                        Marshal.WriteInt32(pcUnusedBIts, (int) certInfo.SubjectPublicKeyInfo.PublicKey.cUnusedBits);

                        // RecipientId
                        IntPtr pRecipientId = new IntPtr((long) pKeyInfo + (long) Marshal.OffsetOf(typeof(CAPI.CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO), "RecipientId"));
                        if (recipient.RecipientIdentifierType == SubjectIdentifierType.SubjectKeyIdentifier) {
                            uint cbData = 0;
                            SafeLocalAllocHandle pbData = SafeLocalAllocHandle.InvalidHandle;
                            if (!CAPI.CAPISafe.CertGetCertificateContextProperty(X509Utils.GetCertContext(certificate),
                                                                                    CAPI.CERT_KEY_IDENTIFIER_PROP_ID,
                                                                                    pbData,
                                                                                    ref cbData))
                                throw new CryptographicException(Marshal.GetLastWin32Error());

                            pbData = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cbData));

                            if (!CAPI.CAPISafe.CertGetCertificateContextProperty(X509Utils.GetCertContext(certificate),
                                                                                    CAPI.CERT_KEY_IDENTIFIER_PROP_ID,
                                                                                    pbData,
                                                                                    ref cbData))
                                throw new CryptographicException(Marshal.GetLastWin32Error());

                            encryptParam.rgSubjectKeyIdentifier[index] = pbData;

                            // RecipientId.dwIdChoice
                            IntPtr pdwIdChoice = new IntPtr((long) pRecipientId + (long) Marshal.OffsetOf(typeof(CAPI.CERT_ID), "dwIdChoice"));
                            Marshal.WriteInt32(pdwIdChoice, (int) CAPI.CERT_ID_KEY_IDENTIFIER);

                            // RecipientId.KeyId
                            IntPtr pKeyId = new IntPtr((long) pRecipientId + (long) Marshal.OffsetOf(typeof(CAPI.CERT_ID), "Value"));

                            // RecipientId.KeyId.cbData
                            pcbData = new IntPtr((long) pKeyId + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "cbData"));
                            Marshal.WriteInt32(pcbData, (int) cbData);

                            // RecipientId.KeyId.pbData
                            ppbData = new IntPtr((long) pKeyId + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "pbData"));
                            Marshal.WriteIntPtr(ppbData, pbData.DangerousGetHandle());
                        }
                        else {
                            // RecipientId.dwIdChoice
                            IntPtr pdwIdChoice = new IntPtr((long) pRecipientId + (long) Marshal.OffsetOf(typeof(CAPI.CERT_ID), "dwIdChoice"));
                            Marshal.WriteInt32(pdwIdChoice, (int) CAPI.CERT_ID_ISSUER_SERIAL_NUMBER);

                            // RecipientId.IssuerSerialNumber
                            IntPtr pIssuerSerialNumber = new IntPtr((long) pRecipientId + (long) Marshal.OffsetOf(typeof(CAPI.CERT_ID), "Value"));

                            // RecipientId.IssuerSerialNumber.Issuer
                            IntPtr pIssuer = new IntPtr((long) pIssuerSerialNumber + (long) Marshal.OffsetOf(typeof(CAPI.CERT_ISSUER_SERIAL_NUMBER), "Issuer"));

                            // RecipientId.IssuerSerialNumber.Issuer.cbData
                            pcbData = new IntPtr((long) pIssuer + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "cbData"));
                            Marshal.WriteInt32(pcbData, (int) certInfo.Issuer.cbData);

                            // RecipientId.IssuerSerialNumber.Issuer.pbData
                            ppbData = new IntPtr((long) pIssuer + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "pbData"));
                            Marshal.WriteIntPtr(ppbData, certInfo.Issuer.pbData);

                            // RecipientId.IssuerSerialNumber.SerialNumber
                            IntPtr pSerialNumber = new IntPtr((long) pIssuerSerialNumber + (long) Marshal.OffsetOf(typeof(CAPI.CERT_ISSUER_SERIAL_NUMBER), "SerialNumber"));

                            // RecipientId.IssuerSerialNumber.SerialNumber.cbData
                            pcbData = new IntPtr((long) pSerialNumber + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "cbData"));
                            Marshal.WriteInt32(pcbData, (int) certInfo.SerialNumber.cbData);

                            // RecipientId.IssuerSerialNumber.SerialNumber.pbData
                            ppbData = new IntPtr((long) pSerialNumber + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "pbData"));
                            Marshal.WriteIntPtr(ppbData, certInfo.SerialNumber.pbData);
                        }
                        pKeyInfo = new IntPtr((long) pKeyInfo + Marshal.SizeOf(typeof(CAPI.CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO)));
                    }
                    else if (recipientInfoTypes[index] == CAPI.CMSG_KEY_AGREE_RECIPIENT) {
                        // Fill in CMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO.

                        // cbSize
                        IntPtr pcbSize = new IntPtr((long) pKeyInfo + (long) Marshal.OffsetOf(typeof(CAPI.CMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO), "cbSize"));
                        Marshal.WriteInt32(pcbSize, Marshal.SizeOf(typeof(CAPI.CMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO)));

                        // KeyEncryptionAlgorithm
                        IntPtr pKeyEncryptionAlgorithm = new IntPtr((long) pKeyInfo + (long) Marshal.OffsetOf(typeof(CAPI.CMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO), "KeyEncryptionAlgorithm"));

                        byte[] objId = Encoding.ASCII.GetBytes(CAPI.szOID_RSA_SMIMEalgESDH);
                        encryptParam.rgszObjId[index] = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(objId.Length + 1));
                        Marshal.Copy(objId, 0, encryptParam.rgszObjId[index].DangerousGetHandle(), objId.Length);

                        // KeyEncryptionAlgorithm.pszObjId
                        IntPtr pszObjId = new IntPtr((long) pKeyEncryptionAlgorithm + (long) Marshal.OffsetOf(typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER), "pszObjId"));
                        Marshal.WriteIntPtr(pszObjId, encryptParam.rgszObjId[index].DangerousGetHandle());

                        // Skip KeyEncryptionAlgorithm.Parameters
                        // Skip pvKeyEncryptionAuxInfo

                        // KeyWrapAlgorithm
                        IntPtr pKeyWrapAlgorithm = new IntPtr((long) pKeyInfo + (long) Marshal.OffsetOf(typeof(CAPI.CMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO), "KeyWrapAlgorithm"));

                        uint algId = X509Utils.OidToAlgId(contentEncryptionAlgorithm.Oid.Value);
                        if (algId == CAPI.CALG_RC2) {
                            objId = Encoding.ASCII.GetBytes(CAPI.szOID_RSA_SMIMEalgCMSRC2wrap);
                        }
                        else {
                            objId = Encoding.ASCII.GetBytes(CAPI.szOID_RSA_SMIMEalgCMS3DESwrap);
                        }
                        encryptParam.rgszKeyWrapObjId[cKeyAgree] = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(objId.Length + 1));
                        Marshal.Copy(objId, 0, encryptParam.rgszKeyWrapObjId[cKeyAgree].DangerousGetHandle(), objId.Length);

                        // KeyWrapAlgorithm.pszObjId
                        pszObjId = new IntPtr((long) pKeyWrapAlgorithm + (long) Marshal.OffsetOf(typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER), "pszObjId"));
                        Marshal.WriteIntPtr(pszObjId, encryptParam.rgszKeyWrapObjId[cKeyAgree].DangerousGetHandle());

                        // Skip KeyWrapAlgorithm.Parameters

                        // Fill in pvKeyWrapAuxInfo for RC2.
                        if (algId == CAPI.CALG_RC2) {
                            IntPtr pKeyWrapAuxInfo = new IntPtr((long) pKeyInfo + (long) Marshal.OffsetOf(typeof(CAPI.CMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO), "pvKeyWrapAuxInfo"));
                            Marshal.WriteIntPtr(pKeyWrapAuxInfo, encryptParam.pvEncryptionAuxInfo.DangerousGetHandle());
                        }

                        // Skip hCryptProv
                        // Skip dwKeySpec

                        // dwKeyChoice
                        IntPtr pdwKeyChoice = new IntPtr((long) pKeyInfo + (long) Marshal.OffsetOf(typeof(CAPI.CMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO), "dwKeyChoice"));
                        Marshal.WriteInt32(pdwKeyChoice, (int) CAPI.CMSG_KEY_AGREE_EPHEMERAL_KEY_CHOICE);

                        // pEphemeralAlgorithm
                        IntPtr pEphemeralAlgorithm = new IntPtr((long) pKeyInfo + (long) Marshal.OffsetOf(typeof(CAPI.CMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO), "pEphemeralAlgorithmOrSenderId"));
                        encryptParam.rgEphemeralIdentifier[cKeyAgree] = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(Marshal.SizeOf(typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER))));
                        Marshal.WriteIntPtr(pEphemeralAlgorithm, encryptParam.rgEphemeralIdentifier[cKeyAgree].DangerousGetHandle());

                        // pEphemeralAlgorithm.pszObjId
                        objId = Encoding.ASCII.GetBytes(certInfo.SubjectPublicKeyInfo.Algorithm.pszObjId);
                        encryptParam.rgszEphemeralObjId[cKeyAgree] = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(objId.Length + 1));
                        Marshal.Copy(objId, 0, encryptParam.rgszEphemeralObjId[cKeyAgree].DangerousGetHandle(), objId.Length);

                        pszObjId = new IntPtr((long) encryptParam.rgEphemeralIdentifier[cKeyAgree].DangerousGetHandle() + (long) Marshal.OffsetOf(typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER), "pszObjId"));
                        Marshal.WriteIntPtr(pszObjId, encryptParam.rgszEphemeralObjId[cKeyAgree].DangerousGetHandle());

                        // pEphemeralAlgorithm.Parameters
                        IntPtr pParameters = new IntPtr((long) encryptParam.rgEphemeralIdentifier[cKeyAgree].DangerousGetHandle() + (long) Marshal.OffsetOf(typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER), "Parameters"));

                        // pEphemeralAlgorithm.Parameters.cbData
                        IntPtr pcbData = new IntPtr((long) pParameters + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "cbData"));
                        Marshal.WriteInt32(pcbData, (int) certInfo.SubjectPublicKeyInfo.Algorithm.Parameters.cbData);

                        // pEphemeralAlgorithm.Parameters.pbData
                        IntPtr ppbData = new IntPtr((long) pParameters + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "pbData"));
                        Marshal.WriteIntPtr(ppbData, certInfo.SubjectPublicKeyInfo.Algorithm.Parameters.pbData);

                        // Skip UserKeyingMaterial

                        // cRecipientEncryptedKeys
                        IntPtr pcRecipientEncryptedKeys = new IntPtr((long) pKeyInfo + (long) Marshal.OffsetOf(typeof(CAPI.CMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO), "cRecipientEncryptedKeys"));
                        Marshal.WriteInt32(pcRecipientEncryptedKeys, 1);

                        // rgpRecipientEncryptedKeys
                        encryptParam.prgpEncryptedKey[cKeyAgree] = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(Marshal.SizeOf(typeof(IntPtr))));
                        IntPtr prgpRecipientEncryptedKeys = new IntPtr((long) pKeyInfo + (long) Marshal.OffsetOf(typeof(CAPI.CMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO), "rgpRecipientEncryptedKeys"));
                        Marshal.WriteIntPtr(prgpRecipientEncryptedKeys, encryptParam.prgpEncryptedKey[cKeyAgree].DangerousGetHandle());
                        encryptParam.rgpEncryptedKey[cKeyAgree] = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(Marshal.SizeOf(typeof(CAPI.CMSG_RECIPIENT_ENCRYPTED_KEY_ENCODE_INFO))));
                        Marshal.WriteIntPtr(encryptParam.prgpEncryptedKey[cKeyAgree].DangerousGetHandle(), encryptParam.rgpEncryptedKey[cKeyAgree].DangerousGetHandle());

                        // rgpRecipientEncryptedKeys.cbSize
                        pcbSize = new IntPtr((long) encryptParam.rgpEncryptedKey[cKeyAgree].DangerousGetHandle() + (long) Marshal.OffsetOf(typeof(CAPI.CMSG_RECIPIENT_ENCRYPTED_KEY_ENCODE_INFO), "cbSize"));
                        Marshal.WriteInt32(pcbSize, Marshal.SizeOf(typeof(CAPI.CMSG_RECIPIENT_ENCRYPTED_KEY_ENCODE_INFO)));

                        // rgpRecipientEncryptedKeys.RecipientPublicKey
                        IntPtr pRecipientPublicKey = new IntPtr((long) encryptParam.rgpEncryptedKey[cKeyAgree].DangerousGetHandle() + (long) Marshal.OffsetOf(typeof(CAPI.CMSG_RECIPIENT_ENCRYPTED_KEY_ENCODE_INFO), "RecipientPublicKey"));

                        // rgpRecipientEncryptedKeys.RecipientPublicKey.cbData
                        pcbData = new IntPtr((long) pRecipientPublicKey + (long) Marshal.OffsetOf(typeof(CAPI.CRYPT_BIT_BLOB), "cbData"));
                        Marshal.WriteInt32(pcbData, (int) certInfo.SubjectPublicKeyInfo.PublicKey.cbData);

                        // rgpRecipientEncryptedKeys.RecipientPublicKey.pbData
                        ppbData = new IntPtr((long) pRecipientPublicKey + (long) Marshal.OffsetOf(typeof(CAPI.CRYPT_BIT_BLOB), "pbData"));
                        Marshal.WriteIntPtr(ppbData, certInfo.SubjectPublicKeyInfo.PublicKey.pbData);

                        // rgpRecipientEncryptedKeys.RecipientPublicKey.cUnusedBits
                        IntPtr pcUnusedBits = new IntPtr((long) pRecipientPublicKey + (long) Marshal.OffsetOf(typeof(CAPI.CRYPT_BIT_BLOB), "cUnusedBits"));
                        Marshal.WriteInt32(pcUnusedBits, (int) certInfo.SubjectPublicKeyInfo.PublicKey.cUnusedBits);                    

                        // rgpRecipientEncryptedKeys.RecipientId
                        IntPtr pRecipientId = new IntPtr((long) encryptParam.rgpEncryptedKey[cKeyAgree].DangerousGetHandle() + (long) Marshal.OffsetOf(typeof(CAPI.CMSG_RECIPIENT_ENCRYPTED_KEY_ENCODE_INFO), "RecipientId"));

                        // rgpRecipientEncryptedKeys.RecipientId.dwIdChoice
                        IntPtr pdwIdChoice = new IntPtr((long) pRecipientId + (long) Marshal.OffsetOf(typeof(CAPI.CERT_ID), "dwIdChoice"));

                        if (recipient.RecipientIdentifierType == SubjectIdentifierType.SubjectKeyIdentifier) {
                            Marshal.WriteInt32(pdwIdChoice, (int) CAPI.CERT_ID_KEY_IDENTIFIER);

                            // rgpRecipientEncryptedKeys.RecipientId.KeyId
                            IntPtr pKeyId = new IntPtr((long) pRecipientId + (long) Marshal.OffsetOf(typeof(CAPI.CERT_ID), "Value"));

                            uint cbKeyId = 0;
                            SafeLocalAllocHandle pbKeyId = SafeLocalAllocHandle.InvalidHandle;
                            if (!CAPI.CAPISafe.CertGetCertificateContextProperty(X509Utils.GetCertContext(certificate),
                                                                                    CAPI.CERT_KEY_IDENTIFIER_PROP_ID,
                                                                                    pbKeyId,
                                                                                    ref cbKeyId))
                                throw new CryptographicException(Marshal.GetLastWin32Error());

                            pbKeyId = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cbKeyId));
                            if (!CAPI.CAPISafe.CertGetCertificateContextProperty(X509Utils.GetCertContext(certificate),
                                                                                    CAPI.CERT_KEY_IDENTIFIER_PROP_ID,
                                                                                    pbKeyId,
                                                                                    ref cbKeyId))
                                throw new CryptographicException(Marshal.GetLastWin32Error());

                            encryptParam.rgSubjectKeyIdentifier[cKeyAgree] = pbKeyId;

                            // rgpRecipientEncryptedKeys.RecipientId.KeyId.cbData
                            pcbData = new IntPtr((long) pKeyId + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "cbData"));
                            Marshal.WriteInt32(pcbData, (int) cbKeyId);

                            // rgpRecipientEncryptedKeys.RecipientId.KeyId.pbData
                            ppbData = new IntPtr((long) pKeyId + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "pbData"));
                            Marshal.WriteIntPtr(ppbData, pbKeyId.DangerousGetHandle());
                        }
                        else {
                            Marshal.WriteInt32(pdwIdChoice, (int) CAPI.CERT_ID_ISSUER_SERIAL_NUMBER);

                            // rgpRecipientEncryptedKeys.RecipientId.IssuerSerialNumber
                            IntPtr pIssuerSerial = new IntPtr((long) pRecipientId + (long) Marshal.OffsetOf(typeof(CAPI.CERT_ID), "Value"));

                            // rgpRecipientEncryptedKeys.RecipientId.IssuerSerialNumber.Issuer
                            IntPtr pIssuer = new IntPtr((long) pIssuerSerial + (long) Marshal.OffsetOf(typeof(CAPI.CERT_ISSUER_SERIAL_NUMBER), "Issuer"));

                            // rgpRecipientEncryptedKeys.RecipientId.IssuerSerialNumber.Issuer.cbData
                            pcbData = new IntPtr((long) pIssuer + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "cbData"));
                            Marshal.WriteInt32(pcbData, (int) certInfo.Issuer.cbData);

                            // rgpRecipientEncryptedKeys.RecipientId.IssuerSerialNumber.Issuer.pbData
                            ppbData = new IntPtr((long) pIssuer + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "pbData"));
                            Marshal.WriteIntPtr(ppbData, certInfo.Issuer.pbData);

                            // rgpRecipientEncryptedKeys.RecipientId.IssuerSerialNumber.SerialNumber
                            IntPtr pSerialNumber = new IntPtr((long) pIssuerSerial + (long) Marshal.OffsetOf(typeof(CAPI.CERT_ISSUER_SERIAL_NUMBER), "SerialNumber"));

                            // rgpRecipientEncryptedKeys.RecipientId.IssuerSerialNumber.SerialNumber.cbData
                            pcbData = new IntPtr((long) pSerialNumber + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "cbData"));
                            Marshal.WriteInt32(pcbData, (int) certInfo.SerialNumber.cbData);

                            // rgpRecipientEncryptedKeys.RecipientId.IssuerSerialNumber.SerialNumber.pbData
                            ppbData = new IntPtr((long) pSerialNumber + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "pbData"));
                            Marshal.WriteIntPtr(ppbData, certInfo.SerialNumber.pbData);
                        }

                        // Bump key agree count.
                        cKeyAgree++;
                        pKeyInfo = new IntPtr((long) pKeyInfo + Marshal.SizeOf(typeof(CAPI.CMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO)));
                    }
                    else {
                        // Should never get here!
                        Debug.Assert(false);
                    }
                }
            }
        }