You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java 6环境下RabbitMQ SSL连接失败问题求助

Java 1.6环境连接RabbitMQ遇insufficient_security(71)错误排查

场景说明

老旧项目必须运行在Java 1.6环境,连接RabbitMQ时触发TLS致命错误,错误代码为insufficient_security(71)。

实现代码

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import org.apache.log4j.Logger;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.Security;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class RabbitMqListenerTest {

    private static Logger log = Logger.getLogger(RabbitMqListenerTest.class);;

    private final Consumer<String> consumer;
    private final RabbitMqConnectionConfig rabbitMqConnectionConfig;
    
    static {
        Security.addProvider(new BouncyCastleProvider());
        Security.addProvider(new BouncyCastleJsseProvider());
    }

    public RabbitMqListenerTest(Consumer<String> consumer, RabbitMqConnectionConfig rabbitMqConnectionConfig) {
        this.consumer = consumer;
        this.rabbitMqConnectionConfig = rabbitMqConnectionConfig;
    }

    public void startConnection() throws IOException, InterruptedException {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.execute(new Runnable() {
            public void run() {
                connect();
            }
        });
        executor.shutdown();
    }

    private void connect() {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(rabbitMqConnectionConfig.getHost());
        factory.setPort(rabbitMqConnectionConfig.getPort());
        factory.setUsername(rabbitMqConnectionConfig.getUsername());
        factory.setPassword(rabbitMqConnectionConfig.getPassword());
        factory.setVirtualHost(rabbitMqConnectionConfig.getVirtualHost());

        if (rabbitMqConnectionConfig.getSslCertPath() != null) {
            try {
                configureSSLSocketFactory(rabbitMqConnectionConfig.getSslCertPath(), factory);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        String queueName = rabbitMqConnectionConfig.getQueueName();
        log.info("Starting connection attempt to queue:" + queueName);

        Connection connection;
        QueueingConsumer consumer = null;
        try {
            log.info("Setting connection...");
            connection = factory.newConnection();
            Channel channel;
            channel = connection.createChannel();
            channel.queueDeclare(queueName, true, false, false, null);
            consumer = new QueueingConsumer(channel);
            channel.basicConsume(queueName, true, consumer);
        } catch (Exception e) {
            log.error("Error when connecting to queue: " + queueName);
            e.printStackTrace();
            return;
        }
        while (true) {
            try {
                QueueingConsumer.Delivery delivery = consumer.nextDelivery();
                String message = new String(delivery.getBody(), "UTF-8");
                this.consumer.accept(message);
            } catch (Exception e) {
                log.error("Error when consuming a message from " + queueName);
            }
        }
    }

    private void configureSSLSocketFactory(String keystorePath, ConnectionFactory factory) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("JKS");
        FileInputStream keystoreFile = new FileInputStream(keystorePath);
        keyStore.load(keystoreFile, rabbitMqConnectionConfig.getKeystorePassword().toCharArray());

        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, rabbitMqConnectionConfig.getKeystorePassword().toCharArray());

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX", "BCJSSE");
        trustManagerFactory.init(keyStore);

        SSLContext sslContext = SSLContext.getInstance("TLS", "BCJSSE");
        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());

        factory.useSslProtocol(sslContext);
    }

}

错误日志

15:08:27,683 ERROR [STDERR]
org.bouncycastle.tls.TlsFatalAlertReceived: insufficient_security(71)
15:30:21,555 ERROR [STDERR]     at
org.bouncycastle.tls.TlsProtocol.handleAlertMessage(Unknown Source)
15:30:21,555 ERROR [STDERR]     at
org.bouncycastle.tls.TlsProtocol.processAlertQueue(Unknown Source)
15:30:21,555 ERROR [STDERR]     at
org.bouncycastle.tls.TlsProtocol.processRecord(Unknown Source)
15:30:21,556 ERROR [STDERR]     at
org.bouncycastle.tls.RecordStream.readRecord(Unknown Source)
15:30:21,556 ERROR [STDERR]     at
org.bouncycastle.tls.TlsProtocol.safeReadRecord(Unknown Source)
15:30:21,556 ERROR [STDERR]     at
org.bouncycastle.tls.TlsProtocol.blockForHandshake(Unknown Source)
15:30:21,557 ERROR [STDERR]     at
org.bouncycastle.tls.TlsClientProtocol.connect(Unknown Source)
15:30:21,557 ERROR [STDERR]     at
org.bouncycastle.jsse.provider.ProvSSLSocketDirect.startHandshake(Unknown
Source) 15:30:21,557 ERROR [STDERR]     at
org.bouncycastle.jsse.provider.ProvSSLSocketDirect.handshakeIfNecessary(Unknown
Source) 15:30:21,558 ERROR [STDERR]     at
org.bouncycastle.jsse.provider.ProvSSLSocketDirect$AppDataOutput.write(Unknown
Source) 15:30:21,558 ERROR [STDERR]     at
java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)
15:30:21,558 ERROR [STDERR]     at
java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)
15:30:21,559 ERROR [STDERR]     at
java.io.DataOutputStream.flush(DataOutputStream.java:106) 15:30:21,559
ERROR [STDERR]     at
com.rabbitmq.client.impl.SocketFrameHandler.sendHeader(SocketFrameHandler.java:121)
15:30:21,559 ERROR [STDERR]     at
com.rabbitmq.client.impl.SocketFrameHandler.sendHeader(SocketFrameHandler.java:126)
15:30:21,559 ERROR [STDERR]     at
com.rabbitmq.client.impl.AMQConnection.start(AMQConnection.java:293)
15:30:21,560 ERROR [STDERR]     at
com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:516)
15:30:21,560 ERROR [STDERR]     at
com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:533)
15:30:21,560 ERROR [STDERR]     at
pl.sygnity.rabbit.utils.RabbitMqListener.connect(RabbitMqListener.java:72)
15:30:21,560 ERROR [STDERR]     at
pl.sygnity.rabbit.utils.RabbitMqListener.access$000(RabbitMqListener.java:22)
15:30:21,561 ERROR [STDERR]     at
pl.sygnity.rabbit.utils.RabbitMqListener$1.run(RabbitMqListener.java:43)
15:30:21,561 ERROR [STDERR]     at
java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
15:30:21,561 ERROR [STDERR]     at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
15:30:21,562 ERROR [STDERR]     at
java.lang.Thread.run(Thread.java:662)

使用的依赖

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.68</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk15on</artifactId>
    <version>1.68</version>
</dependency>

补充信息

  1. 密钥库信息:类型JKS,包含自签名证书条目mykey,有效期至2025-04-01
  2. RabbitMQ状态:监听0.0.0.0:5671,协议为amqp/ssl
  3. openssl检测结果:RabbitMQ使用TLSv1.3协议,密码套件TLS_AES_256_GCM_SHA384,服务器证书为自签名证书

问题分析

  1. 核心冲突:TLS版本不兼容
    Java 1.6原生不支持TLS 1.3,而openssl检测显示RabbitMQ启用了TLSv1.3。握手过程中客户端(Java 6+BouncyCastle)无法匹配服务器要求的TLS版本,触发insufficient_security错误。

  2. BouncyCastle版本兼容性
    BouncyCastle 1.68属于较新版本,虽然标注支持JDK1.5+,但对Java 6的TLS相关支持已逐步弱化,尤其是TLS 1.3相关逻辑可能存在兼容性问题。

  3. 自签名证书问题
    当前错误并非证书验证失败,所以自签名是次要问题,后续若调整后出现证书错误再处理即可。


解决方案

1. 调整RabbitMQ的TLS协议范围

修改RabbitMQ配置文件,禁用TLS 1.3,仅启用Java 6支持的TLS版本(TLS 1.0、TLS 1.1):

# rabbitmq.conf 示例配置
listeners.ssl.default = 5671
# 指定兼容Java 6的密码套件
ssl_options.ciphers = ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:RSA-AES256-SHA256:RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:RSA-AES256-SHA:RSA-AES128-SHA
ssl_options.honor_cipher_order = true
# 限制TLS版本范围
ssl_options.minimum_version = tlsv1.0
ssl_options.maximum_version = tlsv1.1

修改后重启RabbitMQ服务。

2. 降级BouncyCastle版本

更换为更兼容Java 6的BouncyCastle版本,比如1.57:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.57</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk15on</artifactId>
    <version>1.57</version>
</dependency>

3. 明确指定SSLContext的TLS版本

在代码的configureSSLSocketFactory方法中,将SSLContext协议明确指定为Java 6支持的版本:

// 替换原SSLContext.getInstance("TLS", "BCJSSE")
// 若Java 6未打TLS 1.1补丁,改用"TLSv1"
SSLContext sslContext = SSLContext.getInstance("TLSv1.1", "BCJSSE");

注意:Java 6默认仅支持TLS 1.0,TLS 1.1需要安装JDK补丁(如Java 6 Update 111及以上版本)。

4. 自签名证书处理(若后续出现验证错误)

将RabbitMQ服务器证书导入到本地JKS密钥库:

  1. 从openssl输出中提取-----BEGIN CERTIFICATE-----到-----END CERTIFICATE-----的内容,保存为rabbitmq.crt
  2. 执行keytool命令导入:
keytool -importcert -file rabbitmq.crt -alias rabbitmq-server -keystore your-keystore.jks -storepass your-password

内容的提问来源于stack exchange,提问作者Mateusz Niedbal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 13:07:03