MQTT Paho Android 支持SSL/TLS(亲测有效)
登录时支持ssl的交互
这是调测登录界面设计
代码中对ssl/tls的支持
使用MqttAndroidClient
配置mqtt客户端请求时,不加密及加密方式连接存在以下几点差异:
url及端口差异
kt
val uri: String = if (tlsConnection) {
"ssl://$host:$port"
} else {
"tcp://$host:$port"
}
支持tls时,url前缀是ssl:
普通mqtt连接时候,前缀是tcp
端口差异:
tcp请求时,默认端口1883
ssl请求时,默认端口是8883
socketFactory配置项
笔者项目中只支持单向验证,即客户端验证服务端,所以需要在客户端加载服务端证书用于ssl连接
kt
if(connection.isSSL == 1){
//单项验证,客户端验证服务端,onenet提供的.pem证书,需要用keytool转成java支持的bks、或者jks等
connOpts.socketFactory = connection.client.getSSLSocketFactory(context.assets.open("MQTTS-certificate.bks"), "12345678")
connOpts.isHttpsHostnameVerificationEnabled = false
connOpts.setSSLHostnameVerifier { _, _ -> true }
}
说明:
- java中不支持pem证书加载,所以需要使用keytool工具将pem格式证书转成java/android支持的bks或者jks等
bash
keytool -importcert -v -trustcacerts -file ./MQTTS-certificate.pem -alias ca -keystore ./mqtt.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath ./bcprov-ext-jdk18on-176.jar
bcprov-ext-jdk18on-176.jar
需要从以下地址下载
https://www.bouncycastle.org/latest_releases.html
- 证书中会涉及域名验证,如果证书中缺少这个字段,那么运行时候会报下面错误
MqttException (0) - javax.net.ssl.SSLHandshakeException: No subjectAltNames on the certificate match
解决办法是跳过域名及host验证的流程
kt
connOpts.isHttpsHostnameVerificationEnabled = false
connOpts.setSSLHostnameVerifier { _, _ -> true }
client.getSSLSocketFactory实现
下面我们看看MqttAndroidClient创建sslSocketFactory的具体实现代码。
kt
//info.mqtt.android.service.MqttAndroidClient
/**
* Get the SSLSocketFactory using SSL key store and password
* A convenience method, which will help user to create a SSLSocketFactory
* object
*
* @param keyStore the SSL key store which is generated by some SSL key tool,
* such as keytool in Java JDK
* @param password the password of the key store which is set when the key store
* is generated
* @return SSLSocketFactory used to connect to the server with SSL
* authentication
* @throws MqttSecurityException if there was any error when getting the SSLSocketFactory
*/
@Throws(MqttSecurityException::class)
fun getSSLSocketFactory(keyStore: InputStream?, password: String): SSLSocketFactory {
return try {
val sslSockFactory: SSLSocketFactory
val ts: KeyStore = KeyStore.getInstance("BKS")
ts.load(keyStore, password.toCharArray())
val tmf = TrustManagerFactory.getInstance("X509")
tmf.init(ts)
val tm = tmf.trustManagers
val ctx: SSLContext = SSLContext.getInstance("TLSv1")
ctx.init(null, tm, null)
sslSockFactory = ctx.socketFactory
sslSockFactory
} catch (e: KeyStoreException) {
throw MqttSecurityException(e)
} catch (e: CertificateException) {
throw MqttSecurityException(e)
} catch (e: IOException) {
throw MqttSecurityException(e)
} catch (e: NoSuchAlgorithmException) {
throw MqttSecurityException(e)
} catch (e: KeyManagementException) {
throw MqttSecurityException(e)
}
}
Github
https://github.com/hannesa2/paho.mqtt.android
https://github.com/eclipse/paho.mqtt.android
项目中涉及的sample示例代码很值得一探究竟,对你掌握MQTT相关支持很有帮助喔!