MacOS下C语言调用OpenAI API时SSL握手失败问题求助
解决C语言调用OpenAI API的SSL握手失败问题
问题概述
调用OpenAI API时,try_connect()函数触发SSL握手失败,报错:
SSL connect error SSL: error:0A000410:SSL routines::ssl/tls alert handshake failure
相关代码如下:
void printSSLErrors() { char errstr[256] = {}; unsigned long err = ERR_get_error(); while (err != 0) { ERR_error_string_n(err, &errstr[0], sizeof(errstr)); printf("SSL: %s", &errstr); err = ERR_get_error(); } } union ipaddr { uint8_t octect[4]; // e.g. address 192.168.4.5 has octect[0]=192 and octect[3]=5 uint32_t octet_networkOrder; } openai; // fill the fields with the correct IP address //openai.octet[0]=xx //openai.octet[0]=xx //openai.octet[0]=xx //openai.octet[0]=xx bool try_connect() { //TCP socket creation int fd = ::socket(AF_INET, SOCK_STREAM, 0); if (fd == -1) return false; //SSL init ERR_clear_error(); SSL_CTX* sslCtx = SSL_CTX_new(TLS_method()); if (sslCtx == NULL) { //SSL context creation failed printSSLErrors(); return false; } // CA file setup if (SSL_CTX_load_verify_file(sslCtx, "/etc/ssl/cert.pem") == 0) { //SSL load CA file failure printSSLErrors(); return false; } // Certificate setup if (SSL_CTX_use_certificate_chain_file(sslCtx, "mycert.pem") != 1) { //SSL certificate file setup failed printSSLErrors(); return false; } if (SSL_CTX_use_PrivateKey_file(sslCtx, "mykey.pem", SSL_FILETYPE_PEM) != 1) { //SSL private key file setup failed printSSLErrors(); return false; } if (SSL_CTX_check_private_key(sslCtx) != 1) { //SSL private key check failed printSSLErrors(); return false; } SSL_CTX_clear_mode(sslCtx, SSL_MODE_AUTO_RETRY); SSL* m_ssl = SSL_new(m_sslCtx); if (m_ssl == NULL) { printSSLErrors(); return false; } if (SSL_set_fd(m_ssl, m_fd) != 1) { printSSLErrors(); return false; } //connecting sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_port = htons(443); addr.sin_addr.s_addr = openai.octet_networkOrder; int ret = ::connect(m_fd, (sockaddr *)&addr, sizeof(sockaddr_in)); if (ret == -1) return false; if (SSL_connect(m_ssl) != 1) // start SSL client handshaking { printf("SSL connect error"); printSSLErrors(); return false; } return true; }
同一系统中,官方curl示例可正常调用API:
curl https://api.openai.com/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -d '{ "model": "gpt-3.5-turbo", "messages": [ { "role": "system", "content": "You are a helpful assistant." }, { "role": "user", "content": "Hello!" } ] }'
使用openssl命令测试证书与密钥,连接成功,验证结果为OK,说明证书和密钥本身无问题:
openssl s_client -connect api.openai.com:443 -servername api.openai.com -cert mycert.pem -key mykey.pem -CAfile /etc/ssl/cert.pem
问题根源
代码存在两处关键错误:
- 变量名错误:创建SSL对象时使用了未定义的
m_sslCtx,应为sslCtx;后续调用SSL_set_fd和connect时使用了未定义的m_fd,应为之前创建的socket文件描述符fd。 - 缺少SNI设置:OpenAI API服务器依赖SNI(Server Name Indication)返回对应证书,代码未设置SNI导致握手失败。
修复方案
1. 修正变量名错误
将以下代码:
SSL* m_ssl = SSL_new(m_sslCtx);
改为:
SSL* m_ssl = SSL_new(sslCtx);
将:
if (SSL_set_fd(m_ssl, m_fd) != 1)
改为:
if (SSL_set_fd(m_ssl, fd) != 1)
将:
int ret = ::connect(m_fd, (sockaddr *)&addr, sizeof(sockaddr_in));
改为:
int ret = ::connect(fd, (sockaddr *)&addr, sizeof(sockaddr_in));
2. 添加SNI设置
在SSL_set_fd之后、connect之前,添加SNI配置:
if (SSL_set_tlsext_host_name(m_ssl, "api.openai.com") != 1) { printSSLErrors(); return false; }
3. 可选:指定最小TLS版本
为确保兼容性,在创建SSL上下文后添加:
SSL_CTX_set_min_proto_version(sslCtx, TLS1_2_VERSION);
修正后的关键代码片段
// ... 省略其他代码 ... SSL_CTX_clear_mode(sslCtx, SSL_MODE_AUTO_RETRY); SSL* m_ssl = SSL_new(sslCtx); if (m_ssl == NULL) { printSSLErrors(); return false; } if (SSL_set_fd(m_ssl, fd) != 1) { printSSLErrors(); return false; } // 添加SNI设置 if (SSL_set_tlsext_host_name(m_ssl, "api.openai.com") != 1) { printSSLErrors(); return false; } //connecting sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_port = htons(443); addr.sin_addr.s_addr = openai.octet_networkOrder; int ret = ::connect(fd, (sockaddr *)&addr, sizeof(sockaddr_in)); // ... 省略其他代码 ...
内容的提问来源于stack exchange,提问作者HeapOverflow
相关产品推荐
相关产品推荐

