123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- using MediaBrowser.Model.Logging;
- using System;
- using System.IO;
- using System.Runtime.CompilerServices;
- using System.Runtime.InteropServices;
- namespace MediaBrowser.ServerApplication.Networking
- {
- // Copied from: http://blogs.msdn.com/b/dcook/archive/2014/05/16/9143036.aspx
- // In case anybody is interested, source code is attached and is free for use by anybody as long as you don't hold me or Microsoft liable for it --
- // I have no idea whether this is actually the right or best way to do this. Give it the X500 distinguished name, validity start and end dates,
- // and an optional password for encrypting the key data, and it will give you the PFX file data. Let me know if you find any bugs or have any suggestions.
- internal class CertificateGenerator
- {
- internal static void CreateSelfSignCertificatePfx(
- string fileName,
- string hostname,
- ILogger logger)
- {
- if (string.IsNullOrWhiteSpace(fileName))
- {
- throw new ArgumentNullException("fileName");
- }
- string x500 = string.Format("CN={0}", hostname);
- DateTime startTime = DateTime.Now.AddDays(-2);
- DateTime endTime = DateTime.Now.AddYears(10);
- byte[] pfxData = CreateSelfSignCertificatePfx(
- x500,
- startTime,
- endTime);
- File.WriteAllBytes(fileName, pfxData);
- }
- private static byte[] CreateSelfSignCertificatePfx(
- string x500,
- DateTime startTime,
- DateTime endTime)
- {
- byte[] pfxData;
- if (x500 == null)
- {
- x500 = "";
- }
- SystemTime startSystemTime = ToSystemTime(startTime);
- SystemTime endSystemTime = ToSystemTime(endTime);
- string containerName = Guid.NewGuid().ToString();
- GCHandle dataHandle = new GCHandle();
- IntPtr providerContext = IntPtr.Zero;
- IntPtr cryptKey = IntPtr.Zero;
- IntPtr certContext = IntPtr.Zero;
- IntPtr certStore = IntPtr.Zero;
- IntPtr storeCertContext = IntPtr.Zero;
- IntPtr passwordPtr = IntPtr.Zero;
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- Check(NativeMethods.CryptAcquireContextW(
- out providerContext,
- containerName,
- null,
- 1, // PROV_RSA_FULL
- 8)); // CRYPT_NEWKEYSET
- Check(NativeMethods.CryptGenKey(
- providerContext,
- 1, // AT_KEYEXCHANGE
- 1 | 2048 << 16, // CRYPT_EXPORTABLE 2048 bit key
- out cryptKey));
- IntPtr errorStringPtr;
- int nameDataLength = 0;
- byte[] nameData;
- // errorStringPtr gets a pointer into the middle of the x500 string,
- // so x500 needs to be pinned until after we've copied the value
- // of errorStringPtr.
- dataHandle = GCHandle.Alloc(x500, GCHandleType.Pinned);
- if (!NativeMethods.CertStrToNameW(
- 0x00010001, // X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
- dataHandle.AddrOfPinnedObject(),
- 3, // CERT_X500_NAME_STR = 3
- IntPtr.Zero,
- null,
- ref nameDataLength,
- out errorStringPtr))
- {
- string error = Marshal.PtrToStringUni(errorStringPtr);
- throw new ArgumentException(error);
- }
- nameData = new byte[nameDataLength];
- if (!NativeMethods.CertStrToNameW(
- 0x00010001, // X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
- dataHandle.AddrOfPinnedObject(),
- 3, // CERT_X500_NAME_STR = 3
- IntPtr.Zero,
- nameData,
- ref nameDataLength,
- out errorStringPtr))
- {
- string error = Marshal.PtrToStringUni(errorStringPtr);
- throw new ArgumentException(error);
- }
- dataHandle.Free();
- dataHandle = GCHandle.Alloc(nameData, GCHandleType.Pinned);
- CryptoApiBlob nameBlob = new CryptoApiBlob(
- nameData.Length,
- dataHandle.AddrOfPinnedObject());
- CryptKeyProviderInformation kpi = new CryptKeyProviderInformation();
- kpi.ContainerName = containerName;
- kpi.ProviderType = 1; // PROV_RSA_FULL
- kpi.KeySpec = 1; // AT_KEYEXCHANGE
- CryptAlgorithmIdentifier sha256Identifier = new CryptAlgorithmIdentifier();
- sha256Identifier.pszObjId = "1.2.840.113549.1.1.11";
- certContext = NativeMethods.CertCreateSelfSignCertificate(
- providerContext,
- ref nameBlob,
- 0,
- ref kpi,
- ref sha256Identifier,
- ref startSystemTime,
- ref endSystemTime,
- IntPtr.Zero);
- Check(certContext != IntPtr.Zero);
- dataHandle.Free();
- certStore = NativeMethods.CertOpenStore(
- "Memory", // sz_CERT_STORE_PROV_MEMORY
- 0,
- IntPtr.Zero,
- 0x2000, // CERT_STORE_CREATE_NEW_FLAG
- IntPtr.Zero);
- Check(certStore != IntPtr.Zero);
- Check(NativeMethods.CertAddCertificateContextToStore(
- certStore,
- certContext,
- 1, // CERT_STORE_ADD_NEW
- out storeCertContext));
- NativeMethods.CertSetCertificateContextProperty(
- storeCertContext,
- 2, // CERT_KEY_PROV_INFO_PROP_ID
- 0,
- ref kpi);
- CryptoApiBlob pfxBlob = new CryptoApiBlob();
- Check(NativeMethods.PFXExportCertStoreEx(
- certStore,
- ref pfxBlob,
- passwordPtr,
- IntPtr.Zero,
- 7)); // EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY
- pfxData = new byte[pfxBlob.DataLength];
- dataHandle = GCHandle.Alloc(pfxData, GCHandleType.Pinned);
- pfxBlob.Data = dataHandle.AddrOfPinnedObject();
- Check(NativeMethods.PFXExportCertStoreEx(
- certStore,
- ref pfxBlob,
- passwordPtr,
- IntPtr.Zero,
- 7)); // EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY
- dataHandle.Free();
- }
- finally
- {
- if (passwordPtr != IntPtr.Zero)
- {
- Marshal.ZeroFreeCoTaskMemUnicode(passwordPtr);
- }
- if (dataHandle.IsAllocated)
- {
- dataHandle.Free();
- }
- if (certContext != IntPtr.Zero)
- {
- NativeMethods.CertFreeCertificateContext(certContext);
- }
- if (storeCertContext != IntPtr.Zero)
- {
- NativeMethods.CertFreeCertificateContext(storeCertContext);
- }
- if (certStore != IntPtr.Zero)
- {
- NativeMethods.CertCloseStore(certStore, 0);
- }
- if (cryptKey != IntPtr.Zero)
- {
- NativeMethods.CryptDestroyKey(cryptKey);
- }
- if (providerContext != IntPtr.Zero)
- {
- NativeMethods.CryptReleaseContext(providerContext, 0);
- NativeMethods.CryptAcquireContextW(
- out providerContext,
- containerName,
- null,
- 1, // PROV_RSA_FULL
- 0x10); // CRYPT_DELETEKEYSET
- }
- }
- return pfxData;
- }
- private static SystemTime ToSystemTime(DateTime dateTime)
- {
- long fileTime = dateTime.ToFileTime();
- SystemTime systemTime;
- Check(NativeMethods.FileTimeToSystemTime(ref fileTime, out systemTime));
- return systemTime;
- }
- private static void Check(bool nativeCallSucceeded)
- {
- if (!nativeCallSucceeded)
- {
- int error = Marshal.GetHRForLastWin32Error();
- Marshal.ThrowExceptionForHR(error);
- }
- }
- }
- }
|