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

推荐订阅源

博客园 - 三生石上(FineUI控件)
Martin Fowler
Martin Fowler
月光博客
月光博客
AI
AI
B
Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
C
CXSECURITY Database RSS Feed - CXSecurity.com
WordPress大学
WordPress大学
GbyAI
GbyAI
L
Lohrmann on Cybersecurity
O
OpenAI News
Schneier on Security
Schneier on Security
P
Palo Alto Networks Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
Troy Hunt's Blog
V2EX - 技术
V2EX - 技术
W
WeLiveSecurity
L
LINUX DO - 最新话题
人人都是产品经理
人人都是产品经理
S
Security Affairs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
A
Arctic Wolf
Recorded Future
Recorded Future
Microsoft Security Blog
Microsoft Security Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
G
GRAHAM CLULEY
N
Netflix TechBlog - Medium
TaoSecurity Blog
TaoSecurity Blog
C
Check Point Blog
Scott Helme
Scott Helme
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Apple Machine Learning Research
Apple Machine Learning Research
PCI Perspectives
PCI Perspectives
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Vercel News
Vercel News
The Hacker News
The Hacker News
Y
Y Combinator Blog
Latest news
Latest news
SecWiki News
SecWiki News
Hugging Face - Blog
Hugging Face - Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Google Online Security Blog
Google Online Security Blog
Webroot Blog
Webroot Blog
Google DeepMind News
Google DeepMind News
Recent Commits to openclaw:main
Recent Commits to openclaw:main
C
Cisco Blogs
博客园_首页
H
Hackread – Cybersecurity News, Data Breaches, AI and More
宝玉的分享
宝玉的分享
L
LINUX DO - 热门话题

博客园 - 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小赞赞呢)