|
| 1 | +// Licensed to the .NET Foundation under one or more agreements. |
| 2 | +// The .NET Foundation licenses this file to you under the MIT license. |
| 3 | + |
| 4 | +using System.Net; |
| 5 | +using System.Runtime.InteropServices; |
| 6 | + |
| 7 | +namespace TlsFeaturesObserve.HttpSys; |
| 8 | + |
| 9 | +internal static class HttpSysConfigurator |
| 10 | +{ |
| 11 | + const uint HTTP_INITIALIZE_CONFIG = 0x00000002; |
| 12 | + const uint ERROR_ALREADY_EXISTS = 183; |
| 13 | + |
| 14 | + static readonly HTTPAPI_VERSION HttpApiVersion = new HTTPAPI_VERSION(1, 0); |
| 15 | + |
| 16 | + internal static void ConfigureCacheTlsClientHello() |
| 17 | + { |
| 18 | + // Arbitrarily chosen port, but must match the port used in the web server. Via UrlPrefixes or launchsettings. |
| 19 | + var ipPort = new IPEndPoint(new IPAddress([0, 0, 0, 0]), 6000); |
| 20 | + var certThumbprint = "" /* your cert thumbprint here */; |
| 21 | + var appId = Guid.NewGuid(); |
| 22 | + var sslCertStoreName = "My"; |
| 23 | + |
| 24 | + CallHttpApi(() => SetConfiguration(ipPort, certThumbprint, appId, sslCertStoreName)); |
| 25 | + } |
| 26 | + |
| 27 | + static void SetConfiguration(IPEndPoint ipPort, string certThumbprint, Guid appId, string sslCertStoreName) |
| 28 | + { |
| 29 | + var sockAddrHandle = CreateSockaddrStructure(ipPort); |
| 30 | + var pIpPort = sockAddrHandle.AddrOfPinnedObject(); |
| 31 | + var httpServiceConfigSslKey = new HTTP_SERVICE_CONFIG_SSL_KEY(pIpPort); |
| 32 | + |
| 33 | + var hash = GetHash(certThumbprint); |
| 34 | + var handleHash = GCHandle.Alloc(hash, GCHandleType.Pinned); |
| 35 | + var configSslParam = new HTTP_SERVICE_CONFIG_SSL_PARAM |
| 36 | + { |
| 37 | + AppId = appId, |
| 38 | + DefaultFlags = 0x00008000 /* HTTP_SERVICE_CONFIG_SSL_FLAG_ENABLE_CACHE_CLIENT_HELLO */, |
| 39 | + DefaultRevocationFreshnessTime = 0, |
| 40 | + DefaultRevocationUrlRetrievalTimeout = 15, |
| 41 | + pSslCertStoreName = sslCertStoreName, |
| 42 | + pSslHash = handleHash.AddrOfPinnedObject(), |
| 43 | + SslHashLength = hash.Length, |
| 44 | + pDefaultSslCtlIdentifier = null, |
| 45 | + pDefaultSslCtlStoreName = sslCertStoreName |
| 46 | + }; |
| 47 | + |
| 48 | + var configSslSet = new HTTP_SERVICE_CONFIG_SSL_SET |
| 49 | + { |
| 50 | + ParamDesc = configSslParam, |
| 51 | + KeyDesc = httpServiceConfigSslKey |
| 52 | + }; |
| 53 | + |
| 54 | + var pInputConfigInfo = Marshal.AllocCoTaskMem( |
| 55 | + Marshal.SizeOf(typeof(HTTP_SERVICE_CONFIG_SSL_SET))); |
| 56 | + Marshal.StructureToPtr(configSslSet, pInputConfigInfo, false); |
| 57 | + |
| 58 | + var status = HttpSetServiceConfiguration(nint.Zero, |
| 59 | + HTTP_SERVICE_CONFIG_ID.HttpServiceConfigSSLCertInfo, |
| 60 | + pInputConfigInfo, |
| 61 | + Marshal.SizeOf(configSslSet), |
| 62 | + nint.Zero); |
| 63 | + |
| 64 | + if (status == ERROR_ALREADY_EXISTS || status == 0) // already present or success |
| 65 | + { |
| 66 | + Console.WriteLine($"HttpServiceConfiguration is correct"); |
| 67 | + } |
| 68 | + else |
| 69 | + { |
| 70 | + Console.WriteLine("Failed to HttpSetServiceConfiguration: " + status); |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + static byte[] GetHash(string thumbprint) |
| 75 | + { |
| 76 | + var length = thumbprint.Length; |
| 77 | + var bytes = new byte[length / 2]; |
| 78 | + for (var i = 0; i < length; i += 2) |
| 79 | + { |
| 80 | + bytes[i / 2] = Convert.ToByte(thumbprint.Substring(i, 2), 16); |
| 81 | + } |
| 82 | + |
| 83 | + return bytes; |
| 84 | + } |
| 85 | + |
| 86 | + static GCHandle CreateSockaddrStructure(IPEndPoint ipEndPoint) |
| 87 | + { |
| 88 | + var socketAddress = ipEndPoint.Serialize(); |
| 89 | + |
| 90 | + // use an array of bytes instead of the sockaddr structure |
| 91 | + var sockAddrStructureBytes = new byte[socketAddress.Size]; |
| 92 | + var sockAddrHandle = GCHandle.Alloc(sockAddrStructureBytes, GCHandleType.Pinned); |
| 93 | + for (var i = 0; i < socketAddress.Size; ++i) |
| 94 | + { |
| 95 | + sockAddrStructureBytes[i] = socketAddress[i]; |
| 96 | + } |
| 97 | + return sockAddrHandle; |
| 98 | + } |
| 99 | + |
| 100 | + static void CallHttpApi(Action body) |
| 101 | + { |
| 102 | + const uint flags = HTTP_INITIALIZE_CONFIG; |
| 103 | + var retVal = HttpInitialize(HttpApiVersion, flags, IntPtr.Zero); |
| 104 | + body(); |
| 105 | + } |
| 106 | + |
| 107 | +// disabled warning since it is just a sample |
| 108 | +#pragma warning disable SYSLIB1054 // Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time |
| 109 | + [DllImport("httpapi.dll", SetLastError = true)] |
| 110 | + private static extern uint HttpInitialize( |
| 111 | + HTTPAPI_VERSION version, |
| 112 | + uint flags, |
| 113 | + IntPtr pReserved); |
| 114 | + |
| 115 | + [DllImport("httpapi.dll", SetLastError = true)] |
| 116 | + public static extern uint HttpSetServiceConfiguration( |
| 117 | + nint serviceIntPtr, |
| 118 | + HTTP_SERVICE_CONFIG_ID configId, |
| 119 | + nint pConfigInformation, |
| 120 | + int configInformationLength, |
| 121 | + nint pOverlapped); |
| 122 | +#pragma warning restore SYSLIB1054 // Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time |
| 123 | +} |
0 commit comments