zookeeper ssl 双向认证
经过验证,可在本地localhost 机器上完成服务端-客户端 ssl 双向认证的 脚本(Generate by Google Gimini)
demo :https://github.com/PromiseChan/PromiseChan.github.io/releases/download/zk-ssl-demo-all/zk-ssl.zip
在 localhost 机器上使用 Java KeyStore(JKS) 创建服务端和客户端双向认证的 X.509 证书,主要分为以下几个步骤:
- 创建根证书颁发机构(CA):CA 负责签发服务端和客户端证书。
- 为服务端创建密钥库(keystore)和证书签名请求(CSR): 服务端使用自己的私钥生成证书请求。
- 使用 CA 签署服务端证书:CA 验证并签署服务端的 CSR,生成服务端证书。
- 为客户端创建密钥库(keystore)和证书签名请求(CSR): 客户端也使用自己的私钥生成证书请求。
- 使用 CA 签署客户端证书:CA 验证并签署客户端的 CSR,生成客户端证书。
- 导入所有证书到相应的信任库(truststore): 服务端和客户端都需要信任对方的证书。
以下是具体的命令行步骤,您可以使用 keytool 这个 Java 自带的工具来完成所有操作。
第一步:创建根证书颁发机构(CA)
生成 CA 的密钥库和自签名证书
1
keytool -genkeypair -alias root-ca -keyalg RSA -keysize 2048 -storetype JKS -keystore ca-keystore.jks -dname "CN=Root CA, OU=Security, O=My Company, L=Beijing, ST=Beijing, C=CN" -validity 3650
-alias root-ca:给这个密钥库条目起一个别名。-keyalg RSA:使用 RSA 算法。-keysize 2048:密钥长度为 2048 位。-storetype JKS:指定密钥库类型为 JKS。-keystore ca-keystore.jks:生成的密钥库文件名为ca-keystore.jks。-dname "...":证书所有者信息,CN (Common Name) 建议填写组织名称或身份。-validity 3650:证书有效期,单位为天,这里是 10 年。
将 CA 的公钥证书导出
1
keytool -exportcert -alias root-ca -keystore ca-keystore.jks -file ca.cer
-file ca.cer:导出的证书文件,后缀通常为.cer或.crt。
第二步:创建服务端证书
生成服务端的密钥库和私钥
1
keytool -genkeypair -alias server -keyalg RSA -keysize 2048 -storetype JKS -keystore server-keystore.jks -dname "CN=localhost, OU=Server, O=My Company, L=Beijing, ST=Beijing, C=CN"
- 注意:CN 必须是 localhost 或 127.0.0.1,以便在本地运行时匹配主机名。
生成服务端的证书签名请求(CSR)
1
keytool -certreq -alias server -keystore server-keystore.jks -file server.csr
-file server.csr:生成的 CSR 文件。
第三步:使用 CA 签署服务端证书
使用 CA 签署 CSR
1
keytool -gencert -alias root-ca -keystore ca-keystore.jks -infile server.csr -outfile server.cer -ext san=dns:localhost,ip:127.0.0.1 -validity 365 -rfc
-infile server.csr:输入的 CSR 文件。-outfile server.cer:生成的服务端证书文件。-ext san=dns:localhost,ip:127.0.0.1:添加 Subject Alternative Name (SAN) 扩展,这是现代浏览器和客户端强制要求的,用于指定可信的主机名,这里同时支持localhost和127.0.0.1。-validity 365:证书有效期,通常比 CA 证书短。
将 CA 证书导入服务端的密钥库
1
keytool -importcert -alias root-ca -file ca.cer -keystore server-keystore.jks -noprompt
- 这步是告诉服务端,它需要信任 CA,以便验证客户端证书。
将服务端自己的证书链导入其密钥库
1
keytool -importcert -alias server -keystore server-keystore.jks -file server.cer -noprompt
- 将刚刚签署的服务端证书导入到自己的密钥库中,使其成为一个完整的私钥-公钥对。
第四步:创建客户端证书
生成客户端的密钥库和私钥
1
keytool -genkeypair -alias client -keyalg RSA -keysize 2048 -storetype JKS -keystore client-keystore.jks -dname "CN=localhost, OU=Client, O=My Company, L=Beijing, ST=Beijing, C=CN"
-keystore client-keystore.jks:生成的客户端密钥库。
生成客户端的证书签名请求(CSR)
1
keytool -certreq -alias client -keystore client-keystore.jks -file client.csr
第五步:使用 CA 签署客户端证书
使用 CA 签署 CSR
1
keytool -gencert -alias root-ca -keystore ca-keystore.jks -infile client.csr -outfile client.cer -validity 365 -rfc
将 CA 证书导入客户端的密钥库
1
keytool -importcert -alias root-ca -file ca.cer -keystore client-keystore.jks -noprompt
- 这步是告诉客户端,它需要信任 CA,以便验证服务端证书。
将客户端自己的证书链导入其密钥库
1
keytool -importcert -alias client -keystore client-keystore.jks -file client.cer -noprompt
第六步:创建信任库
这一步可选,但为了更好的实践,通常会为服务端和客户端单独创建信任库(truststore),只存放信任的公钥。
为服务端创建信任库
1
keytool -importcert -alias client-trust -file client.cer -keystore server-truststore.jks
- 将客户端的公钥证书导入到服务端的信任库中。
为客户端创建信任库
1
keytool -importcert -alias server-trust -file server.cer -keystore client-truststore.jks
- 将服务端的公钥证书导入到客户端的信任库中。
总结
现在,您就拥有了以下几个关键文件:
- **
ca-keystore.jks**:CA 的密钥库(包含私钥和证书)。 - **
ca.cer**:CA 的公钥证书。 - **
server-keystore.jks**:服务端的密钥库(包含服务端的私钥和证书)。 - **
client-keystore.jks**:客户端的密钥库(包含客户端的私钥和证书)。 - **
server-truststore.jks**(可选):服务端的信任库,用于存放客户端的证书。 - **
client-truststore.jks**(可选):客户端的信任库,用于存放服务端的证书。
在您的 Java 代码中,您需要配置相应的 JKS 文件路径和密码,然后传递给 SSLContext。
希望这些步骤能帮助您顺利配置双向认证!如果您在配置过程中遇到任何问题,或者想了解如何在代码中集成这些证书,随时可以提问。
zoo.cfg 配置
secureClientPort=2281
ssl.quorum=false
ssl.clientAuth=require
serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory
ssl.keyStore.location=/Users/nuopromise/Desktop/code/zookeeper/apache-zookeeper-3.8.4-bin/zookeeper-ssl/tmp/server-keystore.jks
ssl.keyStore.password=password
ssl.trustStore.location=/Users/nuopromise/Desktop/code/zookeeper/apache-zookeeper-3.8.4-bin/zookeeper-ssl/tmp/server-truststore.jks
ssl.trustStore.password=password
客户端配置
export CLIENT_JVMFLAGS=”-Dzookeeper.clientCnxnSocket=org.apache.zookeeper.ClientCnxnSocketNetty -Dzookeeper.ssl.trustStore.location=/Users/nuopromise/Desktop/code/zookeeper/apache-zookeeper-3.8.4-bin/zookeeper-ssl/tmp/client-truststore.jks -Dzookeeper.ssl.trustStore.password=password -Dzookeeper.ssl.keyStore.location=/Users/nuopromise/Desktop/code/zookeeper/apache-zookeeper-3.8.4-bin/zookeeper-ssl/tmp/client-keystore.jks -Dzookeeper.ssl.keyStore.password=password -Dzookeeper.client.secure=true”
shell脚本
./zkServer.sh status
java 客户端
package org.example;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.util.List;
public class Main {
public static void main(String[] args) {
// export CLIENT_JVMFLAGS="-Dzookeeper.clientCnxnSocket=org.apache.zookeeper.ClientCnxnSocketNetty -Dzookeeper.ssl.trustStore.location=/Users/nuopromise/Desktop/code/zookeeper/apache-zookeeper-3.8.4-bin/zookeeper-ssl/tmp/client-truststore.jks -Dzookeeper.ssl.trustStore.password=password -Dzookeeper.ssl.keyStore.location=/Users/nuopromise/Desktop/code/zookeeper/apache-zookeeper-3.8.4-bin/zookeeper-ssl/tmp/client-keystore.jks -Dzookeeper.ssl.keyStore.password=password -Dzookeeper.client.secure=true"
// 1. 设置 SSL/TLS 相关的系统属性
System.setProperty("zookeeper.client.secure", "true");
System.setProperty("zookeeper.clientCnxnSocket", "org.apache.zookeeper.ClientCnxnSocketNetty");
System.setProperty("zookeeper.ssl.trustStore.location", "/Users/nuopromise/Desktop/code/zookeeper/apache-zookeeper-3.8.4-bin/zookeeper-ssl/tmp/client-truststore.jks");
System.setProperty("zookeeper.ssl.trustStore.password", "password");
// 如果服务端配置了双向认证 (clientAuth=need),则需要配置客户端密钥库
System.setProperty("zookeeper.ssl.keyStore.location", "/Users/nuopromise/Desktop/code/zookeeper/apache-zookeeper-3.8.4-bin/zookeeper-ssl/tmp/client-keystore.jks");
System.setProperty("zookeeper.ssl.keyStore.password", "password");
// 2. 指定 ZooKeeper 服务器地址和端口
String connectString = "localhost:2281"; // 这里的端口是您在 zoo.cfg 中设置的 secureClientPort
int sessionTimeout = 5000; // 会话超时时间,单位毫秒
try {
// 3. 创建 ZooKeeper 客户端实例
ZooKeeper zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("start watching ...");
}
});
System.out.println("成功连接到 ZooKeeper!");
System.out.println("打印 子节点 :");
// 4. 执行一些操作(例如 ls 查看子节点 )
// 在实际应用中,您可以在这里进行各种 ZooKeeper 操作
List<String> childrenList = zk.getChildren("/",false);
for (String childPath:childrenList){
System.out.println(" / 下的子节点: "+childPath);
}
} catch (Exception e) {
System.err.println("连接 ZooKeeper 失败!");
e.printStackTrace();
}
}
}
java 验证结果
SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder”.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
成功连接到 ZooKeeper!
打印 子节点 :
SLF4J: Failed to load class “org.slf4j.impl.StaticMDCBinder”.
SLF4J: Defaulting to no-operation MDCAdapter implementation.
SLF4J: See http://www.slf4j.org/codes.html#no_static_mdc_binder for further details.
start watching …
/ 下的子节点: zookeeper