使用X.509证书认证
使用前须知: 生成X509证书的方案有两种,分别为使用openssl
和使用shell脚本生成nopass文件
,这边强烈建议您使用第一种方案(使用openssl
生成),第二种方案(使用shell脚本生成nopass文件
)已被弃用!
生成X.509证书(使用openssl)
找一台Linux主机执行以下命令
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes
此操作将会产生两个文件cert.pem
、key.pem
。
创建设备
详见创建设备章节,这里需要超链接到创建设备章节
在设备的管理认证界面将认证方式选择为MQTT X.509证书,并将cert.pem
的内容完全复制到RSA公钥文本框。如下图所示:
连接平台并上传数据
上传数据之前,确保当前目录拥有三个文件(cert.pem
、key.pem
、mqttserver.pub.pem
),前两个文件可以通过本文开头的生成X509证书生成,最后一个服务器公钥mqttserver.pub.pem
请联系管理员,使用以下代码即可上传数据
import ssl
import json
import paho.mqtt.client as mqtt
things_host = "things.xiaobodata.com"
def things_on_connect(client, userdata, flags, rc):
print("things_on_connect userdata:%s rc:%s", userdata, rc)
# 这是要上传的数据
data = {'key': 'v1'}
client.publish("v1/devices/me/telemetry", json.dumps(data), 1)
def things_on_disconnect(client, userdata, rc):
print("things_on_disconnect userdata:%s rc:%s", userdata, rc)
def things_on_message(client, userdata, msg):
print("things_on_message userdata:%", userdata)
if __name__ == '__main__':
# 连接things mqtt
things_mqtt = mqtt.Client(clean_session=True)
things_mqtt.on_disconnect = things_on_disconnect
things_mqtt.on_message = things_on_message
things_mqtt.on_connect = things_on_connect
things_mqtt.tls_set(ca_certs="mqttserver.pub.pem", certfile="cert.pem", keyfile="key.pem",
tls_version=ssl.PROTOCOL_TLSv1_2, ciphers=None)
things_mqtt.tls_insecure_set(False)
things_mqtt.connect(host=things_host, port=8883)
things_mqtt.loop_forever()
在平台的设备遥测数据中看到上传的数据即代表成功
使用nopass
文件(使用shell脚本生成nopass文件)(已弃用!)
在Linux新建一个空白目录并将以下两个文件放入该目录下
client.keygen.sh
文件的内容如下:
#!/bin/bash
#
# Copyright © 2016-2021 The WaveletThings
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
usage() {
echo "This script generates client public/private key pair, extracts them to a no-password pem file,"
echo "and imports server public key to client keystore"
echo "usage: ./client.keygen.sh [-p file]"
echo " -p | --props | --properties file Properties file. default value is ./keygen.properties"
echo " -h | --help | ? Show this message"
}
PROPERTIES_FILE=keygen.properties
while true; do
case "$1" in
-p | --props | --properties) PROPERTIES_FILE=$2 ;
shift
;;
-h | --help | ?) usage
exit 0
;;
-- ) shift;
break
;;
* ) break
;;
esac
shift
done
. $PROPERTIES_FILE
if [ -f $CLIENT_FILE_PREFIX.jks ] || [ -f $CLIENT_FILE_PREFIX.pub.pem ] || [ -f $CLIENT_FILE_PREFIX.nopass.pem ] || \
[ -f $CLIENT_FILE_PREFIX.pem ] || [ -f $CLIENT_FILE_PREFIX.p12 ] || [ -f $CLIENT_FILE_PREFIX.pk8.pem ];
then
while :
do
read -p "Output files from previous server.keygen.sh script run found. Overwrite? [Y/N]: " response
case $response in
[nN]|[nN][oO])
echo "Skipping"
echo "Done"
exit 0
;;
[yY]|[yY][eE]|[yY][eE][sS]|"")
echo "Cleaning up files"
rm -rf $CLIENT_FILE_PREFIX.jks
rm -rf $CLIENT_FILE_PREFIX.pub.pem
rm -rf $CLIENT_FILE_PREFIX.nopass.pem
rm -rf $CLIENT_FILE_PREFIX.pem
rm -rf $CLIENT_FILE_PREFIX.p12
rm -rf $CLIENT_FILE_PREFIX.pk8.pem
break;
;;
*) echo "Please reply 'yes' or 'no'"
;;
esac
done
fi
OPENSSL_CMD=""
case $CLIENT_KEY_ALG in
RSA)
OPENSSL_CMD="rsa"
;;
EC)
OPENSSL_CMD="ec"
;;
esac
if [ -z "$OPENSSL_CMD" ]; then
echo "Unexpected CLIENT_KEY_ALG. Exiting."
exit 0
fi
echo "INFO: your hostname is $(hostname)"
echo "INFO: your CN (domain suffix) for key is $DOMAIN_SUFFIX"
echo "Generating SSL Key Pair..."
keytool -genkeypair -v \
-alias $CLIENT_KEY_ALIAS \
-keystore $CLIENT_FILE_PREFIX.jks \
-keypass $CLIENT_KEY_PASSWORD \
-storepass $CLIENT_KEYSTORE_PASSWORD \
-keyalg $CLIENT_KEY_ALG \
-keysize $CLIENT_KEY_SIZE\
-validity 9999 \
-dname "CN=$DOMAIN_SUFFIX, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE"
echo "Converting keystore to pkcs12"
keytool -importkeystore \
-srckeystore $CLIENT_FILE_PREFIX.jks \
-destkeystore $CLIENT_FILE_PREFIX.p12 \
-srcalias $CLIENT_KEY_ALIAS \
-srcstoretype jks \
-deststoretype pkcs12 \
-srcstorepass $CLIENT_KEYSTORE_PASSWORD \
-deststorepass $CLIENT_KEY_PASSWORD \
-srckeypass $CLIENT_KEY_PASSWORD \
-destkeypass $CLIENT_KEY_PASSWORD
echo "Converting pkcs12 to pem"
openssl pkcs12 -in $CLIENT_FILE_PREFIX.p12 \
-out $CLIENT_FILE_PREFIX.pem \
-passin pass:$CLIENT_KEY_PASSWORD \
-passout pass:$CLIENT_KEY_PASSWORD
echo "Converting pem to pkcs8"
openssl pkcs8 \
-topk8 \
-nocrypt \
-in $CLIENT_FILE_PREFIX.pem \
-out $CLIENT_FILE_PREFIX.pk8.pem \
-passin pass:$CLIENT_KEY_PASSWORD
echo "Importing server public key to $CLIENT_FILE_PREFIX.jks"
keytool --importcert \
-file $SERVER_FILE_PREFIX.cer \
-keystore $CLIENT_FILE_PREFIX.jks \
-alias $SERVER_KEY_ALIAS \
-keypass $SERVER_KEY_PASSWORD \
-storepass $CLIENT_KEYSTORE_PASSWORD \
-noprompt
echo "Exporting no-password pem certificate"
openssl $OPENSSL_CMD -in $CLIENT_FILE_PREFIX.pem -out $CLIENT_FILE_PREFIX.nopass.pem -passin pass:$CLIENT_KEY_PASSWORD
tail -n +$(($(grep -m1 -n -e '-----BEGIN CERTIFICATE' $CLIENT_FILE_PREFIX.pem | cut -d: -f1) )) \
$CLIENT_FILE_PREFIX.pem >> $CLIENT_FILE_PREFIX.nopass.pem
echo "Exporting client public key"
tail -n +$(($(grep -m1 -n -e '-----BEGIN CERTIFICATE' $CLIENT_FILE_PREFIX.pem | cut -d: -f1) )) \
$CLIENT_FILE_PREFIX.pem >> $CLIENT_FILE_PREFIX.pub.pem
echo "Done."
keygen.properties
文件的内容如下:
DOMAIN_SUFFIX="localhost"
ORGANIZATIONAL_UNIT=WaveletThings
ORGANIZATION=WaveletThings
CITY="Tianjin"
STATE_OR_PROVINCE=CA
TWO_LETTER_COUNTRY_CODE=CN
SERVER_KEYSTORE_PASSWORD=server_ks_password
SERVER_KEY_PASSWORD=server_key_password
SERVER_KEY_ALIAS="serveralias"
SERVER_FILE_PREFIX="mqttserver"
SERVER_KEYSTORE_DIR="/etc/thingsboard/conf/"
CLIENT_KEYSTORE_PASSWORD=password
CLIENT_KEY_PASSWORD=password
CLIENT_KEY_ALIAS="clientalias"
CLIENT_FILE_PREFIX="mqttclient"
CLIENT_KEY_ALG="RSA"
CLIENT_KEY_SIZE="2048"
KEY | 解释 |
---|---|
DOMAIN_SUFFIX | 对应于证书的CN值。必须对应于目标服务器域(允许使用通配符)。默认为当前主机名 |
ORGANIZATIONAL_UNIT | 对应于证书的组织单位。 |
ORGANIZATION | 对应于证书的组织。 |
CITY | 城市 |
STATE_OR_PROVINCE | 国家或省份名称 |
TWO_LETTER_COUNTRY_CODE | 两个字母的国家代码 |
SERVER_KEYSTORE_PASSWORD | 服务器密钥库密码 |
SERVER_KEY_PASSWORD | 服务器密钥密码。可能与服务器密钥库密码相同,也可能与服务器密钥库密码不同 |
SERVER_KEY_ALIAS | 服务器密钥别名。在密钥库中必须是唯一的 |
SERVER_FILE_PREFIX | 所有与服务器密钥生成相关的输出文件的前缀 |
SERVER_KEYSTORE_DIR | 可以选择复制密钥的默认位置。 |
要运行服务器密钥库生成,请使用以下命令:
chmod +x server.keygen.sh
sudo ./server.keygen.sh
执行成功后当前目录会有这些文件:
将mqttclient.pub.pem
文件中的内容复制到RSA公钥
中:
使用下面的Python代码将数据上传,上传数据之前,确保当前目录拥有mqttserver.pub.pem
文件和mqttclient.nopass.pem
文件,mqttclient.nopass.pem
文件为脚本生成,mqttserver.pub.pem
请联系管理员,使用以下代码即可上传数据
import ssl
import json
import paho.mqtt.client as mqtt
things_host = "things.xiaobodata.com"
def things_on_connect(client, userdata, flags, rc):
print("things_on_connect userdata:%s rc:%s", userdata, rc)
data = {"key": "v2"}
client.publish("v1/devices/me/telemetry", json.dumps(data), 1)
def things_on_disconnect(client, userdata, rc):
print("things_on_disconnect userdata:%s rc:%s", userdata, rc)
def things_on_message(client, userdata, msg):
print("things_on_message userdata:%", userdata)
if __name__ == '__main__':
# 连接things mqtt
things_mqtt = mqtt.Client(clean_session=True)
things_mqtt.on_disconnect = things_on_disconnect
things_mqtt.on_message = things_on_message
things_mqtt.on_connect = things_on_connect
things_mqtt.tls_set(ca_certs="mqttserver.pub.pem", certfile="mqttclient.nopass.pem",
tls_version=ssl.PROTOCOL_TLSv1_2, ciphers=None)
things_mqtt.tls_insecure_set(False)
things_mqtt.connect(host=things_host, port=8883)
things_mqtt.loop_forever()
在平台的设备遥测数据中看到上传的数据即代表成功