Skip to content

Commit 4cff774

Browse files
committed
default to RSA on Winderp 10, allow override on Linux
1 parent 4c8cfc5 commit 4cff774

File tree

5 files changed

+132
-34
lines changed

5 files changed

+132
-34
lines changed

Intersect.Server/Web/ApiService.AppSettings.cs

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Intersect.Server.Web.Configuration;
66
using Microsoft.AspNetCore.Authentication.JwtBearer;
77
using Microsoft.IdentityModel.Tokens;
8+
using Newtonsoft.Json;
89
using Newtonsoft.Json.Linq;
910

1011
namespace Intersect.Server.Web;
@@ -64,7 +65,7 @@ private static void ValidateConfiguration()
6465
rawConfiguration = "{}";
6566
}
6667

67-
var configurationJObject = JObject.Parse(rawConfiguration);
68+
var configurationJObject = JsonConvert.DeserializeObject<JObject>(rawConfiguration);
6869
if (!configurationJObject.TryGetValue("Api", out var apiSectionJToken))
6970
{
7071
apiSectionJToken = JObject.FromObject(new ApiConfiguration());
@@ -102,7 +103,10 @@ private static void ValidateConfiguration()
102103

103104
if (apiConfiguration.StaticFilePaths == default)
104105
{
105-
apiConfiguration.StaticFilePaths = new List<StaticFilePathOptions> { new("wwwroot") };
106+
apiConfiguration.StaticFilePaths = new List<StaticFilePathOptions>
107+
{
108+
new("wwwroot")
109+
};
106110
}
107111

108112
var updatedApiConfigurationJObject = JObject.FromObject(apiConfiguration);
@@ -166,21 +170,21 @@ out var validIssuerToken
166170

167171
foreach (var endpointToken in endpoints.PropertyValues())
168172
{
169-
if (endpointToken is not JObject endpoint)
173+
if (endpointToken is not JObject endpointValue)
170174
{
171175
continue;
172176
}
177+
var endpoint = endpointValue.ToObject<PartialKestrelEndpoint>();
173178

174-
if (!endpoint.TryGetValue("Certificate", out var certificateToken) ||
175-
certificateToken is not JObject certificate)
179+
if (endpoint.Certificate is not { } certificate)
176180
{
177181
continue;
178182
}
179183

180184
try
181185
{
182-
var certificatePath = certificate.Value<string>("Path");
183-
var keyPath = certificate.Value<string>("KeyPath");
186+
var certificatePath = certificate.Path;
187+
var keyPath = certificate.KeyPath;
184188

185189
if (!string.Equals(certificatePath, SelfSignedCertificateName) ||
186190
!string.Equals(keyPath, SelfSignedKeyName))
@@ -201,17 +205,79 @@ out var validIssuerToken
201205
);
202206
}
203207

204-
using var ecdsa = ECDsa.Create();
205-
CertificateRequest request = new("cn=self-signed", ecdsa, HashAlgorithmName.SHA384);
206-
var selfSignedCertificate = request.CreateSelfSigned(
208+
AsymmetricAlgorithm algorithm;
209+
CertificateRequest certificateRequest;
210+
var osVersion = Environment.OSVersion;
211+
212+
PartialKestrelEndpointCertificateType certificateType = certificate.SelfSignedCertificateType;
213+
if (certificateType == PartialKestrelEndpointCertificateType.None)
214+
{
215+
certificateType = osVersion.Platform switch
216+
{
217+
PlatformID.Win32S or PlatformID.Win32Windows or PlatformID.Win32NT or PlatformID.WinCE
218+
or PlatformID.Xbox => osVersion.Version.Major < 11
219+
? PartialKestrelEndpointCertificateType.RSA
220+
: PartialKestrelEndpointCertificateType.ECDSA,
221+
PlatformID.Unix or PlatformID.MacOSX or PlatformID.Other =>
222+
PartialKestrelEndpointCertificateType.ECDSA,
223+
_ => throw new ArgumentOutOfRangeException()
224+
};
225+
}
226+
227+
switch (certificateType)
228+
{
229+
case PartialKestrelEndpointCertificateType.None:
230+
throw new InvalidOperationException();
231+
case PartialKestrelEndpointCertificateType.RSA:
232+
{
233+
var rsa = RSA.Create(4096);
234+
algorithm = rsa;
235+
certificateRequest = new CertificateRequest(
236+
"cn=self-signed",
237+
rsa,
238+
HashAlgorithmName.SHA256,
239+
RSASignaturePadding.Pkcs1
240+
);
241+
break;
242+
}
243+
case PartialKestrelEndpointCertificateType.ECDSA:
244+
{
245+
var ecdsa = ECDsa.Create();
246+
algorithm = ecdsa;
247+
certificateRequest = new CertificateRequest("cn=self-signed", ecdsa, HashAlgorithmName.SHA384);
248+
break;
249+
}
250+
default:
251+
throw new ArgumentOutOfRangeException();
252+
}
253+
254+
var selfSignedCertificate = certificateRequest.CreateSelfSigned(
207255
DateTimeOffset.Now,
208256
DateTimeOffset.Now.AddDays(30)
209257
);
210258

211259
var certificatePem = selfSignedCertificate.ExportCertificatePem();
212-
var keyPem = selfSignedCertificate.GetECDsaPrivateKey().ExportECPrivateKeyPem();
260+
261+
string keyPem;
262+
if (selfSignedCertificate.GetRSAPrivateKey() is { } rsaPrivateKey)
263+
{
264+
keyPem = rsaPrivateKey.ExportRSAPrivateKeyPem();
265+
}
266+
else if (selfSignedCertificate.GetECDsaPrivateKey() is { } ecDsaPrivateKey)
267+
{
268+
keyPem = ecDsaPrivateKey.ExportECPrivateKeyPem();
269+
}
270+
else
271+
{
272+
throw new InvalidOperationException(
273+
"Private key should be RSA or ECDSA, why was this code changed without verifying it?"
274+
);
275+
}
276+
213277
File.WriteAllText(SelfSignedCertificateName, certificatePem);
214278
File.WriteAllText(SelfSignedKeyName, keyPem);
279+
280+
algorithm.Dispose();
215281
}
216282
catch (Exception exception)
217283
{

Intersect.Server/Web/ApiService.cs

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
using Intersect.Server.Web.Controllers.Api;
3939
using Intersect.Server.Web.Controllers.AssetManagement;
4040
using Intersect.Server.Web.Types.Chat;
41+
using Microsoft.AspNetCore.Server.Kestrel.Core;
4142

4243
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
4344

@@ -71,29 +72,34 @@ internal partial class ApiService : ApplicationService<ServerContext, IApiServic
7172

7273
ApplicationContext.Context.Value?.Logger.LogInformation($"Launching Intersect REST API in '{builder.Environment.EnvironmentName}' mode...");
7374

74-
builder.WebHost.ConfigureKestrel(
75-
ko =>
76-
{
77-
ko.ConfigureHttpsDefaults(
78-
hcao =>
79-
{
80-
// hcao.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
81-
hcao.SslProtocols = SslProtocols.Tls12;
82-
hcao.OnAuthenticate += (context, options) =>
83-
{
84-
// options.AllowRenegotiation = true;
85-
// options.CipherSuitesPolicy = new CipherSuitesPolicy(
86-
// [
87-
// TlsCipherSuite.TLS_AES_128_GCM_SHA256,
88-
// TlsCipherSuite.TLS_AES_256_GCM_SHA384,
89-
// TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
90-
// ]
91-
// );
92-
};
93-
}
94-
);
95-
}
96-
);
75+
// builder.WebHost.ConfigureKestrel(
76+
// ko =>
77+
// {
78+
// ko.ConfigureEndpointDefaults(lo =>
79+
// {
80+
// lo.Protocols = HttpProtocols.Http1;
81+
// }
82+
// );
83+
// ko.ConfigureHttpsDefaults(
84+
// hcao =>
85+
// {
86+
// // hcao.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
87+
// hcao.SslProtocols = SslProtocols.Tls12;
88+
// hcao.OnAuthenticate += (context, options) =>
89+
// {
90+
// // options.AllowRenegotiation = true;
91+
// // options.CipherSuitesPolicy = new CipherSuitesPolicy(
92+
// // [
93+
// // TlsCipherSuite.TLS_AES_128_GCM_SHA256,
94+
// // TlsCipherSuite.TLS_AES_256_GCM_SHA384,
95+
// // TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
96+
// // ]
97+
// // );
98+
// };
99+
// }
100+
// );
101+
// }
102+
// );
97103

98104
var updateServerSection = builder.Configuration.GetSection(GetOptionsName<UpdateServerOptions>());
99105
builder.Services.Configure<UpdateServerOptions>(updateServerSection);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
namespace Intersect.Server.Web.Configuration;
2+
3+
public sealed record PartialKestrelEndpoint(string Url, PartialKestrelEndpointCertificate? Certificate);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using Newtonsoft.Json;
2+
using Newtonsoft.Json.Converters;
3+
4+
namespace Intersect.Server.Web.Configuration;
5+
6+
public record PartialKestrelEndpointCertificate(
7+
string? Path,
8+
string? KeyPath,
9+
[property: JsonConverter(typeof(StringEnumConverter))]
10+
PartialKestrelEndpointCertificateType SelfSignedCertificateType
11+
);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Newtonsoft.Json;
2+
using Newtonsoft.Json.Converters;
3+
4+
namespace Intersect.Server.Web.Configuration;
5+
6+
[JsonConverter(typeof(StringEnumConverter))]
7+
public enum PartialKestrelEndpointCertificateType
8+
{
9+
None,
10+
ECDSA,
11+
RSA,
12+
}

0 commit comments

Comments
 (0)