写点什么

c# ACME client (补充)

作者:八苦-瞿昙
  • 2025-07-30
    中国台湾
  • 本文字数:3442 字

    阅读完需:约 11 分钟

上一篇 c# ACME client 漏了一部分内容,今天补上


除了之前介绍的在 asp.net core 使用方式,还可以单独在代码中使用 client

简化用法

如果已经集成好完全的自动证书申请验证,就可以使用已经封装好的代码进行简单使用


举例在 asp.net core 提供 一个 api 可以根据参数申请证书


starup


var builder = WebApplication.CreateBuilder(args);builder.Services.AddControllers();builder.Services.AddAcmeChallengeCore(config: c =>{    c.HttpClientConfig = new VKProxy.Config.HttpClientConfig()    {        DangerousAcceptAnyServerCertificate = true    };});
var app = builder.Build();
if (app.Environment.IsDevelopment()){ app.MapOpenApi();}app.UseAuthorization();
app.MapControllers();
app.Run();
复制代码


api


using Microsoft.AspNetCore.Mvc;using VKProxy.ACME.AspNetCore;using VKProxy.Core.Extensions;
namespace WithApi.Controllers;
[ApiController][Route("[controller]")]public class CertController : ControllerBase{ private readonly IAcmeStateIniter initer;
public CertController(IAcmeStateIniter initer) { this.initer = initer; }
[HttpGet] public async Task<string> Get([FromQuery] string domain) { // 证书配置 var o = new AcmeChallengeOptions() { AllowedChallengeTypes = VKProxy.ACME.AspNetCore.ChallengeType.Http01, Server = new Uri("https://127.0.0.1:14000/dir"), DomainNames = new[] { domain }, AdditionalIssuers = new[] { """ -----BEGIN CERTIFICATE----- MIIDGzCCAgOgAwIBAgIIUPFry5qBu34wDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE AxMVUGViYmxlIFJvb3QgQ0EgMjFjNjY3MCAXDTI1MDcyMjAxMTA0OVoYDzIwNTUw NzIyMDExMDQ5WjAgMR4wHAYDVQQDExVQZWJibGUgUm9vdCBDQSAyMWM2NjcwggEi MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxNKa4y93OFYaSx8bcbuWsHHnW mpfsobK5Elf7GE02mi/cDrMP+wR1l53BuucrW04OyoewkBsJNZoxEy1DkCjxv4+g Q+HgGCR5R14ex17ZdFxpcl42H8QnRB3IqVBlJiz0JyGZwiaOamOkUTVEYTGDeuxu PglpvboGeatsWQe0MJJfBN8OxLVUmi6Y/enbzlIdv3tvgQujfPNiS8MLDMBuIiMs ixhu8YAzUqvVKZoQVK7GwbD9WrVBKub8w86StKFmU14aSXahidt8IENdpLO2OT3J y1nt25QDsAmtS1/wGnTDPeefLGsM7kGYNesQkSW0w8Um4p9KLWKnKyOvzPZrAgMB AAGjVzBVMA4GA1UdDwEB/wQEAwIChDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNV HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRoXcwo6c5J8jMweiHKPw4OlcWIQzANBgkq hkiG9w0BAQsFAAOCAQEAad9XT4sN1KserYtCxBKmoPhPAHInHYgG/Z2gd6KqdsK9 biIgEbKo84tClLqA6XCN/yN1bMQL2ZMbWBF8oHv/A5o0atpTpd+Ho+punHYRIpqv akUX21Zsu6NdAuH7g7m9t9h/lc6tgiqaAf2HwpC3NrXmUlPRqLay7/t+BFQU6dBa E+qzmL7lHZQf1UArfb+QDYH2XsFCk9Pjv0xdP+PGwf8HqHhfPLctvus5JL+LXp0X 68eWKQCs1CrL8cUMwcELlW/mR1lKnJL1WgM1Bns9ZF1ha6egG539ruzQjItF6MHB xAEt55nXfs+mjV1p7qrcmR8jIdByR9C36T21r+8pKA== -----END CERTIFICATE-----
""" } };
// 申请全新account o.NewAccount(new string[] { "mailto:test11@xxx.com" }); // 执行全套流程 var cert = await initer.CreateCertificateAsync(o); return cert.ExportPem(); }}
复制代码


默认情况下三种验证方式:

http

如在 asp.net core 中使用,默认已经添加了 app.Map("/.well-known/acme-challenge" 路由处理, 如要申请公网上权威认证的证书,请将 /.well-known/acme-challenge 路由暴露在公网让 acme 服务器可以访问


其次由于默认实现没有持久化和分布式处理验证信息,重启和多实例都会有问题,如有需求可以替换IHttpChallengeResponseStore实现以达到效果


public interface IHttpChallengeResponseStore{    Task AddChallengeResponseAsync(string token, string keyAuth, CancellationToken cancellationToken);
Task<string> GetChallengeResponse(string token, CancellationToken cancellationToken);
Task RemoveChallengeResponseAsync(string token, CancellationToken cancellationToken);}
复制代码

dns

由于不同服务商有各自的 api,所以默认没有实现,该功能其实无效,如需使用,请实现 IDnsChallengeStore


public interface IDnsChallengeStore{    Task AddTxtRecordAsync(string acmeDomain, string dnsTxt, CancellationToken cancellationToken);
Task RemoveTxtRecordAsync(string acmeDomain, string dnsTxt, CancellationToken cancellationToken);}
复制代码

tls

个人并不推荐使用 tls 验证方式,其由于验证自签证书和正式证书都会在 tls 层,运行时不停机重新申请对于 tls 管理还是有些挑战的


如想尝试可以实现ITlsAlpnChallengeStore (默认在 asp.net core 的实现并不能支持过滤验证自签证书只用于 acme 服务器请求)


public interface ITlsAlpnChallengeStore{    Task AddChallengeAsync(string domainName, X509Certificate2 cert, CancellationToken cancellationToken);
Task RemoveChallengeAsync(string domainName, X509Certificate2 cert, CancellationToken cancellationToken);}
复制代码

底层 client

如需直接使用原始 acme 协议 client,可参考如下


starup


var builder = WebApplication.CreateBuilder(args);builder.Services.AddControllers();
builder.Services.AddACME(c =>{ c.HttpClientConfig = new VKProxy.Config.HttpClientConfig() { DangerousAcceptAnyServerCertificate = true };});
复制代码


use


var context = services.BuildServiceProvider().GetRequiredService<IAcmeContext>();
await context.InitAsync(new Uri("https://127.0.0.1:14000/dir"), cancellationToken);
var account = await context.NewAccountAsync(new string[] { "mailto:xxx@xxx.com" }, true, KeyAlgorithm.RS256.NewKey());
var order = await context.NewOrderAsync(new string[] { "test.com" });var aus = order.GetAuthorizationsAsync().ToBlockingEnumerable().ToArray();var a = aus.First();var b = await a.HttpAsync();var c = await b.ValidateAsync();
Key privateKey = KeyAlgorithm.RS256.NewKey();var csrInfo = new CsrInfo{ CommonName = "test.com",};order = await context.FinalizeAsync(csr, key, cancellationToken);var acmeCert = await order.DownloadAsync();
var pfxBuilder = acmeCert.ToPfx(privateKey);if (!string.IsNullOrWhiteSpace(Args.AdditionalIssuer) && File.Exists(Args.AdditionalIssuer)){ pfxBuilder.AddIssuer(File.ReadAllBytes(Args.AdditionalIssuer));}var pfx = pfxBuilder.Build("HTTPS Cert - " + Args.Domain, string.Empty);var r = X509CertificateLoader.LoadPkcs12(pfx, string.Empty, X509KeyStorageFlags.Exportable);
复制代码

ui

在 VKProxy 管理站点的 ui sni 里面添加了 简单的 http 验证方式的 acme 证书界面配置 如下图


(当然使用前提得是 暴露 xxx 域名/.well-known/acme-challenge 接口到公网,这样公网 acme 才能验证)


专职 ACME 管理程序

其实对更多人来说,ACME 都是低频率使用,只是现在免费证书大多 90 天,所以才期望有个工具帮自己干活


现成的很多,比如


  • acme.sh 脚本工具,稍显复杂

  • certimate 目前只支持 dns 验证,不过 dns 服务商/通知/证书部署都支持非常全面


所以珠玉在前,大家可以直接尽情使用


至于用 c#再做一个,多半没有啥人关注,不信,和大家打个赌:评论留言说期望有个 c#版的超过 30 条,就搞一个 (一条评论都没有,信不信,哈哈哈)


VKProxy 是使用 c#开发的基于 Kestrel 实现 L4/L7 的代理(感兴趣的同学烦请点个github小赞赞呢)

发布于: 刚刚阅读数: 5
用户头像

八苦-瞿昙

关注

一个假和尚,不懂人情世故。 2018-11-23 加入

会点点技术,能写些代码,只爱静静。 g hub: https://github.com/fs7744 黑历史:https://www.cnblogs.com/fs7744

评论

发布
暂无评论
c# ACME client (补充)_八苦-瞿昙_InfoQ写作社区