之前一直使用阿里云的免费SSL证书。由于有效期的调整,原本每年20张免费的就有点不够用了(原来一个域名一年占用1张额度,有效期调整后,一年需要占用4张额度)。所以就走上了自己制作免费SSL证书的道路
搜索资料,看起来比较多的方式是用ACME,也有很多自动化的脚本去更新证书,本着自己动手试试的原则,就多查了一些资料
用的比较多的服务商有Let's Encrypt、ZeroSSL等,有些也只是免费几张。最终选择了Let's Encrypt(原因不必多说),另外还可以申请泛域名
https://letsencrypt.org/getting-started/上比较推荐的方式是 Certbot ACME client
。而我想用代码实现一下
ACME Client Implementations提供了很多client方式和lib方式。我选择了自己熟悉的语言,用https://github.com/shred/acme4j
ACME4J DOCS:https://shredzone.org/maven/acme4j/index.html
Maven仓库坐标
<dependency>
<groupId>org.shredzone.acme4j</groupId>
<artifactId>acme4j-client</artifactId>
<version>3.2.1</version>
</dependency>
注册Let's Encrypt账号
首先,需要有一个Let's Encrypt
的账号,才能获取公网可认证的证书。注册账号需要有一个根证书
openssl genrsa -traditional -out root.key 2048
至于为什么加上-traditional
,可看之前的说明:macos下openssl 生成pkcs1格式rsa密钥
KeyPair adminKeyPair = KeyPairUtils.readKeyPair(FileUtil.getReader(getFilePath("root.key"), CharsetUtil.UTF_8));
Session session = new Session("acme://letsencrypt.org");
Account account = new AccountBuilder()
.addContact("mailto:lsw1991abc@163.com")
.agreeToTermsOfService()
.useKeyPair(adminKeyPair)
.create(session);
System.out.println(JSONUtil.toJsonStr(account));
这里你会得到账户地址信息,xxx 是账户ID
{"location":"https://acme-v02.api.letsencrypt.org/acme/acct/xxx"}
创建订单
Login login = new Login(URLUtil.url(accountLocation), adminKeyPair, session);
Order order = login.newOrder().domains("*.vimo.cloud").create();
System.out.println(JSONUtil.toJsonStr(order));
这里你会得到订单信息,yyy 是订单ID
{"location":"https://acme-v02.api.letsencrypt.org/acme/order/xxx/yyy"}
获取验证信息
是为了验证所有权的。获取到的验证信息,需要配置到域名解析或者用服务器文件的方式。
https://shredzone.org/maven/acme4j/usage/order.html#challenge
https://shredzone.org/maven/acme4j/challenge/index.html
我使用了DNS的方式
Login login = ...;
Order bindOrder = login.bindOrder(URLUtil.url(orderLocation));
System.out.println(JSONUtil.toJsonStr(bindOrder));
Authorization auth = bindOrder.getAuthorizations().get(0);
Dns01Challenge challenge = auth.findChallenge(Dns01Challenge.class).get();
String digest = challenge.getDigest();
System.out.println(digest);
这里会的到一个字符串,需要配置一个TXT
的域名解析_acme-challenge.${domain}
参考:https://shredzone.org/maven/acme4j/challenge/dns-01.html
验证配置
配置好DNS
之后,让Let's Encrypt
验证配置
Login login = ...;
Order bindOrder = login.bindOrder(URLUtil.url(orderLocation));
System.out.println(JSONUtil.toJsonStr(bindOrder));
Authorization auth = bindOrder.getAuthorizations().get(0);
Dns01Challenge challenge = auth.findChallenge(Dns01Challenge.class).get();
challenge.trigger();
查询验证结果
Login login = ...;
Order bindOrder = login.bindOrder(URLUtil.url(orderLocation));
System.out.println(JSONUtil.toJsonStr(bindOrder));
Authorization auth = bindOrder.getAuthorizations().get(0);
auth.fetch();
System.out.println(auth.getStatus());
System.out.println(bindOrder.getStatus());
如果验证成功,auth
的结果是VALID
,order
的结果是READY
。这样就可以提交域名证书信息了
注册域名信息
和账号的根证书类似,生成一个域名的根证书
openssl genrsa -traditional -out vimo.cloud.key 2048
Login login = ...;
Order bindOrder = login.bindOrder(URLUtil.url(orderLocation));
KeyPair domainKeyPair = KeyPairUtils.readKeyPair(FileUtil.getReader(getFilePath("vimo.cloud.key"), CharsetUtil.UTF_8));
bindOrder.execute(domainKeyPair, csr -> {
csr.setCountry("CN");
csr.setState("SD");
csr.setLocality("QD");
csr.setOrganization("Vimo");
csr.setOrganizationalUnit("Vimo");
});
System.out.println(JSONUtil.toJsonStr(bindOrder));
bindOrder.fetch();
System.out.println(bindOrder.getStatus());
order
的结果是VALID
后,就可以下载证书信息了
下载SSL证书
Login login = ...;
Order bindOrder = login.bindOrder(URLUtil.url(orderLocation));
Writer writer = FileUtil.getWriter(getFilePath("vimo.cloud.pem"), CharsetUtil.UTF_8, true);
bindOrder.getCertificate().writeCertificate(writer);
writer.flush();
完结
到这里,就可以拿着域名的KEY和下载的PEM,去Nginx配置SSL证书了
其他说明
以上代码示例中,部分工具类依赖于Hutool
感谢大家的阅读, 如有疑问可以加我微信
评论已关闭