我正在尝试用C语言编程连接到OpenAI API以进行一些查询,但在调用try_connect()
时失败,错误信息如下:
SSL connect errorSSL: 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]; // 例如,地址192.168.4.5有octect[0]=192和octect[3]=5 uint32_t octet_networkOrder;} openai;// 用正确的IP地址填充字段//openai.octet[0]=xx//openai.octet[0]=xx//openai.octet[0]=xx//openai.octet[0]=xxbool try_connect(){ //创建TCP套接字 int fd = ::socket(AF_INET, SOCK_STREAM, 0); if (fd == -1) return false; //SSL初始化 ERR_clear_error(); SSL_CTX* sslCtx = SSL_CTX_new(TLS_method()); if (sslCtx == NULL) { //SSL上下文创建失败 printSSLErrors(); return false; } //设置CA文件 if (SSL_CTX_load_verify_file(sslCtx, "/etc/ssl/cert.pem") == 0) { //SSL加载CA文件失败 printSSLErrors(); return false; } //设置证书 if (SSL_CTX_use_certificate_chain_file(sslCtx, "mycert.pem") != 1) { //SSL证书文件设置失败 printSSLErrors(); return false; } if (SSL_CTX_use_PrivateKey_file(sslCtx, "mykey.pem", SSL_FILETYPE_PEM) != 1) { //SSL私钥文件设置失败 printSSLErrors(); return false; } if (SSL_CTX_check_private_key(sslCtx) != 1) { //SSL私钥检查失败 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; } //连接 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) // 开始SSL客户端握手 { printf("SSL connect error"); printSSLErrors(); return false; } return true;}
这段代码已经在其他不需要客户端认证的服务器上测试过,运行正常。 api.openai.com
需要客户端认证,因此在try_connect()
中失败。mykey.pem
和mycert.pem
是从Keychain Access应用程序中导出的文件,起始于com.apple.systemdefault
证书和私钥。我无法在其他系统上进行测试..
有趣的是,在同一系统上使用提供的curl示例,curl运行正常:
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 s_client -connect api.openai.com:443 -servername api.openai.com -cert mycert.pem -key mykey.pem -CAfile /etc/ssl/cert.pem
它能够连接
Connecting to 104.18.7.192CONNECTED(00000006)depth=2 C=US, O=Google Trust Services LLC, CN=GTS Root R1verify return:1depth=1 C=US, O=Google Trust Services LLC, CN=GTS CA 1P5verify return:1depth=0 CN=api.openai.comverify return:1---Certificate chain 0 s:CN=api.openai.com i:C=US, O=Google Trust Services LLC, CN=GTS CA 1P5 a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256 v:NotBefore: Feb 25 00:03:27 2024 GMT; NotAfter: May 25 00:03:26 2024 GMT 1 s:C=US, O=Google Trust Services LLC, CN=GTS CA 1P5 i:C=US, O=Google Trust Services LLC, CN=GTS Root R1 a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256 v:NotBefore: Aug 13 00:00:42 2020 GMT; NotAfter: Sep 30 00:00:42 2027 GMT 2 s:C=US, O=Google Trust Services LLC, CN=GTS Root R1 i:C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA a:PKEY: rsaEncryption, 4096 (bit); sigalg: RSA-SHA256 v:NotBefore: Jun 19 00:00:42 2020 GMT; NotAfter: Jan 28 00:00:42 2028 GMT---Server certificate-----BEGIN CERTIFICATE-----BLA BLA BLA-----END CERTIFICATE-----subject=CN=api.openai.comissuer=C=US, O=Google Trust Services LLC, CN=GTS CA 1P5---No client certificate CA names sentPeer signing digest: SHA256Peer signature type: RSA-PSSServer Temp Key: X25519, 253 bits---SSL handshake has read 4691 bytes and written 402 bytesVerification: OK---New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384Server public key is 2048 bitThis TLS version forbids renegotiation.Compression: NONEExpansion: NONENo ALPN negotiatedEarly data was not sentVerify return code: 0 (ok)
所以我认为我拥有的证书和密钥是正确的,并且被服务器接受
回答:
我找到了原因。
SSL_set_tlsext_host_name()
应该在SSL_connect()
调用之前调用,以便SSL知道服务器的主机名并完成验证。
我添加了这样的调用,现在它工作了。
希望这个答案对某人有用