1、SSL/TLS简介
SSL(SecureSocket Layer)安全套接层,是网景公司提出的用于保证Server与client之间安全通信的一种协议,该协议位于TCP/IP协议与各应用层协议之间,即SSL独立于各应用层协议,因此各应用层协议可以透明地调用SSL来保证自身传输的安全性。目前,SSL被大量应用于http的安全通信中,MQTT协议与http协议同样属于应用层协议,因此也可以像http协议一样使用ssl为自己的通信提供安全保证。
SSL与TLS(Transport LayerSecurity Protocol)之间的关系:TLS(TransportLayer Security,传输层安全协议)是IETF(InternetEngineering Task Force,Internet工程任务组)制定的一种新的协议,它建立在SSL 3.0协议规范之上,是SSL 3.0的后续版本。在TLS与SSL3.0之间存在着显著的差别,主要是它们所支持的加密算法不同,所以TLS与SSL3.0不能互操作。
2、使用Openssl创建tls证书
SSL在身份认证过程中需要有一个双方都信任的CA签发的证书,CA签发证书是需要收费的,但是在测试过程中,可以自己产生一个CA,然后用自己产生的CA签发证书,下面的mosquitto的ssl功能的测试过程就是采用这一方式,其过程如下:
步骤一:产生自己的CA
openssl req -new -x509 -days 36500 -extensions v3_ca -keyout ca.key -out ca.crt
openssl req -new -x509 -days 36500 -extensions v3_ca -keyout ca.key -out ca.pem
步骤二:产生服务端证书
openssl genrsa -des3 -out server.key 2048
openssl req -out server.csr -key server.key -new
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 36500
步骤三:产生客户端证书
openssl genrsa -out client-key.pem 2048
openssl req -out client.csr -key client-key.pem -new
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client-crt.pem -days 36500
客户端使用:ca.pem、client-crt.pem、client-key.pem
服务端使用:ca.crt、server.crt、server.key
3、mosquitto.conf配置如下:
4、golang客户端测试代码
1 package cmd 2 3 import ( 4 "crypto/tls" 5 "crypto/x509" 6 fmt "fmt" 7 "io/IoUtil" 8 "os" 9 "time" 10 11 "github.com/apex/log" 12 MQTT "github.com/eclipse/paho.mqtt.golang" 13 ) 14 15 var ctx log.Interface 16 17 const QoS = 0x02 18 19 func init() { 20 fmt.Printf("init mqtt test\n") 21 22 } 23 24 func RunMqttClient() { 25 fmt.Printf("Run mqtt test\n") 26 var logLevel = log.InfoLevel 27 ctx = &log.Logger{ 28 Level: logLevel, 29 Handler: NewLogHanler(os.Stdout),128);line-height:1.5;"> 30 } 31 32 mqttClient := NewClient( 33 ctx,128);line-height:1.5;"> 34 "ttnhdl",128);line-height:1.5;"> 35 "",128);line-height:1.5;"> 36 "",128);line-height:1.5;"> 37 fmt.Sprintf("ssl://%s","192.168.195.201:8883"),128);line-height:1.5;"> 38 ) 39 40 var err = mqttClient.Connect() 41 if err != nil { 42 ctx.WithError(err).Fatal("Could not connect to MQTT") 43 fmt.Printf("Could not connect to MQTT\n") 44 } else { 45 fmt.Printf("Success connect to MQTT\n") 46 } 47 48 mqttClient.PublishUplink("test","hello mqtt!") 49 mqttClient.SubscribeUplink("test") 50 51 for true { 52 53 } 54 } 55 56 // Client connects to the MQTT server and can publish/subscribe on uplink,downlink and activations from devices 57 type Client interface { 58 Connect() error 59 Disconnect() 60 61 IsConnected() bool 62 63 Uplink pub/sub 64 PublishUplink(topic string,msg string) Token 65 SubscribeUplink(topic string) Token 66 } 67 68 type Token 69 Wait() bool 70 WaitTimeout(time.Duration) bool 71 Error() error 72 } 73 74 type simpleToken struct { 75 err error 76 } 77 78 Wait always returns true 79 func (t *simpleToken) Wait() bool { 80 return true 81 } 82 83 WaitTimeout always returns true 84 func (t *simpleToken) WaitTimeout(_ time.Duration) bool { 85 86 } 87 88 Error contains the error if present 89 func (t *simpleToken) Error() error { 90 return t.err 91 } 92 93 type defaultClient struct { 94 mqtt MQTT.Client 95 ctx log.Interface 96 } 97 98 func NewClient(ctx log.Interface,id,username,password string,brokers ...string) Client { 99 tlsconfig := NewTLSConfig() 100 101 mqttOpts := MQTT.NewClientOptions() 102 103 for _,broker := range brokers { 104 mqttOpts.AddBroker(broker) 105 } 106 107 mqttOpts.SetClientID("ypf_dewqfvcdeqfcdqwcdq") 108 mqttOpts.SetUsername(username) 109 mqttOpts.SetPassword(password) 110 111 TODO: Some tuning of these values probably won't hurt: 112 mqttOpts.SetKeepAlive(30 * time.Second) 113 mqttOpts.SetPingTimeout(10 * time.Second) 114 115 Usually this setting should not be used together with random ClientIDs,but 116 we configured The Things Network's MQTT servers to handle this correctly. 117 mqttOpts.SetCleanSession(false) 118 119 mqttOpts.SetDefaultPublishHandler(func(client MQTT.Client,msg MQTT.Message) { 120 ctx.WithField("message",msg).Warn("Received unhandled message") 121 }) 122 123 mqttOpts.SetConnectionLostHandler(func(client MQTT.Client,err error) { 124 ctx.WithError(err).Warn("Disconnected,reconnecting...") 125 }) 126 127 mqttOpts.SetOnConnectHandler(func(client MQTT.Client) { 128 ctx.Debug("Connected") 129 }) 130 131 mqttOpts.SetTLSConfig(tlsconfig) 132 133 return &defaultClient{ 134 mqtt: MQTT.NewClient(mqttOpts),128);line-height:1.5;">135 ctx: ctx,128);line-height:1.5;">136 } 137 } 138 139 var ( 140 ConnectRetries says how many times the client should retry a Failed connection 141 ConnectRetries = 10 142 ConnectRetryDelay says how long the client should wait between retries 143 ConnectRetryDelay = time.Second 144 ) 145 146 func (c *defaultClient) Connect() error { 147 if c.mqtt.IsConnected() { 148 return nil 149 } 150 var err error 151 for retries := 0; retries < ConnectRetries; retries++ { 152 token := c.mqtt.Connect() 153 token.Wait() 154 err = token.Error() 155 if err == nil { 156 break 157 } 158 <-time.After(ConnectRetryDelay) 159 } 160 161 return fmt.Errorf("Could not connect: %s",err) 162 } 163 164 } 165 166 func (c *defaultClient) Disconnect() { 167 if !c.mqtt.IsConnected() { 168 return 169 } 170 c.mqtt.Disconnect(25) 171 } 172 173 func (c *defaultClient) IsConnected() bool { 174 return c.mqtt.IsConnected() 175 } 176 177 func (c *defaultClient) PublishUplink(topic string,msg string) Token { 178 return c.mqtt.Publish(topic,QoS,false,msg) 179 } 180 181 func (c *defaultClient) SubscribeUplink(topic string) Token { 182 return c.mqtt.Subscribe(topic,func(mqtt MQTT.Client,128);line-height:1.5;">183 Determine the actual topic 184 fmt.Printf("Success SubscribeUplink with msg:%s\n",msg.Payload()) 185 }) 186 } 187 188 func NewTLSConfig() *tls.Config { 189 Import trusted certificates from CAfile.pem. 190 Alternatively,manually add CA certificates to 191 default openssl CA bundle. 192 certpool := x509.NewCertPool() 193 pemCerts,err := IoUtil.ReadFile("samplecerts/ca.pem") 194 195 certpool.AppendCertsFromPEM(pemCerts) 196 } 197 fmt.Println("0. resd pemCerts Success") 198 199 Import client certificate/key pair 200 cert,err := tls.LoadX509KeyPair("samplecerts/client-crt.pem","samplecerts/client-key.pem") 201 202 panic(err) 203 } 204 fmt.Println("1. resd cert Success") 205 206 Just to print out the client certificate.. 207 cert.Leaf,err = x509.ParseCertificate(cert.Certificate[0]) 208 209 panic(err) 210 } 211 fmt.Println("2. resd cert.Leaf Success") 212 213 Create tls.Config with desired tls properties 214 return &tls.Config{ 215 RootCAs = certs used to verify server cert. 216 RootCAs: certpool,128);line-height:1.5;">217 ClientAuth = whether to request cert from server. 218 Since the server is set up for SSL,this happens 219 anyways. 220 ClientAuth: tls.NoClientCert,128);line-height:1.5;">221 ClientCAs = certs used to validate client cert. 222 ClientCAs: nil,128);line-height:1.5;">223 InsecureSkipVerify = verify that cert contents 224 match server. IP matches what is in cert etc. 225 InsecureSkipVerify: true,128);line-height:1.5;">226 Certificates = list of certs client sends to server. 227 Certificates: []tls.Certificate{cert},128);line-height:1.5;">228 } 229 }
5、测试效果
服务端启动:
客户端运行:
6、JAVA版客户端实现
依赖:org.eclipse.paho.client.mqttv3、bcprov-jdk16-1.45.jar
MqttServiceClient代码:
SslUtil代码: