惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
云风的 BLOG
云风的 BLOG
美团技术团队
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
爱范儿
爱范儿
Stack Overflow Blog
Stack Overflow Blog
WordPress大学
WordPress大学
GbyAI
GbyAI
雷峰网
雷峰网
P
Proofpoint News Feed
IT之家
IT之家
人人都是产品经理
人人都是产品经理
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
aimingoo的专栏
aimingoo的专栏
小众软件
小众软件
T
The Blog of Author Tim Ferriss
月光博客
月光博客
V
Visual Studio Blog
L
LINUX DO - 热门话题
L
Lohrmann on Cybersecurity
T
Troy Hunt's Blog
Project Zero
Project Zero
U
Unit 42
T
Tor Project blog
Scott Helme
Scott Helme
L
LINUX DO - 最新话题
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
I
InfoQ
Cloudbric
Cloudbric
P
Proofpoint News Feed
The Cloudflare Blog
H
Heimdal Security Blog
Google DeepMind News
Google DeepMind News
The GitHub Blog
The GitHub Blog
Attack and Defense Labs
Attack and Defense Labs
有赞技术团队
有赞技术团队
T
Threat Research - Cisco Blogs
T
The Exploit Database - CXSecurity.com
J
Java Code Geeks
博客园 - 【当耐特】
Security Latest
Security Latest
The Register - Security
The Register - Security
F
Fortinet All Blogs
I
Intezer
H
Hackread – Cybersecurity News, Data Breaches, AI and More
MongoDB | Blog
MongoDB | Blog
N
Netflix TechBlog - Medium
NISL@THU
NISL@THU
T
Tenable Blog

博客园 - victor.x.qu

MCP 企业托管授权(Enterprise-Managed Authorization) 从状态机到运行时:聊聊 .NET 11 的 Runtime Async 和老 Async/Await 到底差在哪 如何用c# 做 mcp/ChatGPT app 春节期间浅试了 ai 视频生成 举个栗子:做个AI Gateway demo 2025个人总结 实现 json path 来评估函数式解析器的损耗 VKProxy 集成 OpenTelemetry c# ACME client 留个VKProxy性能测试记录 VKProxy新增一些功能 http流量镜像 VKProxy新增CORS设置和http响应缓存 VKProxy新增速率限制功能 VKProxy已提供命令行工具,镜像和简单的ui 已经在为VKProxy写UI配置站点和文档了 莫想到有一天得重新写个 etcd client 如何使用 websocket 完成 socks5 网络穿透 如何基于 Kestrel 实现 socks5 代理 记录一下 简单udp和sni 代理 done 终于写完轮子一部分:tcp代理 了,记录一下
c# ACME client (补充)
victor.x.qu · 2025-07-30 · via 博客园 - victor.x.qu

上一篇 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管理程序

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

现成的很多,比如

  • acme.sh 脚本工具,稍显复杂
  • certimate 目前只支持 dns 验证,不过dns服务商/通知/证书部署都支持非常全面

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

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

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