public static extern IntPtr CertCreateSelfSignCertificate(IntPtr hProv, ref CERT_NAME_BLOB pSubjectIssuerBlob, uint dwFlagsm, ref CRYPT_KEY_PROV_INFO pKeyProvInfo, ref CRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, ref SYSTEM_TIME pStartTime, ref SYSTEM_TIME pEndTime, IntPtr other);
public static extern IntPtr CertCreateSelfSignCertificate( IntPtr hProv, ref CERT_NAME_BLOB pSubjectIssuerBlob, uint dwFlagsm, ref CRYPT_KEY_PROV_INFO pKeyProvInfo, IntPtr pSignatureAlgorithm, IntPtr pStartTime, IntPtr pEndTime, IntPtr other);
/// <summary> /// Decodes a CERT_NAME_BLOB. /// </summary> public static string Decode_CERT_NAME_BLOB(CERT_NAME_BLOB blob) { int dwChars = 0; IntPtr pName = IntPtr.Zero; IntPtr pBlob = IntPtr.Zero; try { pBlob = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32.CERT_NAME_BLOB))); Marshal.StructureToPtr(blob, pBlob, false); int bResult = Win32.CertNameToStrW( Win32.PKCS_7_ASN_ENCODING | Win32.X509_ASN_ENCODING, pBlob, Win32.CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, IntPtr.Zero, dwChars); if (bResult == 0) { throw GetLastError(StatusCodes.BadDecodingError, "Could not get size of CERT_X500_NAME_STR."); } dwChars = bResult; pName = Marshal.AllocHGlobal((dwChars + 1) * 2); bResult = Win32.CertNameToStrW( Win32.PKCS_7_ASN_ENCODING | Win32.X509_ASN_ENCODING, pBlob, Win32.CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, pName, dwChars); if (bResult == 0) { throw GetLastError(StatusCodes.BadDecodingError, "Could not decode CERT_X500_NAME_STR."); } return(Marshal.PtrToStringUni(pName)); } finally { if (pBlob != IntPtr.Zero) { Marshal.FreeHGlobal(pBlob); } if (pName != IntPtr.Zero) { Marshal.FreeHGlobal(pName); } } }
/// <summary> /// Encodes a CERT_NAME_BLOB /// </summary> public static void Encode_CERT_NAME_BLOB(string name, ref CERT_NAME_BLOB pName) { int dwSize = 0; IntPtr pBuffer = IntPtr.Zero; try { // reconstruct name using comma as delimeter. name = ChangeSubjectNameDelimiter(name, ','); int bResult = Win32.CertStrToNameW( X509_ASN_ENCODING, name, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, IntPtr.Zero, IntPtr.Zero, ref dwSize, IntPtr.Zero); if (bResult == 0) { throw GetLastError(StatusCodes.BadEncodingError, "Could not get size for CERT_X500_NAME_STR."); } pBuffer = Marshal.AllocHGlobal(dwSize); bResult = Win32.CertStrToNameW( X509_ASN_ENCODING, name, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, IntPtr.Zero, pBuffer, ref dwSize, IntPtr.Zero); if (bResult == 0) { throw GetLastError(StatusCodes.BadEncodingError, "Could not encode CERT_X500_NAME_STR."); } pName.pbData = pBuffer; pName.cbData = dwSize; pBuffer = IntPtr.Zero; } finally { if (pBuffer != IntPtr.Zero) { Marshal.FreeHGlobal(pBuffer); } } }
public static extern Int32 CertNameToStr( Int32 dwCertEncodingType, ref CERT_NAME_BLOB pName, Int32 dwStrType, StringBuilder psz, Int32 csz );
// frees the memory used by a X500 name blob. private static void DeleteX500Name(ref CERT_NAME_BLOB pName) { Marshal.FreeHGlobal(pName.pbData); pName.pbData = IntPtr.Zero; pName.cbData = 0; }
/// <summary> /// Creates and returns a self-signed X509 certificate. /// </summary> /// <param name="key">The key of the certificate.</param> /// <param name="issuerName">The encoded common name of the issuer.</param> /// <param name="settings">The settings of the X509 certificate.</param> /// <returns>An <see cref="X509Certificate"/> instance.</returns> /// <exception cref="CryptographicException">An error occurs while creating the self-signed certificate.</exception> public static X509Certificate Create(RSACryptoServiceProvider key, string issuerName, CertificateSettings settings) { if (settings == null) settings = new CertificateSettings(); CspKeyContainerInfo container = key.CspKeyContainerInfo; IntPtr certHandle = IntPtr.Zero; byte[] issuerBlob = null; uint pcbEncoded = 0; if (CertStrToName(X509_ASN_ENCODING, issuerName, CERT_X500_NAME_STR, IntPtr.Zero, null, ref pcbEncoded, IntPtr.Zero)) { issuerBlob = new byte[pcbEncoded]; CertStrToName(X509_ASN_ENCODING, issuerName, CERT_X500_NAME_STR, IntPtr.Zero, issuerBlob, ref pcbEncoded, IntPtr.Zero); } else { throw new CryptographicException("Cannot encode the issuer name! Please check whether the string is in a valid format."); } List<IntPtr> extensionList = new List<IntPtr>(); CERT_NAME_BLOB subject = new CERT_NAME_BLOB(); try { subject.pbData = Marshal.AllocHGlobal(issuerBlob.Length); Marshal.Copy(issuerBlob, 0, subject.pbData, issuerBlob.Length); subject.cbData = issuerBlob.Length; SYSTEMTIME startDate = ConvertDateTime(settings.StartDate); SYSTEMTIME endDate = ConvertDateTime(settings.EndDate); CRYPT_KEY_PROV_INFO providerInfo = new CRYPT_KEY_PROV_INFO(); providerInfo.pwszContainerName = container.KeyContainerName; providerInfo.pwszProvName = container.ProviderName; providerInfo.dwProvType = container.ProviderType; providerInfo.dwFlags = (int)0; providerInfo.cProvParam = 0; providerInfo.rgProvParam = IntPtr.Zero; providerInfo.dwKeySpec = (int)container.KeyNumber; // convert the list of extensions to an unmanaged structure // the .NET marshallers don't handle recursive arrays too well, // so we do it manually :-/ extensionList = ConvertExtensions(settings.Extensions); certHandle = CertCreateSelfSignCertificate(IntPtr.Zero, ref subject, settings.Flags, ref providerInfo, IntPtr.Zero, ref startDate, ref endDate, extensionList[0]); if (certHandle == IntPtr.Zero) { throw new CryptographicException("Couldn't create unsigned certificate"); } return new X509Certificate2(certHandle); } finally { if (subject.pbData != IntPtr.Zero) Marshal.FreeHGlobal(subject.pbData); if (extensionList != null && extensionList.Count > 0) { foreach (IntPtr ptr in extensionList) { if (ptr != IntPtr.Zero) Marshal.FreeHGlobal(ptr); } } } }
/// <summary> /// Parses an X500 name blob. /// </summary> private static void ParseX500Name(CERT_NAME_BLOB blob, out string subjectName) { int dwChars = 0; IntPtr pName = IntPtr.Zero; IntPtr pBlob = IntPtr.Zero; try { pBlob = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CERT_NAME_BLOB))); Marshal.StructureToPtr(blob, pBlob, false); int bResult = NativeMethods.CertNameToStrW( PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, pBlob, CERT_X500_NAME_STR, IntPtr.Zero, dwChars); if (bResult == 0) { throw new InvalidOperationException("Could not get size of X500 name."); } dwChars = bResult; pName = Marshal.AllocHGlobal((dwChars+1)*2); bResult = NativeMethods.CertNameToStrW( PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, pBlob, CERT_X500_NAME_STR, pName, dwChars); if (bResult == 0) { throw new InvalidOperationException("Could not decode X500 name blob."); } subjectName = Marshal.PtrToStringUni(pName); } finally { if (pBlob != IntPtr.Zero) { Marshal.FreeHGlobal(pBlob); } if (pName != IntPtr.Zero) { Marshal.FreeHGlobal(pName); } } }
// Encodes an X500 name in a CrytoAPI compatible blob private static void CreateX500Name(string name, ref CERT_NAME_BLOB pName) { int dwSize = 0; IntPtr pBuffer = IntPtr.Zero; try { // reconstruct name using comma as delimeter. name = ChangeSubjectNameDelimiter(name, ','); int bResult = NativeMethods.CertStrToNameW( X509_ASN_ENCODING, name, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, IntPtr.Zero, IntPtr.Zero, ref dwSize, IntPtr.Zero); if (bResult == 0) { throw ServiceResultException.Create( StatusCodes.BadEncodingError, "Could not get size of X500 name blob. Name={0}", name); } pBuffer = Marshal.AllocHGlobal(dwSize); bResult = NativeMethods.CertStrToNameW( X509_ASN_ENCODING, name, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, IntPtr.Zero, pBuffer, ref dwSize, IntPtr.Zero); if (bResult == 0) { throw ServiceResultException.Create( StatusCodes.BadEncodingError, "Could not create X500 name blob. Name={0}", name); } pName.pbData = pBuffer; pName.cbData = dwSize; pBuffer = IntPtr.Zero; } finally { if (pBuffer != IntPtr.Zero) { Marshal.FreeHGlobal(pBuffer); } } }
/// <summary> /// Creates the certificate and adds it to the store. /// </summary> private static string CreateSelfSignedCertificate( IntPtr hProvider, IntPtr hStore, bool useMachineStore, string applicationName, string applicationUri, string subjectName, IList<string> hostNames, ushort keySize, ushort lifetimeInMonths, ushort algorithm = 0) { IntPtr hKey = IntPtr.Zero; IntPtr pKpi = IntPtr.Zero; IntPtr pThumbprint = IntPtr.Zero; IntPtr pContext = IntPtr.Zero; IntPtr pAlgorithmId = IntPtr.Zero; IntPtr pNewContext = IntPtr.Zero; CRYPT_DATA_BLOB publicKeyId = new CRYPT_DATA_BLOB(); CERT_NAME_BLOB subjectNameBlob = new CERT_NAME_BLOB(); SYSTEMTIME stValidTo = new SYSTEMTIME(); CERT_EXTENSIONS extensions = new CERT_EXTENSIONS(); CRYPT_DATA_BLOB friendlyName = new CRYPT_DATA_BLOB(); GCHandle hValidTo = new GCHandle(); GCHandle hExtensionList = new GCHandle(); GCHandle hSubjectNameBlob = new GCHandle(); GCHandle hFriendlyName = new GCHandle(); try { // create a new key pair. int bResult = NativeMethods.CryptGenKey( hProvider, AT_KEYEXCHANGE, CRYPT_EXPORTABLE | (keySize << 16), ref hKey); if (bResult == 0) { Throw("Could not generate a new key pair. Error={0:X8}", Marshal.GetLastWin32Error()); } // gey the public key identifier. GetPublicKeyIdentifier(hProvider, ref publicKeyId); // construct the certificate subject name. CreateX500Name(subjectName, ref subjectNameBlob); GCHandle hSubjectName = GCHandle.Alloc(subjectNameBlob, GCHandleType.Pinned); // allocate memory for all possible extensions. extensions.cExtension = 0; extensions.rgExtension = Marshal.AllocHGlobal(6 * Marshal.SizeOf(typeof(CERT_EXTENSION))); // create the subject key info extension. IntPtr pPos = extensions.rgExtension; CERT_EXTENSION extension = new CERT_EXTENSION(); CreateSubjectKeyIdentifierExtension(ref extension, ref publicKeyId); Marshal.StructureToPtr(extension, pPos, false); pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION))); extensions.cExtension++; // create the authority key info extension. extension = new CERT_EXTENSION(); CreateAuthorityKeyIdentifierExtension(ref extension, ref publicKeyId); Marshal.StructureToPtr(extension, pPos, false); pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION))); extensions.cExtension++; // create the basic constraints extension. extension = new CERT_EXTENSION(); CreateBasicConstraintsExtension(ref extension, false); Marshal.StructureToPtr(extension, pPos, false); pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION))); extensions.cExtension++; // create the key usage extension. extension = new CERT_EXTENSION(); CreateKeyUsageExtension(ref extension, false); Marshal.StructureToPtr(extension, pPos, false); pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION))); extensions.cExtension++; // create the extended key usage extension. extension = new CERT_EXTENSION(); CreateExtendedKeyUsageExtension(ref extension); Marshal.StructureToPtr(extension, pPos, false); pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION))); extensions.cExtension++; // create the subject alternate name extension. extension = new CERT_EXTENSION(); CreateSubjectAltNameExtension(applicationUri, hostNames, ref extension); Marshal.StructureToPtr(extension, pPos, false); pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION))); extensions.cExtension++; // set the expiration date. DateTime validTo = DateTime.UtcNow.AddMonths(lifetimeInMonths); System.Runtime.InteropServices.ComTypes.FILETIME ftValidTo = new System.Runtime.InteropServices.ComTypes.FILETIME(); ulong ticks = (ulong)(validTo.Ticks - new DateTime(1601, 1, 1).Ticks); ftValidTo.dwHighDateTime = (int)((0xFFFFFFFF00000000 & (ulong)ticks) >> 32); ftValidTo.dwLowDateTime = (int)((ulong)ticks & 0x00000000FFFFFFFF); NativeMethods.FileTimeToSystemTime(ref ftValidTo, ref stValidTo); // specify what key is being used to sign the certificate. CRYPT_KEY_PROV_INFO kpi = new CRYPT_KEY_PROV_INFO(); kpi.pwszContainerName = KEY_CONTAINER_NAME; // must be the same as the hProvider kpi.pwszProvName = DEFAULT_CRYPTO_PROVIDER; kpi.dwProvType = PROV_RSA_FULL; kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID; kpi.dwKeySpec = AT_KEYEXCHANGE; if (useMachineStore) { kpi.dwFlags |= CRYPT_MACHINE_KEYSET; } else { kpi.dwFlags |= CRYPT_USER_KEYSET; } pKpi = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CRYPT_KEY_PROV_INFO))); Marshal.StructureToPtr(kpi, pKpi, false); hValidTo = GCHandle.Alloc(stValidTo, GCHandleType.Pinned); hExtensionList = GCHandle.Alloc(extensions, GCHandleType.Pinned); hSubjectNameBlob = GCHandle.Alloc(subjectNameBlob, GCHandleType.Pinned); if (algorithm == 1) { CRYPT_ALGORITHM_IDENTIFIER algorithmID = new CRYPT_ALGORITHM_IDENTIFIER(); algorithmID.pszObjId = "1.2.840.113549.1.1.11"; //SHA256 pAlgorithmId = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CRYPT_ALGORITHM_IDENTIFIER))); Marshal.StructureToPtr(algorithmID, pAlgorithmId, false); //create the certificate pContext = NativeMethods.CertCreateSelfSignCertificate( hProvider, hSubjectNameBlob.AddrOfPinnedObject(), 0, pKpi, pAlgorithmId, IntPtr.Zero, hValidTo.AddrOfPinnedObject(), hExtensionList.AddrOfPinnedObject()); } else { // (default) create the certificate. pContext = NativeMethods.CertCreateSelfSignCertificate( hProvider, hSubjectNameBlob.AddrOfPinnedObject(), 0, pKpi, IntPtr.Zero, IntPtr.Zero, hValidTo.AddrOfPinnedObject(), hExtensionList.AddrOfPinnedObject()); } if (pContext == IntPtr.Zero) { Throw("Could not create self-signed certificate. Error={0:X8}", Marshal.GetLastWin32Error()); } // get the thumbprint. int dwThumbprintSize = 20; pThumbprint = Marshal.AllocHGlobal(dwThumbprintSize); bResult = NativeMethods.CertGetCertificateContextProperty( pContext, CERT_SHA1_HASH_PROP_ID, pThumbprint, ref dwThumbprintSize); if (bResult == 0) { Throw("Could not get the thumbprint of the new certificate. Error={0:X8}", Marshal.GetLastWin32Error()); } byte[] bytes = new byte[dwThumbprintSize]; Marshal.Copy(pThumbprint, bytes, 0, dwThumbprintSize); string thumbprint = Utils.ToHexString(bytes); // set the friendly name. friendlyName.pbData = Marshal.StringToHGlobalUni(applicationName); friendlyName.cbData = (applicationName.Length+1)*Marshal.SizeOf(typeof(ushort)); hFriendlyName = GCHandle.Alloc(friendlyName, GCHandleType.Pinned); bResult = NativeMethods.CertSetCertificateContextProperty( pContext, CERT_FRIENDLY_NAME_PROP_ID, 0, hFriendlyName.AddrOfPinnedObject()); if (bResult == 0) { Throw("Could not set the friendly name for the certificate. Error={0:X8}", Marshal.GetLastWin32Error()); } // add into store. bResult = NativeMethods.CertAddCertificateContextToStore( hStore, pContext, CERT_STORE_ADD_REPLACE_EXISTING, ref pNewContext); if (bResult == 0) { Throw("Could not add the certificate to the store. Error={0:X8}", Marshal.GetLastWin32Error()); } return thumbprint; } finally { if (pContext != IntPtr.Zero) { NativeMethods.CertFreeCertificateContext(pContext); } if (pNewContext != IntPtr.Zero) { NativeMethods.CertFreeCertificateContext(pNewContext); } if (friendlyName.pbData != IntPtr.Zero) { Marshal.FreeHGlobal(friendlyName.pbData); } if (pThumbprint != IntPtr.Zero) { Marshal.FreeHGlobal(pThumbprint); } if (pAlgorithmId != IntPtr.Zero) { Marshal.DestroyStructure(pAlgorithmId, typeof(CRYPT_ALGORITHM_IDENTIFIER)); Marshal.FreeHGlobal(pAlgorithmId); } if (hValidTo.IsAllocated) hValidTo.Free(); if (hExtensionList.IsAllocated) hExtensionList.Free(); if (hSubjectNameBlob.IsAllocated) hSubjectNameBlob.Free(); if (hFriendlyName.IsAllocated) hFriendlyName.Free(); if (pKpi != IntPtr.Zero) { Marshal.DestroyStructure(pKpi, typeof(CRYPT_KEY_PROV_INFO)); Marshal.FreeHGlobal(pKpi); } DeleteExtensions(ref extensions.rgExtension, extensions.cExtension); DeleteX500Name(ref subjectNameBlob); if (publicKeyId.pbData != IntPtr.Zero) { Marshal.FreeHGlobal(publicKeyId.pbData); } if (hKey != IntPtr.Zero) { NativeMethods.CryptDestroyKey(hKey); } } }
/// <summary> /// Decodes a CERT_NAME_BLOB. /// </summary> public static string Decode_CERT_NAME_BLOB(CERT_NAME_BLOB blob) { int dwChars = 0; IntPtr pName = IntPtr.Zero; IntPtr pBlob = IntPtr.Zero; try { pBlob = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32.CERT_NAME_BLOB))); Marshal.StructureToPtr(blob, pBlob, false); int bResult = Win32.CertNameToStrW( Win32.PKCS_7_ASN_ENCODING | Win32.X509_ASN_ENCODING, pBlob, Win32.CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, IntPtr.Zero, dwChars); if (bResult == 0) { throw GetLastError(StatusCodes.BadDecodingError, "Could not get size of CERT_X500_NAME_STR."); } dwChars = bResult; pName = Marshal.AllocHGlobal((dwChars + 1) * 2); bResult = Win32.CertNameToStrW( Win32.PKCS_7_ASN_ENCODING | Win32.X509_ASN_ENCODING, pBlob, Win32.CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, pName, dwChars); if (bResult == 0) { throw GetLastError(StatusCodes.BadDecodingError, "Could not decode CERT_X500_NAME_STR."); } return Marshal.PtrToStringUni(pName); } finally { if (pBlob != IntPtr.Zero) { Marshal.FreeHGlobal(pBlob); } if (pName != IntPtr.Zero) { Marshal.FreeHGlobal(pName); } } }
private static IntPtr CreateUnsignedCertCntxt(String keycontainer, String provider, uint KEYSPEC, uint cspflags, String DN) { const uint AT_KEYEXCHANGE = 0x00000001; const uint AT_SIGNATURE = 0x00000002; const uint CRYPT_MACHINE_KEYSET = 0x00000020; const uint PROV_RSA_FULL = 0x00000001; const String MS_DEF_PROV = "Microsoft Base Cryptographic Provider v1.0"; const String MS_STRONG_PROV = "Microsoft Strong Cryptographic Provider"; const String MS_ENHANCED_PROV = "Microsoft Enhanced Cryptographic Provider v1.0"; const uint CERT_CREATE_SELFSIGN_NO_SIGN = 1; const uint X509_ASN_ENCODING = 0x00000001; const uint CERT_X500_NAME_STR = 3; IntPtr hCertCntxt = IntPtr.Zero; byte[] encodedName = null; uint cbName = 0; if (provider != MS_DEF_PROV && provider != MS_STRONG_PROV && provider != MS_ENHANCED_PROV) return IntPtr.Zero; if (keycontainer == "") return IntPtr.Zero; if (KEYSPEC != AT_SIGNATURE && KEYSPEC != AT_KEYEXCHANGE) return IntPtr.Zero; if (cspflags != 0 && cspflags != CRYPT_MACHINE_KEYSET) //only 0 (Current User) keyset is currently used. return IntPtr.Zero; if (DN == "") return IntPtr.Zero; if (Win32.CertStrToName(X509_ASN_ENCODING, DN, CERT_X500_NAME_STR, IntPtr.Zero, null, ref cbName, IntPtr.Zero)) { encodedName = new byte[cbName]; Win32.CertStrToName(X509_ASN_ENCODING, DN, CERT_X500_NAME_STR, IntPtr.Zero, encodedName, ref cbName, IntPtr.Zero); } CERT_NAME_BLOB subjectblob = new CERT_NAME_BLOB(); subjectblob.pbData = Marshal.AllocHGlobal(encodedName.Length); Marshal.Copy(encodedName, 0, subjectblob.pbData, encodedName.Length); subjectblob.cbData = encodedName.Length; CRYPT_KEY_PROV_INFO pInfo = new CRYPT_KEY_PROV_INFO(); pInfo.pwszContainerName = keycontainer; pInfo.pwszProvName = provider; pInfo.dwProvType = PROV_RSA_FULL; pInfo.dwFlags = cspflags; pInfo.cProvParam = 0; pInfo.rgProvParam = IntPtr.Zero; pInfo.dwKeySpec = KEYSPEC; hCertCntxt = Win32.CertCreateSelfSignCertificate(IntPtr.Zero, ref subjectblob, CERT_CREATE_SELFSIGN_NO_SIGN, ref pInfo, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); if (hCertCntxt == IntPtr.Zero) showWin32Error(Marshal.GetLastWin32Error()); Marshal.FreeHGlobal(subjectblob.pbData); return hCertCntxt; }