You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Paramiko从字符串读取OpenSSH私钥的报错与正确实现问询

问题:使用Paramiko从字符串读取OpenSSH私钥失败

现象与报错

自定义OPENSSH_creator函数直接传入字符串格式的私钥key_str作为key_blob,实例化RSAKey/DSSKey/ECDSAKey类时触发AttributeError,报错信息如下:

Traceback (most recent call last):
  File "/home/meretricula/work/taf.transport.ssh/taf/transport/ssh/local.py", line 40, in <module>
    print(OPENSSH_creator(key_blob=key_str, password=None))
  File "/home/meretricula/work/taf.transport.ssh/taf/transport/ssh/local.py", line 20, in OPENSSH_creator
    key = key_class(key_blob, password)
  File "/home/meretricula/.pyenv/versions/taf.transport.ssh_3.9.18/lib/python3.9/site-packages/paramiko/rsakey.py", line 71, in __init__
    self._check_type_and_load_cert(
  File "/home/meretricula/.pyenv/versions/taf.transport.ssh_3.9.18/lib/python3.9/site-packages/paramiko/pkey.py", line 792, in _check_type_and_load_cert
    msg.rewind()
AttributeError: 'str' object has no attribute 'rewind'

Process finished with exit code 1

传入StringIO(key_str)同样无效,仅当传入paramiko.message.Message()实例作为key_blob时才能正常工作。

报错原因

  1. 参数类型误解:RSAKey/DSSKey/ECDSAKey的构造函数,当第一个参数为字符串时,会被当作密钥文件的路径处理,而非密钥内容。内部逻辑会尝试打开该路径读取文件,后续处理中误将字符串当作Message对象调用rewind()方法,导致属性错误。
  2. 不支持类文件对象直接传入:这些密钥类的构造函数不接受StringIO这类类文件对象作为第一个参数,因此传入后仍无法正确解析。

正确实现方式

方法1:使用PKey.from_private_key()(推荐)

paramiko.pkey.PKey提供了from_private_key()方法,专门处理类文件对象形式的密钥内容,会自动识别密钥类型,无需手动循环尝试:

from io import StringIO
from paramiko.pkey import PKey
from paramiko.ssh_exception import SSHException

def OPENSSH_creator(key_blob, password):
    try:
        # 将字符串包装为类文件对象
        key_file = StringIO(key_blob)
        # 自动解析密钥类型并加载
        return PKey.from_private_key(key_file, password=password)
    except SSHException as e:
        print(f"密钥加载失败: {str(e)}")
        return None

方法2:手动构造Message实例(复杂场景)

若需保留循环尝试密钥类的逻辑,需先解析OpenSSH私钥的base64内容,构造Message实例后传入:

import base64
from paramiko.message import Message
from paramiko.rsakey import RSAKey
from paramiko.dsskey import DSSKey
from paramiko.ecdsakey import ECDSAKey
from paramiko.ssh_exception import SSHException

def OPENSSH_creator(key_blob, password):
    # 提取OpenSSH私钥中的base64内容
    key_lines = key_blob.strip().split('\n')
    if not (key_lines[0].startswith('-----BEGIN OPENSSH PRIVATE KEY-----') 
            and key_lines[-1].startswith('-----END OPENSSH PRIVATE KEY-----')):
        raise ValueError("无效的OPENSSH私钥格式")
    
    b64_content = ''.join(key_lines[1:-1])
    key_bytes = base64.b64decode(b64_content)
    
    # 构造Message实例并跳过开头的标识字符串
    msg = Message(key_bytes)
    msg.get_string()  # 跳过"openssh-key-v1"
    
    # 循环尝试各密钥类
    for key_class in (RSAKey, DSSKey, ECDSAKey):
        try:
            msg.rewind()
            msg.get_string()  # 重新跳过标识
            return key_class(msg, password)
        except SSHException:
            continue
    return None

关于Paramiko尝试多种加密算法的说明

你提到的Paramiko 3.3.1版本client.py第724-732行代码,是密钥加载的核心逻辑:

  • 当通过key_filename加载密钥时,Paramiko会依次尝试RSAKey、DSSKey、ECDSAKey、Ed25519Key四种密钥类
  • 每个类会尝试解析文件内容,若解析失败抛出SSHException,则自动切换到下一个类
  • 该设计的目的是兼容不同类型的密钥文件,无需用户手动指定密钥类型,Paramiko会自动兜底尝试所有支持的类型,直到找到可正确解析的类

内容的提问来源于stack exchange,提问作者Jirafey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 08:54:51