找回密码
 立即注册
首页 业界区 业界 c# ACME client (补充)

c# ACME client (补充)

焦和玉 昨天 14:16
上一篇 c# ACME client 漏了一部分内容,今天补上
除了之前介绍的在 asp.net core 使用方式,还可以单独在代码中使用client
简化用法

如果已经集成好完全的自动证书申请验证,就可以使用已经封装好的代码进行简单使用
举例在asp.net core提供 一个api  可以根据参数申请证书
starup
  1. var builder = WebApplication.CreateBuilder(args);
  2. builder.Services.AddControllers();
  3. builder.Services.AddAcmeChallengeCore(config: c =>
  4. {
  5.     c.HttpClientConfig = new VKProxy.Config.HttpClientConfig()
  6.     {
  7.         DangerousAcceptAnyServerCertificate = true
  8.     };
  9. });
  10. var app = builder.Build();
  11. if (app.Environment.IsDevelopment())
  12. {
  13.     app.MapOpenApi();
  14. }
  15. app.UseAuthorization();
  16. app.MapControllers();
  17. app.Run();
复制代码
api
  1. using Microsoft.AspNetCore.Mvc;
  2. using VKProxy.ACME.AspNetCore;
  3. using VKProxy.Core.Extensions;
  4. namespace WithApi.Controllers;
  5. [ApiController]
  6. [Route("[controller]")]
  7. public class CertController : ControllerBase
  8. {
  9.     private readonly IAcmeStateIniter initer;
  10.     public CertController(IAcmeStateIniter initer)
  11.     {
  12.         this.initer = initer;
  13.     }
  14.     [HttpGet]
  15.     public async Task<string> Get([FromQuery] string domain)
  16.     {
  17.         // 证书配置
  18.         var o = new AcmeChallengeOptions()
  19.         {
  20.             AllowedChallengeTypes = VKProxy.ACME.AspNetCore.ChallengeType.Http01,
  21.             Server = new Uri("https://127.0.0.1:14000/dir"),
  22.             DomainNames = new[] { domain },
  23.             AdditionalIssuers = new[] { """
  24.                         -----BEGIN CERTIFICATE-----
  25.                         MIIDGzCCAgOgAwIBAgIIUPFry5qBu34wDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
  26.                         AxMVUGViYmxlIFJvb3QgQ0EgMjFjNjY3MCAXDTI1MDcyMjAxMTA0OVoYDzIwNTUw
  27.                         NzIyMDExMDQ5WjAgMR4wHAYDVQQDExVQZWJibGUgUm9vdCBDQSAyMWM2NjcwggEi
  28.                         MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxNKa4y93OFYaSx8bcbuWsHHnW
  29.                         mpfsobK5Elf7GE02mi/cDrMP+wR1l53BuucrW04OyoewkBsJNZoxEy1DkCjxv4+g
  30.                         Q+HgGCR5R14ex17ZdFxpcl42H8QnRB3IqVBlJiz0JyGZwiaOamOkUTVEYTGDeuxu
  31.                         PglpvboGeatsWQe0MJJfBN8OxLVUmi6Y/enbzlIdv3tvgQujfPNiS8MLDMBuIiMs
  32.                         ixhu8YAzUqvVKZoQVK7GwbD9WrVBKub8w86StKFmU14aSXahidt8IENdpLO2OT3J
  33.                         y1nt25QDsAmtS1/wGnTDPeefLGsM7kGYNesQkSW0w8Um4p9KLWKnKyOvzPZrAgMB
  34.                         AAGjVzBVMA4GA1UdDwEB/wQEAwIChDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNV
  35.                         HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRoXcwo6c5J8jMweiHKPw4OlcWIQzANBgkq
  36.                         hkiG9w0BAQsFAAOCAQEAad9XT4sN1KserYtCxBKmoPhPAHInHYgG/Z2gd6KqdsK9
  37.                         biIgEbKo84tClLqA6XCN/yN1bMQL2ZMbWBF8oHv/A5o0atpTpd+Ho+punHYRIpqv
  38.                         akUX21Zsu6NdAuH7g7m9t9h/lc6tgiqaAf2HwpC3NrXmUlPRqLay7/t+BFQU6dBa
  39.                         E+qzmL7lHZQf1UArfb+QDYH2XsFCk9Pjv0xdP+PGwf8HqHhfPLctvus5JL+LXp0X
  40.                         68eWKQCs1CrL8cUMwcELlW/mR1lKnJL1WgM1Bns9ZF1ha6egG539ruzQjItF6MHB
  41.                         xAEt55nXfs+mjV1p7qrcmR8jIdByR9C36T21r+8pKA==
  42.                         -----END CERTIFICATE-----
  43.                         """
  44.             }
  45.         };
  46.         // 申请全新account
  47.         o.NewAccount(new string[] { "mailto:test11@xxx.com" });
  48.         // 执行全套流程
  49.         var cert = await initer.CreateCertificateAsync(o);
  50.         return cert.ExportPem();
  51.     }
  52. }
复制代码
默认情况下三种验证方式:
http

如在 asp.net core 中使用,默认已经添加了 app.Map("/.well-known/acme-challenge" 路由处理, 如要申请公网上权威认证的证书,请将 /.well-known/acme-challenge 路由暴露在公网让acme服务器可以访问
其次由于默认实现没有持久化和分布式处理验证信息,重启和多实例都会有问题,如有需求可以替换IHttpChallengeResponseStore实现以达到效果
  1. public interface IHttpChallengeResponseStore
  2. {
  3.     Task AddChallengeResponseAsync(string token, string keyAuth, CancellationToken cancellationToken);
  4.     Task<string> GetChallengeResponse(string token, CancellationToken cancellationToken);
  5.     Task RemoveChallengeResponseAsync(string token, CancellationToken cancellationToken);
  6. }
复制代码
dns

由于不同服务商有各自的api,所以默认没有实现,该功能其实无效,如需使用,请实现 IDnsChallengeStore
  1. public interface IDnsChallengeStore
  2. {
  3.     Task AddTxtRecordAsync(string acmeDomain, string dnsTxt, CancellationToken cancellationToken);
  4.     Task RemoveTxtRecordAsync(string acmeDomain, string dnsTxt, CancellationToken cancellationToken);
  5. }
复制代码
tls

个人并不推荐使用tls验证方式,其由于验证自签证书和正式证书都会在 tls 层,运行时不停机重新申请对于tls管理还是有些挑战的
如想尝试可以实现ITlsAlpnChallengeStore (默认在 asp.net core 的实现并不能支持过滤验证自签证书只用于acme服务器请求)
  1. public interface ITlsAlpnChallengeStore
  2. {
  3.     Task AddChallengeAsync(string domainName, X509Certificate2 cert, CancellationToken cancellationToken);
  4.     Task RemoveChallengeAsync(string domainName, X509Certificate2 cert, CancellationToken cancellationToken);
  5. }
复制代码
底层 client

如需直接使用原始 acme 协议client,可参考如下
starup
  1. var builder = WebApplication.CreateBuilder(args);
  2. builder.Services.AddControllers();
  3. builder.Services.AddACME(c =>
  4. {
  5.     c.HttpClientConfig = new VKProxy.Config.HttpClientConfig()
  6.     {
  7.         DangerousAcceptAnyServerCertificate = true
  8.     };
  9. });
复制代码
use
  1. var context = services.BuildServiceProvider().GetRequiredService<IAcmeContext>();
  2. await context.InitAsync(new Uri("https://127.0.0.1:14000/dir"), cancellationToken);
  3. var account = await context.NewAccountAsync(new string[] { "mailto:xxx@xxx.com" }, true, KeyAlgorithm.RS256.NewKey());
  4. var order = await context.NewOrderAsync(new string[] { "test.com" });
  5. var aus = order.GetAuthorizationsAsync().ToBlockingEnumerable().ToArray();
  6. var a = aus.First();
  7. var b = await a.HttpAsync();
  8. var c = await b.ValidateAsync();
  9. Key privateKey = KeyAlgorithm.RS256.NewKey();
  10. var csrInfo = new CsrInfo
  11. {
  12.     CommonName = "test.com",
  13. };
  14. order = await context.FinalizeAsync(csr, key, cancellationToken);
  15. var acmeCert = await order.DownloadAsync();
  16. var pfxBuilder = acmeCert.ToPfx(privateKey);
  17. if (!string.IsNullOrWhiteSpace(Args.AdditionalIssuer) && File.Exists(Args.AdditionalIssuer))
  18. {
  19.     pfxBuilder.AddIssuer(File.ReadAllBytes(Args.AdditionalIssuer));
  20. }
  21. var pfx = pfxBuilder.Build("HTTPS Cert - " + Args.Domain, string.Empty);
  22. var r = X509CertificateLoader.LoadPkcs12(pfx, string.Empty, X509KeyStorageFlags.Exportable);
复制代码
ui

在 VKProxy管理站点的 ui sni 里面添加了 简单的 http 验证方式的acme证书界面配置 如下图
(当然使用前提得是 暴露 xxx域名/.well-known/acme-challenge 接口到公网,这样公网acme 才能验证)
1.jpeg

专职ACME管理程序

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

  • acme.sh 脚本工具,稍显复杂
  • certimate 目前只支持 dns 验证,不过dns服务商/通知/证书部署都支持非常全面
所以珠玉在前,大家可以直接尽情使用
至于用c#再做一个,多半没有啥人关注,不信,和大家打个赌:评论留言说期望有个c#版的超过 30 条,就搞一个 (一条评论都没有,信不信,哈哈哈)
VKProxy 是使用c#开发的基于 Kestrel 实现 L4/L7的代理(感兴趣的同学烦请点个github小赞赞呢)

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册