目录
1、生成SSL证书方式
根据emqx官方文档获取SSL证书有如下方式:
您可通过以下两种方式获取相关 SSL/TLS 证书:
- 自签名证书:即使用自己签发的证书,由于自签名证书存在较多的安全隐患,因此只建议用于测试验证环境。
- 申请或购买证书:您可以向 Let's Encrypt 或华为云、腾讯云等云厂商申请免费证书,也可以向 DigiCert 等机构购买收费证书。对于企业级用户,一般建议申请收费的 OV 及以上类型的证书,以获取更高等级的安全保护。
本页介绍了如何创建自签名 Certificate Authority (CA) 证书并签发服务器和客户端证书的操作步骤。
SSL证书分为自签名 CA 证书、服务器端证书(安装eqmx的服务器)、客户端证书(连接的设备);
其中自签名 CA 证书和服务器端证书只需要生成一次就行,客户端证书是每一个设备都需要生成。
2、创建自签名 CA 证书
这一步会生成rootCA.key这一步会生成rootCA.key这一步会生成rootCA.key这一步会生成rootCA.key前置准备
已安装 OpenSSL。
2.1、运行以下命令生成密钥对,该命令随即会提示您输入密钥保护密码,后续在生成、签发、验证证书时均需要此密码。请妥善保管相关密钥及密码。
bash
openssl genrsa -des3 -out rootCA.key 2048
这一步会生成rootCA.key
2.2、运行以下命令通过密钥对中的私有密钥生成 CA 证书,该命令随即会提示您设置证书的唯一标识名称 DN(Distinguished Name)。
bash
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 3650 -out rootCA.crt
这一步会生成rootCA.crt
![]()
此处的 -days 3650 指定了证书的有效期限 10年
3、签发服务器证书
使用 CA 证书来签发服务器证书,用于验证服务器所有者的身份,服务器证书通常颁发给主机名、服务器名称或域名(如 www.emqx.com, localhost)。我们需要 CA 密钥
(rootCA.key)、CA 证书( rootCA.crt)和服务端 CSR (server.csr)生成服务器证书。
3.1、运行以下命令生成服务器证书密钥对:
openssl genrsa -out server.key 2048
这一步会生成server.key
3.2、运行以下命令使用 Server 密钥对制作 CSR。经 CA 根证书私钥签名后,CSR 可生成颁发给用户的证书公钥文件。该命令随即也会要求设置证书的唯一标识名称。
openssl req -new -key server.key -out server.csr
这一步会生成server.csr
3.3、使用 CSR 生成服务器证书,此时也可指定证书的有效天数,此处为 365 天:
openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 365
这一步会生成rootCA.srl、server.crt
至此就生成了一组证书
.
├── rootCA.crt
├── rootCA.key
├── rootCA.srl
├── server.crt
├── server.csr
└── server.key设备接入继续签发客户端证书就行了
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr
openssl x509 -req -in client.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out client.crt -days 3650签发客户端证书的步骤与签发服务器证书类似,只是在生成 CSR 时,需要将 Common Name 设置为客户端的唯一标识,如用户名、客户端 ID 等。
客户端证书与服务端证书使用相同的 CA 证书签名,因此客户端证书也可以使用上述 CA 证书签名。
4、客户端连接证书异常
需要注意的是通过以上官方描述创建的证书在使用的时候,客户端连接会报错:
Caused by: javax.net.ssl.SSLHandshakeException: No subject alternative names present。
这个错误 SSLHandshakeException: No subject alternative names present
表示你的 SSL 证书缺少 Subject Alternative Names (SANs),而客户端(如 Java MQTT 客户端)强制要求 SANs 扩展字段。
所以需要根据下面的步骤来生成证书
5、指定subjectAltName
(SANs)生成证书
5.1、证书配置文件创建
5.1.1、CA签发证书配置文件ca.cnf
# ca.cnf - 自建根证书颁发机构 (Root CA) 配置
[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
x509_extensions = v3_ca
prompt = no
default_md = sha256
[ req_distinguished_name ]
C = CN # 国家代码(如 CN/US)
ST = Jiangsu # 省份/州
L = Nanjing # 城市
O = Blbn # 组织名称
OU = Yanfa # 部门(可选)
CN = TestBlbn # CA 名称(必须唯一)
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true # 关键!标记为 CA 证书
keyUsage = critical, keyCertSign, cRLSign # CA 用途
5.1.2、服务端证书配置文件server.cnf
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = 192.168.3.64 # 服务端 IP
[v3_req]
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth # 关键!
subjectAltName = IP:192.168.3.64 # 关键!
有些版本keyUsage 可能是这样配置的,keyUsage = keyEncipherment, dataEncipherment 这个配置通过JAVA代码连接的时候正常,但是通过mqttx等客户端连接的时候会报如下错误:
rror: write EPROTO 8774408:error:1000012e:SSL routines:OPENSSL_internal:KEY_USAGE_BIT_INCORRECT:../../third_party/boringssl/src/ssl/ssl_cert.cc:609:
正确的配置是 keyUsage = digitalSignature, keyEncipherment
digitalSignature
是必须的,因为 TLS 握手过程中需要数字签名操作。
5.1.3、客户端证书配置文件client.cnf
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = client1 # 客户端唯一标识 不同的客户端不一样
[v3_req]
keyUsage = digitalSignature
extendedKeyUsage = clientAuth # 关键!
# subjectAltName 通常不需要(除非服务端要求)
5.2、证书生成
生成CA密钥对,此处会让输入密钥保护密码
openssl genrsa -des3 -out rootCA.key 2048
通过密钥对中的私有密钥生成 CA 证书
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 3650 -out rootCA.crt -config ca.cnf
生成服务器证书密钥对
openssl genrsa -out server.key 2048
使用 Server 密钥对制作 CSR。经 CA 根证书私钥签名后,CSR 可生成颁发给用户的证书公钥文件。该命令随即也会要求设置证书的唯一标识名称。
openssl req -new -key server.key -out server.csr -config server.cnf
使用 CSR 生成服务器证书,此时也可指定证书的有效天数,此处为 3650 天:此处会让输入前面设置的CA密钥保护密码
openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 3650 -extensions v3_req -extfile server.cnf
生成客户端证书密钥对
openssl genrsa -out client.key 2048
使用 Client 密钥对制作 CSR。经 CA 根证书私钥签名后,CSR 可生成颁发给用户的证书公钥文件。该命令随即也会要求设置证书的唯一标识名称。
openssl req -new -key client.key -out client.csr -config client.cnf
使用 CSR 生成客户端证书,此时也可指定证书的有效天数,此处为 365 天:此处会让输入前面设置的CA密钥保护密码
openssl x509 -req -in client.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out client.crt -days 365 -extensions v3_req -extfile client.cnf
5.3、证书说明
├── rootCA.key # CA 私钥(PEM 或 DER)
├── rootCA.crt # CA 证书(PEM 或 DER)
├── rootCA.srl # 序列号文件(可忽略)
├── server.crt # 服务器证书(PEM 或 DER)
├── server.csr # 证书签名请求(可忽略)
└── server.key # 服务器私钥(PEM 或 DER)
如何判断它们是 PEM 还是 DER?
# 如果是 PEM 格式,文件开头会是:
# -----BEGIN CERTIFICATE----- 或 -----BEGIN PRIVATE KEY-----
cat server.crt# 如果是 DER 格式,文件是二进制(乱码)
file server.crt
PEM vs CRT/KEY 的转换
(1)如果文件已经是 PEM 格式
-
直接重命名即可兼容 EMQX:
bash
cp server.crt cert.pem cp server.key key.pem cp rootCA.crt cacert.pem
(2)如果文件是 DER 格式
需转换为 PEM:
bash
# 证书 DER → PEM openssl x509 -inform der -in server.crt -out cert.pem # 私钥 DER → PEM openssl rsa -inform der -in server.key -out key.pem
(3)合并证书链(如需)
如果 server.crt
不包含中间 CA,需合并:
bash
cat server.crt rootCA.crt > fullchain.pem
5.3、证书使用
5.3.1、服务器端证书使用
登录mqtt dashboard 在监听器中修改ssl配置,点击重新设置,TLS Cert此处把server.crt内容复制出来粘贴上去;TLS Key此处把server.key内容复制出来粘贴上去;CA Cert此处把rootCA.crt内容复制出来粘贴上去;然后点击更新按钮完成配置
5.3.2、客户端证书使用
以Java为示列,需要rootCA.crt、client.crt、client.key
多个客户端可以使用同一个客户端证书 client.crt、client.key 这种用法只需要维护一个证书,维护方便,但是只要该证书泄露,所有设备都受影响;生产环境建议为每个设备都生成一个证书(使用客户端唯一id生成)
5.3.3、emqx.conf配置ssl
单向认证时配置
listeners.ssl.default {
bind = "0.0.0.0:8883"
ssl_options {
# PEM 格式的文件,包含一个或多个用于验证客户端证书的根 CA 证书
# 单向认证时,该文件内容可以为空
cacertfile = "${EMQX_ETC_DIR}/certs/rootCA.crt"
# PEM 格式的服务器证书,如果证书不是直接由根 CA 签发,那么中间 CA 的证书必须加在服务器证书的后面组成一个证书链
certfile = "${EMQX_ETC_DIR}/certs/server.crt"
# PEM 格式的密钥文件
keyfile = "${EMQX_ETC_DIR}/certs/server.key"
# 设置成 'verify_peer' 来验证客户端证书是否为 cacertfile 中某个根证书签发。双向认证时,必须设置成 'verify_peer'。
# 设置成 'verify_none' 则不验证客户端证书,即单向认证。
verify = verify_none
# 如果设置成 true,但是客户端在握手时候没有发送证书,服务端会终止握手。双向认证时,必须设置成 true。
# 如果设置成 false,那么服务端只有在客户端发送一个非法证书时才会终止握手
fail_if_no_peer_cert = false
}
}
双向认证时配置
listeners.ssl.default {
bind = "0.0.0.0:8883"
ssl_options {
# PEM 格式的文件,包含一个或多个用于验证客户端证书的根 CA 证书
# 单向认证时,该文件内容可以为空
cacertfile = "${EMQX_ETC_DIR}/certs/rootCA.crt"
# PEM 格式的服务器证书,如果证书不是直接由根 CA 签发,那么中间 CA 的证书必须加在服务器证书的后面组成一个证书链
certfile = "${EMQX_ETC_DIR}/certs/server.crt"
# PEM 格式的密钥文件
keyfile = "${EMQX_ETC_DIR}/certs/server.key"
# 设置成 'verify_peer' 来验证客户端证书是否为 cacertfile 中某个根证书签发。双向认证时,必须设置成 'verify_peer'。
# 设置成 'verify_none' 则不验证客户端证书,即单向认证。
verify = verify_peer
# 如果设置成 true,但是客户端在握手时候没有发送证书,服务端会终止握手。双向认证时,必须设置成 true。
# 如果设置成 false,那么服务端只有在客户端发送一个非法证书时才会终止握手
fail_if_no_peer_cert = true
}
}
需要注意的是,当你配置为单向认证时,客户端连接只需要设置自签名证书
双向认证时,除了自签名证书,客户端证书也需要设置
至此,证书使用介绍完成。