Android与mqtt双向SSL认证
借鉴大佬博客:http://blog.linguofeng.com/
需要的工具:
portecle-1.9
生成ca.bks完成对服务器的自签名单向认证
需要提供ca.crt
$keytool -import\-alias mqtt.broker\-file ca.crt\-keypass password\-keystore ca.bks\-storetype BKS\-storepass passw0rd\-providerClass org.bouncycastle.jce.provider.BouncyCastleProvider\-providerpath bcprov-ext-jdk14-1.54.jar
MqttAndroidClientclint=...MqttConnectOptionsoptions=newMqttConnectOptions();InputStreaminput=this.getApplicationContext().getAssets().open("ca.bks");options.setSocketFactory(client.getSSLSocketFactory(input,"password"));
生成client.bks完成对客户端的认证,需要借portecle工具把pfx转成bks
$openssl pkcs12 -export -inkey client.key -in client.crt -out client.pfx$java -jar portecle.jar
生成client.pfx文件,然后打开
File -> New Keystore -> BKS-V1 -> Tools -> Import Key Pair -> choose -> client.pfx
pfx转化为bks的时候提示:
java.security.InvalidKeyException: Illegal key size
如果密钥大于128, 会抛出上述异常。因为密钥长度是受限制的, java运行时环境读到的是受限的policy文件,文件位于/jre/lib/security下, 这种限制是因为美国对软件出口的控制
解决办法也很简单:
去网上下载相应的jar包下载下来,替换jdk 与jre下两个jar包:local_policy.jar和US_export_policy.jar即可。
jdk对应jar包的路径:C:\Java\jdk1.7.0_25\jre\lib\security
jre对应jar包的路径:C:\Java\jre7\lib\security
根据需求转化JKS和BKS
privateSSLSocketFactorygetSSLSocketFactory(Context context,Stringpassword)throwsMqttSecurityException{
try{
InputStream keyStore=context.getResources().getAssets().open("client.bks");
KeyStore km=KeyStore.getInstance("BKS");
km.load(keyStore,password.toCharArray());
KeyManagerFactory kmf=KeyManagerFactory.getInstance("X509");
kmf.init(km,password.toCharArray());
InputStream trustStore=context.getResources().getAssets().open("ca.bks");
KeyStore ts=KeyStore.getInstance("BKS");
ts.load(trustStore,password.toCharArray());
TrustManagerFactory tmf=TrustManagerFactory.getInstance("X509");
tmf.init(ts);
SSLContext ctx=SSLContext.getInstance("SSL");
ctx.init(kmf.getKeyManagers(),tmf.getTrustManagers(),null);
returnctx.getSocketFactory();
}catch(KeyStoreException|CertificateException|IOException|NoSuchAlgorithmException|KeyManagementException|UnrecoverableKeyException e){
throw newMqttSecurityException(e);
}
}
options.setSocketFactory(getSSLSocketFactory(context,"password"));