private byte[] SaveToMemoryStore(CertStoreSaveAs dwSaveAs) { unsafe { CRYPTOAPI_BLOB blob = new CRYPTOAPI_BLOB(0, null); if (!Interop.crypt32.CertSaveStore(_certStore, CertEncodingType.All, dwSaveAs, CertStoreSaveTo.CERT_STORE_SAVE_TO_MEMORY, ref blob, 0)) { throw Marshal.GetLastWin32Error().ToCryptographicException(); } byte[] exportedData = new byte[blob.cbData]; fixed(byte *pExportedData = exportedData) { blob.pbData = pExportedData; if (!Interop.crypt32.CertSaveStore(_certStore, CertEncodingType.All, dwSaveAs, CertStoreSaveTo.CERT_STORE_SAVE_TO_MEMORY, ref blob, 0)) { throw Marshal.GetLastWin32Error().ToCryptographicException(); } } // When calling CertSaveStore to get the initial length, it returns a cbData that is big enough but // not exactly the right size, at least in the case of PKCS7. So we need to right-size it once we // know exactly how much was written. if (exportedData.Length != blob.cbData) { return(exportedData[0..blob.cbData]);
public static extern Int32 CertNameToStr( Int32 dwCertEncodingType, ref CRYPTOAPI_BLOB pName, Int32 dwStrType, StringBuilder psz, Int32 csz );
private unsafe int SignCallback( IntPtr pCertContext, IntPtr pvExtra, uint algId, byte[] pDigestToSign, uint dwDigestToSign, ref CRYPTOAPI_BLOB blob ) { const int E_INVALIDARG = unchecked ((int)0x80070057); byte[] digest; switch (_signingAlgorithm) { case RSA rsa: digest = rsa.SignHash(pDigestToSign, _fileDigestAlgorithm, RSASignaturePadding.Pkcs1); break; case ECDsa ecdsa: digest = ecdsa.SignHash(pDigestToSign); break; default: return(E_INVALIDARG); } var resultPtr = Marshal.AllocHGlobal(digest.Length); Marshal.Copy(digest, 0, resultPtr, digest.Length); blob.pbData = resultPtr; blob.cbData = (uint)digest.Length; return(0); }
internal byte[] ReadBlob(CRYPTOAPI_BLOB blob) { var buffer = new byte[blob.cbData]; Marshal.Copy(blob.pbData, buffer, 0, buffer.Length); return(buffer); }
public static bool DisableCertificateUsageFlags(System.Security.Cryptography.X509Certificates.X509Certificate2 cert) { // inspired by https://stackoverflow.com/questions/47481158/disable-a-certificate-in-the-root-using-powershell // ASN-encoded empty X509 EKU extension value to explicitly disable EKUs in the property var data = new byte[2] { 0x30, 0 }; uint propId = 0x9; // allocate pbData var pbData = Marshal.AllocHGlobal(data.Length); // copy data to struct Marshal.Copy(data, 0, pbData, data.Length); var blob = new CRYPTOAPI_BLOB { cbData = 2, pbData = pbData }; var pvData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CRYPTOAPI_BLOB))); Marshal.StructureToPtr(blob, pvData, false); var result = CertSetCertificateContextProperty(cert.Handle, propId, 0, pvData); // release unmanaged memory Marshal.FreeHGlobal(pbData); Marshal.FreeHGlobal(pvData); return(result); }
public X509ContentType GetCertContentType(byte[] rawData) { ContentType contentType; unsafe { fixed(byte *pRawData = rawData) { CRYPTOAPI_BLOB certBlob = new CRYPTOAPI_BLOB(rawData.Length, pRawData); if (!Interop.crypt32.CryptQueryObject( CertQueryObjectType.CERT_QUERY_OBJECT_BLOB, &certBlob, ExpectedContentTypeFlags.CERT_QUERY_CONTENT_FLAG_ALL, ExpectedFormatTypeFlags.CERT_QUERY_FORMAT_FLAG_ALL, 0, IntPtr.Zero, out contentType, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)) { throw Marshal.GetLastWin32Error().ToCryptographicException(); } } } return(MapContentType(contentType)); }
public static extern IntPtr CertOpenStore ( [In, MarshalAs(UnmanagedType.LPStr)] string lpszStoreProvider, [In, MarshalAs(UnmanagedType.U4)] CertEncodingType CertEncodingType, [In, MarshalAs(UnmanagedType.SysInt)] IntPtr hCryptProv, [In, MarshalAs(UnmanagedType.U4)] CertOpenStoreFlags dwFlags, [In] ref CRYPTOAPI_BLOB pvPara );
private static extern SafeCertContextHandle CertCreateSelfSignCertificate(SafeNCryptKeyHandle hCryptProvOrNCryptKey, [In] ref CRYPTOAPI_BLOB pSubjectIssuerBlob, X509CertificateCreationOptions dwFlags, [In] ref CRYPT_KEY_PROV_INFO pKeyProvInfo, [In] ref CRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, [In] ref SYSTEMTIME pStartTime, [In] ref SYSTEMTIME pEndTime, [In] ref CERT_EXTENSIONS pExtensions);
internal static extern bool CryptUnprotectData( ref CRYPTOAPI_BLOB pDataIn, string szDataDescr, ref CRYPTOAPI_BLOB pOptionalEntropy, IntPtr pvReserved, ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct, uint dwFlags, ref CRYPTOAPI_BLOB pDataBlob);
public unsafe void FindByThumbprint(byte[] thumbPrint) { fixed(byte *pThumbPrint = thumbPrint) { CRYPTOAPI_BLOB blob = new CRYPTOAPI_BLOB(thumbPrint.Length, pThumbPrint); FindCore(CertFindType.CERT_FIND_HASH, &blob); } }
internal CRYPT_OID_INFO(int size) { cbSize = (uint)size; pszOID = null; pwszName = null; dwGroupId = 0; Algid = 0; ExtraInfo = new CRYPTOAPI_BLOB(); }
internal static extern SafeCertContextHandle CertCreateSelfSignCertificate( SafeCryptProvHandle providerHandle, [In] ref CRYPTOAPI_BLOB subjectIssuerBlob, uint flags, [In] ref CRYPT_KEY_PROV_INFO keyProviderInformation, [In] ref CRYPT_ALGORITHM_IDENTIFIER signatureAlgorithm, [In] ref SYSTEMTIME startTime, [In] ref SYSTEMTIME endTime, [In] ref CERT_EXTENSIONS extensions);
public static extern IntPtr CertCreateSelfSignCertificate( SafeCryptProviderHandle hCryptProvOrNCryptKey, [Out] CRYPTOAPI_BLOB pSubjectIssuerBlob, CertCreationFlags dwFlags, CRYPT_KEY_PROV_INFO pKeyProvInfo, CRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, SYSTEMTIME pStartTime, SYSTEMTIME pEndTime, CERT_EXTENSIONS pExtension );
static extern bool CryptUnprotectData ( ref CRYPTOAPI_BLOB pDataIn, IntPtr ppszDataDescr, ref CRYPTOAPI_BLOB Entropy, IntPtr pvReserved, IntPtr pPromptStruct, uint dwFlags, ref CRYPTOAPI_BLOB pDataOut );
static extern bool CryptProtectData ( ref CRYPTOAPI_BLOB pDataIn, string szDataDescr, ref CRYPTOAPI_BLOB pOptionalEntropy, IntPtr pvReserved, IntPtr pPromptStruct, uint dwFlags, ref CRYPTOAPI_BLOB pDataOut );
public UniversalSubjectIdentifier(CRYPTOAPI_BLOB issuer, CRYPTOAPI_BLOB serialNumber) { var allZeroSerial = IsBlobAllZero(serialNumber); if (allZeroSerial) { var x500Name = LocalBufferSafeHandle.Zero; var flags = EncodingType.PKCS_7_ASN_ENCODING | EncodingType.X509_ASN_ENCODING; uint size = 0; if (Crypt32.CryptDecodeObjectEx(flags, (IntPtr)7, issuer.pbData, issuer.cbData, CryptDecodeFlags.CRYPT_DECODE_ALLOC_FLAG, IntPtr.Zero, out x500Name, ref size)) { using (x500Name) { var info = Marshal.PtrToStructure <CERT_NAME_INFO>(x500Name.DangerousGetHandle()); for (var i = 0L; i < info.cRDN; i++) { var rdn = Marshal.PtrToStructure <CERT_RDN>(new IntPtr(info.rgRDN.ToInt64() + i * Marshal.SizeOf <CERT_RDN>())); for (var j = 0; j < rdn.cRDNAttr; j++) { var attribute = Marshal.PtrToStructure <CERT_RDN_ATTR>(new IntPtr(rdn.rgRDNAttr.ToInt64() + j * Marshal.SizeOf <CERT_RDN_ATTR>())); if (attribute.pszObjId == KnownOids.KeyId) { Type = SubjectIdentifierType.SubjectKeyIdentifier; var ski = new byte[attribute.Value.cbData]; Marshal.Copy(attribute.Value.pbData, ski, 0, ski.Length); Value = HashHelpers.HexEncodeBigEndian(ski); return; } } } } } } unsafe { var result = Crypt32.CertNameToStr(EncodingType.PKCS_7_ASN_ENCODING | EncodingType.X509_ASN_ENCODING, new IntPtr(&issuer), CertNameStrType.CERT_X500_NAME_STR | CertNameStrType.CERT_NAME_STR_REVERSE_FLAG, null, 0); if (result <= 1) { throw new InvalidOperationException(); } var builder = new StringBuilder((int)result); var final = Crypt32.CertNameToStr(EncodingType.PKCS_7_ASN_ENCODING | EncodingType.X509_ASN_ENCODING, new IntPtr(&issuer), CertNameStrType.CERT_X500_NAME_STR | CertNameStrType.CERT_NAME_STR_REVERSE_FLAG, builder, result); if (final <= 1) { throw new InvalidOperationException(); } var serial = new byte[serialNumber.cbData]; Marshal.Copy(serialNumber.pbData, serial, 0, serial.Length); var issuerSerial = new X509IssuerSerial(); issuerSerial.IssuerName = builder.ToString(); issuerSerial.SerialNumber = HashHelpers.HexEncodeBigEndian(serial); Value = issuerSerial; Type = SubjectIdentifierType.IssuerAndSerialNumber; } }
internal static byte[] ToByteArray(this CRYPTOAPI_BLOB blob) { if (blob.cbData == 0) { return(new byte[0]); } byte[] destination = new byte[blob.cbData]; Marshal.Copy(blob.pbData, destination, 0, destination.Length); return(destination); }
public byte[] EncodeX509SubjectKeyIdentifierExtension(byte[] subjectKeyIdentifier) { unsafe { fixed(byte *pSubkectKeyIdentifier = subjectKeyIdentifier) { CRYPTOAPI_BLOB blob = new CRYPTOAPI_BLOB(subjectKeyIdentifier.Length, pSubkectKeyIdentifier); return(Interop.crypt32.EncodeObject(Oids.SubjectKeyIdentifier, &blob)); } } }
private static extern bool CryptQueryObject( CertQueryObjectType dwObjectType, [In] ref CRYPTOAPI_BLOB pvObject, ExpectedContentTypeFlags dwExpectedContentTypeFlags, ExpectedFormatTypeFlags dwExpectedFormatTypeFlags, int dwFlags, // reserved - always pass 0 IntPtr pdwMsgAndCertEncodingType, IntPtr pdwContentType, IntPtr pdwFormatType, IntPtr phCertStore, IntPtr phMsg, out IntPtr ppvContext );
public static CRYPTOAPI_BLOB FromBlob(CRYPTOAPI_BLOB blob) { CRYPTOAPI_BLOB ret = new CRYPTOAPI_BLOB(); if ((blob.cbData > 0) && (blob.pbData != IntPtr.Zero)) { ret.cbData = blob.cbData; ret.pbData = Marshal.AllocHGlobal((int)blob.cbData); byte[] buf = new byte[ret.cbData]; Marshal.Copy(blob.pbData, buf, 0, buf.Length); Marshal.Copy(buf, 0, ret.pbData, (int)ret.cbData); } return(ret); }
private static bool IsBlobAllZero(CRYPTOAPI_BLOB blob) { unsafe { var data = (byte *)blob.pbData.ToPointer(); for (var i = 0; i < blob.cbData; i++) { if (data[i] != 0) { return(false); } } return(true); } }
internal static byte[] ReadBlob(CRYPTOAPI_BLOB capiBlob) { byte[] managedBlob = new byte[capiBlob.cbData]; unsafe { byte* pCapiBlob = (byte*)capiBlob.pbData.ToPointer(); for (int i = 0; i < managedBlob.Length; ++i) { managedBlob[i] = pCapiBlob[i]; } } return managedBlob; }
private unsafe int SignCallback( IntPtr pCertContext, IntPtr pvExtra, uint algId, byte[] pDigestToSign, uint dwDigestToSign, ref CRYPTOAPI_BLOB blob ) { const int E_INVALIDARG = unchecked ((int)0x80070057); byte[] digest; Digest tosign = null; if (_signingAlgorithm == HashAlgorithmName.SHA256.Name) { tosign = new Digest { Sha256 = ByteString.CopyFrom(pDigestToSign), }; } else if (_signingAlgorithm == HashAlgorithmName.SHA384.Name) { tosign = new Digest { Sha384 = ByteString.CopyFrom(pDigestToSign), }; } else if (_signingAlgorithm == HashAlgorithmName.SHA512.Name) { tosign = new Digest { Sha512 = ByteString.CopyFrom(pDigestToSign), }; } else { throw new CryptographicException(_signingAlgorithm + " is not supported!"); } digest = _client.AsymmetricSign(_ckvn, tosign).Signature.ToByteArray(); var resultPtr = Marshal.AllocHGlobal(digest.Length); Marshal.Copy(digest, 0, resultPtr, digest.Length); blob.pbData = resultPtr; blob.cbData = (uint)digest.Length; return(0); }
internal unsafe Signature(AsnEncodedData data, SignatureKind kind) { Kind = kind; fixed(byte *pin = data.RawData) { EncodingType encodingType; CryptQueryContentType contentType; CryptQueryFormatType formatType; CryptMsgSafeHandle msgHandle; var blob = new CRYPTOAPI_BLOB { cbData = (uint)data.RawData.Length, pbData = new IntPtr(pin) }; var result = Crypt32.CryptQueryObject( CryptQueryObjectType.CERT_QUERY_OBJECT_BLOB, ref blob, CryptQueryContentFlagType.CERT_QUERY_CONTENT_FLAG_ALL, CryptQueryFormatFlagType.CERT_QUERY_FORMAT_FLAG_BINARY, CryptQueryObjectFlags.NONE, out encodingType, out contentType, out formatType, IntPtr.Zero, out msgHandle, IntPtr.Zero); if (!result) { msgHandle.Dispose(); throw new InvalidOperationException("Unable to read signature."); } var signerSize = 0u; if (!Crypt32.CryptMsgGetParam(msgHandle, CryptMsgParamType.CMSG_SIGNER_INFO_PARAM, 0, LocalBufferSafeHandle.Zero, ref signerSize)) { throw new InvalidOperationException(); } using (var signerHandle = LocalBufferSafeHandle.Alloc(signerSize)) { if (!Crypt32.CryptMsgGetParam(msgHandle, CryptMsgParamType.CMSG_SIGNER_INFO_PARAM, 0, signerHandle, ref signerSize)) { throw new InvalidOperationException(); } InitFromHandles(msgHandle, signerHandle); } } }
public SafeX509Extension(X509Extension extension) { this.blobPtr = Marshal.AllocHGlobal(extension.RawData.Length); Marshal.Copy(extension.RawData, 0, this.blobPtr, extension.RawData.Length); var blob = new CRYPTOAPI_BLOB { cbData = (uint)extension.RawData.Length, pbData = this.blobPtr }; var nativeExtension = new CERT_EXTENSION { fCritical = extension.Critical, pszObjId = extension.Oid.Value, Value = blob }; this.value = nativeExtension; }
private static IntPtr RetrieveCertificate(byte[] binaryHash, IntPtr certStore, out GCHandle?hashHandle, out GCHandle?hashBlobHandle, Logger logger) { logger.WriteLine("Retrieving certificate from the store", true); hashHandle = GCHandle.Alloc(binaryHash, GCHandleType.Pinned); var blob = new CRYPTOAPI_BLOB { cbData = binaryHash.Length, pbData = hashHandle.Value.AddrOfPinnedObject() }; hashBlobHandle = GCHandle.Alloc(blob, GCHandleType.Pinned); var certificate = NativeMethods.CertFindCertificateInStore(certStore, Constants.MY_ENCODING_TYPE, Constants.DONT_CARE, Constants.CERT_FIND_SHA1_HASH, hashBlobHandle.Value.AddrOfPinnedObject(), IntPtr.Zero); if (certificate == IntPtr.Zero) { var errorResult = Marshal.GetHRForLastWin32Error(); throw new SigningException($"Win32 error in CertFindCertificateInStore: {errorResult}", Marshal.GetExceptionForHR(errorResult)); } return(certificate); }
private byte[] SaveToMemoryStore(CertStoreSaveAs dwSaveAs) { unsafe { CRYPTOAPI_BLOB blob = new CRYPTOAPI_BLOB(0, null); if (!Interop.crypt32.CertSaveStore(_certStore, CertEncodingType.All, dwSaveAs, CertStoreSaveTo.CERT_STORE_SAVE_TO_MEMORY, ref blob, 0)) { throw Marshal.GetLastWin32Error().ToCryptographicException(); } byte[] exportedData = new byte[blob.cbData]; fixed(byte *pExportedData = exportedData) { blob.pbData = pExportedData; if (!Interop.crypt32.CertSaveStore(_certStore, CertEncodingType.All, dwSaveAs, CertStoreSaveTo.CERT_STORE_SAVE_TO_MEMORY, ref blob, 0)) { throw Marshal.GetLastWin32Error().ToCryptographicException(); } } return(exportedData); } }
/// <summary> /// Given a cert, extracts something like "C=US, S=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation" /// </summary> /// <param name="blob"></param> /// <returns></returns> public static string GetCertIssuerString(CRYPTOAPI_BLOB blob) { StringBuilder sb; // Convert CRYPTOAPI_BLOB to unmanaged pointer IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(blob)); try { Marshal.StructureToPtr(blob, p, false); // Get size UInt32 bufferSize = CertNameToStr( X509_ASN_ENCODING, p, CERT_X500_NAME_STR, null, 0); // Create var of that size sb = new StringBuilder((int)bufferSize); // Get the data CertNameToStr( X509_ASN_ENCODING, p, CERT_X500_NAME_STR, sb, bufferSize); } finally { Marshal.FreeHGlobal(p); } return(sb.ToString()); }
internal CRYPT_OID_INFO(int size) { cbSize = (uint) size; pszOID = null; pwszName = null; dwGroupId = 0; Algid = 0; ExtraInfo = new CRYPTOAPI_BLOB(); }
public static extern IntPtr CertFindCertificateInStore(IntPtr hCertStore, uint dwCertEncodingType, uint dwFindFlags, uint dwFindType, ref CRYPTOAPI_BLOB pHash, IntPtr pPrevCertContext);
internal CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA(int size) { cbSize = (uint) size; dwSignerIndex = 0; blob = new CRYPTOAPI_BLOB(); }
public static extern bool CryptUnprotectData(ref CRYPTOAPI_BLOB pDataIn, string szDataDescr, ref CRYPTOAPI_BLOB pOptionalEntropy, IntPtr pvReserved, ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct, uint dwFlags, ref CRYPTOAPI_BLOB pDataOut);
/// <summary> /// Protects the <c>userData</c> parameter and returns a byte array. /// </summary> /// <param name="userData">Byte array containing data to be protected.</param> /// <param name="optionalEntropy">Additional byte array used to encrypt the data.</param> /// <param name="scope">Value from the <see cref="DataProtectionScope"/> enumeration.</param> /// <returns>A byte array representing the encrypted data.</returns> /// <remarks> /// This method can be used to protect data such as passwords, keys, or connection strings. /// The <c>optionalEntropy</c> parameter enables you to use additional information to protect the data. /// This information must also be used when unprotecting the data using the <see cref="Unprotect"/> method. /// </remarks> public static byte[] Protect(byte[] userData, byte[] optionalEntropy, DataProtectionScope scope) { byte[] buffer = null; CRYPTOAPI_BLOB protectedBlob = new CRYPTOAPI_BLOB(); GCHandle gchUserData = new GCHandle(); GCHandle gchEntropy = new GCHandle(); if(userData == null) { throw new ArgumentNullException("userData"); } try { gchUserData = GCHandle.Alloc(userData, GCHandleType.Pinned); CRYPTOAPI_BLOB userDataBlob = new CRYPTOAPI_BLOB(); userDataBlob.cbData = (uint)userData.Length; userDataBlob.pbData = new IntPtr((int)gchUserData.AddrOfPinnedObject() + 4); CRYPTOAPI_BLOB entropyBlob = new CRYPTOAPI_BLOB(); if(optionalEntropy != null) { gchEntropy = GCHandle.Alloc(optionalEntropy, GCHandleType.Pinned); entropyBlob.cbData = (uint)optionalEntropy.Length; entropyBlob.pbData = gchEntropy.AddrOfPinnedObject(); } uint flags = 1; if(scope == DataProtectionScope.LocalMachine) { flags = (uint)(flags | CRYPTPROTECT_LOCAL_MACHINE); } if(!CryptProtectData(ref userDataBlob,string.Empty, ref entropyBlob, IntPtr.Zero,IntPtr.Zero,flags,ref protectedBlob) ) { throw new CryptographicException(Marshal.GetLastWin32Error()); } if(protectedBlob.pbData == IntPtr.Zero) { throw new OutOfMemoryException(); } buffer = new byte[protectedBlob.cbData]; Marshal.Copy(protectedBlob.pbData, buffer, 0, buffer.Length); } finally { if(gchUserData.IsAllocated) { gchUserData.Free(); } if(gchEntropy.IsAllocated) { gchEntropy.Free(); } if(protectedBlob.pbData != IntPtr.Zero) { ZeroMemory(protectedBlob.pbData, protectedBlob.cbData); LocalFree(protectedBlob.pbData); } } return buffer; }
internal static byte[] BlobToByteArray(CRYPTOAPI_BLOB blob) { if (blob.cbData == 0) { return new byte[0]; } byte[] destination = new byte[blob.cbData]; Marshal.Copy(blob.pbData, destination, 0, destination.Length); return destination; }
public CERT_EXTENSION(IntPtr objId, bool critical, byte[] value) { pszObjId = objId; fCritical = critical; Value = new CRYPTOAPI_BLOB(value); }
private static SafeCertContextHandle CreateSelfSignedCertificate(CngKey key, bool takeOwnershipOfKey, byte[] subjectName, X509CertificateCreationOptions creationOptions, string signatureAlgorithmOid, DateTime startTime, DateTime endTime) { // Create an algorithm identifier structure for the signature algorithm CRYPT_ALGORITHM_IDENTIFIER nativeSignatureAlgorithm = new CRYPT_ALGORITHM_IDENTIFIER(); nativeSignatureAlgorithm.pszObjId = signatureAlgorithmOid; nativeSignatureAlgorithm.Parameters = new CRYPTOAPI_BLOB(); nativeSignatureAlgorithm.Parameters.cbData = 0; nativeSignatureAlgorithm.Parameters.pbData = IntPtr.Zero; // Convert the begin and expire dates to system time structures SYSTEMTIME nativeStartTime = new SYSTEMTIME(startTime); SYSTEMTIME nativeEndTime = new SYSTEMTIME(endTime); CERT_EXTENSIONS nativeExtensions = new CERT_EXTENSIONS(); nativeExtensions.cExtension = 0; // Setup a CRYPT_KEY_PROV_INFO for the key CRYPT_KEY_PROV_INFO keyProvInfo = new CRYPT_KEY_PROV_INFO(); keyProvInfo.pwszContainerName = key.UniqueName; keyProvInfo.pwszProvName = key.Provider.Provider; keyProvInfo.dwProvType = 0; // NCRYPT keyProvInfo.dwFlags = 0; keyProvInfo.cProvParam = 0; keyProvInfo.rgProvParam = IntPtr.Zero; keyProvInfo.dwKeySpec = 0; // // Now that all of the needed data structures are setup, we can create the certificate // SafeCertContextHandle selfSignedCertHandle = null; unsafe { fixed (byte* pSubjectName = &subjectName[0]) { // Create a CRYPTOAPI_BLOB for the subject of the cert CRYPTOAPI_BLOB nativeSubjectName = new CRYPTOAPI_BLOB(); nativeSubjectName.cbData = subjectName.Length; nativeSubjectName.pbData = new IntPtr(pSubjectName); // Now that we've converted all the inputs to native data structures, we can generate // the self signed certificate for the input key. using (SafeNCryptKeyHandle keyHandle = key.Handle) { selfSignedCertHandle = CertCreateSelfSignCertificate(keyHandle, ref nativeSubjectName, creationOptions, ref keyProvInfo, ref nativeSignatureAlgorithm, ref nativeStartTime, ref nativeEndTime, ref nativeExtensions); if (selfSignedCertHandle.IsInvalid) { throw new CryptographicException(Marshal.GetLastWin32Error()); } } } } Debug.Assert(selfSignedCertHandle != null, "selfSignedCertHandle != null"); // Attach a key context to the certificate which will allow Windows to find the private key // associated with the certificate if the NCRYPT_KEY_HANDLE is ephemeral. // is done. using (SafeNCryptKeyHandle keyHandle = key.Handle) { CERT_KEY_CONTEXT keyContext = new CERT_KEY_CONTEXT(); keyContext.cbSize = Marshal.SizeOf(typeof(CERT_KEY_CONTEXT)); keyContext.hNCryptKey = keyHandle.DangerousGetHandle(); keyContext.dwKeySpec = KeySpec.NCryptKey; bool attachedProperty = false; int setContextError = 0; // Run in a CER to ensure accurate tracking of the transfer of handle ownership RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { CertificatePropertySetFlags flags = CertificatePropertySetFlags.None; if (!takeOwnershipOfKey) { // If the certificate is not taking ownership of the key handle, then it should // not release the handle when the context is released. flags |= CertificatePropertySetFlags.NoCryptRelease; } attachedProperty = CertSetCertificateContextProperty(selfSignedCertHandle, CertificateProperty.KeyContext, flags, ref keyContext); setContextError = Marshal.GetLastWin32Error(); // If we succesfully transferred ownership of the key to the certificate, // then we need to ensure that we no longer release its handle. if (attachedProperty && takeOwnershipOfKey) { keyHandle.SetHandleAsInvalid(); } } if (!attachedProperty) { throw new CryptographicException(setContextError); } } return selfSignedCertHandle; }
internal static extern bool PFXExportCertStoreEx( SafeCertStoreHandle certificateStoreHandle, ref CRYPTOAPI_BLOB pfxBlob, IntPtr password, IntPtr reserved, uint flags);
public static CRYPTOAPI_BLOB FromBlob(CRYPTOAPI_BLOB blob) { CRYPTOAPI_BLOB ret = new CRYPTOAPI_BLOB(); if ((blob.cbData > 0) && (blob.pbData != IntPtr.Zero)) { ret.cbData = blob.cbData; ret.pbData = Marshal.AllocHGlobal((int)blob.cbData); byte[] buf = new byte[ret.cbData]; Marshal.Copy(blob.pbData, buf, 0, buf.Length); Marshal.Copy(buf, 0, ret.pbData, (int)ret.cbData); } return ret; }
private StorePal CreatedLinkedStoreWithFindResults(X509FindType findType, Object findValue, bool validOnly) { unsafe { switch (findType) { case X509FindType.FindByThumbprint: { byte[] thumbPrint = ConfirmedCast <String>(findValue).DecodeHexString(); fixed(byte *pThumbPrint = thumbPrint) { CRYPTOAPI_BLOB blob = new CRYPTOAPI_BLOB(thumbPrint.Length, pThumbPrint); return(FindCore(CertFindType.CERT_FIND_HASH, &blob, validOnly)); } } case X509FindType.FindBySubjectName: { String subjectName = ConfirmedCast <String>(findValue); fixed(char *pSubjectName = subjectName) { return(FindCore(CertFindType.CERT_FIND_SUBJECT_STR, pSubjectName, validOnly)); } } case X509FindType.FindBySubjectDistinguishedName: { String subjectDistinguishedName = ConfirmedCast <String>(findValue); return(FindCore(validOnly, delegate(SafeCertContextHandle pCertContext) { String actual = GetCertNameInfo(pCertContext, CertNameType.CERT_NAME_RDN_TYPE, CertNameFlags.None); return subjectDistinguishedName.Equals(actual, StringComparison.OrdinalIgnoreCase); } )); } case X509FindType.FindByIssuerName: { String issuerName = ConfirmedCast <String>(findValue); fixed(char *pIssuerName = issuerName) { return(FindCore(CertFindType.CERT_FIND_ISSUER_STR, pIssuerName, validOnly)); } } case X509FindType.FindByIssuerDistinguishedName: { String issuerDistinguishedName = ConfirmedCast <String>(findValue); return(FindCore(validOnly, delegate(SafeCertContextHandle pCertContext) { String actual = GetCertNameInfo(pCertContext, CertNameType.CERT_NAME_RDN_TYPE, CertNameFlags.CERT_NAME_ISSUER_FLAG); return issuerDistinguishedName.Equals(actual, StringComparison.OrdinalIgnoreCase); } )); } case X509FindType.FindBySerialNumber: { String decimalOrHexString = ConfirmedCast <String>(findValue); // FindBySerialNumber allows the input format to be either in hex or decimal. Since we can't know which one was intended, // it compares against both interpretations and treats a match of either as a successful find. byte[] hexBytes = decimalOrHexString.DecodeHexString(); Array.Reverse(hexBytes); // String is big-endian, BigInteger constructor requires little-endian. BigInteger expected1 = PositiveBigIntegerFromByteArray(hexBytes); BigInteger ten = new BigInteger(10); BigInteger expected2 = BigInteger.Zero; foreach (char c in decimalOrHexString) { if (c >= '0' && c <= '9') { expected2 = BigInteger.Multiply(expected2, ten); expected2 = BigInteger.Add(expected2, c - '0'); } } return(FindCore(validOnly, delegate(SafeCertContextHandle pCertContext) { byte[] actual = pCertContext.CertContext->pCertInfo->SerialNumber.ToByteArray(); BigInteger actualAsBigInteger = PositiveBigIntegerFromByteArray(actual); // Convert to BigInteger as the comparison must not fail due to spurious leading zeros GC.KeepAlive(pCertContext); return expected1.Equals(actualAsBigInteger) || expected2.Equals(actualAsBigInteger); } )); } case X509FindType.FindByTimeValid: { DateTime dateTime = ConfirmedCast <DateTime>(findValue); FILETIME fileTime = FILETIME.FromDateTime(dateTime); return(FindCore(validOnly, delegate(SafeCertContextHandle pCertContext) { int comparison = Interop.crypt32.CertVerifyTimeValidity(ref fileTime, pCertContext.CertContext->pCertInfo); GC.KeepAlive(pCertContext); return comparison == 0; } )); } case X509FindType.FindByTimeNotYetValid: { DateTime dateTime = ConfirmedCast <DateTime>(findValue); FILETIME fileTime = FILETIME.FromDateTime(dateTime); return(FindCore(validOnly, delegate(SafeCertContextHandle pCertContext) { int comparison = Interop.crypt32.CertVerifyTimeValidity(ref fileTime, pCertContext.CertContext->pCertInfo); GC.KeepAlive(pCertContext); return comparison == -1; } )); } case X509FindType.FindByTimeExpired: { DateTime dateTime = ConfirmedCast <DateTime>(findValue); FILETIME fileTime = FILETIME.FromDateTime(dateTime); return(FindCore(validOnly, delegate(SafeCertContextHandle pCertContext) { int comparison = Interop.crypt32.CertVerifyTimeValidity(ref fileTime, pCertContext.CertContext->pCertInfo); GC.KeepAlive(pCertContext); return comparison == 1; } )); } case X509FindType.FindByTemplateName: { String expected = ConfirmedCast <String>(findValue); return(FindCore(validOnly, delegate(SafeCertContextHandle pCertContext) { // The template name can have 2 different formats: V1 format (<= Win2K) is just a string // V2 format (XP only) can be a friendly name or an OID. // An example of Template Name can be "ClientAuth". bool foundMatch = false; CERT_INFO *pCertInfo = pCertContext.CertContext->pCertInfo; { CERT_EXTENSION *pV1Template = Interop.crypt32.CertFindExtension(Oids.EnrollCertTypeExtension, pCertInfo->cExtension, pCertInfo->rgExtension); if (pV1Template != null) { byte[] extensionRawData = pV1Template->Value.ToByteArray(); if (!extensionRawData.DecodeObjectNoThrow( CryptDecodeObjectStructType.X509_UNICODE_ANY_STRING, delegate(void *pvDecoded) { CERT_NAME_VALUE *pNameValue = (CERT_NAME_VALUE *)pvDecoded; String actual = Marshal.PtrToStringUni(new IntPtr(pNameValue->Value.pbData)); if (expected.Equals(actual, StringComparison.OrdinalIgnoreCase)) { foundMatch = true; } })) { return false; } } } if (!foundMatch) { CERT_EXTENSION *pV2Template = Interop.crypt32.CertFindExtension(Oids.CertificateTemplate, pCertInfo->cExtension, pCertInfo->rgExtension); if (pV2Template != null) { byte[] extensionRawData = pV2Template->Value.ToByteArray(); if (!extensionRawData.DecodeObjectNoThrow( CryptDecodeObjectStructType.X509_CERTIFICATE_TEMPLATE, delegate(void *pvDecoded) { CERT_TEMPLATE_EXT *pTemplateExt = (CERT_TEMPLATE_EXT *)pvDecoded; String actual = Marshal.PtrToStringAnsi(pTemplateExt->pszObjId); String expectedOidValue = OidInfo.FindOidInfo(CryptOidInfoKeyType.CRYPT_OID_INFO_NAME_KEY, expected, OidGroup.Template, fallBackToAllGroups: true).OID; if (expectedOidValue == null) { expectedOidValue = expected; } if (expected.Equals(actual, StringComparison.OrdinalIgnoreCase)) { foundMatch = true; } })) { return false; } } } GC.KeepAlive(pCertContext); return foundMatch; })); } case X509FindType.FindByApplicationPolicy: { String expected = ConfirmedOidValue(findValue, OidGroup.Policy); return(FindCore(validOnly, delegate(SafeCertContextHandle pCertContext) { int numOids; int cbData = 0; if (!Interop.crypt32.CertGetValidUsages(1, ref pCertContext, out numOids, null, ref cbData)) { return false; } // -1 means the certificate is good for all usages. if (numOids == -1) { return true; } fixed(byte *pOidsPointer = new byte[cbData]) { if (!Interop.crypt32.CertGetValidUsages(1, ref pCertContext, out numOids, pOidsPointer, ref cbData)) { return false; } IntPtr *pOids = (IntPtr *)pOidsPointer; for (int i = 0; i < numOids; i++) { String actual = Marshal.PtrToStringAnsi(pOids[i]); if (expected.Equals(actual, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } } )); } case X509FindType.FindByCertificatePolicy: { String expected = ConfirmedOidValue(findValue, OidGroup.Policy); return(FindCore(validOnly, delegate(SafeCertContextHandle pCertContext) { CERT_INFO *pCertInfo = pCertContext.CertContext->pCertInfo; CERT_EXTENSION *pCertExtension = Interop.crypt32.CertFindExtension(Oids.CertPolicies, pCertInfo->cExtension, pCertInfo->rgExtension); if (pCertExtension == null) { return false; } bool foundMatch = false; byte[] extensionRawData = pCertExtension->Value.ToByteArray(); if (!extensionRawData.DecodeObjectNoThrow( CryptDecodeObjectStructType.X509_CERT_POLICIES, delegate(void *pvDecoded) { CERT_POLICIES_INFO *pCertPoliciesInfo = (CERT_POLICIES_INFO *)pvDecoded; for (int i = 0; i < pCertPoliciesInfo->cPolicyInfo; i++) { CERT_POLICY_INFO *pCertPolicyInfo = &(pCertPoliciesInfo->rgPolicyInfo[i]); String actual = Marshal.PtrToStringAnsi(pCertPolicyInfo->pszPolicyIdentifier); if (expected.Equals(actual, StringComparison.OrdinalIgnoreCase)) { foundMatch = true; break; } } } )) { return false; } GC.KeepAlive(pCertContext); return foundMatch; } )); } case X509FindType.FindByExtension: { String oidValue = ConfirmedOidValue(findValue, OidGroup.ExtensionOrAttribute); return(FindCore(validOnly, delegate(SafeCertContextHandle pCertContext) { CERT_INFO *pCertInfo = pCertContext.CertContext->pCertInfo; CERT_EXTENSION *pCertExtension = Interop.crypt32.CertFindExtension(oidValue, pCertInfo->cExtension, pCertInfo->rgExtension); GC.KeepAlive(pCertContext); return pCertExtension != null; } )); } case X509FindType.FindByKeyUsage: { X509KeyUsageFlags expected = ConfirmedX509KeyUsage(findValue); return(FindCore(validOnly, delegate(SafeCertContextHandle pCertContext) { CERT_INFO *pCertInfo = pCertContext.CertContext->pCertInfo; X509KeyUsageFlags actual; if (!Interop.crypt32.CertGetIntendedKeyUsage(CertEncodingType.All, pCertInfo, out actual, sizeof(X509KeyUsageFlags))) { return true; // no key usage means it is valid for all key usages. } GC.KeepAlive(pCertContext); return (actual & expected) == expected; } )); } case X509FindType.FindBySubjectKeyIdentifier: { byte[] expected = ConfirmedCast <String>(findValue).DecodeHexString(); return(FindCore(validOnly, delegate(SafeCertContextHandle pCertContext) { int cbData = 0; if (!Interop.crypt32.CertGetCertificateContextProperty(pCertContext, CertContextPropId.CERT_KEY_IDENTIFIER_PROP_ID, null, ref cbData)) { return false; } byte[] actual = new byte[cbData]; if (!Interop.crypt32.CertGetCertificateContextProperty(pCertContext, CertContextPropId.CERT_KEY_IDENTIFIER_PROP_ID, actual, ref cbData)) { return false; } return expected.ContentsEqual(actual); } )); } default: throw new CryptographicException(SR.Cryptography_X509_InvalidFindType); } } }
public static void TestHandleCtor() { IntPtr pCertContext = IntPtr.Zero; byte[] rawData = TestData.MsCertificate; unsafe { fixed (byte* pRawData = rawData) { CRYPTOAPI_BLOB certBlob = new CRYPTOAPI_BLOB() { cbData = rawData.Length, pbData = pRawData }; bool success = CryptQueryObject( CertQueryObjectType.CERT_QUERY_OBJECT_BLOB, ref certBlob, ExpectedContentTypeFlags.CERT_QUERY_CONTENT_FLAG_CERT, ExpectedFormatTypeFlags.CERT_QUERY_FORMAT_FLAG_BINARY, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out pCertContext ); if (!success) { int hr = Marshal.GetHRForLastWin32Error(); throw new CryptographicException(hr); } } } // Now, create an X509Certificate around our handle. using (X509Certificate2 c = new X509Certificate2(pCertContext)) { // And release our ref-count on the handle. X509Certificate better be maintaining its own. CertFreeCertificateContext(pCertContext); // Now, test various properties to make sure the X509Certificate actually wraps our CERT_CONTEXT. IntPtr h = c.Handle; Assert.Equal(pCertContext, h); pCertContext = IntPtr.Zero; #if netstandard17 Assert.Equal(rawData, c.GetRawCertData()); Assert.Equal(rawData, c.GetRawCertDataString().HexToByteArray()); #endif string issuer = c.Issuer; Assert.Equal( "CN=Microsoft Code Signing PCA, O=Microsoft Corporation, L=Redmond, S=Washington, C=US", issuer); byte[] expectedPublicKey = ( "3082010a0282010100e8af5ca2200df8287cbc057b7fadeeeb76ac28533f3adb" + "407db38e33e6573fa551153454a5cfb48ba93fa837e12d50ed35164eef4d7adb" + "137688b02cf0595ca9ebe1d72975e41b85279bf3f82d9e41362b0b40fbbe3bba" + "b95c759316524bca33c537b0f3eb7ea8f541155c08651d2137f02cba220b10b1" + "109d772285847c4fb91b90b0f5a3fe8bf40c9a4ea0f5c90a21e2aae3013647fd" + "2f826a8103f5a935dc94579dfb4bd40e82db388f12fee3d67a748864e162c425" + "2e2aae9d181f0e1eb6c2af24b40e50bcde1c935c49a679b5b6dbcef9707b2801" + "84b82a29cfbfa90505e1e00f714dfdad5c238329ebc7c54ac8e82784d37ec643" + "0b950005b14f6571c50203010001").HexToByteArray(); byte[] publicKey = c.GetPublicKey(); Assert.Equal(expectedPublicKey, publicKey); byte[] expectedThumbPrint = "108e2ba23632620c427c570b6d9db51ac31387fe".HexToByteArray(); byte[] thumbPrint = c.GetCertHash(); Assert.Equal(expectedThumbPrint, thumbPrint); } }
/***************************************************************************** * wmain * *****************************************************************************/ static int Main(string[] args) { HRESULT hr = HRESULT.S_OK; SafeHCERTSTORE hStoreHandle = default; string wszStoreName = "MY"; // by default, MY string wszContainerName = "SAMPLE"; uint dwBits = 0; string wszKeyAlgName = "RSA"; // string[] rgwszCNGAlgs = new string[] { "SHA1", "RSA" }; SafeNCRYPT_KEY_HANDLE hCNGKey = default; SafePCCERT_CONTEXT pCertContext = default; CRYPTOAPI_BLOB SubjectName = default; int i; // // options // for (i = 0; i < args.Length; i++) { if (string.Compare(args[i], "/?") == 0 || string.Compare(args[i], "-?") == 0) { Usage("CreateCert.exe"); goto CleanUp; } if (args[i][0] != '-') { break; } if (string.Compare(args[i], "-s") == 0) { if (i + 1 >= args.Length) { hr = HRESULT.E_INVALIDARG; goto CleanUp; } wszStoreName = args[++i]; } else if (string.Compare(args[i], "-c") == 0) { if (i + 1 >= args.Length) { hr = HRESULT.E_INVALIDARG; goto CleanUp; } wszContainerName = args[++i]; } else if (string.Compare(args[i], "-k") == 0) { if (i + 1 >= args.Length) { hr = HRESULT.E_INVALIDARG; goto CleanUp; } wszKeyAlgName = args[++i]; } else if (string.Compare(args[i], "-h") == 0) { if (i + 1 >= args.Length) { hr = HRESULT.E_INVALIDARG; goto CleanUp; } rgwszCNGAlgs[0] = args[++i]; } else if (string.Compare(args[i], "-l") == 0) { if (i + 1 >= args.Length) { hr = HRESULT.E_INVALIDARG; goto CleanUp; } dwBits = uint.Parse(args[++i]); } } if (i >= args.Length) { hr = HRESULT.E_INVALIDARG; goto CleanUp; } var wszSubject = args[i]; // // Find the Signature algorithm // var pOidInfo = CryptFindOIDInfo(CryptOIDInfoFlags.CRYPT_OID_INFO_NAME_KEY, wszKeyAlgName, OIDGroupId.CRYPT_PUBKEY_ALG_OID_GROUP_ID); if (default == pOidInfo) { Console.Write("FAILED: Unable to find Public Key algorithm: '{0}'.\n", wszKeyAlgName); hr = HRESULT.CRYPT_E_UNKNOWN_ALGO; goto CleanUp; } var oidInfo = (CRYPT_OID_INFO)pOidInfo; if (!string.IsNullOrEmpty(oidInfo.pwszCNGExtraAlgid)) { rgwszCNGAlgs[1] = oidInfo.pwszCNGExtraAlgid; } else { rgwszCNGAlgs[1] = oidInfo.pwszCNGAlgid; } using (var pAlgs = SafeLocalHandle.CreateFromStringList(rgwszCNGAlgs, StringListPackMethod.Packed, CharSet.Unicode)) pOidInfo = CryptFindOIDInfo(CryptOIDInfoFlags.CRYPT_OID_INFO_CNG_SIGN_KEY, pAlgs, OIDGroupId.CRYPT_SIGN_ALG_OID_GROUP_ID); if (default == pOidInfo) { Console.Write("FAILED: Unable to find signature algorithm: '{0}:{1}'\n", rgwszCNGAlgs[0], rgwszCNGAlgs[1]); hr = HRESULT.CRYPT_E_UNKNOWN_ALGO; goto CleanUp; } var SignatureAlgorithm = new CRYPT_ALGORITHM_IDENTIFIER { pszObjId = ((CRYPT_OID_INFO)pOidInfo).pszOID }; //------------------------------------------------------------------- // Open a system store, in this case, the My store. hStoreHandle = CertOpenStore(CertStoreProvider.CERT_STORE_PROV_SYSTEM, 0, default, CertStoreFlags.CERT_SYSTEM_STORE_CURRENT_USER, wszStoreName);
/// <summary> /// Create a self-signed x509 certificate. /// </summary> /// <param name="subjectName">The distinguished name.</param> /// <param name="notBefore">The start time.</param> /// <param name="notAfter">the end time.</param> /// <param name="extensions">the extensions.</param> /// <returns>A byte array containing the certificate and private key encoded as PFX.</returns> public static byte[] CreateSelfSignCertificatePfx( string subjectName, DateTime notBefore, DateTime notAfter, params X509Extension[] extensions) { if (subjectName == null) { subjectName = string.Empty; } byte[] pfxData; SYSTEMTIME startSystemTime = ToSystemTime(notBefore); SYSTEMTIME endSystemTime = ToSystemTime(notAfter); string containerName = $"Created by Workstation. {Guid.NewGuid().ToString()}"; GCHandle gcHandle = default(GCHandle); var providerContext = SafeCryptProvHandle.InvalidHandle; var cryptKey = SafeCryptKeyHandle.InvalidHandle; var certContext = SafeCertContextHandle.InvalidHandle; var certStore = SafeCertStoreHandle.InvalidHandle; var storeCertContext = SafeCertContextHandle.InvalidHandle; try { Check(NativeMethods.CryptAcquireContextW( out providerContext, containerName, null, PROV_RSA_FULL, CRYPT_NEWKEYSET)); Check(NativeMethods.CryptGenKey( providerContext, AT_KEYEXCHANGE, CRYPT_EXPORTABLE | (2048 << 16), // 2048bit out cryptKey)); IntPtr pbEncoded = IntPtr.Zero; int cbEncoded = 0; Check(NativeMethods.CertStrToNameW( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, subjectName, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, IntPtr.Zero, IntPtr.Zero, ref cbEncoded, IntPtr.Zero)); pbEncoded = Marshal.AllocHGlobal(cbEncoded); Check(NativeMethods.CertStrToNameW( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, subjectName, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, IntPtr.Zero, pbEncoded, ref cbEncoded, IntPtr.Zero)); var nameBlob = new CRYPTOAPI_BLOB { cbData = (uint)cbEncoded, pbData = pbEncoded }; var kpi = new CRYPT_KEY_PROV_INFO { pwszContainerName = containerName, dwProvType = PROV_RSA_FULL, dwKeySpec = AT_KEYEXCHANGE }; var signatureAlgorithm = new CRYPT_ALGORITHM_IDENTIFIER { pszObjId = OID_RSA_SHA256RSA, Parameters = default(CRYPTOAPI_BLOB) }; IntPtr pInfo = IntPtr.Zero; int cbInfo = 0; byte[] keyHash = null; int cbKeyHash = 0; try { Check(NativeMethods.CryptExportPublicKeyInfoEx( providerContext, AT_KEYEXCHANGE, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, OID_RSA_RSA, 0, IntPtr.Zero, IntPtr.Zero, ref cbInfo)); pInfo = Marshal.AllocHGlobal(cbInfo); Check(NativeMethods.CryptExportPublicKeyInfoEx( providerContext, AT_KEYEXCHANGE, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, OID_RSA_RSA, 0, IntPtr.Zero, pInfo, ref cbInfo)); Check(NativeMethods.CryptHashPublicKeyInfo( providerContext, CALG_SHA1, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pInfo, null, ref cbKeyHash)); keyHash = new byte[cbKeyHash]; Check(NativeMethods.CryptHashPublicKeyInfo( providerContext, CALG_SHA1, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pInfo, keyHash, ref cbKeyHash)); } finally { if (pInfo != IntPtr.Zero) { Marshal.FreeHGlobal(pInfo); } } var safeExtensions = new List<SafeX509Extension>(); var blob = IntPtr.Zero; try { foreach (var item in extensions) { safeExtensions.Add(new SafeX509Extension(item)); } // adding SubjectKeyIdentifier TODO: AuthKeyIdentifier? safeExtensions.Add(new SafeX509Extension(new X509SubjectKeyIdentifierExtension(keyHash, false))); var structSize = Marshal.SizeOf<CERT_EXTENSION>(); blob = Marshal.AllocHGlobal(structSize * safeExtensions.Count); for (int index = 0, offset = 0; index < safeExtensions.Count; index++, offset += structSize) { var marshalX509Extension = safeExtensions[index]; Marshal.StructureToPtr(marshalX509Extension.Value, blob + offset, false); } var certExtensions = new CERT_EXTENSIONS { cExtension = (uint)safeExtensions.Count, rgExtension = blob }; certContext = NativeMethods.CertCreateSelfSignCertificate( providerContext, ref nameBlob, 0, ref kpi, ref signatureAlgorithm, ref startSystemTime, ref endSystemTime, ref certExtensions); Check(!certContext.IsInvalid); } finally { foreach (var safeExtension in safeExtensions) { safeExtension.Dispose(); } safeExtensions.Clear(); Marshal.FreeHGlobal(blob); Marshal.FreeHGlobal(pbEncoded); } certStore = NativeMethods.CertOpenStore( sz_CERT_STORE_PROV_MEMORY, 0, IntPtr.Zero, CERT_STORE_CREATE_NEW_FLAG, IntPtr.Zero); Check(!certStore.IsInvalid); Check(NativeMethods.CertAddCertificateContextToStore( certStore, certContext, CERT_STORE_ADD_NEW, out storeCertContext)); NativeMethods.CertSetCertificateContextProperty( storeCertContext, CERT_KEY_PROV_INFO_PROP_ID, 0, ref kpi); CRYPTOAPI_BLOB pfxBlob = default(CRYPTOAPI_BLOB); Check(NativeMethods.PFXExportCertStoreEx( certStore, ref pfxBlob, IntPtr.Zero, IntPtr.Zero, EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY)); pfxData = new byte[pfxBlob.cbData]; gcHandle = GCHandle.Alloc(pfxData, GCHandleType.Pinned); pfxBlob.pbData = gcHandle.AddrOfPinnedObject(); Check(NativeMethods.PFXExportCertStoreEx( certStore, ref pfxBlob, IntPtr.Zero, IntPtr.Zero, EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY)); gcHandle.Free(); } finally { if (gcHandle.IsAllocated) { gcHandle.Free(); } if (!certContext.IsInvalid) { certContext.Dispose(); } if (!storeCertContext.IsInvalid) { storeCertContext.Dispose(); } if (!certStore.IsInvalid) { certStore.Dispose(); } if (!cryptKey.IsInvalid) { cryptKey.Dispose(); } if (!providerContext.IsInvalid) { providerContext.Dispose(); providerContext = SafeCryptProvHandle.InvalidHandle; // Delete generated keyset. Does not return a providerContext NativeMethods.CryptAcquireContextW( out providerContext, containerName, null, PROV_RSA_FULL, CRYPT_DELETEKEYSET); } } return pfxData; }