aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs')
-rw-r--r--MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs244
1 files changed, 244 insertions, 0 deletions
diff --git a/MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs b/MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs
new file mode 100644
index 000000000..a7b0d6c32
--- /dev/null
+++ b/MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs
@@ -0,0 +1,244 @@
+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);
+ }
+ }
+ }
+}