Browse Source

Create self signed cert if one does not exist

Mike 10 years ago
parent
commit
f6d6d57983

+ 8 - 0
MediaBrowser.Common/Net/INetworkManager.cs

@@ -2,6 +2,7 @@ using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Net;
 using System.Collections.Generic;
 using System.Net;
+using MediaBrowser.Model.Logging;
 
 namespace MediaBrowser.Common.Net
 {
@@ -51,5 +52,12 @@ namespace MediaBrowser.Common.Net
         /// <param name="endpoint">The endpoint.</param>
         /// <returns><c>true</c> if [is in local network] [the specified endpoint]; otherwise, <c>false</c>.</returns>
         bool IsInLocalNetwork(string endpoint);
+
+        /// <summary>
+        /// Generates a self signed certificate at the locatation specified by <paramref name="certificatePath"/>.
+        /// </summary>
+        /// <param name="certificatePath">The path to generate the certificate.</param>
+        /// <param name="hostname">The common name for the certificate.</param>
+        void GenerateSelfSignedSslCertificate(string certificatePath, string hostname);
     }
 }

+ 6 - 0
MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj

@@ -57,6 +57,11 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\Mono.Posix.4.0.0.0\lib\net40\Mono.Posix.dll</HintPath>
     </Reference>
+    <Reference Include="Mono.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\ThirdParty\Mono.Security\Mono.Security.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
     <Reference Include="System" />
     <Reference Include="ServiceStack.Interfaces">
       <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
@@ -70,6 +75,7 @@
       <Link>Properties\SharedVersion.cs</Link>
     </Compile>
     <Compile Include="Native\BaseMonoApp.cs" />
+    <Compile Include="Networking\CertificateGenerator.cs" />
     <Compile Include="Program.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Native\NativeApp.cs" />

+ 88 - 0
MediaBrowser.Server.Mono/Networking/CertificateGenerator.cs

@@ -0,0 +1,88 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Logging;
+using Mono.Security.X509;
+
+namespace MediaBrowser.Server.Mono.Networking
+{
+    internal class CertificateGenerator
+    {
+        private const string MonoTestRootAgency = "<RSAKeyValue><Modulus>v/4nALBxCE+9JgEC0LnDUvKh6e96PwTpN4Rj+vWnqKT7IAp1iK/JjuqvAg6DQ2vTfv0dTlqffmHH51OyioprcT5nzxcSTsZb/9jcHScG0s3/FRIWnXeLk/fgm7mSYhjUaHNI0m1/NTTktipicjKxo71hGIg9qucCWnDum+Krh/k=</Modulus><Exponent>AQAB</Exponent><P>9jbKxMXEruW2CfZrzhxtull4O8P47+mNsEL+9gf9QsRO1jJ77C+jmzfU6zbzjf8+ViK+q62tCMdC1ZzulwdpXQ==</P><Q>x5+p198l1PkK0Ga2mRh0SIYSykENpY2aLXoyZD/iUpKYAvATm0/wvKNrE4dKJyPCA+y3hfTdgVag+SP9avvDTQ==</Q><DP>ISSjCvXsUfbOGG05eddN1gXxL2pj+jegQRfjpk7RAsnWKvNExzhqd5x+ZuNQyc6QH5wxun54inP4RTUI0P/IaQ==</DP><DQ>R815VQmR3RIbPqzDXzv5j6CSH6fYlcTiQRtkBsUnzhWmkd/y3XmamO+a8zJFjOCCx9CcjpVuGziivBqi65lVPQ==</DQ><InverseQ>iYiu0KwMWI/dyqN3RJYUzuuLj02/oTD1pYpwo2rvNCXU1Q5VscOeu2DpNg1gWqI+1RrRCsEoaTNzXB1xtKNlSw==</InverseQ><D>nIfh1LYF8fjRBgMdAH/zt9UKHWiaCnc+jXzq5tkR8HVSKTVdzitD8bl1JgAfFQD8VjSXiCJqluexy/B5SGrCXQ49c78NIQj0hD+J13Y8/E0fUbW1QYbhj6Ff7oHyhaYe1WOQfkp2t/h+llHOdt1HRf7bt7dUknYp7m8bQKGxoYE=</D></RSAKeyValue>";
+
+        internal static void CreateSelfSignCertificatePfx(
+            string fileName,
+            string hostname,
+            ILogger logger)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(fileName))
+                {
+                    logger.Info("No certificate filename specified.");
+                    return;
+                }
+
+                if (File.Exists(fileName))
+                {
+                    logger.Info("Certificate file already exists. To regenerate, delete {0}", fileName);
+                    return;
+                }
+
+                byte[] sn = Guid.NewGuid().ToByteArray();
+                string subject = string.Format("CN={0}", hostname);
+                string issuer = subject;
+                DateTime notBefore = DateTime.Now.AddDays(-2);
+                DateTime notAfter = DateTime.Now.AddYears(10);
+
+                RSA issuerKey = RSA.Create();
+                issuerKey.FromXmlString(MonoTestRootAgency);
+                RSA subjectKey = RSA.Create();
+
+                // serial number MUST be positive
+                if ((sn[0] & 0x80) == 0x80)
+                    sn[0] -= 0x80;
+
+                issuer = subject;
+                issuerKey = subjectKey;
+
+                X509CertificateBuilder cb = new X509CertificateBuilder(3);
+                cb.SerialNumber = sn;
+                cb.IssuerName = issuer;
+                cb.NotBefore = notBefore;
+                cb.NotAfter = notAfter;
+                cb.SubjectName = subject;
+                cb.SubjectPublicKey = subjectKey;
+
+                // signature
+                cb.Hash = "SHA256";
+                byte[] rawcert = cb.Sign(issuerKey);
+
+                PKCS12 p12 = new PKCS12();
+
+
+                ArrayList list = new ArrayList();
+                // we use a fixed array to avoid endianess issues 
+                // (in case some tools requires the ID to be 1).
+                list.Add(new byte[4] {1, 0, 0, 0});
+                Hashtable attributes = new Hashtable(1);
+                attributes.Add(PKCS9.localKeyId, list);
+
+                p12.AddCertificate(new X509Certificate(rawcert), attributes);
+
+                p12.AddPkcs8ShroudedKeyBag(subjectKey, attributes);
+                p12.SaveToFile(fileName);
+            }
+            catch (Exception e)
+            {
+                logger.ErrorException("Error generating self signed ssl certificate: {0}", e, fileName);
+            }
+
+        }
+    }
+}

+ 10 - 0
MediaBrowser.Server.Mono/Networking/NetworkManager.cs

@@ -35,5 +35,15 @@ namespace MediaBrowser.Server.Mono.Networking
         {
 			return new List<FileSystemEntryInfo> ();
         }
+
+        /// <summary>
+        /// Generates a self signed certificate at the locatation specified by <paramref name="certificatePath"/>.
+        /// </summary>
+        /// <param name="certificatePath">The path to generate the certificate.</param>
+        /// <param name="hostname">The common name for the certificate.</param>
+        public void GenerateSelfSignedSslCertificate(string certificatePath, string hostname)
+        {
+            CertificateGenerator.CreateSelfSignCertificatePfx(certificatePath, hostname, Logger);
+        }
     }
 }

+ 31 - 0
MediaBrowser.Server.Startup.Common/ApplicationHost.cs

@@ -779,6 +779,13 @@ namespace MediaBrowser.Server.Startup.Common
         {
             try
             {
+                if (ServerConfigurationManager.Configuration.EnableHttps)
+                {
+                    NetworkManager.GenerateSelfSignedSslCertificate(
+                        ServerConfigurationManager.Configuration.CertificatePath,
+                        GetHostnameFromExternalDns(ServerConfigurationManager.Configuration.WanDdns));
+                }
+
                 ServerManager.Start(GetUrlPrefixes(), ServerConfigurationManager.Configuration.CertificatePath);
             }
             catch (Exception ex)
@@ -1183,5 +1190,29 @@ namespace MediaBrowser.Server.Startup.Common
                 NativeApp.ConfigureAutoRun(autorun);
             }
         }
+
+        /// <summary>
+        /// This returns localhost in the case of no external dns, and the hostname if the 
+        /// dns is prefixed with a valid Uri prefix.
+        /// </summary>
+        /// <param name="externalDns">The external dns prefix to get the hostname of.</param>
+        /// <returns>The hostname in <paramref name="externalDns"/></returns>
+        private static string GetHostnameFromExternalDns(string externalDns)
+        {
+            if (string.IsNullOrWhiteSpace(externalDns))
+            {
+                return "localhost";
+            }
+
+            try
+            {
+                Uri uri = new Uri(externalDns);
+                return uri.Host;
+            }
+            catch (Exception e)
+            {
+                return externalDns;
+            }
+        }
     }
 }

+ 1 - 0
MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj

@@ -109,6 +109,7 @@
     <Compile Include="Native\Standby.cs" />
     <Compile Include="Native\ServerAuthorization.cs" />
     <Compile Include="Native\WindowsApp.cs" />
+    <Compile Include="Networking\CertificateGenerator.cs" />
     <Compile Include="Networking\NativeMethods.cs" />
     <Compile Include="Networking\NetworkManager.cs" />
     <Compile Include="Networking\NetworkShares.cs" />

+ 263 - 0
MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs

@@ -0,0 +1,263 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Logging;
+
+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)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(fileName))
+                {
+                    logger.Info("No certificate filename specified.");
+                    return;
+                }
+
+                if (File.Exists(fileName))
+                {
+                    logger.Info("Certificate file already exists. To regenerate, delete {0}", fileName);
+                    return;
+                }
+
+                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);
+            }
+            catch (Exception e)
+            {
+                logger.ErrorException("Error generating self signed ssl certificate: {0}", e, fileName);
+            }
+        }
+
+        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);
+            }
+        }
+    }
+}

+ 155 - 1
MediaBrowser.ServerApplication/Networking/NativeMethods.cs

@@ -47,9 +47,108 @@ namespace MediaBrowser.ServerApplication.Networking
         /// <returns>System.Int32.</returns>
         [DllImport("Netapi32", SetLastError = true),
         SuppressUnmanagedCodeSecurity]
-
         public static extern int NetApiBufferFree(
             IntPtr pBuf);
+
+        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        public static extern bool FileTimeToSystemTime(
+            [In] ref long fileTime,
+            out SystemTime systemTime);
+
+        [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        public static extern bool CryptAcquireContextW(
+            out IntPtr providerContext,
+            [MarshalAs(UnmanagedType.LPWStr)] string container,
+            [MarshalAs(UnmanagedType.LPWStr)] string provider,
+            int providerType,
+            int flags);
+
+        [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        public static extern bool CryptReleaseContext(
+            IntPtr providerContext,
+            int flags);
+
+        [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        public static extern bool CryptGenKey(
+            IntPtr providerContext,
+            int algorithmId,
+            int flags,
+            out IntPtr cryptKeyHandle);
+
+        [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        public static extern bool CryptDestroyKey(
+            IntPtr cryptKeyHandle);
+
+        [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        public static extern bool CertStrToNameW(
+            int certificateEncodingType,
+            IntPtr x500,
+            int strType,
+            IntPtr reserved,
+            [MarshalAs(UnmanagedType.LPArray)] [Out] byte[] encoded,
+            ref int encodedLength,
+            out IntPtr errorString);
+
+        [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
+        public static extern IntPtr CertCreateSelfSignCertificate(
+            IntPtr providerHandle,
+            [In] ref CryptoApiBlob subjectIssuerBlob,
+            int flags,
+            [In] ref CryptKeyProviderInformation keyProviderInformation,
+            [In] ref CryptAlgorithmIdentifier algorithmIdentifier,
+            [In] ref SystemTime startTime,
+            [In] ref SystemTime endTime,
+            IntPtr extensions);
+
+        [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        public static extern bool CertFreeCertificateContext(
+            IntPtr certificateContext);
+
+        [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
+        public static extern IntPtr CertOpenStore(
+            [MarshalAs(UnmanagedType.LPStr)] string storeProvider,
+            int messageAndCertificateEncodingType,
+            IntPtr cryptProvHandle,
+            int flags,
+            IntPtr parameters);
+
+        [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        public static extern bool CertCloseStore(
+            IntPtr certificateStoreHandle,
+            int flags);
+
+        [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        public static extern bool CertAddCertificateContextToStore(
+            IntPtr certificateStoreHandle,
+            IntPtr certificateContext,
+            int addDisposition,
+            out IntPtr storeContextPtr);
+
+        [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        public static extern bool CertSetCertificateContextProperty(
+            IntPtr certificateContext,
+            int propertyId,
+            int flags,
+            [In] ref CryptKeyProviderInformation data);
+
+        [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        public static extern bool PFXExportCertStoreEx(
+            IntPtr certificateStoreHandle,
+            ref CryptoApiBlob pfxBlob,
+            IntPtr password,
+            IntPtr reserved,
+            int flags);
     }
 
     //create a _SERVER_INFO_100 STRUCTURE
@@ -69,4 +168,59 @@ namespace MediaBrowser.ServerApplication.Networking
         [MarshalAs(UnmanagedType.LPWStr)]
         internal string sv100_name;
     }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct SystemTime
+    {
+        public short Year;
+        public short Month;
+        public short DayOfWeek;
+        public short Day;
+        public short Hour;
+        public short Minute;
+        public short Second;
+        public short Milliseconds;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct CryptObjIdBlob
+    {
+        public uint cbData;
+        public IntPtr pbData;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct CryptAlgorithmIdentifier
+    {
+        [MarshalAs(UnmanagedType.LPStr)]
+        public String pszObjId;
+        public CryptObjIdBlob Parameters;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct CryptoApiBlob
+    {
+        public int DataLength;
+        public IntPtr Data;
+
+        public CryptoApiBlob(int dataLength, IntPtr data)
+        {
+            this.DataLength = dataLength;
+            this.Data = data;
+        }
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct CryptKeyProviderInformation
+    {
+        [MarshalAs(UnmanagedType.LPWStr)]
+        public string ContainerName;
+        [MarshalAs(UnmanagedType.LPWStr)]
+        public string ProviderName;
+        public int ProviderType;
+        public int Flags;
+        public int ProviderParameterCount;
+        public IntPtr ProviderParameters; // PCRYPT_KEY_PROV_PARAM
+        public int KeySpec;
+    }
 }

+ 10 - 0
MediaBrowser.ServerApplication/Networking/NetworkManager.cs

@@ -155,6 +155,16 @@ namespace MediaBrowser.ServerApplication.Networking
             });
         }
 
+        /// <summary>
+        /// Generates a self signed certificate at the locatation specified by <paramref name="certificatePath"/>.
+        /// </summary>
+        /// <param name="certificatePath">The path to generate the certificate.</param>
+        /// <param name="hostname">The common name for the certificate.</param>
+        public void GenerateSelfSignedSslCertificate(string certificatePath, string hostname)
+        {
+            CertificateGenerator.CreateSelfSignCertificatePfx(certificatePath, hostname, Logger);
+        }
+
         /// <summary>
         /// Gets the network prefix.
         /// </summary>