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>
补充信息
- 密钥库信息:类型JKS,包含自签名证书条目
mykey,有效期至2025-04-01 - RabbitMQ状态:监听0.0.0.0:5671,协议为amqp/ssl
- openssl检测结果:RabbitMQ使用TLSv1.3协议,密码套件
TLS_AES_256_GCM_SHA384,服务器证书为自签名证书
问题分析
核心冲突:TLS版本不兼容
Java 1.6原生不支持TLS 1.3,而openssl检测显示RabbitMQ启用了TLSv1.3。握手过程中客户端(Java 6+BouncyCastle)无法匹配服务器要求的TLS版本,触发insufficient_security错误。BouncyCastle版本兼容性
BouncyCastle 1.68属于较新版本,虽然标注支持JDK1.5+,但对Java 6的TLS相关支持已逐步弱化,尤其是TLS 1.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密钥库:
- 从openssl输出中提取
-----BEGIN CERTIFICATE-----到-----END CERTIFICATE-----的内容,保存为rabbitmq.crt - 执行keytool命令导入:
keytool -importcert -file rabbitmq.crt -alias rabbitmq-server -keystore your-keystore.jks -storepass your-password
内容的提问来源于stack exchange,提问作者Mateusz Niedbal
相关产品推荐
相关产品推荐

