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点检查,定时更新

相关推荐
c238561 小时前
Linux C++ 进度条进阶美化与工程化封装
linux·运维·服务器
李小白661 小时前
第四天-WEB服务器基本原理,IIS服务
运维·服务器·前端
爱喝水的鱼丶1 小时前
SAP-ABAP:SAP视图开发入门:四类标准视图的适用场景与创建步骤详解
服务器·数据库·性能优化·sap·abap
回忆2012初秋4 小时前
【Nginx】优雅地走进高性能 Web 服务器世界(1)
服务器·前端·nginx
信创工程师-小杨4 小时前
Linux内网环境如何解决依赖的问题
linux·运维·服务器
不吃土豆的马铃薯5 小时前
C++ 高性能网络缓冲区 Buffer 源码解析
linux·服务器·开发语言·网络·c++
小小龙学IT5 小时前
Go 泛型深度解析:从设计哲学到工程实践
服务器·数据库·golang
YJlio6 小时前
《Sysinternals实战指南》16.5 Ctrl2Cap 工具详解:把 Caps Lock 变成 Ctrl 的键盘改造与回退方法
linux·运维·服务器·网络·python·学习·计算机外设
麦麦麦当劳大王7 小时前
Linux SSH服务端配置指南
linux·运维·服务器·ssh
Yiyaoshujuku8 小时前
化学谱图数据API接口,数据字段一览!
linux·服务器·前端