mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-11-03 19:17:24 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			245 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			245 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
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);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |