AWS Java Lambda连接IBM Queue SSL认证失败问题排查
需实现定时触发的AWS Java Lambda连接外部IBM Queue并拉取消息:
- 最初采用C#开发,MQ服务器关闭SSL时可正常连接拉取消息;开启SSL后无法完成认证,中间件负责人告知需使用JKS文件进行客户端证书认证。
- 改用Java 11开发后,关闭SSL时Lambda可正常连接;开启SSL后仍无法认证,MQ服务器报错:
AMQ9637E: During handshake, the remote partner sent no certificate.
以下为Java Lambda代码(从资源加载JKS文件写入/tmp目录,并设置SSL系统属性):
InputStream keystoreInputStream = ScheduledLambda.class.getClassLoader() .getResourceAsStream("/awsfenxdevsit.jks"); if (keystoreInputStream == null) { context.getLogger().log("Keystore file not found in resources folder..."); throw new RuntimeException("Keystore file not found in resources folder"); } else { context.getLogger().log("Keystore file was found in resources" + keystoreInputStream); } // Copy key.jks to /tmp (for AWS Lambda) try { Files.copy(keystoreInputStream, Paths.get("/tmp/keystore.jks")); } catch (IOException e) { context.getLogger().log("Error occured: " + e.getMessage()); e.printStackTrace(); } File trustStoreFile = new File("/tmp/keystore.jks"); if (!trustStoreFile.exists()) { context.getLogger().log("Truststore not found at /tmp/keystore.jks"); throw new RuntimeException("Truststore not found at /tmp/keystore.jks"); } else{ context.getLogger().log("Truststore found at /tmp/keystore.jks"); } // Set Java system properties for SSL System.setProperty("javax.net.ssl.keyStore", "/tmp/keystore.jks"); System.setProperty("javax.net.ssl.keyStorePassword", "XXXX"); System.setProperty("javax.net.ssl.keyStoreType", "JKS");
已确认JKS文件存在于资源目录及/tmp目录,请问代码存在什么问题?
1. SSL系统属性设置时机过晚
Java的SSL上下文会在第一次使用SSL连接时读取全局系统属性,如果IBM MQ客户端的初始化代码(或Lambda运行环境提前加载的SSL相关类)在设置这些属性之前执行,新配置的属性不会生效。
修正建议:将系统属性设置代码放在Lambda执行逻辑的最开始位置,甚至类初始化阶段,确保在任何SSL相关操作前完成配置。
2. 输入流处理不当,可能导致JKS文件损坏
代码中使用Files.copy后未关闭输入流,且如果输入流因日志输出操作被读取过,直接复制会导致写入的JKS文件不完整。
修正建议:使用try-with-resources自动管理输入流,确保文件完整写入:
try (InputStream keystoreInputStream = ScheduledLambda.class.getClassLoader() .getResourceAsStream("/awsfenxdevsit.jks")) { if (keystoreInputStream == null) { context.getLogger().log("Keystore file not found in resources folder..."); throw new RuntimeException("Keystore file not found in resources folder"); } // 覆盖已有文件,确保写入完整 Files.copy(keystoreInputStream, Paths.get("/tmp/keystore.jks"), StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { context.getLogger().log("Error occured: " + e.getMessage()); throw new RuntimeException("Failed to copy keystore to /tmp", e); }
3. 混淆KeyStore与TrustStore的作用,且未针对IBM MQ显式配置SSL
当前代码错误地将KeyStore称为TrustStore,更关键的是:IBM MQ Java客户端不一定会自动读取全局SSL系统属性,通常需要显式在MQConnectionFactory中配置SSL上下文,才能确保客户端证书被正确发送。
修正建议:直接在MQ连接工厂中配置SSL,不依赖全局系统属性:
// 初始化MQ连接工厂 MQConnectionFactory factory = new MQConnectionFactory(); factory.setHostName("your-mq-host"); factory.setPort(1414); factory.setQueueManager("your-queue-manager"); factory.setChannel("your-ssl-channel"); // 设置匹配MQ服务器的加密套件 factory.setSSLCipherSuite("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"); // 加载KeyStore并初始化SSL上下文 KeyStore keyStore = KeyStore.getInstance("JKS"); try (FileInputStream fis = new FileInputStream("/tmp/keystore.jks")) { keyStore.load(fis, "XXXX".toCharArray()); } KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, "XXXX".toCharArray()); SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); // 若需要信任MQ服务器证书,需额外配置TrustManager sslContext.init(kmf.getKeyManagers(), null, null); // 将SSL上下文绑定到MQ连接工厂 factory.setSSLSocketFactory(sslContext.getSocketFactory());
4. 验证JKS文件内容有效性
确保JKS文件中包含客户端私钥+完整证书链,而非仅证书。可通过以下命令验证:
keytool -list -v -keystore awsfenxdevsit.jks
查看输出中是否存在PrivateKeyEntry类型的条目。
内容的提问来源于stack exchange,提问作者Dan Tromp

