接下来会简述证书生成,ssl端口开放
##下载SSL证书生成器
git clone https://github.com/Berico-Technologies/CMF-AMQP-Configuration.git
cd CMF-AMQP-Configuration/ssl
配置当前目录下的openssl.cnf,基本上不需要改动,证书默认生成后的有效期是一年,如果需要延长可以修改default_days = 365.
该脚本是会在当前目录下生成一个ca目录,里面存放着一些证书颁发机构信息,和已经签发的证书记录
sh setup_ca.sh RabbitSSL
RabbitSSL: 签发机构名称,自定义。
该脚本是会在当前目录下生成一个server目录,里面存放着服务端的公钥,和私钥文件。该文件生成后会在ca目录文件中有签发记录。
sh make_server_cert.sh rabbit-server rabbit
rabbit-server: 生成的密钥前缀名,自定义。
rabbit: 访问该密钥的密码,自定义。
该脚本是会在当前目录下生成一个client目录,里面存放着客户端的公钥,和私钥文件。该文件生成后会在ca目录文件中有签发记录。
sh create_client_cert.sh rabbit-client rabbit
rabbit-client: 生成的密钥前缀名,自定义。
rabbit: 访问该密钥的密码,自定义。
不同的语言操作方式不一样,这里我们使用的是java语言,使用java的keytool工具生成,首先确保已经安装java并且在环境变量中已经配置
keytool -import -alias rabbit-server -file server/rabbit-server.cert.pem -keystore rabbitStore -storepass rabbit
用服务端的公钥生成证书,这个步骤很关键,该证书用于客户端和服务端通信。
server/rabbit-server.cert.pem: 上个步骤已经生成好的服务端公钥
如果需要删除已经生成的证书,可执行以下命令
keytool -delete -alias rabbit-server -keystore rabbitStore -storepass rabbit
如果你跟着文章一步一步做到这,说明你离成功就只差最后一步了,接下来检查我们前几个步骤的结果,经过几个步骤我们在CMF-AMQP-Configuration/ssl/目录下生成了:
ca
server
client
rabbitStore 证书
如果以上的几个目录和这个证书都存在,说明该大步骤已经完美结束。接下来进入最关键的一步了。
接下来的步骤就比较关键了,需要用到我们上面所有生成的文件,将它们配置到RabbitMQ的config文件中.
将ca,server,client,rabbitStore拷贝到/etc/rabbitmq目录下
cp -r ca server client rabbitStore /etc/rabbitmq/ssl
如果/etc/rabbitmq目录下没有rabbitmq.config,创建该文件。
vim /etc/rabbitmq/rabbitmq.config
将以下配置复制到rabbitmq.config中
%%Disable SSLv3.0 and TLSv1.0 support.
[
{ssl, [{versions, ['tlsv1.2', 'tlsv1.1']}]},
{rabbit, [
{tcp_listeners, [5672]},
{ssl_listeners, [5671]},
{ssl_options, [{cacertfile,"/etc/rabbitmq/ssl/ca/cacert.pem"},
{certfile,"/etc/rabbitmq/ssl/server/rabbit-server.cert.pem"},
{keyfile,"/etc/rabbitmq/ssl/server/rabbit-server.key.pem"},
{verify, verify_peer},
{ciphers, ["ECDHE-ECDSA-AES256-GCM-SHA384","ECDHE-RSA-AES256-GCM-SHA384",
"ECDHE-ECDSA-AES256-SHA384","ECDHE-RSA-AES256-SHA384", "ECDHE-ECDSA-DES-CBC3-SHA",
"ECDH-ECDSA-AES256-GCM-SHA384","ECDH-RSA-AES256-GCM-SHA384","ECDH-ECDSA-AES256-SHA384",
"ECDH-RSA-AES256-SHA384","DHE-DSS-AES256-GCM-SHA384","DHE-DSS-AES256-SHA256",
"AES256-GCM-SHA384","AES256-SHA256","ECDHE-ECDSA-AES128-GCM-SHA256",
"ECDHE-RSA-AES128-GCM-SHA256","ECDHE-ECDSA-AES128-SHA256","ECDHE-RSA-AES128-SHA256",
"ECDH-ECDSA-AES128-GCM-SHA256","ECDH-RSA-AES128-GCM-SHA256","ECDH-ECDSA-AES128-SHA256",
"ECDH-RSA-AES128-SHA256","DHE-DSS-AES128-GCM-SHA256","DHE-DSS-AES128-SHA256",
"AES128-GCM-SHA256","AES128-SHA256","ECDHE-ECDSA-AES256-SHA",
"ECDHE-RSA-AES256-SHA","DHE-DSS-AES256-SHA","ECDH-ECDSA-AES256-SHA",
"ECDH-RSA-AES256-SHA","AES256-SHA","ECDHE-ECDSA-AES128-SHA",
"ECDHE-RSA-AES128-SHA","DHE-DSS-AES128-SHA","ECDH-ECDSA-AES128-SHA",
"ECDH-RSA-AES128-SHA","AES128-SHA"]},
{honor_cipher_order, true},
{fail_if_no_peer_cert, true},
{versions, ['tlsv1.2', 'tlsv1.1']}
]},
{auth_mechanisms,['PLAIN', 'AMQPLAIN', 'EXTERNAL']}
]}
].
复制代码在以上配置中我们将证书颁发机构以及服务端的公钥和私钥配置进去了。client目录和rabbitStore是给客户端使用的,我们使用5671端口作为我们ssl通信端口,5672保持不变,继续为内网tcp提供服务
以下命令是参考,每个人服务安装方式不一样,总之将它重启就可以
systemctl restart rabbit-server.service
查看rabbitmq日志输出
less /var/log/rabbitmq/xxx.log
log显示成这样,代表ssl开启成功
或者访问网页查看5671是否开启ssl
如上,ssl服务已经开启.最后一步代码测试
编写Java代码测试证书授权
将前面还没有用到的client目录和rabbitStore证书拷贝的项目中,放入到resource目录下,执行以下代码做测试;
public class SslReceiver {
public static void main(String[] args) throws TimeoutException {
String classpath = SslReceiver.class.getResource("/").getPath();
//证书密码
char[] sslPwd = "rabbit".toCharArray();
//读取client密钥,和rabbitStore证书
try (InputStream sslCardStream = new FileInputStream(classpath + "keyStore/client/rabbit-client.keycert.p12");
InputStream rabbitStoreStream = new FileInputStream(classpath + "keyStore/rabbitStore")) {
//加载秘钥
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(sslCardStream, sslPwd);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(ks, sslPwd);
//读取授权证书,只含有服务端的公钥
KeyStore jks = KeyStore.getInstance("JKS");
jks.load(rabbitStoreStream, sslPwd);
TrustManagerFactory keyStoreManager = TrustManagerFactory.getInstance("SunX509");
keyStoreManager.init(jks);
SSLContext context = SSLContext.getInstance("TLSv1.2");
context.init(keyManagerFactory.getKeyManagers(), keyStoreManager.getTrustManagers(), null);
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("rabbitTest");
factory.setPassword("123456");
factory.setHost("127.0.0.1");
factory.setPort(5671);
factory.setAutomaticRecoveryEnabled(true);
//设置sslContext
factory.useSslProtocol(context);
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("rabbitmq-queue", false, true, true, null); //rabbitmq-queue是rabbitmq队列
channel.basicPublish("", "rabbitmq-queue", null, "Test,Test".getBytes());
GetResponse chResponse = channel.basicGet("rabbitmq-queue", false);
if (chResponse == null){
System.out.println("No message retrieved");
}else {
byte[] body = chResponse.getBody();
System.out.println("Recieved: " + new String(body));
}
channel.close();
connection.close();
} catch (KeyStoreException | UnrecoverableKeyException | KeyManagementException
| CertificateException | NoSuchAlgorithmException | IOException e) {
log.error("SSL证书解析失败", e);
}
}
}
复制代码如果收到了那条消息,到此ssl结束,如果有异常信息,请在启动jvm中传递参数
-Djavax.net.debug=all,查看连接过程,在结合服务端/var/log/rabbitmq下的log一起分析。