MQTT and Websockets
paho.mqtt.c
ssl_opts.CApath = opts.capath; //指向一个包含PEM格式的CA证书的目录。
ssl_opts.keyStore = opts.cert; //客户机的公共证书链。它还可以包括客户机的私钥。
ssl_opts.trustStore = opts.cafile; //客户端信任的公共数字证书。
ssl_opts.privateKey = opts.key; //客户机私钥的PEM格式的文件
ssl_opts.privateKeyPassword = opts.keypass; //客户端私钥的密码(如果加密)
/** samples/paho_c_sub.c **/
if (opts.connection && (strncmp(opts.connection, "ssl://", 6) == 0 ||
strncmp(opts.connection, "wss://", 6) == 0))
{
if (opts.insecure)
ssl_opts.verify = 0;
ssl_opts.CApath = opts.capath;
ssl_opts.keyStore = opts.cert;
ssl_opts.trustStore = opts.cafile;
ssl_opts.privateKey = opts.key;
ssl_opts.privateKeyPassword = opts.keypass;
ssl_opts.enabledCipherSuites = opts.ciphers; //密码列表格式
conn_opts.ssl = &ssl_opts;
}
libwebsockets
Server
struct lws_context_creation_info info;
info.ssl_ca_filepath=""; /*CA根证书*/
info.ssl_cert_filepath=""; /*服务器公钥证书 */
info.ssl_private_key_filepath=""; /*服务器的私钥*/
info.ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-GCM-SHA384:"
"DHE-RSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-SHA384:"
"HIGH:!aNULL:!eNULL:!EXPORT:"
"!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
"!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
"!DHE-RSA-AES128-SHA256:"
"!AES128-GCM-SHA256:"
"!AES128-SHA256:"
"!DHE-RSA-AES256-SHA256:"
"!AES256-GCM-SHA384:"
"!AES256-SHA256";
case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
{
X509 *cert;
if (!len || (SSL_get_verify_result((SSL*) in) != X509_V_OK))
{
int err = X509_STORE_CTX_get_error((X509_STORE_CTX*) user);
int depth = X509_STORE_CTX_get_error_depth((X509_STORE_CTX*) user);
const char* msg = X509_verify_cert_error_string(err);
log_d(": SSL error: %s (%d), depth: %d\n", msg, err, depth);
return 1;
}
Client
struct lws_context_creation_info info;
struct lws_context *context = NULL;
struct lws_user_client user;
memset(&user, 0, sizeof(user));
memset(&info, 0, sizeof info);
info.port = CONTEXT_PORT_NO_LISTEN;
info.gid = -1;
info.uid = -1;
info.user = &user;
info.protocols = protocols;
nlogd("use ssl\n");
//info.options |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
//info.options |= LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS;
info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
if (access("./ssl_key/ca.crt", F_OK) != 0)
nloge("ca crt not exists\n");
const char *ssl_ca_filepath = "./ssl_key/ca.crt";
const char *ssl_cert_filepath = "./ssl_key/client.crt";
const char *ssl_private_key_filepath = "./ssl_key/client_rsa_private.pem";
info.ssl_ca_filepath = ssl_ca_filepath;
info.ssl_cert_filepath = ssl_cert_filepath;
info.ssl_private_key_filepath = ssl_private_key_filepath;
struct lws_client_connect_info i;
memset(&i, 0, sizeof(i));
i.path = "/aa";
i.address = "192.168.0.81";
i.port = 2000;
i.context = context;
i.host = "SERVER"; //注意这里,必须写成服务器证书里面的CN=
i.origin = "CA";
i.ietf_version_or_minus_one = -1;
i.protocol = protoname;
i.ssl_connection = 1;
// 下面的调用触发LWS_CALLBACK_PROTOCOL_INIT事件
wsi = lws_client_connect_via_info(&i);
....
关于QT
connect(socket, SIGNAL(connected()), this, SLOT(onConnected()));
//sslErrors 一定要设置
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(onSslErrors(QList<QSslError>)));
/*识别服务器证书是否合法*/
#if 0
QList<QSslCertificate> cacert = QSslCertificate::fromPath("G:\\openssl-key\\cacert.crt", QSsl::Pem);
//QList<QSslCertificate> cacert = QSslCertificate::fromPath("G:\\openssl-key\\single.server.crt", QSsl::Pem); //err
QFile certFile(QString("G:\\openssl-key\\client.crt"));
certFile.open(QIODevice::ReadOnly);
QFile keyFile(QString("G:\\openssl-key\\client.key.pem"));
keyFile.open(QIODevice::ReadOnly);
QSslCertificate certs = QSslCertificate(certFile.readAll(), QSsl::Pem);
QSslKey key= QSslKey(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
//The certificate is self-signed, and untrusted
QSslConfiguration config;
config.setCaCertificates(cacert);
config.setLocalCertificate(certs);
config.setPrivateKey(key);
//config.setPeerVerifyMode(QSslSocket::VerifyNone);
config.setProtocol(QSsl::TlsV1SslV3);
#else
//The host name did not match any of the valid hosts for this certificate
//openssl req -newkey rsa:2048 -nodes -keyout single.server.key.pem -x509 -days 365 -out single.server.crt -subj "/C=CN/ST=GD/L=SZ/O=hh/OU=dev/CN=192.168.2.130/emailAddress=aa@aa.com"
QList<QSslCertificate> cacert = QSslCertificate::fromPath("G:\\openssl-key\\single.crt", QSsl::Pem);
QSslConfiguration config;
config.setCaCertificates(cacert);
config.setProtocol(QSsl::TlsV1SslV3);
#endif
QT运行SSL需要 libeay32.dll和ssleay32.dll:
C:\Qt\Qt5.8.0\Tools\QtCreator\bin下的 libeay32.dll 和 ssleay32.dll 库复制到C:\Qt\Qt5.8.0\5.8\msvc2015_64\bin下。
单向认证:只需要验证SSL服务器身份,不需要验证SSL客户端身份。
双向认证:要求服务器和客户端双方都有证书,客户端需要校验服务端,服务端也需要校验客户端。
openssl生成证书
生成 RSA 私钥和自签名证书
#生成私钥与证书
openssl req -newkey rsa:2048 -nodes -keyout rsa_private.pem -x509 -days 365 -out cert.crt -subj "/C=CN/ST=GD/L=SZ/O=hh/OU=dev/CN=192.168.2.130/emailAddress=aa@aa.com"
#生成私钥
openssl genrsa -out privkey.pem 2048
#生成证书(含公钥)-已有key
openssl req -new -x509 -key privkey.pem -out cacert.crt -days 3650
这样便可以做单向认证,自己使用 privkey.pem 提供 cacert.crt 给其他人使用
#查看证书信息
openssl req -noout -text -in cacert.crt
#生成根证书私钥( cakey.pem 文件)
openssl genrsa -out cakey.pem 2048
#生成根证书签发申请文件( ca.csr 文件)
openssl req -new -key cakey.pem -out ca.csr -subj "/C=CN/ST=GD/L=SZ/O=hh/OU=dev/CN=192.168.2.130/emailAddress=aa@aa.com"
#自签发根证书(cer文件) ,依据 ca.csr 和 cakey.pem 生成 cacert.pem
openssl x509 -req -days 365 -sha1 -extensions v3_ca -signkey cakey.pem -in ca.csr -out cacert.pem
######################################################
#服务端私钥和证书
openssl genrsa -out server.key.pem 2048
openssl req -new -key server.key.pem -out server.csr -subj "/C=CN/ST=myprovince/L=mycity/O=myorganization/OU=mygroup/CN=myServer"
#使用根证书签发服务端证书 server.cert.pem
openssl x509 -req -days 365 -sha1 -extensions v3_req -CA cacert.pem -CAkey cakey.pem -CAserial ca.srl -CAcreateserial -in server.csr -out server.cert.pem
#使用CA证书验证server端证书,查看server.cert.pem,得到颁发者为192.168.2.130
openssl verify -CAfile cacert.pem server.cert.pem
######################################################
#客户端私钥和证书 client.key.pem
openssl genrsa -out client.key.pem 2048
openssl req -new -key client.key.pem -out client.csr -subj "/C=CN/ST=myprovince/L=mycity/O=myorganization/OU=mygroup/CN=myClient"
#使用根证书签发客户端证书 依据 cacert.pem + cakey.pem -> client.cert.pem
openssl x509 -req -days 365 -sha1 -extensions v3_req -CA cacert.pem -CAkey cakey.pem -CAserial ca.srl -in client.csr -out client.cert.pem
#使用CA证书验证客户端证书
openssl verify -CAfile cacert.pem client.cert.pem
cakey.pem cacert.pem ca.csr ca.srl
client.cert.pem client.csr client.key.pem
server.cert.pem server.csr server.key.pem
其中client.csr与server.csr只作用于证书申请功能需要CA证书颁发
#1,CA证书
openssl req -newkey rsa:2048 -nodes -keyout ca_rsa_private.pem -x509 -days 365 -out ca.crt -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=CA/emailAddress=aaaa@qq.com"
#2,服务器私钥与待签名
openssl req -newkey rsa:2048 -nodes -keyout server_rsa_private.pem -out server.csr -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=SERVER/emailAddress=aaa@qq.com"
#3,使用CA证书及密钥对服务器证书进行签名
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca_rsa_private.pem -CAcreateserial -out server.crt
#######
openssl verify -CAfile ca.crt server.crt
#4, client
openssl req -newkey rsa:2048 -nodes -keyout client_rsa_private.pem -out client.csr -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=CLIENT/emailAddress=youremail@qq.com"
#5 使用CA证书及密钥对客户端证书进行签名:
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca_rsa_private.pem -CAcreateserial -out client.crt
openssl verify -CAfile ca.crt client.crt
参考链接
https://www.cnblogs.com/Anker/p/6018032.html #基于openssl的单向和双向认证(有问题,双向认证的时候)
https://blog.csdn.net/weixin_34363171/article/details/86005536 注意在服务器做验证客户端证书 SSL_get_verify_result