-
Notifications
You must be signed in to change notification settings - Fork 10.4k
[HTTPS] Adds PEM support for Kestrel #23584
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
4eca8a4
fbe5489
eb7ddcd
aeb9ea7
e05299f
4ad1faf
2e68fd3
2c1f5cc
4aa4ef7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ | |
using System.IO; | ||
using System.Linq; | ||
using System.Net; | ||
using System.Runtime.InteropServices; | ||
using System.Security.Authentication; | ||
using System.Security.Cryptography; | ||
using System.Security.Cryptography.X509Certificates; | ||
|
@@ -435,14 +436,129 @@ private X509Certificate2 LoadCertificate(CertificateConfig certInfo, string endp | |
} | ||
else if (certInfo.IsFileCert) | ||
{ | ||
var env = Options.ApplicationServices.GetRequiredService<IHostEnvironment>(); | ||
return new X509Certificate2(Path.Combine(env.ContentRootPath, certInfo.Path), certInfo.Password); | ||
var environment = Options.ApplicationServices.GetRequiredService<IHostEnvironment>(); | ||
var certificatePath = Path.Combine(environment.ContentRootPath, certInfo.Path); | ||
if (certInfo.KeyPath != null) | ||
{ | ||
var certificateKeyPath = Path.Combine(environment.ContentRootPath, certInfo.KeyPath); | ||
X509Certificate2 certificate = GetCertificate(certInfo, certificatePath, certificateKeyPath); | ||
|
||
if (!certificate.HasPrivateKey) | ||
{ | ||
certificate = LoadCertificateKey(certificate, certificateKeyPath, certInfo.Password); | ||
javiercn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
if (certificate != null) | ||
{ | ||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) | ||
{ | ||
return PersistKey(certificate); | ||
} | ||
|
||
return certificate; | ||
} | ||
|
||
throw new InvalidOperationException(CoreStrings.InvalidPemKey); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could throw if the cert is missing, not just because the key was missing or invalid right? This exception message implies the key must be the problem? And why do we use the exact same exception and log message any time the key is missing or invalid? It would be a lot better to log exactly why the key is invalid and to be very clear when the key is actually missing vs being invalid in both the exception and log messages. |
||
} | ||
|
||
return new X509Certificate2(Path.Combine(environment.ContentRootPath, certInfo.Path), certInfo.Password); | ||
} | ||
else if (certInfo.IsStoreCert) | ||
{ | ||
return LoadFromStoreCert(certInfo); | ||
} | ||
return null; | ||
|
||
static X509Certificate2 PersistKey(X509Certificate2 fullCertificate) | ||
{ | ||
// We need to force the key to be persisted. | ||
javiercn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// See https://github.com/dotnet/runtime/issues/23749 | ||
var certificateBytes = fullCertificate.Export(X509ContentType.Pkcs12, ""); | ||
return new X509Certificate2(certificateBytes, "", X509KeyStorageFlags.DefaultKeySet); | ||
} | ||
|
||
static X509Certificate2 LoadCertificateKey(X509Certificate2 certificate, string keyPath, string password) | ||
{ | ||
var keyText = File.ReadAllText(keyPath); | ||
return TryAttachPemRSAKey(certificate, keyText, password) ?? | ||
TryAttachPemDSAKey(certificate, keyText, password) ?? | ||
TryAttachPemECDSAKey(certificate, keyText, password); | ||
} | ||
|
||
static X509Certificate2 GetCertificate(CertificateConfig certInfo, string certificatePath, string certificateKeyPath) | ||
{ | ||
if (X509Certificate2.GetCertContentType(certificatePath) != X509ContentType.Unknown) | ||
javiercn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
return new X509Certificate2(certificatePath); | ||
} | ||
|
||
return certInfo.Password != null ? | ||
X509Certificate2.CreateFromEncryptedPemFile(certificatePath, certInfo.Password, certificateKeyPath) : | ||
X509Certificate2.CreateFromPemFile(certificatePath, certificateKeyPath); | ||
} | ||
} | ||
|
||
private static X509Certificate2 TryAttachPemRSAKey(X509Certificate2 certificate, string keyText, string password) | ||
javiercn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
const string RSAOid = "1.2.840.113549.1.1.1"; | ||
if (string.Equals(RSAOid, certificate.PublicKey.Oid.Value, StringComparison.Ordinal)) | ||
{ | ||
using var rsa = RSA.Create(); | ||
if (password == null) | ||
{ | ||
rsa.ImportFromPem(keyText); | ||
} | ||
else | ||
{ | ||
rsa.ImportFromEncryptedPem(keyText, password); | ||
} | ||
|
||
return certificate.CopyWithPrivateKey(rsa); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
private static X509Certificate2 TryAttachPemDSAKey(X509Certificate2 certificate, string keyText, string password) | ||
{ | ||
const string DSAOid = "1.2.840.10040.4.1"; | ||
if (string.Equals(DSAOid, certificate.PublicKey.Oid.Value, StringComparison.Ordinal)) | ||
{ | ||
using var dsa = DSA.Create(); | ||
if (password == null) | ||
{ | ||
dsa.ImportFromPem(keyText); | ||
} | ||
else | ||
{ | ||
dsa.ImportFromEncryptedPem(keyText, password); | ||
} | ||
|
||
return certificate.CopyWithPrivateKey(dsa); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
private static X509Certificate2 TryAttachPemECDSAKey(X509Certificate2 certificate, string keyText, string password) | ||
{ | ||
const string ECDsaOid = "1.2.840.10045.2.1"; | ||
if (string.Equals(ECDsaOid, certificate.PublicKey.Oid.Value, StringComparison.Ordinal)) | ||
{ | ||
using var ecdsa = ECDsa.Create(); | ||
if (password == null) | ||
{ | ||
ecdsa.ImportFromPem(keyText); | ||
} | ||
else | ||
{ | ||
ecdsa.ImportFromEncryptedPem(keyText, password); | ||
} | ||
|
||
return certificate.CopyWithPrivateKey(ecdsa); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
private static X509Certificate2 LoadFromStoreCert(CertificateConfig certInfo) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
*.key binary | ||
*.pem binary |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
-----BEGIN ENCRYPTED PRIVATE KEY----- | ||
MIIFNjBgBgkqhkiG9w0BBQ0wUzAyBgkqhkiG9w0BBQwwJQQQ93oRxzJ5UoNOb/zN | ||
x5cdsAIDAYagMAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBAuHsE18X/Z9ZVe | ||
aBl7C55nBIIE0AABqjc9ERcLYNpCRpA6c/TFG62m4Mr9J4dU4g1WD07t7uLxZiRi | ||
Pl0YOjCulljMsevAW5PlLxi4ffJ+I0/UB1WOfzEMhcj7o1qG0Uv55B7WRuWKw1Zr | ||
jo0bDY5Man48ZjpqMMBdnWhyHdIDm+WD0OyN98mpsN6SCQMjvx91M+klrsp7LOMM | ||
88HHS0RFVKGF9hYSy6rCwMJWf+7QGO2wXfq+MKvJ/bBgPGDLwN4phUCyocnR0swD | ||
/XZNiiw0xIC8OxAKhc6BV4AJkjNs32THdBOCGY6B4P/9Zo5W29S3ja/hGsMQAA27 | ||
QtIDg74HpX7TgIyqoc1oiLNIWW/+jUHSEYJsTPlg5VYWsXUfSHZpz8EJvKt2tyvt | ||
vBGOCLDDZD4GVXhPigKG6zJSJeTe94/VlwPhNSEucKeaALdax5t3HvPNzWKFX57E | ||
aC82/IxRrjgHmgsGSZdMi08HY6K9GAVBFpIGvXOGtRq7w8zO/KagAvSwAOLLtOs7 | ||
iEuAQxD+cKLRT59c4E7r5W7BT+faq85ovqdXe5Edtl3cT81zsl27pZvQrcrTPbZe | ||
4OeIdWxOmOnC/bXvRHNd9XuYadXXazBoFbe9yPwjqnflEh39CyvlOZXeaQXSdsEM | ||
1IBhddRTorO/I8M/znu9glqIa5ya1NA+4ujmf4OnJLtsrlKQa65VPVTrFdeYuMr0 | ||
VfOuuIye2OdyJ6jS0a1PYQm4bEEz6UR88dnmnhDx6i8/l2wW5+CArA/x8IBYboBM | ||
NJpJY9bHpic1AhjnjnTtFz2s4uYPi5g9peBizarZn+6OJvgYqs4a8SI92dA3E2o4 | ||
a/1j7xlLlgXnVRLBMibxqzjMt4Zt7Nj+BaN1owrB/q04AWS2M4TSQz+NYOZwNFxB | ||
dzb+fysTLK5XNEYq6rSg+0i+EKZl8Jb/t4d8SLPVr/tdfDt9BtZ0nTgjvy1HWy1p | ||
kQdm13XfK1/9KsePH/Jb6dvN/u6ubV+ZqI7Bc7VyTi0bKMdpH2K8/dtopNyDZ/P+ | ||
/IsyyDYTorgJB/klSih/W0hqpSBbEAmlSBfBxP1/ozBEGR2oF20JOCFyD6UXQR/1 | ||
V7r2KtplpyfXaIWh4fABitAMHz7VgmEIQ2H9cB4Ey9jdRPQ/1p+OgGjfaFJQ0uYM | ||
987TDtjkuukJYnPZNIIx0Yv3iAX16XmhzJixWSMUIJiWfSiz0aTjBxsPQVPTQV+M | ||
6BgFf3riBApZYlVVJsGIie2XTvu/tHRhfQrxccl63HN7yAeJheQnoscin6Z5TKN/ | ||
U8Ouy/QGiATatKUEUjr4lN+BYySf8F6e3cAAeAx/ZnFvGw5z8fwNYBjVWg/83bTw | ||
9rS+tSk8VsvTdkcKoNbbDtw+SwYfZSbMUBFm0B13190iJZoyWI+5ZKPnZ2CvOZhX | ||
PjGTOnh6Diq907l2Q7S/v8SLe0bCHCHVBy+CcPWVDZ6Z7V5cJ/W8TvFPcSGw1UCl | ||
tKPp862uDaPKvGxqGDq0vGouEUrtJKZ279Lnrtz1n8raUj0Gxa+KXqLACh8dXCzK | ||
ZgCTPhfAjZcYgA73edW0whNNH9MNInDGulT/arCK3HTkFPczD+7wA8Ojw/LxKFJs | ||
0d8vtILbmLv46CO+wvIdWrW1c7PCrGJDf9Zuw06vIH7hpW9swSM55k9/ | ||
-----END ENCRYPTED PRIVATE KEY----- |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
-----BEGIN ENCRYPTED PRIVATE KEY----- | ||
MIICzTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIgeRX7Sed0OsCAggA | ||
MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBC7vpWM+TQ9caiqYAn+h95YBIIC | ||
cM8+gZPkVJZF1ICFi6lHHNjV9FSdH+qwvUOzBEVP1oZtmdMBhEVqqm+3H+Gcm6dn | ||
C4JjMGCu3KqQ096JoWeFE/ksuTcL7MBtvyY35NIbbVxEW7YxvTiICADfjwL0mDYH | ||
SlUr0LLrMhoYfN01HE2kkkJLZLt58zjzxyUdc//SlIC6rJyd6NZdNrmRQ9JZzd7G | ||
SZtkXRMvrzyJoxwOc+5ERmYtOtOWmyF2pAFwoEnb8VOQgoM3gy/3lO5aVpPj76Rr | ||
MmJwdruU39nUPTe6VI6ukF5T27pu0XmX5YWqy5+PZ5cFhmOxGSxwvw6dbJSM1+Sg | ||
ZcKohhQQtNFYFgFpp8cz84o1lHWprjGxHRVKEeKAwgX8v8glDTKzwS0DKsTzdn0s | ||
2RCwjr+GxH7wpjwzllny2xU/h5ZR2j8gldfCNFVPtFrGleuEduPA4U04Mkije0l9 | ||
AdPHDK8drvUuIZLb2HCQu0wYmUDmtY/SWuMl1xRonrF+cPPcmAdQDP8IH2MGL8SO | ||
9h/CCextHsqzXl7PJzHcjLmn8qmlocmSJRALCrWpgoE/d7Wjle8LmtQVWSw0UgTo | ||
GEoWpFcz6JJBUrcasGQHokLtuyAjjSRgSt1DFK2iW+O7MARK9wJNCGUfDNWavO4p | ||
hA7MR1muv/xwwr0oi2OJ5rc3N+6M/Fb1fRlU5+EsaE2xee7nV3JdVmdYKpOM8ksX | ||
F5XV/b4BbaPbTRppr0aZk6RDqy5jlYVsNy1Npwqw/2pJqS7wTWZgaWQgePsRGsb5 | ||
3SZC64XpHlzBgYOgpnOC9x9KJuWt2z4OS0l70Nvv7+VLzem0fVMb+Dh/5VFjYfp2 | ||
4w== | ||
-----END ENCRYPTED PRIVATE KEY----- |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
-----BEGIN PRIVATE KEY----- | ||
MIICZQIBADCCAjkGByqGSM44BAEwggIsAoIBAQDJCLNPr0jF6+UOqnftcW7OC+Pj | ||
rSOLhk277kKiWlQvOGNyqFbL3UvOfVAbKFcMOumZhftsSvHUf9Q47xS5XKBbhIpz | ||
UhyxLVQ435mqQWYOpnqLm5glxUwUn8DKfFiOTznimdypgnGQt5mTdVJ7IG574fAn | ||
5IdC6oE1KReDxdXqN0M4i2e6x/O+uc7r8ePsuQ/Eoo3w4iM/VxHKVLNoOBWZwgPh | ||
EpxumDFslRGal2j7IlUit1tEC9k9D32G+VL7gqu0m6LO5JXJNFIcU6RH36YDfLd3 | ||
eTKbwZjQW57uj1JkR/flm0xR7IUVKmcVs0QaW5fyeudt+U78fCGfd8CSa2cDAiEA | ||
vDbQX231dqIHsQePToTsintUmbtI+/MqrR27x0fHysMCggEAHCTRNw9ZVDUcUtD0 | ||
LKL1ophDNXZFjegz6DwHEsGwMcW+fT9QwfVmcZy5xLqqloapOdE7ejWJ6rKHPmQz | ||
m+73u7fuDDSJMl7MaxVDSMLmlCnJO84tNtCLyRsYxsneSFs9rw0tEIJGAgJHnAgz | ||
usj/hNEhTZT5oLjVPT/DjEqqOvVTlBaVZnd1fkeuI03M/J86R2mIm0VkFLpufnW/ | ||
McebsHwpz9Y2jxr2Wvz0+xv5ooLwZeVFRVl95SsHypjkCGtWKcqxA+lZdYkqLbwt | ||
WvIfo+gJJS6iLsxs9I6OBqh85+bp4xlYsXnmr3JrsuV7OyfCppfgIABbHops57Vg | ||
rbRwswQjAiEAksfTXws/dqMjlRmytRcU4qDef3La1STF5WSsoMdYYqs= | ||
-----END PRIVATE KEY----- |
Uh oh!
There was an error while loading. Please reload this page.