来自:http://blog.csdn.net/lingshi210/article/details/52439050
mqtt 的ssl配置可以参阅
http://houjixin.blog.163.com/blog/static/35628410201432205042955/
然后注意开启防火墙端口。
mqtt的命令和Java端的ssl 必须同时要带上ca.crt、clilent.crt、client.key三个文件,即CA证书、客户证书、客户私钥。
由于java 端不支持client.key的格式,需要命令进行转化
openssl pkcs8 -topk8 -in client.key -out client.pem -nocrypt
另外:
不知为何ubuntu下关闭防火墙后还是握手失败,cenos下正常,抓包后已经看不到明文了。
Java部分:
1.核心部分只需要设置SSLSocketFactory
MqttConnectOptions options = new MqttConnectOptions();
SSLSocketFactory factory=getSSLSocktet("youpath/ca.crt","youpath/client.crt","youpath/client.pem","password");
options.setSocketFactory(factory);
2.自定义SSLSocketFactory (改进于http://gist.github.com/4104301)
此处的密码应为生成证书的时候输入的密码,未认证。
private SSLSocketFactory getSSLSocktet(StringcaPath,String crtPath, String keyPath, String password) throws Exception {
// CAcertificate is used to authenticate server
CertificateFactory cAf = CertificateFactory.getInstance("X.509");
FileInputStream caIn = new FileInputStream(caPath);
X509Certificate ca = (X509Certificate) cAf.generateCertificate(caIn);
KeyStorecaKs = KeyStore.getInstance("JKS");
caKs.load(null, null);
caKs.setCertificateEntry("ca-certificate", ca);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(caKs);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
FileInputStreamcrtIn = new FileInputStream(crtPath);
X509Certificate caCert = (X509Certificate) cf.generateCertificate(crtIn);
crtIn.close();
// clientkey and certificatesare sent to server so it can authenticate
// us
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
// ks.load(caIn,password.toCharArray());
ks.load(null, null);
ks.setCertificateEntry("certificate", caCert);
ks.setKeyEntry("private-key",getPrivateKey(keyPath), password.toCharArray(),
newjava.security.cert.Certificate[]{caCert} );
KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
kmf.init(ks, password.toCharArray());
// keyIn.close();
// finally,create SSL socket factory
SSLContextcontext = SSLContext.getInstance("TLSv1");
context.init(kmf.getKeyManagers(),tmf.getTrustManagers(), newSecureRandom());
returncontext.getSocketFactory();
}
Android上会报错,改进如下:
privateSSLSocketFactory getSSLSocktet(String caPath,String crtPath, String keyPath,String password) throws Exception {
// CAcertificate is used to authenticate server
CertificateFactory cAf = CertificateFactory.getInstance("X.509");
FileInputStream caIn = new FileInputStream(caPath);
X509Certificate ca = (X509Certificate) cAf.generateCertificate(caIn);
KeyStorecaKs = KeyStore.getInstance(KeyStore.getDefaultType());
caKs.load(null, null);
caKs.setCertificateEntry("ca-certificate", ca);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(caKs);
caIn.close();
CertificateFactory cf = CertificateFactory.getInstance("X.509");
FileInputStream crtIn = new FileInputStream(crtPath);
X509CertificatecaCert = (X509Certificate) cf.generateCertificate(crtIn);
crtIn.close();
// clientkey and certificatesare sent to server so it can authenticate
// us
KeyStore ks= KeyStore.getInstance(KeyStore.getDefaultType());
// ks.load(caIn,password.toCharArray());
ks.load(null, null);
ks.setCertificateEntry("certificate", caCert);
ks.setKeyEntry("private-key",getPrivateKey(keyPath), password.toCharArray(),
newjava.security.cert.Certificate[]{caCert} );
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, password.toCharArray());
// keyIn.close();
// finally,create SSL socket factory
SSLContext context = SSLContext.getInstance("TLSv1");
context.init(kmf.getKeyManagers(),tmf.getTrustManagers(), newSecureRandom());
returncontext.getSocketFactory();
}
3.获取私钥代码部分
由于只能读取PKCS8的格式,所以需要转成pem
public PrivateKey getPrivateKey(String path) throws Exception{
org.apache.commons.codec.binary.Base64 base64=new Base64();
byte[] buffer= base64.decode(getPem(path));
PKCS8EncodedKeySpec keySpec= new PKCS8EncodedKeySpec(buffer);
KeyFactorykeyFactory= KeyFactory.getInstance("RSA");
return (RSAPrivateKey)keyFactory.generatePrivate(keySpec);
}
附录:
package com;
importjava.awt.BorderLayout;
importjava.awt.Container;
importjava.awt.event.ActionEvent;
importjava.awt.event.ActionListener;
importjava.io.BufferedReader;
importjava.io.FileInputStream;
import java.io.InputStream;
importjava.io.InputStreamReader;
importjava.security.KeyFactory;
importjava.security.KeyStore;
importjava.security.PrivateKey;
importjava.security.SecureRandom;
importjava.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
importjava.security.cert.X509Certificate;
importjava.security.interfaces.RSAPrivateKey;
importjava.security.spec.PKCS8EncodedKeySpec;
importjavax.net.ssl.KeyManagerFactory;
importjavax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
importjavax.net.ssl.TrustManager;
importjavax.net.ssl.TrustManagerFactory;
importjavax.net.ssl.X509TrustManager;
importjavax.swing.JButton;
importjavax.swing.JFrame;
importjavax.swing.JLabel;
importjavax.swing.JOptionPane;
import javax.swing.JPanel;
importjavax.swing.JTextArea;
importorg.apache.commons.codec.binary.Base64;
importorg.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
importorg.eclipse.paho.client.mqttv3.MqttCallback;
importorg.eclipse.paho.client.mqttv3.MqttClient;
importorg.eclipse.paho.client.mqttv3.MqttConnectOptions;
importorg.eclipse.paho.client.mqttv3.MqttMessage;
importorg.eclipse.paho.client.mqttv3.MqttTopic;
importorg.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
publicclassServerextendsJFrame {
privatestaticfinallongserialVersionUID = 1L;
private JPanel panel;
private JPanelpanelText;
private JPanelpanelText2;
private JButton button;
private JButtonbutton2;
private JButtonsubscribeButton;
private JTextAreatextHost;
private JTextAreatextClientID;
private JTextAreatextPublishMsg;
private JTextAreatextTopic;
private MqttClientclient;
private String host = "ssl://192.168.10.233:1883";
private MqttTopictopic;
private MqttMessagemessage;
private StringuserToken = "999999";
private StringmyTopicRoot = "test";
private String myTopic= null;
private String clienID= "test1234567";
publicServer() {
Containercontainer = this.getContentPane();
panel = new JPanel();
panelText =new JPanel();
panelText2= new JPanel();
button = new JButton("发布主题消息");
button2 = new JButton("更换客户机地址和IP");
button.addActionListener(new ActionListener() {
@Override
publicvoidactionPerformed(ActionEvent ae){
try {
host = textHost.getText();
clienID = textClientID.getText();
if (client == null) {
client = newMqttClient(host, clienID, new MemoryPersistence());
}
if(!client.isConnected()) {
connect();
}
publishMsg(textTopic.getText(), textPublishMsg.getText());
} catch (Exception e) {
e.printStackTrace();
showErrorMsg(e.toString());
}
}
});
button2.addActionListener(new ActionListener() {
@Override
publicvoidactionPerformed(ActionEventarg0) {
// TODO Auto-generated method stub
host = textHost.getText();
clienID = textClientID.getText();
try {
if (client != null)
client.disconnectForcibly();
client = newMqttClient(host, clienID, new MemoryPersistence());
connect();
} catch (Exception e) {
e.printStackTrace();
showErrorMsg(e.toString());
}
}
});
subscribeButton = new JButton("订阅主题");
subscribeButton.addActionListener(newActionListener() {
@Override
publicvoidactionPerformed(ActionEventarg0) {
// TODO Auto-generated method stub
try {
if (client == null) {
client = newMqttClient(host, clienID, new MemoryPersistence());
}
if(!client.isConnected()) {
connect();
}
if (myTopic != null &&!myTopic.equals(textTopic.getText())) {
client.subscribe(myTopic);
}
client.subscribe(textTopic.getText());
myTopic = textTopic.getText();
} catch (Exception e) {
e.printStackTrace();
showErrorMsg(e.toString());
}
}
});
textHost = new JTextArea();
textHost.setText(host);
textClientID = new JTextArea();
textClientID.setText(clienID);
panel.add(button);
panel.add(subscribeButton);
panelText.add(button2);
panelText.add(new JLabel("mqtt地址"));
panelText.add(textHost);
panelText.add(new JLabel("ClienId"));
panelText.add(textClientID);
panelText.add(new JLabel("主题"));
textTopic =new JTextArea();
textTopic.setText(myTopicRoot);
panelText.add(textTopic);
textPublishMsg = new JTextArea();
textPublishMsg.setText("@" + userToken + "@E@5@" + userToken + "@");
panelText2.add(new JLabel("mqtt消息"));
panelText2.add(textPublishMsg);
container.add(panel, BorderLayout.NORTH);
container.add(panelText, BorderLayout.CENTER);
container.add(panelText2, BorderLayout.SOUTH);
// try {
// client = new MqttClient(host,clienID,
// new MemoryPersistence());
// connect();
// } catch (Exception e) {
// showErrorMsg(e.toString());
// }
}
privateSSLSocketFactory getSSLSocktet(StringcaPath,String crtPath, String keyPath, String password) throws Exception {
// CA certificate is used toauthenticate server
CertificateFactorycAf = CertificateFactory.getInstance("X.509");
FileInputStream caIn = new FileInputStream(caPath);
X509Certificate ca = (X509Certificate) cAf.generateCertificate(caIn);
KeyStorecaKs = KeyStore.getInstance("JKS");
caKs.load(null, null);
caKs.setCertificateEntry("ca-certificate", ca);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(caKs);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
FileInputStream crtIn = new FileInputStream(crtPath);
X509Certificate caCert = (X509Certificate)cf.generateCertificate(crtIn);
crtIn.close();
// client key and certificates are sentto server so it can authenticate
// us
KeyStore ks= KeyStore.getInstance(KeyStore.getDefaultType());
// ks.load(caIn,password.toCharArray());
ks.load(null, null);
ks.setCertificateEntry("certificate", caCert);
ks.setKeyEntry("private-key", getPrivateKey(keyPath),password.toCharArray(),
newjava.security.cert.Certificate[]{caCert} );
KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
kmf.init(ks, password.toCharArray());
// keyIn.close();
// finally, create SSL socket factory
SSLContextcontext = SSLContext.getInstance("TLSv1");
context.init(kmf.getKeyManagers(),tmf.getTrustManagers(), new SecureRandom());
returncontext.getSocketFactory();
}
private String getPem(String path) throws Exception{
FileInputStream fin=new FileInputStream(path);
BufferedReader br= new BufferedReader(new InputStreamReader(fin));
String readLine= null;
StringBuilder sb= new StringBuilder();
while((readLine=br.readLine())!=null){
if(readLine.charAt(0)=='-'){
continue;
}else{
sb.append(readLine);
sb.append('\r');
}
}
fin.close();
return sb.toString();
}
public PrivateKey getPrivateKey(String path) throws Exception{
org.apache.commons.codec.binary.Base64 base64=new Base64();
byte[] buffer= base64.decode(getPem(path));
PKCS8EncodedKeySpec keySpec= new PKCS8EncodedKeySpec(buffer);
KeyFactorykeyFactory= KeyFactory.getInstance("RSA");
return (RSAPrivateKey)keyFactory.generatePrivate(keySpec);
}
privatevoidconnect() {
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(false);
// options.setUserName(userName);
//options.setPassword(passWord.toCharArray());
// 设置超时时间
// options.setConnectionTimeout(10);
// 设置会话心跳时间
// options.setKeepAliveInterval(20);
// try {
// options.setWill("willtest","SENDgpslost".getBytes(), 1, false);
// } catch (Exception e1) {
// // TODO Auto-generated catch block
// System.out.print(e1);
// }
try {
if(!SSLSocketFactoryFactory.isSupportedOnJVM()) {
System.out.print("isSupportedOnJVM=false");
}
SSLSocketFactoryfactory=getSSLSocktet("F:/ssl/ca.crt","F:/ssl/client.crt","F:/ssl/client.pem","brt123");
options.setSocketFactory(factory);
client.setCallback(new MqttCallback() {
@Override
publicvoidconnectionLost(Throwablecause) {
System.out.println("connectionLost-----------");
}
@Override
publicvoiddeliveryComplete(IMqttDeliveryTokentoken) {
System.out.println("deliveryComplete---------" +token.isComplete());
}
@Override
publicvoidmessageArrived(String topic,MqttMessage arg1) throws Exception {
System.out.println("messageArrived----------");
String msg = newString(arg1.getPayload());
showErrorMsg("主题:" + topic + "\r\n消息:" + msg);
}
});
topic =client.getTopic(myTopicRoot + userToken);
client.connect(options);
} catch (Exception e) {
e.printStackTrace();
}
}
publicvoidpublishMsg(String topoc,String msg) {
message = new MqttMessage();
message.setQos(0);
message.setRetained(false);
System.out.println(message.isRetained() + "------ratained状态");
try {
message.setPayload(msg.getBytes("UTF-8"));
client.publish(topoc, message);
} catch (Exception e) {
e.printStackTrace();
showErrorMsg(e.toString());
}
}
privatevoidshowErrorMsg(String msg) {
JOptionPane.showMessageDialog(null, msg);
}
}