// TestEcho-SSL-Console Client-Native // 纯OpenSSL+Winsock实现的SSL客户端(不使用HP-Socket) #include "pch.h" // SSL证书配置(与Client项目相同) static const char* g_c_lpszPemCert = "-----BEGIN CERTIFICATE-----\n" "MIIDszCCApugAwIBAgIBATANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJDTjEL\n" "MAkGA1UECAwCR0QxCzAJBgNVBAcMAkdaMQwwCgYDVQQKDANTU1QxDzANBgNVBAsM\n" "Bkplc3NtYTETMBEGA1UEAwwKamVzc21hLm9yZzEeMBwGCSqGSIb3DQEJARYPbGRj\n" "c2FhQDIxY24uY29tMCAXDTI0MDYyNjA1MjUwOFoYDzIyNDMwNzA5MDUyNTA4WjBu\n" "MQswCQYDVQQGEwJDTjELMAkGA1UECAwCR0QxDDAKBgNVBAoMA1NTVDEPMA0GA1UE\n" "CwwGSmVzc21hMRMwEQYDVQQDDApqZXNzbWEub3JnMR4wHAYJKoZIhvcNAQkBFg9s\n" "ZGNzYWFAMjFjbi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCD\n" "+MyrJEKCheRoOpMRjR78S8hr9W7XN0/EZWyVKwXRT7EE0aGiQdH/W2a+qpWRMa6E\n" "Qi47zdBnt0P8ZoFiItQhuhwUJ064afpVoaHHX25UdbF8r+sRTofadughETBBj2Cf\n" "qh0ia6EOB0QvpJpywWmGZPoMtypjbUiTb/YGOJh2qsVr67MN/E48vt7qt0VxF9SE\n" "pucvqhraTBljWCeRVCae2c0yBSpq/n+7NhamK7+g3xxCKWRz4pN3wrIoEsXTboTh\n" "z940caDgthCc23VJ080DN44jZg6c87huKIuxbebJqw2HCM4DwrW+OSzTLszpFAXZ\n" "yarllOzWnBut20zmYnl1AgMBAAGjTTBLMAkGA1UdEwQCMAAwHQYDVR0OBBYEFJ5E\n" "RJmJ4pUzEbcU9Yge6nr0oi51MB8GA1UdIwQYMBaAFN49z48DywmoD4cNTQgC6nn2\n" "QJoUMA0GCSqGSIb3DQEBCwUAA4IBAQBpoSFfDDDKMAy95tSROpYu5WSWQXe6B7kl\n" "PGJAF6mWe/4b7jHQqDUVkEmFmbMWUAtpTC3P01TrV77dhIosAnC/B76fb7Pto8W4\n" "cjGpWAT0sSegZuhnLtguTGlnR0vVSh/yRRDEtjN8loWpu3BLWVHYOKnn62QGfY0B\n" "sRGrfZsKvwB+1w+HOvGopnWv6UYwrzEKthjPMR65rOsoManOv24ua8baJmq0gqF9\n" "752kD8n703uWUBx79/QlNIPMZC1iUIi1mEjyrTgSag6+3sWAIKihaoF/Nf9d01nw\n" "iL16EIT5dJ0QJWDCeIxhuTZckw+gL1pBeQU7pqzKHPnvo+8GBnTG\n" "-----END CERTIFICATE-----\n"; static const char* g_c_lpszPemKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" "MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIK2UJW9QXIj4CAggA\n" "MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCDDZQLhAdT91jd6v/5H0+GBIIE\n" "0PH6tKl+nPi8sU0ryjxDIrHwrT/ZFah+3TAHGE/YFAOZnzRyCFHQTvUZX4p8eSmw\n" "WOpt5NBUPJ3mT0Ctt7lGBRy4AXSyBrFSamlTruM3P1e3ijluYjMbweZFfCWPq8c/\n" "jPjbcUkXe6mD96aPSTt/jIunexS8AKovu8c/bFLyTLDk38lATc+GnXQQJ0KiXCRu\n" "vpjVSKcv2Br6cWqaNTZ71FvH1RmSD6K6givc0w65pKruHYTMApIRR8YC5Y0vx0gD\n" "6nS12LV/EJEtxTfZFlrzZTRWZISPIzYGuTfS+3bPePlxpbwzhN6vmvgjKhdk+3lN\n" "3W3ZfqODNhoOKG+mG5Fdj7vR2PU1UND6UUd3+FrzWkXikmalAAwKzRLnyTR1T2rl\n" "RhM0Qe/HZianeEQTHpCw27gOz1OMw2EKfIEHM6W2BKGOTY5ls5dqgMfP1ZoQUrOr\n" "59tJo4GpWYFGCuHhTEa5OS/gsgnzymGrkuEwPsdSQaBdzV7lFGTv2/ryKX+vNm9V\n" "CmKw0nHzOVP19+WL4vPDtbRnLUk8KV9Mg7PdSbGbNcMmTEBk8ju8OvjIUIWZbRTa\n" "n5C6fhD1DYZcczmlCILYgXyJISu7EDf3z9cKRAf5VbRAedDMB/xHWmrmlxUJ37Kt\n" "tVgaCD0U6Q3q+3y6OOwugc8UbSo4yA/DbLlG0/U7afwQaNxTLa4HGBQljpoNStIt\n" "Vgfy2olqHXaf2doSQtsYEl9MHa6neuGfZQMtonDkejnx4KKU+cMhe+KijEUwieYx\n" "7aoPB71b82XODquDPAL5zOegj0eYgKn5iXyOx5W44S34zfclxtxxgfsDJ3qJ9qoL\n" "sSenrQ3xAYHJSZRcqEgO31XhoEnkyt1V7G0Bk4/GUMD6uQudr3nsw/ulJpAlNK15\n" "ZxTSKWrtwOWdwcTj6B14K6wcqMFVNF1Ydbv/qp0b5q5S/orYHzRIPcFmdOAIsjyO\n" "6na7+D31BH/4pf+TASBNqRNRw5CBqNcGcfiXk11AywxUnmD5ZvC/C0pTpTD/9qC4\n" "LucWJ0sNAtPq8suFjKqQ+wMvq3rUh050NRm2cm2nUJLxafTnr0v3+kKYbVW8pSWB\n" "NMelZMVGF1MDYBujg8Mw/xuMhPeLozCZeKmo7eu7aDMXzQMZLfAEJAzU9Du8H4nq\n" "GgQVUgEkS5rdbjZGkHP0FuM8m8lueKEPDYwHCJv9Be5Z/uxp9OO/Lmdlha0J7gJu\n" "pihNkAYVxRst96b5okXKooYi/TZxAdThoPYH28VwinGR1I3/8I3M5DbUPIgHhDeB\n" "ga3u7jt7ZNDUgavukUD0S7WioRb5ooXrXGZ1xmzKLCmMdCDC5S32fQS0wRGfVoMl\n" "hWbaT+0uak+fOpqVRxSNyE3Ek788ua5iPHaTSXJSoe5lv7OQKDSZ/+wFeLmDPf4M\n" "BHL2gBLD6RNkz5cWgy14sQcJKNAnyptU4EGPyURZcB8APtB/ITAS2Az/JSxvSBgq\n" "g/L1FujnP2QEpWpVKkTNxsF867bUPN34KrlPKYjNqcKA2pD4fkFoKSeeNtOEWa++\n" "d6q9y+mDD97SnIFAAhDFlukzXtyl4MU6uiqRldFiuEt3KzvV19n8M+NyyYIFhfdg\n" "6TkYEbMJPQ/Y3EGNmyMqbFdJzrdl/B8pr7JQnikTfUZZ\n" "-----END ENCRYPTED PRIVATE KEY-----\n"; static const char* g_c_lpszCAPemCert = "-----BEGIN CERTIFICATE-----\n" "MIID2TCCAsGgAwIBAgIUM8TTtPU+ejzffYXCcs/zZsU7OuIwDQYJKoZIhvcNAQEL\n" "BQAwezELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJHWjEMMAoG\n" "A1UECgwDU1NUMQ8wDQYDVQQLDAZKZXNzbWExEzARBgNVBAMMCmplc3NtYS5vcmcx\n" "HjAcBgkqhkiG9w0BCQEWD2xkY3NhYUAyMWNuLmNvbTAgFw0yNDA2MjYwNTA0NDNa\n" "GA8yMjcwMTEyNDA1MDQ0M1owezELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQsw\n" "CQYDVQQHDAJHWjEMMAoGA1UECgwDU1NUMQ8wDQYDVQQLDAZKZXNzbWExEzARBgNV\n" "BAMMCmplc3NtYS5vcmcxHjAcBgkqhkiG9w0BCQEWD2xkY3NhYUAyMWNuLmNvbTCC\n" "ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAML+v79+aLQt0Za0dTIZHI5B\n" "NDs0g5G8bhdOTlW/kNWflaziZ3GY6d6nJSkQ5e29kyFKxlOD6Gls6bOJ86U71u4R\n" "bCmoFvRTDH4q2cJ/+PbiioLpNveDG6lnRCs9JNRQoJrkpRo6urnVnAdsIf6UFjLI\n" "dlByNMPGYJ0V8/oKJG5Vu5gcbZV0jVA5+tswkH/zquexEXoKvp18mcwl+pNc/LwW\n" "0WnGj0uoJjxHg4GsS78PASjhxMR/2d/1OpgPauldFaNHjVPtaLqJnuejwA6M6Sz8\n" "iFPybAQAMpHL9W8kf08jtbnFvnm4ibUkQL5h+OJoIEQa9AVZOSoFG2/g5Zcn8X8C\n" "AwEAAaNTMFEwHQYDVR0OBBYEFN49z48DywmoD4cNTQgC6nn2QJoUMB8GA1UdIwQY\n" "MBaAFN49z48DywmoD4cNTQgC6nn2QJoUMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI\n" "hvcNAQELBQADggEBALJnYrYBSZLyYX14FQ04zxG3AX0CtQzNOOa7LDrr+H8Ly+nK\n" "qS87gg2njMVZH1zM2demtMwydR/F2Ui8ggaduMvc9h5YgQKEwYl8KarJEY03oZoe\n" "zbQGBxCXpDOtMs1vujzcl/iZbSzwEDF3g4la5U8q4MlmfGFKz9CJbvoxecqYA206\n" "nNbW2XZsW/xMiQv6iAw5iP/LOR9HAyxcvXIsL790nfcgnTYLmyP254Dj4outc6R+\n" "PA+f/c1FvkbUBTR5vJt2tsvHcNU218rY2hyOIhDmZeUWprqBO19sUk3scLbVPr3+\n" "WEWEl2XaCekKuPtAnMgVQuFsocXGyiuIhkOe5Z4=\n" "-----END CERTIFICATE-----\n"; static const char* g_c_lpszKeyPassword = "123456"; static const char* DEFAULT_ADDRESS = "127.0.0.1"; static const int DEFAULT_PORT = 5555; // 全局变量 SSL_CTX* g_ssl_ctx = nullptr; SSL* g_ssl = nullptr; SOCKET g_socket = INVALID_SOCKET; // 打印OpenSSL错误 void PrintSSLError(const char* msg) { unsigned long err = ERR_get_error(); char errBuf[256]; ERR_error_string_n(err, errBuf, sizeof(errBuf)); std::cout << "[错误] " << msg << ": " << errBuf << std::endl; } // 从内存加载证书 X509* LoadCertFromMemory(const char* certPem) { BIO* bio = BIO_new_mem_buf(certPem, -1); if (!bio) { PrintSSLError("创建BIO失败"); return nullptr; } X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); BIO_free(bio); if (!cert) { PrintSSLError("读取证书失败"); } return cert; } // 从内存加载私钥 EVP_PKEY* LoadKeyFromMemory(const char* keyPem, const char* password) { BIO* bio = BIO_new_mem_buf(keyPem, -1); if (!bio) { PrintSSLError("创建BIO失败"); return nullptr; } EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, (void*)password); BIO_free(bio); if (!pkey) { PrintSSLError("读取私钥失败"); } return pkey; } // 初始化OpenSSL bool InitializeSSL() { // OpenSSL 1.1.0+ 自动初始化,不需要手动调用初始化函数 // 创建SSL上下文(使用TLS客户端方法) g_ssl_ctx = SSL_CTX_new(TLS_client_method()); if (!g_ssl_ctx) { PrintSSLError("创建SSL上下文失败"); return false; } // 设置验证模式:验证服务器证书 SSL_CTX_set_verify(g_ssl_ctx, SSL_VERIFY_PEER, nullptr); // 加载CA证书(用于验证服务器) BIO* ca_bio = BIO_new_mem_buf(g_c_lpszCAPemCert, -1); if (!ca_bio) { PrintSSLError("创建CA BIO失败"); return false; } X509* ca_cert = PEM_read_bio_X509(ca_bio, nullptr, nullptr, nullptr); BIO_free(ca_bio); if (!ca_cert) { PrintSSLError("读取CA证书失败"); return false; } // 将CA证书添加到信任列表 X509_STORE* store = SSL_CTX_get_cert_store(g_ssl_ctx); if (X509_STORE_add_cert(store, ca_cert) != 1) { PrintSSLError("添加CA证书失败"); X509_free(ca_cert); return false; } X509_free(ca_cert); // 加载客户端证书 X509* client_cert = LoadCertFromMemory(g_c_lpszPemCert); if (!client_cert) { return false; } if (SSL_CTX_use_certificate(g_ssl_ctx, client_cert) != 1) { PrintSSLError("使用客户端证书失败"); X509_free(client_cert); return false; } X509_free(client_cert); // 加载客户端私钥 EVP_PKEY* client_key = LoadKeyFromMemory(g_c_lpszPemKey, g_c_lpszKeyPassword); if (!client_key) { return false; } if (SSL_CTX_use_PrivateKey(g_ssl_ctx, client_key) != 1) { PrintSSLError("使用客户端私钥失败"); EVP_PKEY_free(client_key); return false; } EVP_PKEY_free(client_key); // 验证私钥和证书是否匹配 if (SSL_CTX_check_private_key(g_ssl_ctx) != 1) { std::cout << "[错误] 客户端私钥和证书不匹配" << std::endl; return false; } std::cout << "[客户端] SSL环境初始化成功" << std::endl; return true; } // 初始化Winsock bool InitializeWinsock() { WSADATA wsaData; int result = WSAStartup(MAKEWORD(2, 2), &wsaData); if (result != 0) { std::cout << "[错误] WSAStartup失败,错误码: " << result << std::endl; return false; } return true; } // 连接到服务器 bool ConnectToServer() { // 创建socket g_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (g_socket == INVALID_SOCKET) { std::cout << "[错误] 创建socket失败,错误码: " << WSAGetLastError() << std::endl; return false; } // 设置服务器地址 sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(DEFAULT_PORT); inet_pton(AF_INET, DEFAULT_ADDRESS, &serverAddr.sin_addr); // 连接到服务器 std::cout << "[客户端] 正在连接服务器 " << DEFAULT_ADDRESS << ":" << DEFAULT_PORT << " ..." << std::endl; if (connect(g_socket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { std::cout << "[错误] 连接失败,错误码: " << WSAGetLastError() << std::endl; closesocket(g_socket); g_socket = INVALID_SOCKET; return false; } std::cout << "[客户端] TCP连接成功" << std::endl; // 创建SSL对象 g_ssl = SSL_new(g_ssl_ctx); if (!g_ssl) { PrintSSLError("创建SSL对象失败"); closesocket(g_socket); g_socket = INVALID_SOCKET; return false; } // 将SSL绑定到socket if (SSL_set_fd(g_ssl, (int)g_socket) != 1) { PrintSSLError("绑定SSL到socket失败"); SSL_free(g_ssl); g_ssl = nullptr; closesocket(g_socket); g_socket = INVALID_SOCKET; return false; } // 执行SSL握手 std::cout << "[客户端] 正在进行SSL握手..." << std::endl; int ret = SSL_connect(g_ssl); if (ret != 1) { int err = SSL_get_error(g_ssl, ret); std::cout << "[错误] SSL握手失败,错误码: " << err << std::endl; PrintSSLError("SSL_connect"); SSL_free(g_ssl); g_ssl = nullptr; closesocket(g_socket); g_socket = INVALID_SOCKET; return false; } std::cout << "[客户端] SSL握手完成!" << std::endl; // 显示SSL信息 std::cout << "[客户端] 使用的加密套件: " << SSL_get_cipher(g_ssl) << std::endl; return true; } // 发送数据 bool SendData(const std::string& data) { if (!g_ssl) { std::cout << "[错误] SSL未初始化" << std::endl; return false; } int sent = SSL_write(g_ssl, data.c_str(), (int)data.length()); if (sent <= 0) { int err = SSL_get_error(g_ssl, sent); std::cout << "[错误] 发送失败,错误码: " << err << std::endl; PrintSSLError("SSL_write"); return false; } std::cout << "[客户端] 发送数据: \"" << data << "\" (" << sent << " 字节)" << std::endl; return true; } // 接收数据(非阻塞检查) void ReceiveData() { if (!g_ssl) { return; } // 设置socket为非阻塞模式 u_long mode = 1; ioctlsocket(g_socket, FIONBIO, &mode); char buffer[1024]; int received = SSL_read(g_ssl, buffer, sizeof(buffer) - 1); if (received > 0) { buffer[received] = '\0'; std::cout << "[客户端] 收到数据: \"" << buffer << "\" (" << received << " 字节)" << std::endl; } else if (received < 0) { int err = SSL_get_error(g_ssl, received); if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { // 真正的错误(不是"没有数据") if (err != SSL_ERROR_ZERO_RETURN) { // 忽略正常关闭 std::cout << "[错误] 接收失败,错误码: " << err << std::endl; } } } // 恢复为阻塞模式 mode = 0; ioctlsocket(g_socket, FIONBIO, &mode); } // 清理资源 void Cleanup() { if (g_ssl) { SSL_shutdown(g_ssl); SSL_free(g_ssl); g_ssl = nullptr; } if (g_socket != INVALID_SOCKET) { closesocket(g_socket); g_socket = INVALID_SOCKET; } if (g_ssl_ctx) { SSL_CTX_free(g_ssl_ctx); g_ssl_ctx = nullptr; } WSACleanup(); } // 打印菜单 void PrintMenu() { std::cout << "\n命令菜单:" << std::endl; std::cout << " 1 - 发送 \"hello\"" << std::endl; std::cout << " q - 退出程序" << std::endl; std::cout << "请输入命令: " << std::flush; } int main() { // 设置控制台UTF-8编码 SetConsoleOutputCP(CP_UTF8); std::locale::global(std::locale("")); std::cout << "========================================" << std::endl; std::cout << " SSL Client Native (纯OpenSSL+Winsock)" << std::endl; std::cout << "========================================" << std::endl; std::cout << std::endl; // 初始化Winsock if (!InitializeWinsock()) { std::cout << "按任意键退出..." << std::endl; _getch(); return 1; } // 初始化SSL if (!InitializeSSL()) { std::cout << "按任意键退出..." << std::endl; _getch(); Cleanup(); return 1; } // 连接到服务器 if (!ConnectToServer()) { std::cout << "按任意键退出..." << std::endl; _getch(); Cleanup(); return 1; } std::cout << "[客户端] 已连接到服务器,可以开始发送消息" << std::endl; // 主循环 PrintMenu(); bool running = true; while (running) { // 检查是否有数据可接收 ReceiveData(); // 检查键盘输入 if (_kbhit()) { char ch = _getch(); std::cout << ch << std::endl; if (ch == '1') { // 发送 "hello" if (SendData("hello")) { Sleep(100); // 等待一下让服务器响应 ReceiveData(); // 接收响应 } PrintMenu(); } else if (ch == 'q' || ch == 'Q') { running = false; } else { std::cout << "无效命令,请重新输入。" << std::endl; PrintMenu(); } } Sleep(100); } // 清理资源 std::cout << "[客户端] 正在断开连接..." << std::endl; Cleanup(); std::cout << "[客户端] 已断开连接" << std::endl; std::cout << "按任意键退出..." << std::endl; _getch(); return 0; }