目的:使用C mbedtls库实现https(RSA证书)双向认证毗连。
开发环境:windows 11, VS2022,mbedtls-3.6.2
私钥格式:p1/p8
- #include "mbedtls/net_sockets.h"
- #include "mbedtls/ssl.h"
- #include "mbedtls/entropy.h"
- #include "mbedtls/ctr_drbg.h"
- #include "mbedtls/debug.h"
- #include "mbedtls/error.h"
- #include "mbedtls/certs.h"
- #include <mbedtls/pk.h>
- #include <mbedtls/pem.h>
- #include <mbedtls/base64.h>
- // 这个函数假设 SSL 握手已经成功完成
- void print_server_san(mbedtls_ssl_context* ssl) {
- const mbedtls_x509_crt* cert = mbedtls_ssl_get_peer_cert(ssl);
- if (cert == NULL) {
- printf("No server certificate found.\n");
- return;
- }
- // 确保 `subject_alt_names` 是正确的类型
- const mbedtls_x509_sequence* san = &cert->subject_alt_names;
- while (san != NULL) {
- const mbedtls_asn1_buf* entry = &san->buf;
- unsigned char tag = entry->tag;
- // 检查是否为 IP 地址 (context-specific tag 7)
- if ((tag & MBEDTLS_ASN1_TAG_CLASS_MASK) == MBEDTLS_ASN1_CONTEXT_SPECIFIC &&
- (tag & MBEDTLS_ASN1_TAG_VALUE_MASK) == 7) {
- // IP 地址以二进制形式存储
- if (entry->len == 4) { // IPv4 地址
- printf("Server certificate SAN (IP): %u.%u.%u.%u\n",
- entry->p[0], entry->p[1], entry->p[2], entry->p[3]);
- }
- else if (entry->len == 16) { // IPv6 地址
- printf("Server certificate SAN (IPv6): ");
- for (int i = 0; i < 16; i++) {
- printf("%02x", entry->p[i]);
- if (i % 2 == 1 && i < 15) {
- printf(":");
- }
- }
- printf("\n");
- }
- }
- san = san->next;
- }
- }
- void print_mbedtls_error(int ret) {
- char error_buf[100];
- mbedtls_strerror(ret, error_buf, sizeof(error_buf));
- fprintf(stderr, "Error: %s\n", error_buf);
- }
- int print_prikey_b64() {
- int ret;
- mbedtls_pk_context pkey;
- unsigned char* key_buffer = NULL;
- size_t key_len = 0;
- unsigned char* base64_buffer = NULL;
- size_t base64_len = 0;
- mbedtls_pk_init(&pkey);
- // 读取私钥文件
- ret = mbedtls_pk_parse_keyfile(&pkey, "res/yax/client_p8.key", NULL);
- if (ret != 0) {
- print_mbedtls_error(ret);
- goto cleanup;
- }
- // 获取私钥的 DER 格式所需的缓冲区长度
- key_len = mbedtls_pk_write_key_der(&pkey, NULL, 0);
- if (key_len <= 0) {
- print_mbedtls_error(key_len);
- goto cleanup;
- }
- printf("Key length required for DER format: %zu bytes\n", key_len);
- // 分配比所需长度更大的缓冲区来确保足够的空间
- key_buffer = (unsigned char*)malloc(key_len);
- if (!key_buffer) {
- fprintf(stderr, "Memory allocation failed for key_buffer with size %zu bytes\n", key_len);
- ret = -1;
- goto cleanup;
- }
- // 将私钥写入 DER 格式缓冲区,注意返回值是实际写入的字节数
- ret = mbedtls_pk_write_key_der(&pkey, key_buffer, key_len);
- if (ret < 0) {
- print_mbedtls_error(ret);
- goto cleanup;
- }
- // 计算 Base64 编码所需的缓冲区长度
- key_len = ret; // 实际写入的字节数
- ret = mbedtls_base64_encode(NULL, 0, &base64_len, key_buffer + (key_len - ret), key_len);
- if (ret != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) {
- print_mbedtls_error(ret);
- goto cleanup;
- }
- // 分配缓冲区以存储 Base64 编码的数据
- base64_buffer = (unsigned char*)malloc(base64_len);
- if (!base64_buffer) {
- fprintf(stderr, "Memory allocation failed for base64_buffer\n");
- ret = -1;
- goto cleanup;
- }
- // 将 DER 格式的私钥转换为 Base64 编码
- ret = mbedtls_base64_encode(base64_buffer, base64_len, &base64_len, key_buffer + (key_len - ret), key_len);
- if (ret != 0) {
- print_mbedtls_error(ret);
- goto cleanup;
- }
- // 打印 Base64 编码的私钥
- printf("Base64 Encoded Private Key:\n%s\n", base64_buffer);
- cleanup:
- mbedtls_pk_free(&pkey);
- if (key_buffer) {
- free(key_buffer);
- }
- if (base64_buffer) {
- free(base64_buffer);
- }
- //Key length required for DER format: 18446744073709551508 bytes
- //Memory allocation failed for key_buffer with size 18446744073709551508 bytes
- return ret != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
- }
- int testHttps_p8() {
- printf("Https功能演示start:\n");
- int ret;
- mbedtls_net_context server_fd;
- mbedtls_ssl_context ssl;
- mbedtls_ssl_config conf;
- mbedtls_x509_crt cacert, clicert;
- mbedtls_pk_context pkey;
- mbedtls_entropy_context entropy;
- mbedtls_ctr_drbg_context ctr_drbg;
- const char* pers = "ssl_client1";
- // Initialize structures
- mbedtls_net_init(&server_fd);
- mbedtls_ssl_init(&ssl);
- mbedtls_ssl_config_init(&conf);
- mbedtls_x509_crt_init(&cacert);
- mbedtls_x509_crt_init(&clicert);
- mbedtls_pk_init(&pkey);
- mbedtls_ctr_drbg_init(&ctr_drbg);
- mbedtls_entropy_init(&entropy);
- // 设置 RNG
- //ret = mbedtls_ctr_drbg_seed(&(hc->tls.ctr_drbg), mbedtls_entropy_func, &hc->tls.entropy, NULL, 0);
- //ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)pers, strlen(pers));
- ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
- if (ret != 0)
- {
- return ret;
- }
- // Load certificates
- //mbedtls_x509_crt_parse_file(&clicert, "res/yax/client_https_with_chain.cer"); //含有客户端证书链
- mbedtls_x509_crt_parse_file(&clicert, "res/yax/client_https.cer"); //不含有证书链
- mbedtls_x509_crt_parse_file(&cacert, "res/yax/ca.cer"); //所有客户端和服务端所有证书链
- //mbedtls_x509_crt_parse_file(&cacert, "res/yax/root.cer"); //只提供一个服务器证书的root证书即可
- mbedtls_pk_parse_keyfile(&pkey, "res/yax/client_p8.key", NULL); //p1或p8格式都可以,但必须包含头尾
- //mbedtls_pk_parse_keyfile(&pkey, "res/yax2/client_p8.key", NULL);
- mbedtls_ssl_conf_authmode(&ssl, MBEDTLS_SSL_VERIFY_REQUIRED); //设置认证模式为 MBEDTLS_SSL_VERIFY_OPTIONAL 或 MBEDTLS_SSL_VERIFY_NONE 来调试是否由于证书验证失败引起的问题
- //但是,这仅用于调试,生产环境应该始终使用 MBEDTLS_SSL_VERIFY_REQUIRED。
- // Seed the random number generator
- //mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)pers, strlen(pers));
- // Setup SSL configuration
- mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
- mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
- mbedtls_ssl_conf_dbg(&conf, my_debug, stdout);
- mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);
- mbedtls_ssl_conf_own_cert(&conf, &clicert, &pkey);
-
- //mbedtls_ssl_set_hostname(&ssl, "4.3.2.1"); //服务器证书alt name dns = ip而不是域名时,不要调用这个
- //mbedtls_ssl_set_hostname(&ssl, NULL); //忽略主机名验证
- //mbedtls_ssl_conf_verify(&conf, 0, NULL); // 设置自定义的证书验证回调
- // Connect to server
- mbedtls_net_connect(&server_fd, "4.3.2.1", "443", MBEDTLS_NET_PROTO_TCP); //rsa https 测试服务端
- //mbedtls_net_connect(&server_fd, "4.3.2.1", "443", MBEDTLS_NET_PROTO_TCP); //RSA 国密sm2 双算法证书https 不适用。
- // Setup SSL context
- mbedtls_ssl_setup(&ssl, &conf);
- mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
- // Perform SSL handshake
- while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) {
- if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
- char error_buf[100];
- mbedtls_strerror(ret, error_buf, 100);
- printf("Handshake failed: %s\n", error_buf);
- return 1;
- }
- }
- //打印服务器证书的san
- print_server_san(&ssl);
- //print_prikey_b64();
- // Communicate with the server
- // ...
- // Send data to server
- const char* msg = "Hello, Server!";
- mbedtls_ssl_write(&ssl, (const unsigned char*)msg, strlen(msg));
- // Read response
- char buffer[1024];
- do {
- memset(buffer, 0, sizeof(buffer));
- ret = mbedtls_ssl_read(&ssl, (unsigned char*)buffer, sizeof(buffer) - 1);
- if (ret > 0) {
- printf("Received %d bytes:\n%s\n", ret, buffer);
- }
- else if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
- break;
- }
- } while (ret > 0);
- // Clean up
- mbedtls_ssl_close_notify(&ssl);
- mbedtls_net_free(&server_fd);
- mbedtls_x509_crt_free(&cacert);
- mbedtls_x509_crt_free(&clicert);
- mbedtls_pk_free(&pkey);
- mbedtls_ssl_free(&ssl);
- mbedtls_ssl_config_free(&conf);
- mbedtls_ctr_drbg_free(&ctr_drbg);
- mbedtls_entropy_free(&entropy);
- printf("Https功能演示End:\n");
- return EXIT_SUCCESS;
- }
- //在证书base64编码前后添加头尾
- char* cert_add_ht(const char* filename) {
- FILE* file = fopen(filename, "r");
- if (!file) {
- perror("无法打开文件");
- return NULL;
- }
- // 获取文件大小
- fseek(file, 0, SEEK_END);
- long filesize = ftell(file);
- fseek(file, 0, SEEK_SET);
- // 分配内存读取文件内容
- char* base64_data = (char*)malloc(filesize + 1);
- if (!base64_data) {
- perror("内存分配失败");
- fclose(file);
- return NULL;
- }
- fread(base64_data, 1, filesize, file);
- base64_data[filesize] = '\0'; // 确保字符串结束
- fclose(file);
- const char* pem_header = "-----BEGIN CERTIFICATE-----\n";
- const char* pem_footer = "\n-----END CERTIFICATE-----\n";
- // 计算 PEM 格式总长度
- size_t pem_size = strlen(pem_header) + strlen(base64_data) + strlen(pem_footer) + 1;
- // 分配内存以存储 PEM 格式数据
- char* pem_cert = (char*)malloc(pem_size);
- if (!pem_cert) {
- perror("内存分配失败");
- free(base64_data);
- return NULL;
- }
- // 生成 PEM 格式数据
- snprintf(pem_cert, pem_size, "%s%s%s", pem_header, base64_data, pem_footer);
- free(base64_data);
- return pem_cert;
- }
- char* pkcs1_key_add_ht(const char* filename) {
- FILE* file = fopen(filename, "r");
- if (!file) {
- perror("无法打开文件");
- return NULL;
- }
- // 获取文件大小
- fseek(file, 0, SEEK_END);
- long filesize = ftell(file);
- fseek(file, 0, SEEK_SET);
- // 分配内存读取文件内容
- char* base64_data = (char*)malloc(filesize + 1);
- if (!base64_data) {
- perror("内存分配失败");
- fclose(file);
- return NULL;
- }
- fread(base64_data, 1, filesize, file);
- base64_data[filesize] = '\0'; // 确保字符串结束
- fclose(file);
- const char* pem_header = "-----BEGIN RSA PRIVATE KEY-----\n";
- const char* pem_footer = "\n-----END RSA PRIVATE KEY-----\n";
- // 计算 PEM 格式总长度
- size_t pem_size = strlen(pem_header) + strlen(base64_data) + strlen(pem_footer) + 1;
- // 分配内存以存储 PEM 格式数据
- char* pem_cert = (char*)malloc(pem_size);
- if (!pem_cert) {
- perror("内存分配失败");
- free(base64_data);
- return NULL;
- }
- // 生成 PEM 格式数据
- snprintf(pem_cert, pem_size, "%s%s%s", pem_header, base64_data, pem_footer);
- free(base64_data);
- return pem_cert;
- }
- int testHttps() {
- printf("Https功能演示start:\n");
- int ret;
- mbedtls_net_context server_fd;
- mbedtls_ssl_context ssl;
- mbedtls_ssl_config conf;
- mbedtls_x509_crt cacert, clicert;
- mbedtls_pk_context pkey;
- mbedtls_entropy_context entropy;
- mbedtls_ctr_drbg_context ctr_drbg;
- const char* pers = "ssl_client1";
- // Initialize structures
- mbedtls_net_init(&server_fd);
- mbedtls_ssl_init(&ssl);
- mbedtls_ssl_config_init(&conf);
- mbedtls_x509_crt_init(&cacert);
- mbedtls_x509_crt_init(&clicert);
- mbedtls_pk_init(&pkey);
- mbedtls_ctr_drbg_init(&ctr_drbg);
- mbedtls_entropy_init(&entropy);
- // 设置 RNG
- //ret = mbedtls_ctr_drbg_seed(&(hc->tls.ctr_drbg), mbedtls_entropy_func, &hc->tls.entropy, NULL, 0);
- //ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)pers, strlen(pers));
- ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
- if (ret != 0)
- {
- return ret;
- }
- const char* file_cert_b64 = "res/yax2/client_https_noht.cer";
- // 读取证书文件(不包含头尾)并转换为 PEM 格式(包含头尾)
- char* pem_cert = cert_add_ht(file_cert_b64);
- // 获取 PEM 证书的大小
- size_t pem_size = strlen(pem_cert) + 1; // 包括终止符
- mbedtls_x509_crt_parse(&clicert, (const unsigned char*)pem_cert, pem_size);
- free(pem_cert);
- const char* file_key_p1_b64 = "res/yax2/client_p1_noht.key";
- // 读取证书文件(不包含头尾)并转换为 PEM 格式(包含头尾)
- char* pem_key = pkcs1_key_add_ht(file_key_p1_b64);
- // 获取 PEM 证书的大小
- size_t key_size = strlen(pem_key) + 1; // 包括终止符
- mbedtls_pk_parse_key(&pkey, (const unsigned char*)pem_key, key_size,NULL,0);
- free(pem_key);
- mbedtls_x509_crt_parse_file(&cacert, "res/yax/ca.cer"); //所有客户端和服务端所有证书链
- // Load certificates
- //mbedtls_x509_crt_parse_file(&clicert, "res/yax/client_https_with_chain.cer"); //含有客户端证书链
- //mbedtls_x509_crt_parse_file(&clicert, "res/yax2/client_https_noht.cer"); //不含有证书链
- //mbedtls_x509_crt_parse_file(&cacert, "res/yax/ca.cer"); //所有客户端和服务端所有证书链
- //mbedtls_x509_crt_parse_file(&cacert, "res/yax/root.cer"); //只提供一个服务器证书的root证书即可
- //mbedtls_pk_parse_keyfile(&pkey, "res/yax2/client_p1.key", NULL); //p1或p8格式都可以,但必须包含头尾
- //mbedtls_pk_parse_keyfile(&pkey, "res/yax2/client_p8.key", NULL);
- mbedtls_ssl_conf_authmode(&ssl, MBEDTLS_SSL_VERIFY_REQUIRED); //设置认证模式为 MBEDTLS_SSL_VERIFY_OPTIONAL 或 MBEDTLS_SSL_VERIFY_NONE 来调试是否由于证书验证失败引起的问题
- //但是,这仅用于调试,生产环境应该始终使用 MBEDTLS_SSL_VERIFY_REQUIRED。
- // Seed the random number generator
- //mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)pers, strlen(pers));
- // Setup SSL configuration
- mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
- mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
- mbedtls_ssl_conf_dbg(&conf, my_debug, stdout);
- mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);
- mbedtls_ssl_conf_own_cert(&conf, &clicert, &pkey);
- //mbedtls_ssl_set_hostname(&ssl, "1.2.3.4"); //服务器证书alt name dns = ip而不是域名时,不要调用这个
- //mbedtls_ssl_set_hostname(&ssl, NULL); //忽略主机名验证
- //mbedtls_ssl_conf_verify(&conf, 0, NULL); // 设置自定义的证书验证回调
- // Connect to server
- mbedtls_net_connect(&server_fd, "2.3.4.5", "443", MBEDTLS_NET_PROTO_TCP); //rsa https 测试服务端
- //mbedtls_net_connect(&server_fd, "2.3.4.5", "443", MBEDTLS_NET_PROTO_TCP); //RSA 国密sm2 双算法证书https 不适用。
- // Setup SSL context
- mbedtls_ssl_setup(&ssl, &conf);
- mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
- // Perform SSL handshake
- while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) {
- if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
- char error_buf[100];
- mbedtls_strerror(ret, error_buf, 100);
- printf("Handshake failed: %s\n", error_buf);
- return 1;
- }
- }
- //打印服务器证书的san
- print_server_san(&ssl);
- //print_prikey_b64();
- // Communicate with the server
- // ...
- // Send data to server
- const char* msg = "Hello, Server!";
- mbedtls_ssl_write(&ssl, (const unsigned char*)msg, strlen(msg));
- // Read response
- char buffer[1024];
- do {
- memset(buffer, 0, sizeof(buffer));
- ret = mbedtls_ssl_read(&ssl, (unsigned char*)buffer, sizeof(buffer) - 1);
- if (ret > 0) {
- printf("Received %d bytes:\n%s\n", ret, buffer);
- }
- else if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
- break;
- }
- } while (ret > 0);
- // Clean up
- mbedtls_ssl_close_notify(&ssl);
- mbedtls_net_free(&server_fd);
- mbedtls_x509_crt_free(&cacert);
- mbedtls_x509_crt_free(&clicert);
- mbedtls_pk_free(&pkey);
- mbedtls_ssl_free(&ssl);
- mbedtls_ssl_config_free(&conf);
- mbedtls_ctr_drbg_free(&ctr_drbg);
- mbedtls_entropy_free(&entropy);
- printf("Https功能演示End:\n");
- return EXIT_SUCCESS;
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |