OpenSSH 未提供 authorized_keys 标准解析 API,第三方解析需严格遵循其私有规则:正确处理 options 引号转义、base64 blob 边界、注释、空格语义及公钥有效性校验。

authorized_keys 文件格式其实没有标准解析器
OpenSSH 官方不提供 C++(或任何语言)的 authorized_keys 解析 API,它只在 sshd 内部用 C 实现了一套私有解析逻辑,且未暴露为库。这意味着你不能直接 #include 一个头文件就“安全解析”——所有第三方解析都属于自行实现,必须严格遵循 OpenSSH 的实际行为,否则会漏掉合法密钥或误判恶意输入。
手动解析要处理三类字段:key type、base64 blob、options
一行典型的 authorized_keys 格式是:command="...",no-port-forwarding ssh-rsa AAAAB3NzaC...。看似简单,但真实场景中存在大量边界情况:
- 选项(options)部分可为空、可含空格/等号/引号,且必须按逗号分隔;引号内逗号不拆分(如
environment="PATH=/bin:/usr/bin") - 公钥 blob 是 base64 编码,但末尾可能带注释(空格后任意字符串),注释里允许空格和
#字符(#不是行注释!) - key type(如
ssh-rsa,ecdsa-sha2-nistp256)必须精确匹配,大小写敏感,且不能是任意字符串 - 整行可能有前导/尾随空白,但中间空白不可随意 trim —— 比如
from="192.168.1.*"中的空格是语义的一部分
别用正则硬刚,用现成的轻量级 parser 更可靠
自己写状态机解析虽可控,但容易忽略 OpenSSH 的隐式规则(比如对反斜杠转义的支持、对非 ASCII 字符的容忍度)。更务实的做法是复用已被验证的 C/C++ 库:
-
libssh提供ssh_pki_import_pubkey_file(),但它只读单个公钥文件,不支持直接解析authorized_keys多行内容 -
libssh2没有内置解析器,需自行拆行 + 调用libssh2_userauth_host_based_frommemory()类接口,但该函数不校验 options - 推荐用
sshtools(C++17,header-only)里的sshtools::authorized_keys::parse_line(),它显式处理了引号转义、base64 边界、option key/value 分离,并返回结构化对象 - 若只能用标准库,至少用
std::string_view做无拷贝切片,先跳过首尾空格,再找第一个非空格非逗号字符定位 key type 起始点,避免std::stringstream错误吞掉中间空格
解析后务必校验公钥 blob 的有效性,而非仅 base64 decode
仅仅 base64 解码成功不代表是合法公钥。OpenSSH 在加载时会做额外检查:
立即学习“C++免费学习笔记(深入)”;
- 解码后字节流必须以有效算法标识开头(如
"ssh-rsa"的 UTF-8 字节 + 长度前缀) - RSA 密钥的模数长度必须 ≥ 1024 位(OpenSSH 8.8+ 默认拒绝
- ECDSA 密钥的曲线名称必须在白名单内(
nistp256,nistp384,nistp521) - Ed25519 公钥长度固定为 32 字节,多一个少一个都不行
这些校验不能省略,否则攻击者可通过构造畸形 blob 触发后续逻辑错误(比如 buffer over-read 或类型混淆)。
真正麻烦的不是拆字段,而是 OpenSSH 每次版本更新都可能微调解析策略 —— 比如 9.0 版本加强了对 option 值中未闭合引号的拒绝,而旧 parser 会静默截断。所以如果你的程序需要长期兼容,最好把解析逻辑和 OpenSSH 的 ssh-keygen -lf 输出做对齐校验,而不是只信自己的代码。


















