AbpVnext 阿里云ssl证书多个生产环境自动更新

一个站点对应多个域名,所以要有多个证书

打开Host下面Program.cs,加入代码,如下图

Kestrel 的证书支持热更新机制,新的证书文件覆盖后会自动更新

cs 复制代码
 //下面代码作用是证书文件更新后,自动更新证书,不需要重启应用
 builder.WebHost.ConfigureKestrel(serverOptions =>
 {
     serverOptions.ConfigureHttpsDefaults(httpsOptions =>
     {
         httpsOptions.ServerCertificateSelector = (connectionContext, name) =>
         {
             return name?.ToLower() switch
             {
                 "域名1.com" or "www.域名1.com" => new X509Certificate2("证书1.pfx", "密码"),
                 "域名2.net" or "www.域名2.net" => new X509Certificate2("证书2.pfx", "密码"),
                 _ => throw new NotSupportedException($"No certificate available for {name}")
             };
         };
     });
 });

如果你只有一个域名,代码如下:

cs 复制代码
 httpsOptions.ServerCertificateSelector = (ctx, host) =>
    {
        // 可用代码动态读取最新的证书
        return new X509Certificate2("/path/to/your/cert.pfx", "your_cert_password");
    };

自动更新算法

1、程序启动时判断证书过期,程序启动后每天定时检查证书过期

2、过期后从阿里云自动下载文件覆盖本地文件即可。

自动下载证书参考我上次的文章,参考地址如下

net9阿里云自动申请ssl证书,下载证书pfx格式-CSDN博客

判断证书是否过期代码

cs 复制代码
 var cert = new System.Security.Cryptography.X509Certificates.X509Certificate2("证书文件", "密码");
 DateTime expirationDate = DateTime.Parse(cert.GetExpirationDateString());
 if (expirationDate.AddDays(-1).Date <= DateTime.Now.Date)
 {
     //下载证书
 }

多个证书过期判断

cs 复制代码
 // 搜索所有.pfx文件
 string[] pfxFiles = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.pfx", SearchOption.AllDirectories);
 foreach (var filePath in pfxFiles)
 {
     var cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(filePath, "密码");
     DateTime expirationDate = DateTime.Parse(cert.GetExpirationDateString());
     if (expirationDate.AddDays(-1).Date <= DateTime.Now.Date)
     {
         //下载证书
     }
 }

完整的代码如下:

接口IOpenSSLAppService,目的是为了兼容其它云平台域名证书

cs 复制代码
/// <summary>
/// 检查证书是否过期
/// </summary>
/// <returns></returns>
public async Task IsCertificateExpiredAsync()
{
    // 搜索所有.pfx文件
    string[] pfxFiles = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.pfx", SearchOption.AllDirectories);
    string password = _configuration["Aliyun:password"];
    foreach (var filePath in pfxFiles)
    {
        var cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(filePath, password);
        DateTime expirationDate = DateTime.Parse(cert.GetExpirationDateString());
        if (expirationDate.AddDays(-1).Date <= DateTime.Now.Date)
        {
           //更新证书
            await GetPfxAsync(filePath.GetFileNameWithoutExtension(), password);
        }
    }
}
/// <summary>
/// 获取域名证书
/// </summary>
/// <param name="domain"></param>
/// <param name="password"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task GetPfxAsync(string domain,string password)
{
    var akConfig = new Aliyun.Credentials.Models.Config
    {
        Type = "access_key",
        AccessKeyId = _configuration["Aliyun:accessKeyId"],
        AccessKeySecret = _configuration["Aliyun:secret"]
    };
    var akCredential = new Aliyun.Credentials.Client(akConfig);
    AlibabaCloud.OpenApiClient.Models.Config config = new AlibabaCloud.OpenApiClient.Models.Config
    {
        Credential = akCredential,
        RegionId = _configuration["Aliyun:regionId"]
    };
    AlibabaCloud.SDK.Cas20200407.Client client = new AlibabaCloud.SDK.Cas20200407.Client(config);

    try
    {
        var request = new CreateCertificateRequestRequest
        {
            Username = _configuration["Aliyun:mail"],
            Phone = _configuration["Aliyun:phone"],
            Email = _configuration["Aliyun:mail"],
            Domain = domain,
            ValidateType = "DNS"
        };

        var runtime = new RuntimeOptions();
        var certificateResponse = await client.CreateCertificateRequestAsync(request);
        DescribeCertificateStateResponse certificateStateResponse;
        if (certificateResponse.StatusCode == 200 && certificateResponse.Body != null)
        {
            var orderId = certificateResponse.Body.OrderId;
            certificateStateResponse = await client.DescribeCertificateStateAsync(new DescribeCertificateStateRequest { OrderId = orderId });
            while (certificateStateResponse.Body.PrivateKey == null)
            {
                Thread.Sleep(30000);//休息30秒
                certificateStateResponse = await client.DescribeCertificateStateAsync(new DescribeCertificateStateRequest { OrderId = orderId });
            }
            if (certificateStateResponse.Body.PrivateKey != null)
            {
                //保存证书
                string certPem = certificateStateResponse.Body.Certificate;      // 公钥证书 PEM 字符串
                string keyPem = certificateStateResponse.Body.PrivateKey; // 私钥 PEM 字符串                        
                CryptorHelper.PemToPfx(certPem, keyPem, $"{domain}.pfx", password);
            }
        }
    }
    catch (TeaException ex)
    {
        throw new Exception($"阿里云API错误: {ex.Message}", ex);
    }
}

添加程序启动更新代码

加入到hangfire定时任务,每天1点检查,定时更新

相关推荐
钮钴禄·爱因斯晨21 分钟前
Linux(一) | 初识Linux与目录管理基础命令掌握
linux·运维·服务器
BioRunYiXue36 分钟前
FRET、PLA、Co-IP和GST pull-down有何区别? 应该如何选择?
java·服务器·网络·人工智能·网络协议·tcp/ip·eclipse
起个昵称吧42 分钟前
TCP并发服务器构建
服务器·数据库·tcp/ip
Coision.1 小时前
linux 网络:并发服务器及IO多路复用
linux·服务器·网络
FreeBuf_3 小时前
Kea DHCP高危漏洞CVE-2025-40779:单个数据包即可导致服务器崩溃
服务器·网络·安全
朱包林4 小时前
数据库服务-日志管理-备份与恢复-主从同步
linux·运维·服务器·数据库·mysql·云计算
眰恦ゞLYF7 小时前
服务器类型与TCP并发服务器构建(SELECT)
服务器·select·io多路复用
万花丛中一抹绿7 小时前
服务器硬件电路设计之 SPI 问答(六):如何提升服务器硬件电路中的性能?如何强化稳定性?
服务器·spi·服务器硬件电路设计
2401_888423097 小时前
网络编程-TCP的并发服务器构建
服务器·网络·tcp/ip