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

相关推荐
未来之窗软件服务3 小时前
服务器运维(六)跨域配置 Preflight 问题——东方仙化神期
运维·服务器·服务器运维·仙盟创梦ide·东方仙盟
AORO20254 小时前
智能三防手机哪款好?22000mAh+夜视+露营灯打造专业户外装备
服务器·网络·智能手机·电脑·1024程序员节
winner88815 小时前
Linux 软件安装 “命令密码本”:yum/apt/brew 一网打尽
linux·运维·服务器
九河云6 小时前
软件开发平台 DevCloud
运维·服务器·数据库·科技·华为云
firstacui6 小时前
DNS高速缓存&分离解析
服务器
思麟呀7 小时前
Linux的基础IO流
linux·运维·服务器·开发语言·c++
Archy_Wang_18 小时前
脚本自动生成专业Linux巡检报告
linux·运维·服务器
piaoxue82010 小时前
MFA MACOS 安装流程
linux·运维·服务器
柱子子子子11 小时前
Ubuntu24.04 不能使用todesk 解决办法
运维·服务器
天若有情67311 小时前
新闻通稿 | 软件产业迈入“智能重构”新纪元:自主进化、人机共生与责任挑战并存
服务器·前端·后端·重构·开发·资讯·新闻