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

发布于:2025-08-31 ⋅ 阅读:(20) ⋅ 点赞:(0)

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

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

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

 //下面代码作用是证书文件更新后,自动更新证书,不需要重启应用
 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}")
             };
         };
     });
 });

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

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

自动更新算法

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

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

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

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

判断证书是否过期代码

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

多个证书过期判断

 // 搜索所有.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,目的是为了兼容其它云平台域名证书

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