使用 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-01、dns-01、tls-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()检查过期时间,提前重新申请。
详细文档可参考 官方使用指南。