diff options
Diffstat (limited to 'MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs')
| -rw-r--r-- | MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs | 244 |
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); + } + } + } +} |
