Administrator
Published on 2025-10-27 / 1 Visits
0

如何使用 acme4j 库进行证书申请

#AI

使用 acme4j 库进行证书申请主要涉及以下步骤,结合官方示例和文档整理如下:

1. 准备工作

  • 依赖引入:在项目中添加 acme4j-client 依赖(Maven 示例):

    <dependency>
        <groupId>org.shredzone.acme4j</groupId>
        <artifactId>acme4j-client</artifactId>
        <version>最新版本</version>
    </dependency>
    

    最新版本可从 Maven Central 获取。

  • 环境要求:JRE 17 或更高版本,以及依赖库(如 Bouncy Castle、jose4j 等,会自动引入)。

2. 核心步骤

以申请 TLS 证书为例,关键流程如下:

步骤 1:创建 ACME 会话(Session)

会话用于与 ACME 服务器(如 Let's Encrypt)建立连接,需指定服务器 URI(目录地址):

import org.shredzone.acme4j.Session;

// ACME 服务器目录 URI(Let's Encrypt 测试环境示例)
String caUri = "https://acme-staging-v02.api.letsencrypt.org/directory";
Session session = new Session(caUri);

步骤 2:创建或加载账户(Account)

ACME 协议要求通过账户进行认证,需使用密钥对标识账户:

import org.shredzone.acme4j.Account;
import org.shredzone.acme4j.Login;
import java.security.KeyPair;
import java.security.KeyPairGenerator;

// 生成或加载账户密钥对(建议持久化存储,避免重复注册)
KeyPair userKeyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();

// 注册新账户(若已有账户,可通过账户 URL 和密钥登录)
Account account = new AccountBuilder()
    .agreeToTermsOfService()
    .create(session, userKeyPair);

// 登录账户(后续操作需通过 Login 实例)
Login login = session.login(account.getLocation(), userKeyPair);

步骤 3:创建证书订单(Order)

指定需要申请证书的域名(支持多个域名):

import org.shredzone.acme4j.Order;
import java.util.Collections;

Order order = login.newOrder()
    .domains("example.com", "www.example.com") // 待认证的域名
    .create(); // 提交订单

步骤 4:完成域名验证(Authorization)

ACME 服务器会为每个域名生成验证挑战(Challenge),需证明对域名的控制权。支持 http-01dns-01tls-alpn-01 等方式,以下为 http-01 示例:

import org.shredzone.acme4j.Authorization;
import org.shredzone.acme4j.challenge.Http01Challenge;

for (Authorization auth : order.getAuthorizations()) {
    // 获取 http-01 挑战(若不支持,可尝试其他挑战类型)
    Http01Challenge challenge = auth.findChallenge(Http01Challenge.class)
        .orElseThrow(() -> new AcmeException("http-01 challenge not supported"));

    // 准备验证资源:需在域名的 .well-known/acme-challenge/ 路径下放置指定内容
    String token = challenge.getToken();
    String content = challenge.getAuthorization();

    // TODO:将 content 写入文件 <webroot>/.well-known/acme-challenge/<token>
    // 确保通过 http://<域名>/.well-known/acme-challenge/<token> 可访问到 content

    // 通知 ACME 服务器开始验证
    challenge.trigger();

    // 等待验证完成
    auth.waitUntilValid(30, TimeUnit.SECONDS);
}

步骤 5:生成并提交 CSR(证书签名请求)

验证通过后,生成域名密钥对和 CSR,提交给 ACME 服务器:

import org.shredzone.acme4j.Certificate;
import java.security.KeyPair;
import java.security.cert.X509Certificate;

// 生成域名密钥对(用于证书,与账户密钥对分离)
KeyPair domainKeyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();

// 提交 CSR 完成订单
order.execute(domainKeyPair);

// 等待订单完成
order.waitForCompletion(30, TimeUnit.SECONDS);

// 获取颁发的证书
Certificate certificate = order.getCertificate();
X509Certificate cert = certificate.getCertificate(); // 终端实体证书
List<X509Certificate> chain = certificate.getCertificateChain(); // 完整证书链(含中间证书)

步骤 6:保存证书

将证书和密钥对持久化存储,用于服务器配置:

import org.shredzone.acme4j.util.AcmeUtils;
import java.io.FileWriter;

// 保存证书链(PEM 格式)
try (FileWriter fw = new FileWriter("cert-chain.pem")) {
    certificate.writeCertificate(fw);
}

// 保存域名私钥(PEM 格式)
try (FileWriter fw = new FileWriter("domain-key.pem")) {
    AcmeUtils.writeKeyPair(domainKeyPair, fw);
}

3. 示例代码参考

官方提供了完整示例 org.shredzone.acme4j.example.ClientTest(位于 acme4j-example 模块),涵盖从账户创建到证书获取的全流程,可直接参考实现细节。

4. 注意事项

  • 环境选择:测试阶段建议使用 Let's Encrypt 测试环境(https://acme-staging-v02.api.letsencrypt.org/directory),避免生产环境速率限制。
  • 挑战类型:根据部署场景选择合适的验证方式(如 DNS 验证适合无法暴露 HTTP 服务的场景)。
  • 证书续期:ACME 证书通常有效期为 90 天,需定期续期,可通过 order.getCertificate().getNotAfter() 检查过期时间,提前重新申请。

详细文档可参考 官方使用指南