PingmanTools.AspNet.EncryptWeMust
1.0.0
See the version list below for details.
dotnet add package PingmanTools.AspNet.EncryptWeMust --version 1.0.0
NuGet\Install-Package PingmanTools.AspNet.EncryptWeMust -Version 1.0.0
<PackageReference Include="PingmanTools.AspNet.EncryptWeMust" Version="1.0.0" />
paket add PingmanTools.AspNet.EncryptWeMust --version 1.0.0
#r "nuget: PingmanTools.AspNet.EncryptWeMust, 1.0.0"
// Install PingmanTools.AspNet.EncryptWeMust as a Cake Addin #addin nuget:?package=PingmanTools.AspNet.EncryptWeMust&version=1.0.0 // Install PingmanTools.AspNet.EncryptWeMust as a Cake Tool #tool nuget:?package=PingmanTools.AspNet.EncryptWeMust&version=1.0.0
The simplest LetsEncrypt setup for ASP .NET Core. Almost no server configuration needed.
Install-Package FluffySpoon.AspNet.EncryptWeMust
This project used to be called FluffySpoon.AspNet.LetsEncrypt
, but due to a trademark claim from LetsEncrypt, we had to rename it. The name now follows Yoda Speak.
Requirements
- Kestrel (which is default)
- ASP .NET Core 2.1+
- An always-on app-pool
Getting an always-on app pool
This is required because the renewal job runs on a background thread and polls once every hour to see if the certificate needs renewal (this is a very cheap operation).
It can be enabled using just one the following techniques:
- Enabling Always On if using Azure App Service.
- Setting
StartMode
of the app pool toAlwaysRunning
if using IIS. - Hosting your ASP .NET Core application as a Windows Service.
Usage example
If you want to try it yourself, you can also browse the sample project code here:
Configure the services
Add the following code to your Startup
class' ConfigureServices
method with real values instead of the sample values:
Note that you can set either TimeUntilExpiryBeforeRenewal
, TimeAfterIssueDateBeforeRenewal
or both, but at least one of them has to be specified.
//the following line adds the automatic renewal service.
services.AddFluffySpoonLetsEncrypt(new LetsEncryptOptions()
{
Email = "some-email@github.com", //LetsEncrypt will send you an e-mail here when the certificate is about to expire
UseStaging = false, //switch to true for testing
Domains = new[] { DomainToUse },
TimeUntilExpiryBeforeRenewal = TimeSpan.FromDays(30), //renew automatically 30 days before expiry
TimeAfterIssueDateBeforeRenewal = TimeSpan.FromDays(7), //renew automatically 7 days after the last certificate was issued
CertificateSigningRequest = new CsrInfo() //these are your certificate details
{
CountryName = "Denmark",
Locality = "DK",
Organization = "Fluffy Spoon",
OrganizationUnit = "Hat department",
State = "DK"
}
});
//the following line tells the library to persist the certificate to a file, so that if the server restarts, the certificate can be re-used without generating a new one.
services.AddFluffySpoonLetsEncryptFileCertificatePersistence();
//the following line tells the library to persist challenges in-memory. challenges are the "/.well-known" URL codes that LetsEncrypt will call.
services.AddFluffySpoonLetsEncryptMemoryChallengePersistence();
Inject the middleware
Inject the middleware in the Startup
class' Configure
method as such:
public void Configure()
{
app.UseFluffySpoonLetsEncrypt();
}
Set default bindings
Call UseUrls with http://* and https://* in Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseUrls(new string[] { "http://*", "https://*" });
webBuilder.UseStartup<Startup>();
});
Tada! Your application now supports SSL via LetsEncrypt, even from the first HTTPS request. It will even renew your certificate automatically in the background.
Optional: Configuring persistence
Persistence tells the middleware how to persist and retrieve the certificate, so that if the server restarts, the certificate can be re-used without generating a new one.
A certificate has a key to distinguish between certificates, since there is both an account certificate and a site certificate that needs to be stored.
File persistence
services.AddFluffySpoonLetsEncryptFileCertificatePersistence();
services.AddFluffySpoonLetsEncryptFileChallengePersistence();
Custom persistence
services.AddFluffySpoonLetsEncryptCertificatePersistence(/* your own ILetsEncryptPersistence implementation */);
services.AddFluffySpoonLetsEncryptChallengePersistence(/* your own ILetsEncryptPersistence implementation */);
//you can also customize persistence via delegates.
services.AddFluffySpoonLetsEncryptCertificatePersistence(
async (key, bytes) => File.WriteAllBytes("certificate_" + key, bytes),
async (key) => File.ReadAllBytes("certificate_" + key, bytes));
//the same can be done for challenges, with different arguments.
services.AddFluffySpoonLetsEncryptChallengePersistence(
async (challenges) => ... /* Do something to serialize the collection of challenges and store it */,
async () => ... /* Retrieve the stored collection of challenges */,
async (challenges) => ... /* Delete the specified challenges */);
The resource group for the App Service can also easily be accessed through an environment variable, as specified above.
Hooking into events
You can register a an ICertificateRenewalLifecycleHook
implementation which does something when certain events occur, as shown below. This can be useful if you need to notify a Slack channel or send an e-mail if an error occurs, or when the certificate has indeed been renewed.
class MyLifecycleHook : ICertificateRenewalLifecycleHook {
public async Task OnStartAsync() {
//when the renewal background job has started.
}
public async Task OnStopAsync() {
//when the renewal background job (or the application) has stopped.
//this is not guaranteed to fire in critical application crash scenarios.
}
public async Task OnRenewalSucceededAsync() {
//when the renewal has completed.
}
public async Task OnExceptionAsync(Exception error) {
//when an error happened during the renewal process.
}
}
//this is how to wire up the hook.
services.AddFluffySpoonLetsEncryptRenewalLifecycleHook<MyLifecycleHook>();
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- certes (>= 3.0.4)
- Microsoft.AspNetCore.Connections.Abstractions (>= 2.2.0)
- Microsoft.AspNetCore.Http.Abstractions (>= 2.2.0)
- Microsoft.AspNetCore.Server.Kestrel.Core (>= 2.2.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 2.2.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 2.2.0)
- Microsoft.Extensions.Logging.Abstractions (>= 2.2.0)
- Microsoft.Extensions.Options (>= 2.2.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.