• +65 6652 3398
  • info@wgrow.com
  • 114 Lavender St, #07-51 CT Hub 2

wGrow - Team Notes

Sharing Expertise: Tech Insights and Case Studies

Creating a Windows Service to Automate Let's Encrypt SSL Certificate Management for IIS Websites

Let's Encrypt is a free, automated, and open Certificate Authority (CA) that provides SSL/TLS certificates for websites. The ACME (Automatic Certificate Management Environment) protocol is used to automate the process of obtaining and renewing SSL certificates. In this article, we will create a Windows Service that automates the process of obtaining, installing, and renewing Let's Encrypt SSL certificates for IIS websites using the ACME challenge.

Prerequisites

  1. Familiarity with C# and the .NET Framework
  2. Visual Studio installed on your system
  3. Administrative access to the target Windows machine running IIS
  4. A registered domain name with the ability to create and manage subdomains (e.g., SSLTest.wGrow.com)

Step 1: Install the required NuGet packages

  1. Launch Visual Studio and create a new project by selecting the "Windows Service (.NET Framework)" template.
  2. Name the project "IISLEService" and click "Create".
  3. Right-click on the project and select "Manage NuGet Packages".
  4. Install the following packages:
    • Certes
    • Microsoft.Web.Administration

Step 2: Implement the IISLEService

Open the "Service1.cs" file and rename the class to "IISLEService". Implement the required logic as follows:

using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.ServiceProcess;
using System.Threading.Tasks;
using Certes;
using Certes.Acme;
using Certes.Acme.Resource;
using Microsoft.Web.Administration;

public partial class IISLEService : ServiceBase
{
    private const string Domain = "SSLTest.wGrow.com";
    private const string Email = "SSLTest@wGrow.com";
    private const string SiteName = "Default Web Site"; // Change this to your IIS site name
    private const int RenewalPeriodDays = 30;

    private Timer _renewalTimer;

    public IISLEService()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
        // Schedule the first renewal check
        _renewalTimer = new Timer(CheckCertificateRenewal, null, TimeSpan.Zero, TimeSpan.FromDays(RenewalPeriodDays));
    }

    protected override void OnStop()
    {
        _renewalTimer.Dispose();
    }

    private async void CheckCertificateRenewal(object state)
    {
        var cert = GetExistingCertificate();
        if (cert == null || DateTime.UtcNow >= cert.NotAfter.AddDays(-RenewalPeriodDays))
        {
            await ObtainAndInstallCertificate();
        }
    }

    private async Task ObtainAndInstallCertificate()
    {
        var acme = new AcmeContext(WellKnownServers.LetsEncryptV2);
        var account = await acme.NewAccount(Email, true);

        var order = await acme.NewOrder(new[] { Domain });
        var authz = (await order.Authorizations()).First();
        var httpChallenge = await authz.Http();

        var keyAuthz = acme.AccountKey.ComputeKeyAuthorization(httpChallenge);
        SaveChallengeFile(httpChallenge.Token, keyAuthz);

        var challenge = await httpChallenge.Validate();
        while (challenge.Status == ChallengeStatus.Pending)
        {
            await Task.Delay(2000);
            challenge = await httpChallenge.Resource();
        }

        if (challenge.Status != ChallengeStatus.Valid)
        {
            throw new InvalidOperationException("ACME challenge validation failed");
        }

        var certChain = await order.Generate(new CsrInfo
        {
            CommonName = Domain,
        });

        InstallCertificate(certChain.Certificate.ToPem(), certChain.IssuerCertificate.ToPem());
        DeleteChallengeFile(httpChallenge.Token);
    }

    private X509Certificate2 GetExistingCertificate()
    {
        using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
        {
            store.Open(OpenFlags.ReadOnly);
            return store.Certificates.Find(X509FindType.FindBySubjectName, Domain, false).OfType().FirstOrDefault();
        }
    }

    private void SaveChallengeFile(string token, string keyAuthorization)
    {
        // Replace this path with the appropriate path for your IIS configuration
        var challengeFilePath = Path.Combine("C:\\inetpub\\wwwroot\\.well-known\\acme-challenge", token);
        File.WriteAllText(challengeFilePath, keyAuthorization);
    }

    private void DeleteChallengeFile(string token)
    {
        // Replace this path with the appropriate path for your IIS configuration
        var challengeFilePath = Path.Combine("C:\\inetpub\\wwwroot\\.well-known\\acme-challenge", token);
        File.Delete(challengeFilePath);
    }

    private void InstallCertificate(string certificatePem, string issuerCertificatePem)
    {
        var certificate = new X509Certificate2(Convert.FromBase64String(Certes.Pkcs12Converter.Convert(certificatePem, issuerCertificatePem, "")));

        using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
        {
            store.Open(OpenFlags.ReadWrite);
            store.Add(certificate);
        }

        using (var serverManager = new ServerManager())
        {
            var site = serverManager.Sites[SiteName];
            var binding = site.Bindings.FirstOrDefault(b => b.Protocol == "https");

            if (binding != null)
            {
                binding.CertificateHash = certificate.GetCertHash();
                binding.CertificateStoreName = StoreName.My.ToString();
            }
            else
            {
                site.Bindings.Add($"*:{443}:{Domain}", certificate.GetCertHash(), StoreName.My.ToString(), "https");
            }

            serverManager.CommitChanges();
        }
    }
}

Step 3: Install and test the service

  1. Build the IISLEService project.
  2. Open a command prompt with administrative privileges and navigate to the directory containing the compiled IISLEService.exe.
  3. Install the service using the following command: `sc create IISLEService binPath= "C:\path\to\IISLEService.exe"`
  4. Start the service using the command: `sc start IISLEService`

The IISLEService will now automatically obtain, install, and renew the SSL certificate for SSLTest.wGrow.com.

-----

Monitoring, Troubleshooting, and Maintenance

To ensure the IISLEService operates effectively, it is crucial to monitor its performance and address any potential issues. This section will discuss best practices for monitoring, troubleshooting, and maintaining the service.

1. Logging and Monitoring

Implement logging to track the IISLEService's activity and performance. Utilize the built-in Windows Event Log to log relevant information, such as when a certificate is obtained, installed, or renewed, as well as any errors or warnings that may occur.

To add logging to the IISLEService, follow these steps:

- Add a new EventLog component to the IISLEService class and name it "eventLog".
- Set the "Log" property of the eventLog component to "Application".
- Set the "Source" property of the eventLog component to "IISLEService".

Now, you can use the `eventLog.WriteEntry()` method to log messages throughout the IISLEService. For example:


private void Log(string message, EventLogEntryType entryType = EventLogEntryType.Information)
{
    eventLog.WriteEntry(message, entryType);
}

 

Call the `Log()` method in the appropriate places in your code to log relevant information.

2. Troubleshooting

If the IISLEService encounters issues, such as failing to obtain, install, or renew certificates, consult the logs to identify the root cause. Common issues may include:

- Incorrect domain or site configuration: Ensure that the domain name and site name in the IISLEService code match your IIS setup.
- Insufficient permissions: Verify that the IISLEService runs under an account with adequate permissions to access the IIS configuration, the certificate store, and the ACME challenge directory.
- Network issues: Check for connectivity issues between the IIS server and the Let's Encrypt infrastructure, as well as any firewalls or proxy settings that could block access.

3. Maintenance and Updates

Keep the IISLEService up-to-date with the latest security standards and best practices:

- Regularly update the .NET Framework and any third-party NuGet packages to the latest versions.
- Review and update the ACME protocol implementation to comply with any changes introduced by Let's Encrypt or the ACME standard.
- Periodically test the IISLEService's functionality to ensure it continues to operate correctly, especially after making changes to your IIS configuration or server environment.

By following these guidelines, you can effectively monitor, troubleshoot, and maintain the IISLEService, ensuring the secure and efficient management of SSL certificates for your IIS websites.

 


 

 

Related

SQL Server Hardening Checklist & Best Practices

SQL Server Hardening Checklist & Best Practices

It's essential to ensure that SQL Server is secure and protected against potential cyber threats...

Read More >
Exploring Reflection in C#: Dynamically Accessing Object Properties and Database Operations

Exploring Reflection in C#: Dynamically Accessing Object Properties and Database Operations

Reflection is a powerful feature in C# that allows us to inspect and interact with the metadata of t...

Read More >
Secured 3G/4G SMS Gateway with HTTP API

Secured 3G/4G SMS Gateway with HTTP API

Constructed by our expert team in Singapore, this industrial-grade SMS Gateway boasts a minimum of 8...

Read More >
Implementing a Global Chemical Compliance Check System for a Multinational Corporation

Implementing a Global Chemical Compliance Check System for a Multinational Corporation

In the complex world of global chemical imports, multinational corporations face the challenge of na...

Read More >
Implementing Business Continuity Management for a Singapore Real Estate Agency's ERP Solution

Implementing Business Continuity Management for a Singapore Real Estate Agency's ERP Solution

This technical summary documents the process of designing and implementing a Business Continuity Man...

Read More >
Securing Data in ASP.NET Web Projects with SQL Server Transparent Data Encryption (TDE)

Securing Data in ASP.NET Web Projects with SQL Server Transparent Data Encryption (TDE)

This technical article provides an overview of SQL Server Transparent Data Encryption (TDE) and demo...

Read More >
Contact Us
  • Our Address:
    114 Lavender Street, #07-51, CT Hub 2, Singapore 338729
    Malaysia Johor - 99-01 Jalan Adda 3/1 Taman Adda Height 81100 Johor Bahru Johor, Malaysia
  • Phone Number:
    +65 6652 3398
  • WhatsApp:
    WhatsApp Us
  • Email:
    info@wgrow.com