Reputation: 16411
I have generated a certificate using powershell using this suggestion found at Stackoverflow:
New-SelfSignedCertificate -Subject "CN=Test Code Signing" -Type CodeSigningCert -KeySpec "Signature" -KeyUsage "DigitalSignature" -FriendlyName "Test Code Signing" -NotAfter (get-date).AddYears(5)
I have copied and pasted this certificate into Trusted Root Certification Authorities.
My NET Core WebAPI Program.cs is set as follows:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseKestrel(options=> {
options.Listen(IPAddress.Loopback, 5000); // http:localhost:5000
options.Listen(IPAddress.Any, 80); // http:*:80
options.Listen(IPAddress.Loopback, 443, listenOptions =>
{
//how to use a certificate store here?
//listenOptions.UseHttps("certificate.pfx", "password");
//listenOptions.UseHttps(StoreName.My, "Test Code Signing", allowInvalid: true);
listenOptions.UseHttps(StoreName.My, "localhost", allowInvalid: true);
});
});
Neither localhost or Test Code Signing worked in this code since they cannot be found. Maybe I am missing something. Tried to follow this MSDN documentation with no luck.
For now the certificate shown on Google Chrome is different from the ones I have in Personal and Trusted Root Certification authorities:
How to setup Kestrel in order to pick a self signed certificate that is trusted by browsers and avoiding blocking messages such as NET::ERR_CERT_AUTHORITY_INVALID
?
Upvotes: 8
Views: 4247
Reputation: 366
I think you should (first) create a right trusted local certificate, (second) associate it with your ip and port, and (third) you should use 'localhost' (the 'CN' for which the certificate was created) in your browser instead the loopback ip.
The following commands in PowerShell (run as admin) will create a root certificate and its associated trusted certificate:
1.- We create a new root trusted cert:
$rootCert = New-SelfSignedCertificate -Subject 'CN=TestRootCA,O=TestRootCA,OU=TestRootCA' -KeyExportPolicy Exportable -KeyUsage CertSign,CRLSign,DigitalSignature -KeyLength 2048 -KeyUsageProperty All -KeyAlgorithm 'RSA' -HashAlgorithm 'SHA256' -Provider 'Microsoft Enhanced RSA and AES Cryptographic Provider'
2.- We create the cert from the root trusted cert chain:
New-SelfSignedCertificate -DnsName "localhost" -CertStoreLocation "cert:\LocalMachine\My" -Signer $rootCert -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.1") -Provider "Microsoft Strong Cryptographic Provider" -HashAlgorithm "SHA256" -NotAfter (Get-Date).AddYears(10)
3.- We copy the thumbprint returned by the last command
4.- (If neccesary) We remove the last association ip/port/cert:
netsh http delete sslcert ipport=0.0.0.0:443
5.- We associate the new certificate with any ip and port 443 (the appid value does not matter, is any valid guid):
netsh http add sslcert ipport=0.0.0.0:443 appid='{214124cd-d05b-4309-9af9-9caa44b2b74a}' certhash=here_the_copied_thumbprint
6.- Now, you must drag and drop the TestRootCA from Personal/Certificates folder to Trusted Root Certification Authorities/Certificates.
These commands also resolve the error ERR_CERT_WEAK_SIGNATURE_ALGORITHM returned later by Google Chrome because the certificate is created with SHA1 instead of SHA256.
Upvotes: 0
Reputation: 402
This method worked for me :
private static X509Certificate2 LoadCertificate(string p_storeName, string p_storeLocation, string p_Host, string p_FilePath, string p_Password, IHostingEnvironment environment)
{
if (p_storeName != "" && p_storeLocation != "")
{
using (var store = new X509Store(p_storeName, Enum.Parse<StoreLocation>(p_storeLocation)))
{
store.Open(OpenFlags.ReadOnly);
bool validOnly = false;
if (environment.IsDevelopment() == true) { validOnly = false; }
else
{
if (p_Host == "localhost") { validOnly = false; }
else { validOnly = true; }
}
var certificate = store.Certificates.Find(
X509FindType.FindBySubjectName,p_Host,validOnly: validOnly);
if (certificate.Count == 0)
{
throw new InvalidOperationException($"Certificate not found for {p_Host}.");
}
return certificate[0];
}
}
if (p_FilePath != "" && p_Password != "")
{
return new X509Certificate2(p_FilePath, p_Password);
}
throw new InvalidOperationException("No valid certificate configuration found for the current endpoint.");
}
Upvotes: 0
Reputation: 273
The UseHttps overload you are using doesn't allow you to specific the store location so it defaults to StoreLocation.CurrentUser. You need to do call a method which retrieves the certificate from the store and passes it to the UseHttps method. There's an MSDN article that gives more detail that I've included at the bottom but here's an example (you need to replace 'your common name here' with the certificate common name):
static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel(options =>
{
options.Listen(IPAddress.Any, 443, listenOptions =>
{
listenOptions.UseHttps(GetHttpsCertificateFromStore());
listenOptions.NoDelay = true;
});
})
.Build();
}
private static X509Certificate2 GetHttpsCertificateFromStore()
{
using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadOnly);
var certCollection = store.Certificates;
var currentCerts = certCollection.Find(X509FindType.FindBySubjectDistinguishedName, "CN=[your common name here]", false);
if (currentCerts.Count == 0)
{
throw new Exception("Https certificate is not found.");
}
return currentCerts[0];
}
}
Upvotes: 3