Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / whidbey / NetFxQFE / ndp / clr / src / ManagedLibraries / Security / System / Security / Cryptography / Pkcs / PkcsUtils.cs / 1 / PkcsUtils.cs
// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== // // PkcsUtils.cs // namespace System.Security.Cryptography.Pkcs { using System.Collections; using System.Diagnostics; using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Security; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.Xml; using System.Text; internal class PkcsUtils { private PkcsUtils () {} private static int m_cmsSupported = -1; private struct I_CRYPT_ATTRIBUTE { internal IntPtr pszObjId; internal uint cValue; internal IntPtr rgValue; // PCRYPT_ATTR_BLOB } internal static uint AlignedLength (uint length) { return ((length + (uint) 7) & ((uint) 0xfffffff8)); } internal static unsafe bool CmsSupported () { if (m_cmsSupported == -1) { IntPtr hModule = CAPI.LoadLibrary("Crypt32.dll"); if (hModule != IntPtr.Zero) { IntPtr pFunc = CAPI.GetProcAddress(hModule, "CryptMsgVerifyCountersignatureEncodedEx"); m_cmsSupported = pFunc == IntPtr.Zero ? 0 : 1; CAPI.FreeLibrary(hModule); } } return m_cmsSupported == 0 ? false : true; } internal static RecipientInfoType GetRecipientInfoType (X509Certificate2 certificate) { RecipientInfoType recipientInfoType = RecipientInfoType.Unknown; if (certificate != null) { CAPI.CERT_CONTEXT pCertContext = (CAPI.CERT_CONTEXT) Marshal.PtrToStructure(X509Utils.GetCertContext(certificate).DangerousGetHandle(), typeof(CAPI.CERT_CONTEXT)); CAPI.CERT_INFO certInfo = (CAPI.CERT_INFO) Marshal.PtrToStructure(pCertContext.pCertInfo, typeof(CAPI.CERT_INFO)); uint algId = X509Utils.OidToAlgId(certInfo.SubjectPublicKeyInfo.Algorithm.pszObjId); if (algId == CAPI.CALG_RSA_KEYX) recipientInfoType = RecipientInfoType.KeyTransport; else if (algId == CAPI.CALG_DH_SF || algId == CAPI.CALG_DH_EPHEM) recipientInfoType = RecipientInfoType.KeyAgreement; else recipientInfoType = RecipientInfoType.Unknown; } return recipientInfoType; } internal static unsafe int GetMaxKeyLength (SafeCryptProvHandle safeCryptProvHandle, uint algId) { uint enumFlag = CAPI.CRYPT_FIRST; uint cbPeex = (uint) Marshal.SizeOf(typeof(CAPI.PROV_ENUMALGS_EX)); SafeLocalAllocHandle pPeex = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(Marshal.SizeOf(typeof(CAPI.PROV_ENUMALGS_EX)))); using (pPeex) { while (CAPI.CryptGetProvParam(safeCryptProvHandle, CAPI.PP_ENUMALGS_EX, pPeex.DangerousGetHandle(), new IntPtr(&cbPeex), enumFlag)) { CAPI.PROV_ENUMALGS_EX peex = (CAPI.PROV_ENUMALGS_EX) Marshal.PtrToStructure(pPeex.DangerousGetHandle(), typeof(CAPI.PROV_ENUMALGS_EX)); if (peex.aiAlgid == algId) return (int) peex.dwMaxLen; enumFlag = 0; } } throw new CryptographicException(CAPI.CRYPT_E_UNKNOWN_ALGO); } internal static unsafe uint GetVersion (SafeCryptMsgHandle safeCryptMsgHandle) { uint dwVersion = 0; uint cbCount = (uint) Marshal.SizeOf(typeof(uint)); if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, CAPI.CMSG_VERSION_PARAM, 0, new IntPtr(&dwVersion), new IntPtr(&cbCount))) checkErr(Marshal.GetLastWin32Error()); return dwVersion; } internal static unsafe uint GetMessageType (SafeCryptMsgHandle safeCryptMsgHandle) { uint dwMsgType = 0; uint cbMsgType = (uint) Marshal.SizeOf(typeof(uint)); if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, CAPI.CMSG_TYPE_PARAM, 0, new IntPtr(&dwMsgType), new IntPtr(&cbMsgType))) checkErr(Marshal.GetLastWin32Error()); return dwMsgType; } internal static unsafe AlgorithmIdentifier GetAlgorithmIdentifier (SafeCryptMsgHandle safeCryptMsgHandle) { AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(); uint cbAlgorithm = 0; if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, CAPI.CMSG_ENVELOPE_ALGORITHM_PARAM, 0, IntPtr.Zero, new IntPtr(&cbAlgorithm))) checkErr(Marshal.GetLastWin32Error()); if (cbAlgorithm > 0) { SafeLocalAllocHandle pbAlgorithm = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(cbAlgorithm)); if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, CAPI.CMSG_ENVELOPE_ALGORITHM_PARAM, 0, pbAlgorithm, new IntPtr(&cbAlgorithm))) checkErr(Marshal.GetLastWin32Error()); CAPI.CRYPT_ALGORITHM_IDENTIFIER cryptAlgorithmIdentifier = (CAPI.CRYPT_ALGORITHM_IDENTIFIER) Marshal.PtrToStructure(pbAlgorithm.DangerousGetHandle(), typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER)); algorithmIdentifier = new AlgorithmIdentifier(cryptAlgorithmIdentifier); pbAlgorithm.Dispose(); } return algorithmIdentifier; } internal static unsafe void GetParam (SafeCryptMsgHandle safeCryptMsgHandle, uint paramType, uint index, out SafeLocalAllocHandle pvData, out uint cbData) { cbData = 0; pvData = SafeLocalAllocHandle.InvalidHandle; fixed (uint * pcbData = &cbData) { if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, paramType, index, pvData, new IntPtr(pcbData))) checkErr(Marshal.GetLastWin32Error()); if (cbData > 0) { pvData = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cbData)); if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, paramType, index, pvData, new IntPtr(pcbData))) checkErr(Marshal.GetLastWin32Error()); } } } internal static unsafe void GetParam (SafeCryptMsgHandle safeCryptMsgHandle, uint paramType, uint index, out byte[] pvData, out uint cbData) { cbData = 0; pvData = new byte[0]; fixed (uint * pcbData = &cbData) { if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, paramType, index, IntPtr.Zero, new IntPtr(pcbData))) checkErr(Marshal.GetLastWin32Error()); if (cbData > 0) { pvData = new byte[cbData]; fixed (byte * ppvData = &pvData[0]) { if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, paramType, index, new IntPtr(ppvData), new IntPtr(pcbData))) checkErr(Marshal.GetLastWin32Error()); } } } } internal static unsafe X509Certificate2Collection GetCertificates (SafeCryptMsgHandle safeCryptMsgHandle) { uint dwCount = 0; uint cbCount = (uint) Marshal.SizeOf(typeof(uint)); X509Certificate2Collection certificates = new X509Certificate2Collection(); if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, CAPI.CMSG_CERT_COUNT_PARAM, 0, new IntPtr(&dwCount), new IntPtr(&cbCount))) checkErr(Marshal.GetLastWin32Error()); for (uint index = 0; index < dwCount; index++) { uint cbEncoded = 0; SafeLocalAllocHandle pbEncoded = SafeLocalAllocHandle.InvalidHandle; GetParam(safeCryptMsgHandle, CAPI.CMSG_CERT_PARAM, index, out pbEncoded, out cbEncoded); if (cbEncoded > 0) { SafeCertContextHandle safeCertContextHandle = CAPI.CertCreateCertificateContext(CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING, pbEncoded, cbEncoded); if (safeCertContextHandle == null || safeCertContextHandle.IsInvalid) throw new CryptographicException(Marshal.GetLastWin32Error()); certificates.Add(new X509Certificate2(safeCertContextHandle.DangerousGetHandle())); safeCertContextHandle.Dispose(); } } return certificates; } internal static unsafe byte[] GetContent (SafeCryptMsgHandle safeCryptMsgHandle) { uint cbContent = 0; byte[] content = new byte[0]; GetParam(safeCryptMsgHandle, CAPI.CMSG_CONTENT_PARAM, 0, out content, out cbContent); return content; } internal static unsafe Oid GetContentType (SafeCryptMsgHandle safeCryptMsgHandle) { uint cbContentType = 0; byte[] contentType = new byte[0]; GetParam(safeCryptMsgHandle, CAPI.CMSG_INNER_CONTENT_TYPE_PARAM, 0, out contentType, out cbContentType); if (contentType.Length > 0 && contentType[contentType.Length - 1] == 0) { byte[] temp = new byte[contentType.Length - 1]; Array.Copy(contentType, 0, temp, 0, temp.Length); contentType = temp; } return new Oid(Encoding.ASCII.GetString(contentType)); } internal static unsafe byte[] GetMessage (SafeCryptMsgHandle safeCryptMsgHandle) { uint cbMessage = 0; byte[] message = new byte[0]; GetParam(safeCryptMsgHandle, CAPI.CMSG_ENCODED_MESSAGE, 0, out message, out cbMessage); return message; } internal static unsafe int GetSignerIndex (SafeCryptMsgHandle safeCrytpMsgHandle, SignerInfo signerInfo, int startIndex) { uint dwSigners = 0; uint cbCount = (uint) Marshal.SizeOf(typeof(uint)); if (!CAPI.CryptMsgGetParam(safeCrytpMsgHandle, CAPI.CMSG_SIGNER_COUNT_PARAM, 0, new IntPtr(&dwSigners), new IntPtr(&cbCount))) checkErr(Marshal.GetLastWin32Error()); for (int index = startIndex; index < (int) dwSigners; index++) { uint cbCmsgSignerInfo = 0; if (!CAPI.CryptMsgGetParam(safeCrytpMsgHandle, CAPI.CMSG_SIGNER_INFO_PARAM, (uint) index, IntPtr.Zero, new IntPtr(&cbCmsgSignerInfo))) checkErr(Marshal.GetLastWin32Error()); if (cbCmsgSignerInfo > 0) { SafeLocalAllocHandle pbCmsgSignerInfo = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(cbCmsgSignerInfo)); if (!CAPI.CryptMsgGetParam(safeCrytpMsgHandle, CAPI.CMSG_SIGNER_INFO_PARAM, (uint) index, pbCmsgSignerInfo, new IntPtr(&cbCmsgSignerInfo))) checkErr(Marshal.GetLastWin32Error()); CAPI.CMSG_SIGNER_INFO cmsgSignerInfo1 = signerInfo.GetCmsgSignerInfo(); CAPI.CMSG_SIGNER_INFO cmsgSignerInfo2 = (CAPI.CMSG_SIGNER_INFO) Marshal.PtrToStructure(pbCmsgSignerInfo.DangerousGetHandle(), typeof(CAPI.CMSG_SIGNER_INFO)); if (X509Utils.MemEqual((byte *) cmsgSignerInfo1.Issuer.pbData, cmsgSignerInfo1.Issuer.cbData, (byte *) cmsgSignerInfo2.Issuer.pbData, cmsgSignerInfo2.Issuer.cbData) && X509Utils.MemEqual((byte *) cmsgSignerInfo1.SerialNumber.pbData, cmsgSignerInfo1.SerialNumber.cbData, (byte *) cmsgSignerInfo2.SerialNumber.pbData, cmsgSignerInfo2.SerialNumber.cbData)) { return index; // Signer's index is found. } // Keep alive. pbCmsgSignerInfo.Dispose(); } } throw new CryptographicException(CAPI.CRYPT_E_SIGNER_NOT_FOUND); } internal static unsafe CryptographicAttributeObjectCollection GetUnprotectedAttributes (SafeCryptMsgHandle safeCryptMsgHandle) { uint cbUnprotectedAttr = 0; CryptographicAttributeObjectCollection attributes = new CryptographicAttributeObjectCollection(); SafeLocalAllocHandle pbUnprotectedAttr = SafeLocalAllocHandle.InvalidHandle; if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, CAPI.CMSG_UNPROTECTED_ATTR_PARAM, 0, pbUnprotectedAttr, new IntPtr(&cbUnprotectedAttr))) { int lastWin32Error = Marshal.GetLastWin32Error(); if (lastWin32Error != CAPI.CRYPT_E_ATTRIBUTES_MISSING) checkErr(Marshal.GetLastWin32Error()); } if (cbUnprotectedAttr > 0) { using (pbUnprotectedAttr = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cbUnprotectedAttr))) { if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, CAPI.CMSG_UNPROTECTED_ATTR_PARAM, 0, pbUnprotectedAttr, new IntPtr(&cbUnprotectedAttr))) checkErr(Marshal.GetLastWin32Error()); attributes = new CryptographicAttributeObjectCollection(pbUnprotectedAttr); } } return attributes; } internal unsafe static X509IssuerSerial DecodeIssuerSerial (CAPI.CERT_ISSUER_SERIAL_NUMBER pIssuerAndSerial) { SafeLocalAllocHandle ptr = SafeLocalAllocHandle.InvalidHandle; uint cbSize = CAPI.CertNameToStrW(CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING, new IntPtr(&pIssuerAndSerial.Issuer), CAPI.CERT_X500_NAME_STR | CAPI.CERT_NAME_STR_REVERSE_FLAG, ptr, 0); if (cbSize <= 1) // The API actually return 1 when It fails; which is not what the documentation says. throw new CryptographicException(Marshal.GetLastWin32Error()); ptr = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(2 * cbSize)); cbSize = CAPI.CertNameToStrW(CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING, new IntPtr(&pIssuerAndSerial.Issuer), CAPI.CERT_X500_NAME_STR | CAPI.CERT_NAME_STR_REVERSE_FLAG, ptr, cbSize); if (cbSize <= 1) throw new CryptographicException(Marshal.GetLastWin32Error()); X509IssuerSerial issuerSerial = new X509IssuerSerial(); issuerSerial.IssuerName = Marshal.PtrToStringUni(ptr.DangerousGetHandle()); byte[] serial = new byte[pIssuerAndSerial.SerialNumber.cbData]; Marshal.Copy(pIssuerAndSerial.SerialNumber.pbData, serial, 0, serial.Length); issuerSerial.SerialNumber = X509Utils.EncodeHexStringFromInt(serial); ptr.Dispose(); return issuerSerial; } internal static string DecodeOctetString (byte[] encodedOctetString) { uint cbDecoded = 0; SafeLocalAllocHandle pbDecoded = null; if (!CAPI.DecodeObject(new IntPtr(CAPI.X509_OCTET_STRING), encodedOctetString, out pbDecoded, out cbDecoded)) throw new CryptographicException(Marshal.GetLastWin32Error()); if (cbDecoded == 0) return String.Empty; CAPI.CRYPTOAPI_BLOB decodedBlob = (CAPI.CRYPTOAPI_BLOB) Marshal.PtrToStructure(pbDecoded.DangerousGetHandle(), typeof(CAPI.CRYPTOAPI_BLOB)); if (decodedBlob.cbData == 0) return String.Empty; string octetString = Marshal.PtrToStringUni(decodedBlob.pbData); pbDecoded.Dispose(); return octetString; } internal static byte[] DecodeOctetBytes (byte[] encodedOctetString) { uint cbDecoded = 0; SafeLocalAllocHandle pbDecoded = null; if (!CAPI.DecodeObject(new IntPtr(CAPI.X509_OCTET_STRING), encodedOctetString, out pbDecoded, out cbDecoded)) throw new CryptographicException(Marshal.GetLastWin32Error()); if (cbDecoded == 0) return new byte[0]; using (pbDecoded) { return CAPI.BlobToByteArray(pbDecoded.DangerousGetHandle()); } } internal static byte[] EncodeOctetString (string octetString) { // Marshal data to be encoded to unmanaged memory. byte[] octets = new byte[2 * (octetString.Length + 1)]; Encoding.Unicode.GetBytes(octetString, 0, octetString.Length, octets, 0); return EncodeOctetString(octets); } internal static unsafe byte[] EncodeOctetString (byte[] octets) { fixed (byte * pbOctets = octets) { CAPI.CRYPTOAPI_BLOB octetsBlob = new CAPI.CRYPTOAPI_BLOB(); octetsBlob.cbData = (uint) octets.Length; octetsBlob.pbData = new IntPtr(pbOctets); // Encode data. byte[] encodedOctets = new byte[0]; if (!CAPI.EncodeObject(new IntPtr((long) CAPI.X509_OCTET_STRING), new IntPtr((long) &octetsBlob), out encodedOctets)) { throw new CryptographicException(Marshal.GetLastWin32Error()); } return encodedOctets; } } internal static string DecodeObjectIdentifier (byte[] encodedObjId, int offset) { StringBuilder objId = new StringBuilder(""); if (0 < (encodedObjId.Length - offset)) { byte b = encodedObjId[offset]; byte c = (byte) ((uint) b / 40); objId.Append(c.ToString(null, null)); objId.Append("."); c = (byte) ((uint) b % 40); objId.Append(c.ToString(null, null)); ulong s = 0; for (int index = offset + 1; index < encodedObjId.Length; index++) { c = encodedObjId[index]; s = (s << 7) + (ulong) (c & 0x7f); if (0 == (c & 0x80)) { objId.Append("."); objId.Append(s.ToString(null, null)); s = 0; } } // s should be 0 at this point, otherwise we have a bad ASN. if (0 != s) { throw new CryptographicException(CAPI.CRYPT_E_BAD_ENCODE); } } return objId.ToString(); } internal static CmsRecipientCollection SelectRecipients (SubjectIdentifierType recipientIdentifierType) { X509Store store = new X509Store("AddressBook"); store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); X509Certificate2Collection certificates = new X509Certificate2Collection(store.Certificates); foreach (X509Certificate2 certificate in store.Certificates) { if (certificate.NotBefore <= DateTime.Now && certificate.NotAfter >= DateTime.Now) { bool validUsages = true; foreach (X509Extension extension in certificate.Extensions) { if (String.Compare(extension.Oid.Value, CAPI.szOID_KEY_USAGE, StringComparison.OrdinalIgnoreCase) == 0) { X509KeyUsageExtension keyUsage = new X509KeyUsageExtension(); keyUsage.CopyFrom(extension); if ((keyUsage.KeyUsages & X509KeyUsageFlags.KeyEncipherment) == 0 && (keyUsage.KeyUsages & X509KeyUsageFlags.KeyAgreement) == 0) { validUsages = false; } break; } } if (validUsages) { certificates.Add(certificate); } } } if (certificates.Count < 1) throw new CryptographicException(CAPI.CRYPT_E_RECIPIENT_NOT_FOUND); X509Certificate2Collection recipients = X509Certificate2UI.SelectFromCollection(certificates, null, null, X509SelectionFlag.MultiSelection); if (recipients.Count < 1) throw new CryptographicException(CAPI.ERROR_CANCELLED); return new CmsRecipientCollection(recipientIdentifierType, recipients); } internal static X509Certificate2 SelectSignerCertificate () { X509Store store = new X509Store(); store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly | OpenFlags.IncludeArchived); X509Certificate2Collection certificates = new X509Certificate2Collection(); foreach (X509Certificate2 certificate in store.Certificates) { if (certificate.HasPrivateKey && certificate.NotBefore <= DateTime.Now && certificate.NotAfter >= DateTime.Now) { bool validUsages = true; foreach (X509Extension extension in certificate.Extensions) { if (String.Compare(extension.Oid.Value, CAPI.szOID_KEY_USAGE, StringComparison.OrdinalIgnoreCase) == 0) { X509KeyUsageExtension keyUsage = new X509KeyUsageExtension(); keyUsage.CopyFrom(extension); if ((keyUsage.KeyUsages & X509KeyUsageFlags.DigitalSignature) == 0 && (keyUsage.KeyUsages & X509KeyUsageFlags.NonRepudiation) == 0) { validUsages = false; } break; } } if (validUsages) { certificates.Add(certificate); } } } if (certificates.Count < 1) throw new CryptographicException(CAPI.CRYPT_E_SIGNER_NOT_FOUND); certificates = X509Certificate2UI.SelectFromCollection(certificates, null, null, X509SelectionFlag.SingleSelection); if (certificates.Count < 1) throw new CryptographicException(CAPI.ERROR_CANCELLED); Debug.Assert(certificates.Count == 1); return certificates[0]; } internal static AsnEncodedDataCollection GetAsnEncodedDataCollection (CAPI.CRYPT_ATTRIBUTE cryptAttribute) { AsnEncodedDataCollection list = new AsnEncodedDataCollection(); Oid oid = new Oid(cryptAttribute.pszObjId); string szOid = oid.Value; for (uint index = 0; index < cryptAttribute.cValue; index++) { IntPtr pAttributeBlob = new IntPtr((long)cryptAttribute.rgValue + (index * Marshal.SizeOf(typeof(CAPI.CRYPTOAPI_BLOB)))); Pkcs9AttributeObject attribute = new Pkcs9AttributeObject(oid, CAPI.BlobToByteArray(pAttributeBlob)); Pkcs9AttributeObject customAttribute = CryptoConfig.CreateFromName(szOid) as Pkcs9AttributeObject; if (customAttribute != null) { customAttribute.CopyFrom(attribute); attribute = customAttribute; } list.Add(attribute); } return list; } internal static AsnEncodedDataCollection GetAsnEncodedDataCollection (CAPI.CRYPT_ATTRIBUTE_TYPE_VALUE cryptAttribute) { AsnEncodedDataCollection list = new AsnEncodedDataCollection(); list.Add(new Pkcs9AttributeObject(new Oid(cryptAttribute.pszObjId), CAPI.BlobToByteArray(cryptAttribute.Value))); return list; } internal static unsafe IntPtr CreateCryptAttributes (CryptographicAttributeObjectCollection attributes) { // NULL if no attribute. if (attributes.Count == 0) return IntPtr.Zero; // // The goal here is to compute the size needed for the attributes we are passing to CMSG_SIGNER_ENCODE_INFO // The unmanaged memory structure we are creating here has the following layout: // // Let cAttr = number of attributes. // // This to create the array of CRYPT_ATTRIBUTE // for i = 0 to cAttr { // CRYPT_ATTRRIBUTE[i] // pszObjId | cValue | rgValue // } // // This is to fill in the data for each entry of CRYPT_ATTRIBUTE array above. // for i = 0 to cAttr { // objId[i] // Value of the Oid, i.e "1.2.3.4" // for j = 0 to CRYPT_ATTRIBUTE[i].cValue - 1 { // Array of CRYPTOAPI_BLOB // CRYPT_ATTRIBUTE[i].rgValue[j].cbData // Data size // CRYPT_ATTRIBUTE[i].rgValue[j].pbData // Pointer to data // } // for j = 0 to CRYPT_ATTRIBUTE[i].cValue - 1 { // Data for each entry of the CRYPTOAPI_BLOB array above. // *CRYPT_ATTRIBUTE[i].rgValue[j].pbData // The actual data // } // } uint totalLength = 0; uint cryptAttrSize = AlignedLength((uint) Marshal.SizeOf(typeof(I_CRYPT_ATTRIBUTE))); uint cryptBlobSize = AlignedLength((uint) Marshal.SizeOf(typeof(CAPI.CRYPTOAPI_BLOB))); // First compute the total serialized unmanaged memory size needed. // For each attribute, we add the CRYPT_ATTRIBUTE size, the size // needed for the ObjId, and the size needed for all the values // inside each attribute which is computed in inner loop. foreach (CryptographicAttributeObject attribute in attributes) { totalLength += cryptAttrSize; // sizeof(CRYPT_ATTRIBUTE) totalLength += AlignedLength((uint) (attribute.Oid.Value.Length + 1)); // strlen(pszObjId) + 1 // For each value within the attribute, we add the CRYPT_ATTR_BLOB size and // the actual size needed for the data. foreach (AsnEncodedData attributeValue in attribute.Values) { totalLength += cryptBlobSize; // Add CRYPT_ATTR_BLOB size totalLength += AlignedLength((uint) attributeValue.RawData.Length); // Data size } } // Allocate the unmanaged memory blob to hold the entire serialized CRYPT_ATTRIBUTE array. SafeLocalAllocHandle pCryptAttributes = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(totalLength)); // Now fill up unmanaged memory with data from the managed side. I_CRYPT_ATTRIBUTE * pCryptAttribute = (I_CRYPT_ATTRIBUTE *) pCryptAttributes.DangerousGetHandle(); IntPtr pAttrData = new IntPtr((long) pCryptAttributes.DangerousGetHandle() + (cryptAttrSize * attributes.Count)); foreach (CryptographicAttributeObject attribute in attributes) { byte * pszObjId = (byte *) pAttrData; byte[] objId = new byte[attribute.Oid.Value.Length + 1]; CAPI.CRYPTOAPI_BLOB * pDataBlob = (CAPI.CRYPTOAPI_BLOB *) (pszObjId + AlignedLength((uint) objId.Length)); // CRYPT_ATTRIBUTE.pszObjId pCryptAttribute->pszObjId = (IntPtr) pszObjId; // CRYPT_ATTRIBUTE.cValue pCryptAttribute->cValue = (uint) attribute.Values.Count; // CRYPT_ATTRIBUTE.rgValue pCryptAttribute->rgValue = (IntPtr) pDataBlob; // ObjId - The actual dotted value of the OID. Encoding.ASCII.GetBytes(attribute.Oid.Value, 0, attribute.Oid.Value.Length, objId, 0); Marshal.Copy(objId, 0, pCryptAttribute->pszObjId, objId.Length); // cValue of CRYPT_ATTR_BLOBs followed by cValue of actual data. IntPtr pbEncodedData = new IntPtr((long) pDataBlob + (attribute.Values.Count * cryptBlobSize)); foreach (AsnEncodedData value in attribute.Values) { // Retrieve encoded data. byte[] encodedData = value.RawData; // Write data if (encodedData.Length > 0) { // CRYPT_ATTR_BLOB.cbData pDataBlob->cbData = (uint) encodedData.Length; // CRYPT_ATTR_BLOB.pbData pDataBlob->pbData = pbEncodedData; Marshal.Copy(encodedData, 0, pbEncodedData, encodedData.Length); pbEncodedData = new IntPtr((long) pbEncodedData + AlignedLength((uint) encodedData.Length)); } // Advance pointer. pDataBlob++; } // Advance pointers. pCryptAttribute++; pAttrData = pbEncodedData; } // Since we are returning IntPtr, we MUST supress finalizer, otherwise // the GC can collect the memory underneath us!!! GC.SuppressFinalize(pCryptAttributes); return pCryptAttributes.DangerousGetHandle(); } internal static unsafe CAPI.CMSG_SIGNER_ENCODE_INFO CreateSignerEncodeInfo (CmsSigner signer) { return CreateSignerEncodeInfo(signer, false); } internal static unsafe CAPI.CMSG_SIGNER_ENCODE_INFO CreateSignerEncodeInfo (CmsSigner signer, bool silent) { CAPI.CMSG_SIGNER_ENCODE_INFO cmsSignerEncodeInfo = new CAPI.CMSG_SIGNER_ENCODE_INFO(Marshal.SizeOf(typeof(CAPI.CMSG_SIGNER_ENCODE_INFO))); SafeCryptProvHandle safeCryptProvHandle = SafeCryptProvHandle.InvalidHandle; uint keySpec = 0; bool freeCsp = false; cmsSignerEncodeInfo.HashAlgorithm.pszObjId = signer.DigestAlgorithm.Value; if (0 == String.Compare( signer.Certificate.PublicKey.Oid.Value, CAPI.szOID_X957_DSA, StringComparison.Ordinal)) { cmsSignerEncodeInfo.HashEncryptionAlgorithm.pszObjId = CAPI.szOID_X957_sha1DSA; } cmsSignerEncodeInfo.cAuthAttr = (uint) signer.SignedAttributes.Count; cmsSignerEncodeInfo.rgAuthAttr = CreateCryptAttributes(signer.SignedAttributes); cmsSignerEncodeInfo.cUnauthAttr = (uint) signer.UnsignedAttributes.Count; cmsSignerEncodeInfo.rgUnauthAttr = CreateCryptAttributes(signer.UnsignedAttributes); if (signer.SignerIdentifierType == SubjectIdentifierType.NoSignature) { cmsSignerEncodeInfo.HashEncryptionAlgorithm.pszObjId = CAPI.szOID_PKIX_NO_SIGNATURE; cmsSignerEncodeInfo.pCertInfo = IntPtr.Zero; cmsSignerEncodeInfo.dwKeySpec = keySpec; // If the HashEncryptionAlgorithm is set to szOID_PKIX_NO_SIGNATURE, then, // the signature value only contains the hash octets. hCryptProv must still // be specified. However, since a private key isn't used the hCryptProv can be // acquired using CRYPT_VERIFYCONTEXT. if (!CAPI.CryptAcquireContext(ref safeCryptProvHandle, null, null, CAPI.PROV_RSA_FULL, CAPI.CRYPT_VERIFYCONTEXT)) { // call it again for win9x platforms if (!CAPI.CryptAcquireContext(ref safeCryptProvHandle, null, null, CAPI.PROV_RSA_FULL, 0)) throw new CryptographicException(Marshal.GetLastWin32Error()); } cmsSignerEncodeInfo.hCryptProv = safeCryptProvHandle.DangerousGetHandle(); GC.SuppressFinalize(safeCryptProvHandle); // Fake up the SignerId so our server can recognize it // dwIdChoice cmsSignerEncodeInfo.SignerId.dwIdChoice = CAPI.CERT_ID_ISSUER_SERIAL_NUMBER; // Issuer X500DistinguishedName dummyName = new X500DistinguishedName(CAPI.DummySignerCommonName); dummyName.Oid = new Oid(CAPI.szOID_RDN_DUMMY_SIGNER); cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.Issuer.cbData = (uint)dummyName.RawData.Length; SafeLocalAllocHandle pbDataIssuer = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.Issuer.cbData)); Marshal.Copy(dummyName.RawData, 0, pbDataIssuer.DangerousGetHandle(), dummyName.RawData.Length); cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.Issuer.pbData = pbDataIssuer.DangerousGetHandle(); GC.SuppressFinalize(pbDataIssuer); // SerialNumber cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.SerialNumber.cbData = (uint)1; SafeLocalAllocHandle pbDataSerialNumber = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.SerialNumber.cbData)); byte * pSerialNumber = (byte *)pbDataSerialNumber.DangerousGetHandle(); *pSerialNumber = 0x00; cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.SerialNumber.pbData = pbDataSerialNumber.DangerousGetHandle(); GC.SuppressFinalize(pbDataSerialNumber); return cmsSignerEncodeInfo; } SafeCertContextHandle safeCertContextHandle = X509Utils.GetCertContext(signer.Certificate); if (!CAPI.CryptAcquireCertificatePrivateKey(safeCertContextHandle, (silent? CAPI.CRYPT_SILENT | CAPI.CRYPT_ACQUIRE_USE_PROV_INFO_FLAG | CAPI.CRYPT_ACQUIRE_COMPARE_KEY_FLAG: CAPI.CRYPT_ACQUIRE_USE_PROV_INFO_FLAG | CAPI.CRYPT_ACQUIRE_COMPARE_KEY_FLAG), IntPtr.Zero, ref safeCryptProvHandle, ref keySpec, ref freeCsp)) throw new CryptographicException(Marshal.GetLastWin32Error()); cmsSignerEncodeInfo.dwKeySpec = keySpec; cmsSignerEncodeInfo.hCryptProv = safeCryptProvHandle.DangerousGetHandle(); // Since we are storing only IntPtr in CMSG_SIGNER_ENCODE_INFO.hCryptProv, we MUST then // supress the finalizer, otherwise the GC can collect the resource underneath us!!! GC.SuppressFinalize(safeCryptProvHandle); CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle()); cmsSignerEncodeInfo.pCertInfo = pCertContext.pCertInfo; // If CMS, then fill in the Subject Key Identifier (SKI) or SignerId. if (signer.SignerIdentifierType == SubjectIdentifierType.SubjectKeyIdentifier) { uint cbData = 0; SafeLocalAllocHandle pbData = SafeLocalAllocHandle.InvalidHandle; if (!CAPI.CertGetCertificateContextProperty(safeCertContextHandle, CAPI.CERT_KEY_IDENTIFIER_PROP_ID, pbData, ref cbData)) throw new CryptographicException(Marshal.GetLastWin32Error()); if (cbData > 0) { pbData = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cbData)); if (!CAPI.CertGetCertificateContextProperty(safeCertContextHandle, CAPI.CERT_KEY_IDENTIFIER_PROP_ID, pbData, ref cbData)) throw new CryptographicException(Marshal.GetLastWin32Error()); cmsSignerEncodeInfo.SignerId.dwIdChoice = CAPI.CERT_ID_KEY_IDENTIFIER; cmsSignerEncodeInfo.SignerId.Value.KeyId.cbData = cbData; cmsSignerEncodeInfo.SignerId.Value.KeyId.pbData = pbData.DangerousGetHandle(); // Since we are storing only IntPtr in CMSG_SIGNER_ENCODE_INFO.SignerId.KeyId.pbData, // we MUST supress finalizer, otherwise the GC can collect the resource underneath us!!! GC.SuppressFinalize(pbData); } } return cmsSignerEncodeInfo; } internal static X509Certificate2Collection CreateBagOfCertificates (CmsSigner signer) { X509Certificate2Collection certificates = new X509Certificate2Collection(); // // First add extra bag of certs. // certificates.AddRange(signer.Certificates); // // Then include chain option. // if (signer.IncludeOption != X509IncludeOption.None) { if (signer.IncludeOption == X509IncludeOption.EndCertOnly) { certificates.Add(signer.Certificate); } else { int cCerts = 1; X509Chain chain = new X509Chain(); chain.Build(signer.Certificate); // Can't honor the option if we only have a partial chain. if ((chain.ChainStatus.Length > 0) && ((chain.ChainStatus[0].Status & X509ChainStatusFlags.PartialChain) == X509ChainStatusFlags.PartialChain)) throw new CryptographicException(CAPI.CERT_E_CHAINING); if (signer.IncludeOption == X509IncludeOption.WholeChain) { cCerts = chain.ChainElements.Count; } else { // Default to ExcludeRoot. if (chain.ChainElements.Count > 1) { cCerts = chain.ChainElements.Count - 1; } } for (int i = 0; i < cCerts; i++) { certificates.Add(chain.ChainElements[i].Certificate); } } } return certificates; } internal static unsafe SafeLocalAllocHandle CreateEncodedCertBlob (X509Certificate2Collection certificates) { SafeLocalAllocHandle certBlob = SafeLocalAllocHandle.InvalidHandle; if (certificates.Count > 0) { certBlob = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(certificates.Count * Marshal.SizeOf(typeof(CAPI.CRYPTOAPI_BLOB)))); CAPI.CRYPTOAPI_BLOB * pCertBlob = (CAPI.CRYPTOAPI_BLOB * ) certBlob.DangerousGetHandle(); foreach (X509Certificate2 certificate in certificates) { SafeCertContextHandle safeCertContextHandle = X509Utils.GetCertContext(certificate); CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle()); pCertBlob->cbData = pCertContext.cbCertEncoded; pCertBlob->pbData = pCertContext.pbCertEncoded; pCertBlob++; } } return certBlob; } internal static unsafe uint AddCertsToMessage (SafeCryptMsgHandle safeCryptMsgHandle, X509Certificate2Collection bagOfCerts, X509Certificate2Collection chainOfCerts) { uint certsAdded = 0; foreach (X509Certificate2 certificate in chainOfCerts) { // Skip it if already in the bag of certs. X509Certificate2Collection foundCerts = bagOfCerts.Find(X509FindType.FindByThumbprint, certificate.Thumbprint, false); if (foundCerts.Count == 0) { SafeCertContextHandle safeCertContextHandle = X509Utils.GetCertContext(certificate); CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle()); CAPI.CRYPTOAPI_BLOB certBlob = new CAPI.CRYPTOAPI_BLOB(); certBlob.cbData = pCertContext.cbCertEncoded; certBlob.pbData = pCertContext.pbCertEncoded; if (!CAPI.CryptMsgControl(safeCryptMsgHandle, 0, CAPI.CMSG_CTRL_ADD_CERT, new IntPtr((long) &certBlob))) throw new CryptographicException(Marshal.GetLastWin32Error()); certsAdded++; } } return certsAdded; } internal static X509Certificate2 FindCertificate (SubjectIdentifier identifier, X509Certificate2Collection certificates) { X509Certificate2 certificate = null; if (certificates != null && certificates.Count > 0) { X509Certificate2Collection filters; switch (identifier.Type) { case SubjectIdentifierType.IssuerAndSerialNumber: filters = certificates.Find(X509FindType.FindByIssuerDistinguishedName, ((X509IssuerSerial) identifier.Value).IssuerName, false); if (filters.Count > 0) { filters = filters.Find(X509FindType.FindBySerialNumber, ((X509IssuerSerial) identifier.Value).SerialNumber, false); if (filters.Count > 0) certificate = filters[0]; } break; case SubjectIdentifierType.SubjectKeyIdentifier: filters = certificates.Find(X509FindType.FindBySubjectKeyIdentifier, identifier.Value, false); if (filters.Count > 0) certificate = filters[0]; break; } } return certificate; } private static void checkErr (int err) { if (CAPI.CRYPT_E_INVALID_MSG_TYPE != err) throw new CryptographicException(err); } // for Key ID signing only internal static unsafe X509Certificate2 CreateDummyCertificate (CspParameters parameters) { SafeCertContextHandle handle = SafeCertContextHandle.InvalidHandle; // hProv SafeCryptProvHandle hProv = SafeCryptProvHandle.InvalidHandle; UInt32 dwFlags = 0; if (0 != (parameters.Flags & CspProviderFlags.UseMachineKeyStore)) { dwFlags |= CAPI.CRYPT_MACHINE_KEYSET; } if (0 != (parameters.Flags & CspProviderFlags.UseDefaultKeyContainer)) { dwFlags |= CAPI.CRYPT_VERIFYCONTEXT; } if (0 != (parameters.Flags & CspProviderFlags.NoPrompt)) { dwFlags |= CAPI.CRYPT_SILENT; } bool rc = CAPI.CryptAcquireContext(ref hProv, parameters.KeyContainerName, parameters.ProviderName, (uint)parameters.ProviderType, dwFlags); if (!rc) throw new CryptographicException(Marshal.GetLastWin32Error()); // pKeyProvInfo CAPI.CRYPT_KEY_PROV_INFO KeyProvInfo = new CAPI.CRYPT_KEY_PROV_INFO(); KeyProvInfo.pwszProvName = parameters.ProviderName; KeyProvInfo.pwszContainerName = parameters.KeyContainerName; KeyProvInfo.dwProvType = (uint)parameters.ProviderType; KeyProvInfo.dwKeySpec = (uint)parameters.KeyNumber ; KeyProvInfo.dwFlags = (uint)((parameters.Flags & CspProviderFlags.UseMachineKeyStore) == CspProviderFlags.UseMachineKeyStore ? CAPI.CRYPT_MACHINE_KEYSET : 0); SafeLocalAllocHandle pKeyProvInfo = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(Marshal.SizeOf(typeof(CAPI.CRYPT_KEY_PROV_INFO)))); Marshal.StructureToPtr(KeyProvInfo, pKeyProvInfo.DangerousGetHandle(), false); // Signature CAPI.CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm = new CAPI.CRYPT_ALGORITHM_IDENTIFIER(); SignatureAlgorithm.pszObjId = CAPI.szOID_OIWSEC_sha1RSASign; SafeLocalAllocHandle pSignatureAlgorithm = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr( Marshal.SizeOf(typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER)))); Marshal.StructureToPtr(SignatureAlgorithm, pSignatureAlgorithm.DangerousGetHandle(), false); // pSubjectIssuerBlob X500DistinguishedName subjectName = new X500DistinguishedName("cn=CMS Signer Dummy Certificate"); fixed (byte * pbOctets = subjectName.RawData) { CAPI.CRYPTOAPI_BLOB SubjectIssuerBlob = new CAPI.CRYPTOAPI_BLOB(); SubjectIssuerBlob.cbData = (uint)subjectName.RawData.Length; SubjectIssuerBlob.pbData = new IntPtr(pbOctets); handle = CAPI.CertCreateSelfSignCertificate(hProv, new IntPtr(&SubjectIssuerBlob), 1, pKeyProvInfo.DangerousGetHandle(), pSignatureAlgorithm.DangerousGetHandle(), IntPtr.Zero, //StartTime IntPtr.Zero, //EndTime IntPtr.Zero); //Extensions } Marshal.DestroyStructure(pKeyProvInfo.DangerousGetHandle(), typeof(CAPI.CRYPT_KEY_PROV_INFO)); pKeyProvInfo.Dispose(); Marshal.DestroyStructure(pSignatureAlgorithm.DangerousGetHandle(), typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER)); pSignatureAlgorithm.Dispose(); if (handle == null || handle.IsInvalid) throw new CryptographicException(Marshal.GetLastWin32Error()); X509Certificate2 certificate = new X509Certificate2(handle.DangerousGetHandle()); handle.Dispose(); return certificate; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== // // PkcsUtils.cs // namespace System.Security.Cryptography.Pkcs { using System.Collections; using System.Diagnostics; using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Security; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.Xml; using System.Text; internal class PkcsUtils { private PkcsUtils () {} private static int m_cmsSupported = -1; private struct I_CRYPT_ATTRIBUTE { internal IntPtr pszObjId; internal uint cValue; internal IntPtr rgValue; // PCRYPT_ATTR_BLOB } internal static uint AlignedLength (uint length) { return ((length + (uint) 7) & ((uint) 0xfffffff8)); } internal static unsafe bool CmsSupported () { if (m_cmsSupported == -1) { IntPtr hModule = CAPI.LoadLibrary("Crypt32.dll"); if (hModule != IntPtr.Zero) { IntPtr pFunc = CAPI.GetProcAddress(hModule, "CryptMsgVerifyCountersignatureEncodedEx"); m_cmsSupported = pFunc == IntPtr.Zero ? 0 : 1; CAPI.FreeLibrary(hModule); } } return m_cmsSupported == 0 ? false : true; } internal static RecipientInfoType GetRecipientInfoType (X509Certificate2 certificate) { RecipientInfoType recipientInfoType = RecipientInfoType.Unknown; if (certificate != null) { CAPI.CERT_CONTEXT pCertContext = (CAPI.CERT_CONTEXT) Marshal.PtrToStructure(X509Utils.GetCertContext(certificate).DangerousGetHandle(), typeof(CAPI.CERT_CONTEXT)); CAPI.CERT_INFO certInfo = (CAPI.CERT_INFO) Marshal.PtrToStructure(pCertContext.pCertInfo, typeof(CAPI.CERT_INFO)); uint algId = X509Utils.OidToAlgId(certInfo.SubjectPublicKeyInfo.Algorithm.pszObjId); if (algId == CAPI.CALG_RSA_KEYX) recipientInfoType = RecipientInfoType.KeyTransport; else if (algId == CAPI.CALG_DH_SF || algId == CAPI.CALG_DH_EPHEM) recipientInfoType = RecipientInfoType.KeyAgreement; else recipientInfoType = RecipientInfoType.Unknown; } return recipientInfoType; } internal static unsafe int GetMaxKeyLength (SafeCryptProvHandle safeCryptProvHandle, uint algId) { uint enumFlag = CAPI.CRYPT_FIRST; uint cbPeex = (uint) Marshal.SizeOf(typeof(CAPI.PROV_ENUMALGS_EX)); SafeLocalAllocHandle pPeex = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(Marshal.SizeOf(typeof(CAPI.PROV_ENUMALGS_EX)))); using (pPeex) { while (CAPI.CryptGetProvParam(safeCryptProvHandle, CAPI.PP_ENUMALGS_EX, pPeex.DangerousGetHandle(), new IntPtr(&cbPeex), enumFlag)) { CAPI.PROV_ENUMALGS_EX peex = (CAPI.PROV_ENUMALGS_EX) Marshal.PtrToStructure(pPeex.DangerousGetHandle(), typeof(CAPI.PROV_ENUMALGS_EX)); if (peex.aiAlgid == algId) return (int) peex.dwMaxLen; enumFlag = 0; } } throw new CryptographicException(CAPI.CRYPT_E_UNKNOWN_ALGO); } internal static unsafe uint GetVersion (SafeCryptMsgHandle safeCryptMsgHandle) { uint dwVersion = 0; uint cbCount = (uint) Marshal.SizeOf(typeof(uint)); if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, CAPI.CMSG_VERSION_PARAM, 0, new IntPtr(&dwVersion), new IntPtr(&cbCount))) checkErr(Marshal.GetLastWin32Error()); return dwVersion; } internal static unsafe uint GetMessageType (SafeCryptMsgHandle safeCryptMsgHandle) { uint dwMsgType = 0; uint cbMsgType = (uint) Marshal.SizeOf(typeof(uint)); if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, CAPI.CMSG_TYPE_PARAM, 0, new IntPtr(&dwMsgType), new IntPtr(&cbMsgType))) checkErr(Marshal.GetLastWin32Error()); return dwMsgType; } internal static unsafe AlgorithmIdentifier GetAlgorithmIdentifier (SafeCryptMsgHandle safeCryptMsgHandle) { AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(); uint cbAlgorithm = 0; if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, CAPI.CMSG_ENVELOPE_ALGORITHM_PARAM, 0, IntPtr.Zero, new IntPtr(&cbAlgorithm))) checkErr(Marshal.GetLastWin32Error()); if (cbAlgorithm > 0) { SafeLocalAllocHandle pbAlgorithm = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(cbAlgorithm)); if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, CAPI.CMSG_ENVELOPE_ALGORITHM_PARAM, 0, pbAlgorithm, new IntPtr(&cbAlgorithm))) checkErr(Marshal.GetLastWin32Error()); CAPI.CRYPT_ALGORITHM_IDENTIFIER cryptAlgorithmIdentifier = (CAPI.CRYPT_ALGORITHM_IDENTIFIER) Marshal.PtrToStructure(pbAlgorithm.DangerousGetHandle(), typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER)); algorithmIdentifier = new AlgorithmIdentifier(cryptAlgorithmIdentifier); pbAlgorithm.Dispose(); } return algorithmIdentifier; } internal static unsafe void GetParam (SafeCryptMsgHandle safeCryptMsgHandle, uint paramType, uint index, out SafeLocalAllocHandle pvData, out uint cbData) { cbData = 0; pvData = SafeLocalAllocHandle.InvalidHandle; fixed (uint * pcbData = &cbData) { if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, paramType, index, pvData, new IntPtr(pcbData))) checkErr(Marshal.GetLastWin32Error()); if (cbData > 0) { pvData = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cbData)); if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, paramType, index, pvData, new IntPtr(pcbData))) checkErr(Marshal.GetLastWin32Error()); } } } internal static unsafe void GetParam (SafeCryptMsgHandle safeCryptMsgHandle, uint paramType, uint index, out byte[] pvData, out uint cbData) { cbData = 0; pvData = new byte[0]; fixed (uint * pcbData = &cbData) { if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, paramType, index, IntPtr.Zero, new IntPtr(pcbData))) checkErr(Marshal.GetLastWin32Error()); if (cbData > 0) { pvData = new byte[cbData]; fixed (byte * ppvData = &pvData[0]) { if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, paramType, index, new IntPtr(ppvData), new IntPtr(pcbData))) checkErr(Marshal.GetLastWin32Error()); } } } } internal static unsafe X509Certificate2Collection GetCertificates (SafeCryptMsgHandle safeCryptMsgHandle) { uint dwCount = 0; uint cbCount = (uint) Marshal.SizeOf(typeof(uint)); X509Certificate2Collection certificates = new X509Certificate2Collection(); if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, CAPI.CMSG_CERT_COUNT_PARAM, 0, new IntPtr(&dwCount), new IntPtr(&cbCount))) checkErr(Marshal.GetLastWin32Error()); for (uint index = 0; index < dwCount; index++) { uint cbEncoded = 0; SafeLocalAllocHandle pbEncoded = SafeLocalAllocHandle.InvalidHandle; GetParam(safeCryptMsgHandle, CAPI.CMSG_CERT_PARAM, index, out pbEncoded, out cbEncoded); if (cbEncoded > 0) { SafeCertContextHandle safeCertContextHandle = CAPI.CertCreateCertificateContext(CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING, pbEncoded, cbEncoded); if (safeCertContextHandle == null || safeCertContextHandle.IsInvalid) throw new CryptographicException(Marshal.GetLastWin32Error()); certificates.Add(new X509Certificate2(safeCertContextHandle.DangerousGetHandle())); safeCertContextHandle.Dispose(); } } return certificates; } internal static unsafe byte[] GetContent (SafeCryptMsgHandle safeCryptMsgHandle) { uint cbContent = 0; byte[] content = new byte[0]; GetParam(safeCryptMsgHandle, CAPI.CMSG_CONTENT_PARAM, 0, out content, out cbContent); return content; } internal static unsafe Oid GetContentType (SafeCryptMsgHandle safeCryptMsgHandle) { uint cbContentType = 0; byte[] contentType = new byte[0]; GetParam(safeCryptMsgHandle, CAPI.CMSG_INNER_CONTENT_TYPE_PARAM, 0, out contentType, out cbContentType); if (contentType.Length > 0 && contentType[contentType.Length - 1] == 0) { byte[] temp = new byte[contentType.Length - 1]; Array.Copy(contentType, 0, temp, 0, temp.Length); contentType = temp; } return new Oid(Encoding.ASCII.GetString(contentType)); } internal static unsafe byte[] GetMessage (SafeCryptMsgHandle safeCryptMsgHandle) { uint cbMessage = 0; byte[] message = new byte[0]; GetParam(safeCryptMsgHandle, CAPI.CMSG_ENCODED_MESSAGE, 0, out message, out cbMessage); return message; } internal static unsafe int GetSignerIndex (SafeCryptMsgHandle safeCrytpMsgHandle, SignerInfo signerInfo, int startIndex) { uint dwSigners = 0; uint cbCount = (uint) Marshal.SizeOf(typeof(uint)); if (!CAPI.CryptMsgGetParam(safeCrytpMsgHandle, CAPI.CMSG_SIGNER_COUNT_PARAM, 0, new IntPtr(&dwSigners), new IntPtr(&cbCount))) checkErr(Marshal.GetLastWin32Error()); for (int index = startIndex; index < (int) dwSigners; index++) { uint cbCmsgSignerInfo = 0; if (!CAPI.CryptMsgGetParam(safeCrytpMsgHandle, CAPI.CMSG_SIGNER_INFO_PARAM, (uint) index, IntPtr.Zero, new IntPtr(&cbCmsgSignerInfo))) checkErr(Marshal.GetLastWin32Error()); if (cbCmsgSignerInfo > 0) { SafeLocalAllocHandle pbCmsgSignerInfo = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(cbCmsgSignerInfo)); if (!CAPI.CryptMsgGetParam(safeCrytpMsgHandle, CAPI.CMSG_SIGNER_INFO_PARAM, (uint) index, pbCmsgSignerInfo, new IntPtr(&cbCmsgSignerInfo))) checkErr(Marshal.GetLastWin32Error()); CAPI.CMSG_SIGNER_INFO cmsgSignerInfo1 = signerInfo.GetCmsgSignerInfo(); CAPI.CMSG_SIGNER_INFO cmsgSignerInfo2 = (CAPI.CMSG_SIGNER_INFO) Marshal.PtrToStructure(pbCmsgSignerInfo.DangerousGetHandle(), typeof(CAPI.CMSG_SIGNER_INFO)); if (X509Utils.MemEqual((byte *) cmsgSignerInfo1.Issuer.pbData, cmsgSignerInfo1.Issuer.cbData, (byte *) cmsgSignerInfo2.Issuer.pbData, cmsgSignerInfo2.Issuer.cbData) && X509Utils.MemEqual((byte *) cmsgSignerInfo1.SerialNumber.pbData, cmsgSignerInfo1.SerialNumber.cbData, (byte *) cmsgSignerInfo2.SerialNumber.pbData, cmsgSignerInfo2.SerialNumber.cbData)) { return index; // Signer's index is found. } // Keep alive. pbCmsgSignerInfo.Dispose(); } } throw new CryptographicException(CAPI.CRYPT_E_SIGNER_NOT_FOUND); } internal static unsafe CryptographicAttributeObjectCollection GetUnprotectedAttributes (SafeCryptMsgHandle safeCryptMsgHandle) { uint cbUnprotectedAttr = 0; CryptographicAttributeObjectCollection attributes = new CryptographicAttributeObjectCollection(); SafeLocalAllocHandle pbUnprotectedAttr = SafeLocalAllocHandle.InvalidHandle; if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, CAPI.CMSG_UNPROTECTED_ATTR_PARAM, 0, pbUnprotectedAttr, new IntPtr(&cbUnprotectedAttr))) { int lastWin32Error = Marshal.GetLastWin32Error(); if (lastWin32Error != CAPI.CRYPT_E_ATTRIBUTES_MISSING) checkErr(Marshal.GetLastWin32Error()); } if (cbUnprotectedAttr > 0) { using (pbUnprotectedAttr = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cbUnprotectedAttr))) { if (!CAPI.CryptMsgGetParam(safeCryptMsgHandle, CAPI.CMSG_UNPROTECTED_ATTR_PARAM, 0, pbUnprotectedAttr, new IntPtr(&cbUnprotectedAttr))) checkErr(Marshal.GetLastWin32Error()); attributes = new CryptographicAttributeObjectCollection(pbUnprotectedAttr); } } return attributes; } internal unsafe static X509IssuerSerial DecodeIssuerSerial (CAPI.CERT_ISSUER_SERIAL_NUMBER pIssuerAndSerial) { SafeLocalAllocHandle ptr = SafeLocalAllocHandle.InvalidHandle; uint cbSize = CAPI.CertNameToStrW(CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING, new IntPtr(&pIssuerAndSerial.Issuer), CAPI.CERT_X500_NAME_STR | CAPI.CERT_NAME_STR_REVERSE_FLAG, ptr, 0); if (cbSize <= 1) // The API actually return 1 when It fails; which is not what the documentation says. throw new CryptographicException(Marshal.GetLastWin32Error()); ptr = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(2 * cbSize)); cbSize = CAPI.CertNameToStrW(CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING, new IntPtr(&pIssuerAndSerial.Issuer), CAPI.CERT_X500_NAME_STR | CAPI.CERT_NAME_STR_REVERSE_FLAG, ptr, cbSize); if (cbSize <= 1) throw new CryptographicException(Marshal.GetLastWin32Error()); X509IssuerSerial issuerSerial = new X509IssuerSerial(); issuerSerial.IssuerName = Marshal.PtrToStringUni(ptr.DangerousGetHandle()); byte[] serial = new byte[pIssuerAndSerial.SerialNumber.cbData]; Marshal.Copy(pIssuerAndSerial.SerialNumber.pbData, serial, 0, serial.Length); issuerSerial.SerialNumber = X509Utils.EncodeHexStringFromInt(serial); ptr.Dispose(); return issuerSerial; } internal static string DecodeOctetString (byte[] encodedOctetString) { uint cbDecoded = 0; SafeLocalAllocHandle pbDecoded = null; if (!CAPI.DecodeObject(new IntPtr(CAPI.X509_OCTET_STRING), encodedOctetString, out pbDecoded, out cbDecoded)) throw new CryptographicException(Marshal.GetLastWin32Error()); if (cbDecoded == 0) return String.Empty; CAPI.CRYPTOAPI_BLOB decodedBlob = (CAPI.CRYPTOAPI_BLOB) Marshal.PtrToStructure(pbDecoded.DangerousGetHandle(), typeof(CAPI.CRYPTOAPI_BLOB)); if (decodedBlob.cbData == 0) return String.Empty; string octetString = Marshal.PtrToStringUni(decodedBlob.pbData); pbDecoded.Dispose(); return octetString; } internal static byte[] DecodeOctetBytes (byte[] encodedOctetString) { uint cbDecoded = 0; SafeLocalAllocHandle pbDecoded = null; if (!CAPI.DecodeObject(new IntPtr(CAPI.X509_OCTET_STRING), encodedOctetString, out pbDecoded, out cbDecoded)) throw new CryptographicException(Marshal.GetLastWin32Error()); if (cbDecoded == 0) return new byte[0]; using (pbDecoded) { return CAPI.BlobToByteArray(pbDecoded.DangerousGetHandle()); } } internal static byte[] EncodeOctetString (string octetString) { // Marshal data to be encoded to unmanaged memory. byte[] octets = new byte[2 * (octetString.Length + 1)]; Encoding.Unicode.GetBytes(octetString, 0, octetString.Length, octets, 0); return EncodeOctetString(octets); } internal static unsafe byte[] EncodeOctetString (byte[] octets) { fixed (byte * pbOctets = octets) { CAPI.CRYPTOAPI_BLOB octetsBlob = new CAPI.CRYPTOAPI_BLOB(); octetsBlob.cbData = (uint) octets.Length; octetsBlob.pbData = new IntPtr(pbOctets); // Encode data. byte[] encodedOctets = new byte[0]; if (!CAPI.EncodeObject(new IntPtr((long) CAPI.X509_OCTET_STRING), new IntPtr((long) &octetsBlob), out encodedOctets)) { throw new CryptographicException(Marshal.GetLastWin32Error()); } return encodedOctets; } } internal static string DecodeObjectIdentifier (byte[] encodedObjId, int offset) { StringBuilder objId = new StringBuilder(""); if (0 < (encodedObjId.Length - offset)) { byte b = encodedObjId[offset]; byte c = (byte) ((uint) b / 40); objId.Append(c.ToString(null, null)); objId.Append("."); c = (byte) ((uint) b % 40); objId.Append(c.ToString(null, null)); ulong s = 0; for (int index = offset + 1; index < encodedObjId.Length; index++) { c = encodedObjId[index]; s = (s << 7) + (ulong) (c & 0x7f); if (0 == (c & 0x80)) { objId.Append("."); objId.Append(s.ToString(null, null)); s = 0; } } // s should be 0 at this point, otherwise we have a bad ASN. if (0 != s) { throw new CryptographicException(CAPI.CRYPT_E_BAD_ENCODE); } } return objId.ToString(); } internal static CmsRecipientCollection SelectRecipients (SubjectIdentifierType recipientIdentifierType) { X509Store store = new X509Store("AddressBook"); store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); X509Certificate2Collection certificates = new X509Certificate2Collection(store.Certificates); foreach (X509Certificate2 certificate in store.Certificates) { if (certificate.NotBefore <= DateTime.Now && certificate.NotAfter >= DateTime.Now) { bool validUsages = true; foreach (X509Extension extension in certificate.Extensions) { if (String.Compare(extension.Oid.Value, CAPI.szOID_KEY_USAGE, StringComparison.OrdinalIgnoreCase) == 0) { X509KeyUsageExtension keyUsage = new X509KeyUsageExtension(); keyUsage.CopyFrom(extension); if ((keyUsage.KeyUsages & X509KeyUsageFlags.KeyEncipherment) == 0 && (keyUsage.KeyUsages & X509KeyUsageFlags.KeyAgreement) == 0) { validUsages = false; } break; } } if (validUsages) { certificates.Add(certificate); } } } if (certificates.Count < 1) throw new CryptographicException(CAPI.CRYPT_E_RECIPIENT_NOT_FOUND); X509Certificate2Collection recipients = X509Certificate2UI.SelectFromCollection(certificates, null, null, X509SelectionFlag.MultiSelection); if (recipients.Count < 1) throw new CryptographicException(CAPI.ERROR_CANCELLED); return new CmsRecipientCollection(recipientIdentifierType, recipients); } internal static X509Certificate2 SelectSignerCertificate () { X509Store store = new X509Store(); store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly | OpenFlags.IncludeArchived); X509Certificate2Collection certificates = new X509Certificate2Collection(); foreach (X509Certificate2 certificate in store.Certificates) { if (certificate.HasPrivateKey && certificate.NotBefore <= DateTime.Now && certificate.NotAfter >= DateTime.Now) { bool validUsages = true; foreach (X509Extension extension in certificate.Extensions) { if (String.Compare(extension.Oid.Value, CAPI.szOID_KEY_USAGE, StringComparison.OrdinalIgnoreCase) == 0) { X509KeyUsageExtension keyUsage = new X509KeyUsageExtension(); keyUsage.CopyFrom(extension); if ((keyUsage.KeyUsages & X509KeyUsageFlags.DigitalSignature) == 0 && (keyUsage.KeyUsages & X509KeyUsageFlags.NonRepudiation) == 0) { validUsages = false; } break; } } if (validUsages) { certificates.Add(certificate); } } } if (certificates.Count < 1) throw new CryptographicException(CAPI.CRYPT_E_SIGNER_NOT_FOUND); certificates = X509Certificate2UI.SelectFromCollection(certificates, null, null, X509SelectionFlag.SingleSelection); if (certificates.Count < 1) throw new CryptographicException(CAPI.ERROR_CANCELLED); Debug.Assert(certificates.Count == 1); return certificates[0]; } internal static AsnEncodedDataCollection GetAsnEncodedDataCollection (CAPI.CRYPT_ATTRIBUTE cryptAttribute) { AsnEncodedDataCollection list = new AsnEncodedDataCollection(); Oid oid = new Oid(cryptAttribute.pszObjId); string szOid = oid.Value; for (uint index = 0; index < cryptAttribute.cValue; index++) { IntPtr pAttributeBlob = new IntPtr((long)cryptAttribute.rgValue + (index * Marshal.SizeOf(typeof(CAPI.CRYPTOAPI_BLOB)))); Pkcs9AttributeObject attribute = new Pkcs9AttributeObject(oid, CAPI.BlobToByteArray(pAttributeBlob)); Pkcs9AttributeObject customAttribute = CryptoConfig.CreateFromName(szOid) as Pkcs9AttributeObject; if (customAttribute != null) { customAttribute.CopyFrom(attribute); attribute = customAttribute; } list.Add(attribute); } return list; } internal static AsnEncodedDataCollection GetAsnEncodedDataCollection (CAPI.CRYPT_ATTRIBUTE_TYPE_VALUE cryptAttribute) { AsnEncodedDataCollection list = new AsnEncodedDataCollection(); list.Add(new Pkcs9AttributeObject(new Oid(cryptAttribute.pszObjId), CAPI.BlobToByteArray(cryptAttribute.Value))); return list; } internal static unsafe IntPtr CreateCryptAttributes (CryptographicAttributeObjectCollection attributes) { // NULL if no attribute. if (attributes.Count == 0) return IntPtr.Zero; // // The goal here is to compute the size needed for the attributes we are passing to CMSG_SIGNER_ENCODE_INFO // The unmanaged memory structure we are creating here has the following layout: // // Let cAttr = number of attributes. // // This to create the array of CRYPT_ATTRIBUTE // for i = 0 to cAttr { // CRYPT_ATTRRIBUTE[i] // pszObjId | cValue | rgValue // } // // This is to fill in the data for each entry of CRYPT_ATTRIBUTE array above. // for i = 0 to cAttr { // objId[i] // Value of the Oid, i.e "1.2.3.4" // for j = 0 to CRYPT_ATTRIBUTE[i].cValue - 1 { // Array of CRYPTOAPI_BLOB // CRYPT_ATTRIBUTE[i].rgValue[j].cbData // Data size // CRYPT_ATTRIBUTE[i].rgValue[j].pbData // Pointer to data // } // for j = 0 to CRYPT_ATTRIBUTE[i].cValue - 1 { // Data for each entry of the CRYPTOAPI_BLOB array above. // *CRYPT_ATTRIBUTE[i].rgValue[j].pbData // The actual data // } // } uint totalLength = 0; uint cryptAttrSize = AlignedLength((uint) Marshal.SizeOf(typeof(I_CRYPT_ATTRIBUTE))); uint cryptBlobSize = AlignedLength((uint) Marshal.SizeOf(typeof(CAPI.CRYPTOAPI_BLOB))); // First compute the total serialized unmanaged memory size needed. // For each attribute, we add the CRYPT_ATTRIBUTE size, the size // needed for the ObjId, and the size needed for all the values // inside each attribute which is computed in inner loop. foreach (CryptographicAttributeObject attribute in attributes) { totalLength += cryptAttrSize; // sizeof(CRYPT_ATTRIBUTE) totalLength += AlignedLength((uint) (attribute.Oid.Value.Length + 1)); // strlen(pszObjId) + 1 // For each value within the attribute, we add the CRYPT_ATTR_BLOB size and // the actual size needed for the data. foreach (AsnEncodedData attributeValue in attribute.Values) { totalLength += cryptBlobSize; // Add CRYPT_ATTR_BLOB size totalLength += AlignedLength((uint) attributeValue.RawData.Length); // Data size } } // Allocate the unmanaged memory blob to hold the entire serialized CRYPT_ATTRIBUTE array. SafeLocalAllocHandle pCryptAttributes = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(totalLength)); // Now fill up unmanaged memory with data from the managed side. I_CRYPT_ATTRIBUTE * pCryptAttribute = (I_CRYPT_ATTRIBUTE *) pCryptAttributes.DangerousGetHandle(); IntPtr pAttrData = new IntPtr((long) pCryptAttributes.DangerousGetHandle() + (cryptAttrSize * attributes.Count)); foreach (CryptographicAttributeObject attribute in attributes) { byte * pszObjId = (byte *) pAttrData; byte[] objId = new byte[attribute.Oid.Value.Length + 1]; CAPI.CRYPTOAPI_BLOB * pDataBlob = (CAPI.CRYPTOAPI_BLOB *) (pszObjId + AlignedLength((uint) objId.Length)); // CRYPT_ATTRIBUTE.pszObjId pCryptAttribute->pszObjId = (IntPtr) pszObjId; // CRYPT_ATTRIBUTE.cValue pCryptAttribute->cValue = (uint) attribute.Values.Count; // CRYPT_ATTRIBUTE.rgValue pCryptAttribute->rgValue = (IntPtr) pDataBlob; // ObjId - The actual dotted value of the OID. Encoding.ASCII.GetBytes(attribute.Oid.Value, 0, attribute.Oid.Value.Length, objId, 0); Marshal.Copy(objId, 0, pCryptAttribute->pszObjId, objId.Length); // cValue of CRYPT_ATTR_BLOBs followed by cValue of actual data. IntPtr pbEncodedData = new IntPtr((long) pDataBlob + (attribute.Values.Count * cryptBlobSize)); foreach (AsnEncodedData value in attribute.Values) { // Retrieve encoded data. byte[] encodedData = value.RawData; // Write data if (encodedData.Length > 0) { // CRYPT_ATTR_BLOB.cbData pDataBlob->cbData = (uint) encodedData.Length; // CRYPT_ATTR_BLOB.pbData pDataBlob->pbData = pbEncodedData; Marshal.Copy(encodedData, 0, pbEncodedData, encodedData.Length); pbEncodedData = new IntPtr((long) pbEncodedData + AlignedLength((uint) encodedData.Length)); } // Advance pointer. pDataBlob++; } // Advance pointers. pCryptAttribute++; pAttrData = pbEncodedData; } // Since we are returning IntPtr, we MUST supress finalizer, otherwise // the GC can collect the memory underneath us!!! GC.SuppressFinalize(pCryptAttributes); return pCryptAttributes.DangerousGetHandle(); } internal static unsafe CAPI.CMSG_SIGNER_ENCODE_INFO CreateSignerEncodeInfo (CmsSigner signer) { return CreateSignerEncodeInfo(signer, false); } internal static unsafe CAPI.CMSG_SIGNER_ENCODE_INFO CreateSignerEncodeInfo (CmsSigner signer, bool silent) { CAPI.CMSG_SIGNER_ENCODE_INFO cmsSignerEncodeInfo = new CAPI.CMSG_SIGNER_ENCODE_INFO(Marshal.SizeOf(typeof(CAPI.CMSG_SIGNER_ENCODE_INFO))); SafeCryptProvHandle safeCryptProvHandle = SafeCryptProvHandle.InvalidHandle; uint keySpec = 0; bool freeCsp = false; cmsSignerEncodeInfo.HashAlgorithm.pszObjId = signer.DigestAlgorithm.Value; if (0 == String.Compare( signer.Certificate.PublicKey.Oid.Value, CAPI.szOID_X957_DSA, StringComparison.Ordinal)) { cmsSignerEncodeInfo.HashEncryptionAlgorithm.pszObjId = CAPI.szOID_X957_sha1DSA; } cmsSignerEncodeInfo.cAuthAttr = (uint) signer.SignedAttributes.Count; cmsSignerEncodeInfo.rgAuthAttr = CreateCryptAttributes(signer.SignedAttributes); cmsSignerEncodeInfo.cUnauthAttr = (uint) signer.UnsignedAttributes.Count; cmsSignerEncodeInfo.rgUnauthAttr = CreateCryptAttributes(signer.UnsignedAttributes); if (signer.SignerIdentifierType == SubjectIdentifierType.NoSignature) { cmsSignerEncodeInfo.HashEncryptionAlgorithm.pszObjId = CAPI.szOID_PKIX_NO_SIGNATURE; cmsSignerEncodeInfo.pCertInfo = IntPtr.Zero; cmsSignerEncodeInfo.dwKeySpec = keySpec; // If the HashEncryptionAlgorithm is set to szOID_PKIX_NO_SIGNATURE, then, // the signature value only contains the hash octets. hCryptProv must still // be specified. However, since a private key isn't used the hCryptProv can be // acquired using CRYPT_VERIFYCONTEXT. if (!CAPI.CryptAcquireContext(ref safeCryptProvHandle, null, null, CAPI.PROV_RSA_FULL, CAPI.CRYPT_VERIFYCONTEXT)) { // call it again for win9x platforms if (!CAPI.CryptAcquireContext(ref safeCryptProvHandle, null, null, CAPI.PROV_RSA_FULL, 0)) throw new CryptographicException(Marshal.GetLastWin32Error()); } cmsSignerEncodeInfo.hCryptProv = safeCryptProvHandle.DangerousGetHandle(); GC.SuppressFinalize(safeCryptProvHandle); // Fake up the SignerId so our server can recognize it // dwIdChoice cmsSignerEncodeInfo.SignerId.dwIdChoice = CAPI.CERT_ID_ISSUER_SERIAL_NUMBER; // Issuer X500DistinguishedName dummyName = new X500DistinguishedName(CAPI.DummySignerCommonName); dummyName.Oid = new Oid(CAPI.szOID_RDN_DUMMY_SIGNER); cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.Issuer.cbData = (uint)dummyName.RawData.Length; SafeLocalAllocHandle pbDataIssuer = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.Issuer.cbData)); Marshal.Copy(dummyName.RawData, 0, pbDataIssuer.DangerousGetHandle(), dummyName.RawData.Length); cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.Issuer.pbData = pbDataIssuer.DangerousGetHandle(); GC.SuppressFinalize(pbDataIssuer); // SerialNumber cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.SerialNumber.cbData = (uint)1; SafeLocalAllocHandle pbDataSerialNumber = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.SerialNumber.cbData)); byte * pSerialNumber = (byte *)pbDataSerialNumber.DangerousGetHandle(); *pSerialNumber = 0x00; cmsSignerEncodeInfo.SignerId.Value.IssuerSerialNumber.SerialNumber.pbData = pbDataSerialNumber.DangerousGetHandle(); GC.SuppressFinalize(pbDataSerialNumber); return cmsSignerEncodeInfo; } SafeCertContextHandle safeCertContextHandle = X509Utils.GetCertContext(signer.Certificate); if (!CAPI.CryptAcquireCertificatePrivateKey(safeCertContextHandle, (silent? CAPI.CRYPT_SILENT | CAPI.CRYPT_ACQUIRE_USE_PROV_INFO_FLAG | CAPI.CRYPT_ACQUIRE_COMPARE_KEY_FLAG: CAPI.CRYPT_ACQUIRE_USE_PROV_INFO_FLAG | CAPI.CRYPT_ACQUIRE_COMPARE_KEY_FLAG), IntPtr.Zero, ref safeCryptProvHandle, ref keySpec, ref freeCsp)) throw new CryptographicException(Marshal.GetLastWin32Error()); cmsSignerEncodeInfo.dwKeySpec = keySpec; cmsSignerEncodeInfo.hCryptProv = safeCryptProvHandle.DangerousGetHandle(); // Since we are storing only IntPtr in CMSG_SIGNER_ENCODE_INFO.hCryptProv, we MUST then // supress the finalizer, otherwise the GC can collect the resource underneath us!!! GC.SuppressFinalize(safeCryptProvHandle); CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle()); cmsSignerEncodeInfo.pCertInfo = pCertContext.pCertInfo; // If CMS, then fill in the Subject Key Identifier (SKI) or SignerId. if (signer.SignerIdentifierType == SubjectIdentifierType.SubjectKeyIdentifier) { uint cbData = 0; SafeLocalAllocHandle pbData = SafeLocalAllocHandle.InvalidHandle; if (!CAPI.CertGetCertificateContextProperty(safeCertContextHandle, CAPI.CERT_KEY_IDENTIFIER_PROP_ID, pbData, ref cbData)) throw new CryptographicException(Marshal.GetLastWin32Error()); if (cbData > 0) { pbData = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(cbData)); if (!CAPI.CertGetCertificateContextProperty(safeCertContextHandle, CAPI.CERT_KEY_IDENTIFIER_PROP_ID, pbData, ref cbData)) throw new CryptographicException(Marshal.GetLastWin32Error()); cmsSignerEncodeInfo.SignerId.dwIdChoice = CAPI.CERT_ID_KEY_IDENTIFIER; cmsSignerEncodeInfo.SignerId.Value.KeyId.cbData = cbData; cmsSignerEncodeInfo.SignerId.Value.KeyId.pbData = pbData.DangerousGetHandle(); // Since we are storing only IntPtr in CMSG_SIGNER_ENCODE_INFO.SignerId.KeyId.pbData, // we MUST supress finalizer, otherwise the GC can collect the resource underneath us!!! GC.SuppressFinalize(pbData); } } return cmsSignerEncodeInfo; } internal static X509Certificate2Collection CreateBagOfCertificates (CmsSigner signer) { X509Certificate2Collection certificates = new X509Certificate2Collection(); // // First add extra bag of certs. // certificates.AddRange(signer.Certificates); // // Then include chain option. // if (signer.IncludeOption != X509IncludeOption.None) { if (signer.IncludeOption == X509IncludeOption.EndCertOnly) { certificates.Add(signer.Certificate); } else { int cCerts = 1; X509Chain chain = new X509Chain(); chain.Build(signer.Certificate); // Can't honor the option if we only have a partial chain. if ((chain.ChainStatus.Length > 0) && ((chain.ChainStatus[0].Status & X509ChainStatusFlags.PartialChain) == X509ChainStatusFlags.PartialChain)) throw new CryptographicException(CAPI.CERT_E_CHAINING); if (signer.IncludeOption == X509IncludeOption.WholeChain) { cCerts = chain.ChainElements.Count; } else { // Default to ExcludeRoot. if (chain.ChainElements.Count > 1) { cCerts = chain.ChainElements.Count - 1; } } for (int i = 0; i < cCerts; i++) { certificates.Add(chain.ChainElements[i].Certificate); } } } return certificates; } internal static unsafe SafeLocalAllocHandle CreateEncodedCertBlob (X509Certificate2Collection certificates) { SafeLocalAllocHandle certBlob = SafeLocalAllocHandle.InvalidHandle; if (certificates.Count > 0) { certBlob = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(certificates.Count * Marshal.SizeOf(typeof(CAPI.CRYPTOAPI_BLOB)))); CAPI.CRYPTOAPI_BLOB * pCertBlob = (CAPI.CRYPTOAPI_BLOB * ) certBlob.DangerousGetHandle(); foreach (X509Certificate2 certificate in certificates) { SafeCertContextHandle safeCertContextHandle = X509Utils.GetCertContext(certificate); CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle()); pCertBlob->cbData = pCertContext.cbCertEncoded; pCertBlob->pbData = pCertContext.pbCertEncoded; pCertBlob++; } } return certBlob; } internal static unsafe uint AddCertsToMessage (SafeCryptMsgHandle safeCryptMsgHandle, X509Certificate2Collection bagOfCerts, X509Certificate2Collection chainOfCerts) { uint certsAdded = 0; foreach (X509Certificate2 certificate in chainOfCerts) { // Skip it if already in the bag of certs. X509Certificate2Collection foundCerts = bagOfCerts.Find(X509FindType.FindByThumbprint, certificate.Thumbprint, false); if (foundCerts.Count == 0) { SafeCertContextHandle safeCertContextHandle = X509Utils.GetCertContext(certificate); CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle()); CAPI.CRYPTOAPI_BLOB certBlob = new CAPI.CRYPTOAPI_BLOB(); certBlob.cbData = pCertContext.cbCertEncoded; certBlob.pbData = pCertContext.pbCertEncoded; if (!CAPI.CryptMsgControl(safeCryptMsgHandle, 0, CAPI.CMSG_CTRL_ADD_CERT, new IntPtr((long) &certBlob))) throw new CryptographicException(Marshal.GetLastWin32Error()); certsAdded++; } } return certsAdded; } internal static X509Certificate2 FindCertificate (SubjectIdentifier identifier, X509Certificate2Collection certificates) { X509Certificate2 certificate = null; if (certificates != null && certificates.Count > 0) { X509Certificate2Collection filters; switch (identifier.Type) { case SubjectIdentifierType.IssuerAndSerialNumber: filters = certificates.Find(X509FindType.FindByIssuerDistinguishedName, ((X509IssuerSerial) identifier.Value).IssuerName, false); if (filters.Count > 0) { filters = filters.Find(X509FindType.FindBySerialNumber, ((X509IssuerSerial) identifier.Value).SerialNumber, false); if (filters.Count > 0) certificate = filters[0]; } break; case SubjectIdentifierType.SubjectKeyIdentifier: filters = certificates.Find(X509FindType.FindBySubjectKeyIdentifier, identifier.Value, false); if (filters.Count > 0) certificate = filters[0]; break; } } return certificate; } private static void checkErr (int err) { if (CAPI.CRYPT_E_INVALID_MSG_TYPE != err) throw new CryptographicException(err); } // for Key ID signing only internal static unsafe X509Certificate2 CreateDummyCertificate (CspParameters parameters) { SafeCertContextHandle handle = SafeCertContextHandle.InvalidHandle; // hProv SafeCryptProvHandle hProv = SafeCryptProvHandle.InvalidHandle; UInt32 dwFlags = 0; if (0 != (parameters.Flags & CspProviderFlags.UseMachineKeyStore)) { dwFlags |= CAPI.CRYPT_MACHINE_KEYSET; } if (0 != (parameters.Flags & CspProviderFlags.UseDefaultKeyContainer)) { dwFlags |= CAPI.CRYPT_VERIFYCONTEXT; } if (0 != (parameters.Flags & CspProviderFlags.NoPrompt)) { dwFlags |= CAPI.CRYPT_SILENT; } bool rc = CAPI.CryptAcquireContext(ref hProv, parameters.KeyContainerName, parameters.ProviderName, (uint)parameters.ProviderType, dwFlags); if (!rc) throw new CryptographicException(Marshal.GetLastWin32Error()); // pKeyProvInfo CAPI.CRYPT_KEY_PROV_INFO KeyProvInfo = new CAPI.CRYPT_KEY_PROV_INFO(); KeyProvInfo.pwszProvName = parameters.ProviderName; KeyProvInfo.pwszContainerName = parameters.KeyContainerName; KeyProvInfo.dwProvType = (uint)parameters.ProviderType; KeyProvInfo.dwKeySpec = (uint)parameters.KeyNumber ; KeyProvInfo.dwFlags = (uint)((parameters.Flags & CspProviderFlags.UseMachineKeyStore) == CspProviderFlags.UseMachineKeyStore ? CAPI.CRYPT_MACHINE_KEYSET : 0); SafeLocalAllocHandle pKeyProvInfo = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr(Marshal.SizeOf(typeof(CAPI.CRYPT_KEY_PROV_INFO)))); Marshal.StructureToPtr(KeyProvInfo, pKeyProvInfo.DangerousGetHandle(), false); // Signature CAPI.CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm = new CAPI.CRYPT_ALGORITHM_IDENTIFIER(); SignatureAlgorithm.pszObjId = CAPI.szOID_OIWSEC_sha1RSASign; SafeLocalAllocHandle pSignatureAlgorithm = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr( Marshal.SizeOf(typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER)))); Marshal.StructureToPtr(SignatureAlgorithm, pSignatureAlgorithm.DangerousGetHandle(), false); // pSubjectIssuerBlob X500DistinguishedName subjectName = new X500DistinguishedName("cn=CMS Signer Dummy Certificate"); fixed (byte * pbOctets = subjectName.RawData) { CAPI.CRYPTOAPI_BLOB SubjectIssuerBlob = new CAPI.CRYPTOAPI_BLOB(); SubjectIssuerBlob.cbData = (uint)subjectName.RawData.Length; SubjectIssuerBlob.pbData = new IntPtr(pbOctets); handle = CAPI.CertCreateSelfSignCertificate(hProv, new IntPtr(&SubjectIssuerBlob), 1, pKeyProvInfo.DangerousGetHandle(), pSignatureAlgorithm.DangerousGetHandle(), IntPtr.Zero, //StartTime IntPtr.Zero, //EndTime IntPtr.Zero); //Extensions } Marshal.DestroyStructure(pKeyProvInfo.DangerousGetHandle(), typeof(CAPI.CRYPT_KEY_PROV_INFO)); pKeyProvInfo.Dispose(); Marshal.DestroyStructure(pSignatureAlgorithm.DangerousGetHandle(), typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER)); pSignatureAlgorithm.Dispose(); if (handle == null || handle.IsInvalid) throw new CryptographicException(Marshal.GetLastWin32Error()); X509Certificate2 certificate = new X509Certificate2(handle.DangerousGetHandle()); handle.Dispose(); return certificate; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ControlBuilderAttribute.cs
- ButtonChrome.cs
- bindurihelper.cs
- ItemDragEvent.cs
- XamlGridLengthSerializer.cs
- StylusEditingBehavior.cs
- DynamicPropertyReader.cs
- AutomationProperties.cs
- HwndSourceParameters.cs
- securitycriticaldata.cs
- DiscreteKeyFrames.cs
- DataObject.cs
- ListCollectionView.cs
- DefaultObjectMappingItemCollection.cs
- ObjectDataSourceEventArgs.cs
- SortKey.cs
- DeviceContext.cs
- Compilation.cs
- PublisherMembershipCondition.cs
- TypeResolvingOptionsAttribute.cs
- SettingsContext.cs
- ItemCollectionEditor.cs
- MessageQueueEnumerator.cs
- TreeNodeSelectionProcessor.cs
- OpenFileDialog.cs
- Converter.cs
- ReadOnlyNameValueCollection.cs
- HttpApplication.cs
- IDispatchConstantAttribute.cs
- MailHeaderInfo.cs
- HtmlShim.cs
- BuildManager.cs
- TextServicesCompartmentEventSink.cs
- ContractMapping.cs
- SelectedPathEditor.cs
- LogSwitch.cs
- HandleCollector.cs
- _MultipleConnectAsync.cs
- _AutoWebProxyScriptWrapper.cs
- ReaderContextStackData.cs
- Thickness.cs
- DefaultProxySection.cs
- XpsFilter.cs
- Executor.cs
- TreeViewBindingsEditor.cs
- XslAstAnalyzer.cs
- ContentElement.cs
- ArcSegment.cs
- DataViewSetting.cs
- TdsParserStaticMethods.cs
- ToolStripButton.cs
- PathSegment.cs
- SqlXmlStorage.cs
- SqlTypesSchemaImporter.cs
- CompositionTarget.cs
- CategoryAttribute.cs
- SQLBinaryStorage.cs
- WebPartConnectVerb.cs
- HttpCachePolicyElement.cs
- ModelTypeConverter.cs
- HeaderedContentControl.cs
- TextOptions.cs
- StorageBasedPackageProperties.cs
- HttpAsyncResult.cs
- OLEDB_Util.cs
- AVElementHelper.cs
- X509Utils.cs
- CollectionViewGroup.cs
- DbMetaDataFactory.cs
- EventWaitHandleSecurity.cs
- NavigationProperty.cs
- CharacterMetrics.cs
- SerializationEventsCache.cs
- TaiwanCalendar.cs
- DataGridTextBox.cs
- XmlAttributes.cs
- DataControlField.cs
- TemplateGroupCollection.cs
- Registry.cs
- PathData.cs
- RoutedCommand.cs
- CodeAttachEventStatement.cs
- NativeMethods.cs
- InputLangChangeRequestEvent.cs
- ProcessHost.cs
- SBCSCodePageEncoding.cs
- WSSecurityOneDotZeroSendSecurityHeader.cs
- ToolBarOverflowPanel.cs
- BuildDependencySet.cs
- EventLogPermissionAttribute.cs
- ToolStripItemDesigner.cs
- dbdatarecord.cs
- WCFServiceClientProxyGenerator.cs
- AppDomainShutdownMonitor.cs
- ThreadAbortException.cs
- DiscoveryClientReferences.cs
- ModelTypeConverter.cs
- WindowsFormsSectionHandler.cs
- ControlIdConverter.cs
- _NegotiateClient.cs