curl/libcurl 使用 CURLOPT_SSH_PRIVATE_KEYFILE 配置 SSH 私钥进行 SFTP/SCP 认证
【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl
本文基于 curl 官方 libcurl 选项文档,讲解CURLOPT_SSH_PRIVATE_KEYFILE的完整用法:它用于为 SFTP 与 SCP 协议指定 SSH 私钥文件路径,配合CURLOPT_KEYPASSWD处理加密私钥、配合CURLOPT_SSH_PUBLIC_KEYFILE处理公钥派生失败场景。读完本文,你将掌握在 libcurl 程序中配置密钥认证的关键调用方式、默认密钥查找规则、认证失败的常见原因,以及 libcurl 内部如何借助 libssh2/libssh 后端完成公钥认证。
选项概览
CURLOPT_SSH_PRIVATE_KEYFILE用于设置 SSH 认证使用的私钥文件路径。它在 libcurl 选项表(include/curl/curl.h)中定义,从 curl 7.16.1 版本起可用,仅适用于 SFTP 与 SCP 两种协议。
函数原型如下:
#include <curl/curl.h> CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSH_PRIVATE_KEYFILE, char *filename);其中filename是私钥文件的路径字符串,可以是绝对路径或相对路径。该选项属于字符串类选项,通过curl_easy_setopt传入后,libcurl 会拷贝该字符串(见下文源码分析),因此应用程序在调用之后无需继续保持该字符串有效。
默认行为:未设置时使用哪些私钥
如果不使用本选项指定私钥,libcurl 会按照以下顺序自动查找默认私钥文件:
- 若设置了
HOME环境变量,依次尝试$HOME/.ssh/id_rsa和$HOME/.ssh/id_dsa; - 若
HOME未设置,或上述路径均不存在,则在当前工作目录下依次尝试id_rsa和id_dsa。
上述默认查找逻辑可以在源码 lib/vssh/vssh.c 的Curl_ssh_setup_pkey()函数中看到完整实现(约第 365~448 行):它先读取HOME环境变量拼出~/.ssh/id_rsa,不存在则尝试~/.ssh/id_dsa,再退而求其次在当前目录查找,全部失败后置为空字符串以避免产生误导性日志。
基础用法示例
下面是一个最小可运行的示例,它使用sftp://协议连接服务器,显式指定私钥文件,并用CURLOPT_KEYPASSWD提供私钥口令(若私钥有口令保护):
int main(void) { CURL *curl = curl_easy_init(); if(curl) { CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "sftp://example.com/file"); curl_easy_setopt(curl, CURLOPT_SSH_PRIVATE_KEYFILE, "/home/clarkkent/.ssh/id_rsa"); curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "password"); result = curl_easy_perform(curl); curl_easy_cleanup(curl); } }同样的选项也适用于scp://协议,例如将 URL 换成scp://example.com/path/to/file即可。
关于口令参数的补充
- 私钥文件若设置了口令保护,必须通过
CURLOPT_KEYPASSWD传入口令,否则认证会失败。相关细节参见 docs/libcurl/opts/CURLOPT_KEYPASSWD.md。 - 在 lib/vssh/vssh.c 第 430~432 行可以看到,未设置口令时 libcurl 会将其置为空字符串
""传给 SSH 后端,因此无口令私钥也能正常工作。
公钥的派生规则
SSH 公钥认证需要同时具备公钥与私钥。libcurl 的处理规则是:
- 优先从私钥派生公钥:SSH 库在可能的情况下会直接从私钥文件中派生对应的公钥,此时无需单独指定公钥文件;
- 派生失败时必须显式提供公钥:如果 SSH 库无法从私钥推导出公钥,且应用程序也没有通过
CURLOPT_SSH_PUBLIC_KEYFILE指定公钥文件,则本次传输会失败。
在 libssh2 后端(lib/vssh/libssh2.c 的ssh_state_auth_pkey(),约第 1493~1524 行)中,认证调用为:
libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session, user, strlen(user), sshc->pub_key, sshc->priv_key, sshc->passphrase);可以看到公钥(pub_key)与私钥(priv_key)同时被传入认证函数。当用户未显式指定公钥时,lib/vssh/vssh.c 第 418~428 行的注释明确指出:libcurl 会保持pub_key为 NULL,交由 SSH 库从私钥中提取公钥。
在 libssh 后端(lib/vssh/libssh.c 的myssh_in_AUTH_PKEY_INIT(),约第 856~899 行)中,流程类似:有私钥时调用ssh_pki_import_privkey_file()导入私钥;无显式私钥时则调用ssh_userauth_publickey_auto()使用默认密钥自动认证。
因此实际使用中常见两种配置方式:
| 场景 | 配置方式 |
|---|---|
| 私钥可派生公钥(大多数 RSA/Ed25519 私钥) | 仅设置CURLOPT_SSH_PRIVATE_KEYFILE即可 |
| 私钥无法派生公钥 | 同时设置CURLOPT_SSH_PRIVATE_KEYFILE与CURLOPT_SSH_PUBLIC_KEYFILE |
公钥选项的详细说明见 docs/libcurl/opts/CURLOPT_SSH_PUBLIC_KEYFILE.md。
选项生效时机与连接复用
文档明确说明:CURLOPT_SSH_PRIVATE_KEYFILE仅在新建 SSH 连接时生效。私钥用于 libcurl 建立新 SSH 连接的时刻;一旦连接成功建立并通过验证,该连接即被视为"已审查"(vetted),之后 libcurl可能复用这条连接——即使在此期间修改了本选项的值,也不会影响已复用连接上的认证身份。
这一点对长连接、连接池复用场景有实际意义:如果你需要在同一程序中切换不同身份访问不同服务器,应确保不同身份使用独立的 easy handle,或通过连接相关选项(如 URL 中的主机端口差异)避免复用旧连接。
底层实现:选项如何存储与传递
CURLOPT_SSH_PRIVATE_KEYFILE的处理入口位于 lib/setopt.c 的setopt_cptr_ssh()(约第 2148~2162 行):
case CURLOPT_SSH_PRIVATE_KEYFILE: /* * Use this file instead of the $HOME/.ssh/id_dsa file */ return Curl_setstropt(data, STRING_SSH_PRIVATE_KEY, ptr);它通过Curl_setstropt()将字符串拷贝并存入data->set结构中的字符串槽位STRING_SSH_PRIVATE_KEY(该枚举定义于 lib/urldata.h 第 761 行,注释为"path to the private key file for auth")。这正解释了文档中"应用程序无需在设置后继续保留该字符串"的约定——libcurl 内部已经完成了拷贝。
随后在建立 SSH 连接时,lib/vssh/vssh.c 的Curl_ssh_setup_pkey()会读取该字符串(第 373~378 行),有则直接使用,无则走默认查找逻辑。认证阶段再由具体 SSH 后端(libssh2 或 libssh)读取并执行密钥认证。
从代码结构看,curl 对 SSH 协议族采用了可插拔后端设计:lib/vssh/目录下同时存在 libssh2.c(libssh2 后端)与 libssh.c(libssh 后端),两者都实现了私钥导入与公钥认证的状态机逻辑。具体使用哪个后端取决于编译时的配置。
返回值与错误处理
curl_easy_setopt()总是返回CURLcode类型:
CURLE_OK(值为 0)表示设置成功;- 非零值表示出错,具体错误码参见 docs/libcurl/libcurl-errors.md。
需要注意的是,CURLE_OK只代表选项被正确接收并存储,并不代表私钥文件真实存在、格式合法或认证能够通过。私钥加载失败、口令错误等认证阶段的问题,会在curl_easy_perform()阶段以CURLE_LOGIN_DENIED等错误码返回。例如在 lib/vssh/libssh.c 第 882~886 行,私钥文件加载失败时会打印"Could not load private key file %s"并返回CURLE_LOGIN_DENIED。
相关选项与测试验证
与 SSH 认证相关的配套选项包括:
- CURLOPT_SSH_PUBLIC_KEYFILE:指定公钥文件路径;
- CURLOPT_KEYPASSWD:私钥口令;
- CURLOPT_SSH_AUTH_TYPES:指定允许的 SSH 认证方式(如仅启用
CURLSSH_AUTH_PUBLICKEY); CURLOPT_SSH_HOST_PUBLIC_KEY_MD5/CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256:校验服务器主机公钥指纹。
在测试套件中,tests/libtest/lib582.c 与 tests/libtest/lib583.c 是直接引用该选项的 libtest 用例,可用于验证选项的编译与使用方式;docs/libcurl/symbols-in-versions中则记录了CURLOPT_SSH_PRIVATE_KEYFILE自 7.16.1 起进入公共 API 的版本信息。
常见问题排查要点
结合文档与源码,遇到 SSH 密钥认证失败时可依次检查:
- 私钥路径是否正确:路径可以是绝对的或相对的,但必须是 libcurl 运行进程可读的文件;
- 口令是否匹配:私钥有口令时必须设置
CURLOPT_KEYPASSWD,且 libcurl 会直接将其透传给 SSH 后端; - 公钥是否缺失:若 SSH 库无法从私钥派生公钥,必须同时设置
CURLOPT_SSH_PUBLIC_KEYFILE,否则传输失败; - 认证方式是否被启用:确认
CURLOPT_SSH_AUTH_TYPES中包含了CURLSSH_AUTH_PUBLICKEY,否则私钥配置不会被使用; - 连接是否被复用:修改本选项只影响新建连接,排查问题时可通过
curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1L)强制建立新连接来验证。
【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考