一. wss 认证
关于SSL认证说明(转载)
https://www.cnblogs.com/bluestorm/p/10571989.html
二.从浏览器生成wss客户端
1.用谷歌打开网站
2.点击"小锁", 在证书路径中所有的证书都需要生成*.pem。
3.导出证书
选择第一级证书,点击查看证书
>>详细信息
>>复制到文件
>>下一步
>>选择DER 编码二进制X.509(.CER)
>>下一步
>>指定导出文件路径文件名(可直接导出到openssl的bin目录下),完成证书导出(文件后缀.cer)。
4.生成 .pem 文件
使用 openssl.exe 将证书文件转为.pem 文件,命令如下
openssl x509 -in xxx.cer -inform der -out xxx.pem
重复 2 3 4
步骤依次将各级证书,全部生成对应的.pem
文件。然后将各个.pem
文件的内容copy到同一个.pem
文件内(如client.pem)。
websocketpp/examples/print_client_tls/print_client_tls.cpp测试只生成一级目录证书,即可连接成功
5. websockets
- websockets默认端口 443
- 地址格式: wss://ip地址:443
三.websocketpp的wss client demo
修改 websocketpp/examples/print_client_tls/print_client_tls.cpp内容
/// Verify that one of the subject alternative names matches the given hostname
bool verify_subject_alternative_name(const char * hostname, X509 * cert)
{
STACK_OF(GENERAL_NAME) * san_names = NULL;
san_names = (STACK_OF(GENERAL_NAME) *) X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
if (san_names == NULL) {
return false;
}
int san_names_count = sk_GENERAL_NAME_num(san_names);
bool result = false;
for (int i = 0; i < san_names_count; i++) {
const GENERAL_NAME * current_name = sk_GENERAL_NAME_value(san_names, i);
if (current_name->type != GEN_DNS) {
continue;
}
char * dns_name = (char *) ASN1_STRING_data(current_name->d.dNSName);
// Make sure there isn't an embedded NUL character in the DNS name
if ((size_t)ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) {
break;
}
// Compare expected hostname with the CN
result = (strcasecmp(hostname, dns_name) == 0);
}
sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
return result;
}
/// Verify that the certificate common name matches the given hostname
bool verify_common_name(const char * hostname, X509 * cert)
{
// Find the position of the CN field in the Subject field of the certificate
int common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name(cert), NID_commonName, -1);
if (common_name_loc < 0) {
return false;
}
// Extract the CN field
X509_NAME_ENTRY * common_name_entry = X509_NAME_get_entry(X509_get_subject_name(cert), common_name_loc);
if (common_name_entry == NULL) {
return false;
}
// Convert the CN field to a C string
ASN1_STRING * common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
if (common_name_asn1 == NULL) {
return false;
}
char * common_name_str = (char *) ASN1_STRING_data(common_name_asn1);
// Make sure there isn't an embedded NUL character in the CN
if ((size_t)ASN1_STRING_length(common_name_asn1) != strlen(common_name_str)) {
return false;
}
// Compare expected hostname with the CN
return (strcasecmp(hostname, common_name_str) == 0);
}
bool verify_certificate(const char * hostname, bool preverified, boost::asio::ssl::verify_context& ctx)
{
int depth = X509_STORE_CTX_get_error_depth(ctx.native_handle());
if (depth == 0 && preverified) {
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
if (verify_subject_alternative_name(hostname, cert)) {
return true;
} else if (verify_common_name(hostname, cert)) {
return true;
} else {
return false;
}
}
return preverified;
}
context_ptr on_tls_init(const char * hostname, websocketpp::connection_hdl) {
context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);
try {
ctx->set_options(boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::no_sslv3 |
boost::asio::ssl::context::single_dh_use);
// 注意此处:set_verify_mode
// 选择 verify_peer 需要 load_verify_file
// 选择 verify_none 不需要 load_verify_file
ctx->set_verify_mode(boost::asio::ssl::verify_peer);
ctx->set_verify_callback(bind(&Mverify_certificate, hostname, ::_1, ::_2));
// Here we load the CA certificates of all CA's that this client trusts.
ctx->load_verify_file("client.pem");
} catch (std::exception& e) {
std::cout << e.what() << std::endl;
}
return ctx;
}