Smack 4.4.2使用三份指定证书建立XMPP安全连接的方法问询
Smack 4.4.2 自定义证书连接配置修改方案
报错原因
- 你当前配置的KeyStore使用Android系统默认CA存储
AndroidCAStore,未导入你持有的root.crt、network.crt、components.crt三份自定义证书,服务端返回的证书链无法被信任,触发java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.错误 - Smack 4.4.2版本已弃用
setCustomSSLContext方法,替代方案为setSslContext,同时你原有代码未完成SSLContext的初始化逻辑,也会导致连接失败
修改步骤
1. 加载三份自定义证书到KeyStore
将你的自定义证书放到Android项目的assets目录下,首先实现证书加载逻辑,合并自定义证书和系统CA证书到同一个KeyStore(如果不需要保留系统CA信任可跳过加载系统CA的步骤)
2. 基于自定义KeyStore构建SSLContext
使用合并后的KeyStore生成TrustManager,正确初始化SSLContext后传入Smack配置Builder
完整修改后代码示例
XMPPTCPConnectionConfiguration config = null; XMPPTCPConnectionConfiguration.Builder builder; try { builder = XMPPTCPConnectionConfiguration.builder() .setServiceName(JidCreate.domainBareFrom(Important.getXmppHost())) .setHost(Important.getXmppHost()) //.setDebuggerEnabled(true) .setPort(Integer.parseInt(Important.getXmppPort())) .setUsernameAndPassword(getLoginInfo("phone_no"), getLoginInfo("sec")) .setSecurityMode(ConnectionConfiguration.SecurityMode.required) .setResource(Important.getXmppResource()) .setCompressionEnabled(false); KeyStore keyStore = configKeyStore(); SSLContext sslContext = configSSLContext(keyStore); // 4.4.x版本使用setSslContext替代弃用的setCustomSSLContext builder.setSslContext(sslContext); config = builder.build(); AbstractXMPPConnection connection = new XMPPTCPConnection(config); connection.connect(); connection.login(); } catch (XmppStringprepException | KeyStoreException | KeyManagementException | NoSuchAlgorithmException | CertificateException | IOException e) { e.printStackTrace(); }
private KeyStore configKeyStore() throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException { // 先加载系统CA证书,不需要系统信任可替换为下方注释的初始化代码 KeyStore keyStore = KeyStore.getInstance("AndroidCAStore"); keyStore.load(null, null); // 不需要系统CA信任时使用以下两行替代上面两行 // KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); // keyStore.load(null); // 加载自定义三份证书,依次添加到KeyStore String[] certNames = {"root.crt", "network.crt", "components.crt"}; CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); for (String certName : certNames) { InputStream certInputStream = getAssets().open(certName); Certificate certificate = certificateFactory.generateCertificate(certInputStream); // 给每个证书设置唯一别名 keyStore.setCertificateEntry(certName, certificate); certInputStream.close(); } return keyStore; } private SSLContext configSSLContext(KeyStore keyStore) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { TrustManagerFactory trustManagerFactory = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); // 初始化SSLContext SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom()); return sslContext; }
注意事项
- 如果你的证书域名和实际连接的Host不匹配,可额外添加配置
builder.setHostnameVerifier((hostname, session) -> true)跳过域名校验,生产环境建议确保证书域名匹配避免安全风险 - 如果证书存放在res/raw目录,将
getAssets().open(certName)替换为getResources().openRawResource(R.raw.xxx)即可
内容的提问来源于stack exchange,提问作者Fahim Fahad Leo
相关产品推荐
相关产品推荐

