英文:
Can I read private SSH key without knowing its type with paramiko?
问题
我正在使用paramiko.SSHClient
通过SSH密钥对远程服务器进行身份验证。为此,我有几个不同服务器的配置,其中包含SSH密钥的路径。而且碰巧有一些是Ed25519类型,另一些是RSA类型。仅使用密码进行连接是不可接受的选项。
起初,我想使用以下代码进行连接:
keyfilename = build_configuration['sshPrivateKeypath']
keyfilename = os.path.expandvars(keyfilename)
key = paramiko.Ed25519Key.from_private_key_file(keyfilename)
ssh.connect(ssh_host, port=port, username=ssh_user, pkey=key)
但结果是,某些密钥是RSA类型而不是Ed25519类型。
是否有一种方法可以读取任何类型的SSH密钥,而不使用具体的实现?或者也许有一种方法可以确定密钥的类型吗?
还是唯一的方法是将所有密钥更改为已知类型?
从文档中无法找到这些信息。
在测试真实配置之前,我尝试从Jupyter上的本地PC上读取SSH密钥。当像这样读取错误类型的密钥时:
keyfilename = "%USERPROFILE%/.ssh/id_rsa"
keyfilename = os.path.expandvars(keyfilename)
privateKey = paramiko.Ed25519Key.from_private_key_file(keyfilename)
它会引发以下错误:
---------------------------------------------------------------------------
SSHException Traceback (most recent call last)
Cell In[2], line 6
4 keyfilename = "%USERPROFILE%/.ssh/id_rsa"
5 keyfilename = os.path.expandvars(keyfilename)
----> 6 privateKey = paramiko.Ed25519Key.from_private_key_file(keyfilename)
7 display(key)
8 display(privateKey)
File ~\AppData\Local\Programs\Python\Python311\Lib\site-packages\paramiko\pkey.py:421, in PKey.from_private_key_file(cls, filename, password)
400 @classmethod
401 def from_private_key_file(cls, filename, password=None):
402 """
403 Create a key object by reading a private key file. If the private
404 key is encrypted and ``password`` is not ``None``, the given password
(...)
419 :raises: `.SSHException` -- if the key file is invalid
420 """
--> 421 key = cls(filename=filename, password=password)
422 return key
File ~\AppData\Local\Programs\Python\Python311\Lib\site-packages\paramiko\ed25519key.py:65, in Ed25519Key.__init__(self, msg, data, filename, password, file_obj)
62 pkformat, data = self._read_private_key("OPENSSH", file_obj)
64 if filename or file_obj:
---> 65 signing_key = self._parse_signing_key_data(data, password)
67 if signing_key is None and verifying_key is None:
68 raise ValueError("need a key")
File ~\AppData\Local\Programs\Python\Python311\Lib\site-packages\paramiko\ed25519key.py:114, in Ed25519Key._parse_signing_key_data(self, data, password)
112 pubkey = Message(message.get_binary())
113 if pubkey.get_text() != self.name:
--> 114 raise SSHException("Invalid key")
115 public_keys.append(pubkey.get_binary())
117 private_ciphertext = message.get_binary()
SSHException: Invalid key
因此,我期望在尝试与真实配置进行相同操作时会引发相同的错误。
英文:
I'm authenticating to remote server with paramiko.SSHClient
by SSH key. For it I have several configs for different servers with paths to SSH keys. And it happened so that some of them are Ed25519 and some are RSA. Connecting with just password is not an acceptable option.
At first I wanted to connect with the following code:
keyfilename = build_configuration['sshPrivateKeypath']
keyfilename = os.path.expandvars(keyfilename)
key = paramiko.Ed25519Key.from_private_key_file(keyfilename)
ssh.connect(ssh_host, port=port, username=ssh_user, pkey=key)
But it turned out that some keys are RSA (not Ed25519).
Is there any way of reading SSH key of any type without using concrete implementation? Or maybe there's some way to determine the type?
Or the only way is to change all keys to known type?
Could not figure that of from documentation
Before testing real configs I tried to read SSH key on my local PC from Jupyter.
When reading wrong type like this:
keyfilename = "%USERPROFILE%/.ssh/id_rsa"
keyfilename = os.path.expandvars(keyfilename)
privateKey = paramiko.Ed25519Key.from_private_key_file(keyfilename)
It throws:
---------------------------------------------------------------------------
SSHException Traceback (most recent call last)
Cell In[2], line 6
4 keyfilename = "%USERPROFILE%/.ssh/id_rsa"
5 keyfilename = os.path.expandvars(keyfilename)
----> 6 privateKey = paramiko.Ed25519Key.from_private_key_file(keyfilename)
7 display(key)
8 display(privateKey)
File ~\AppData\Local\Programs\Python\Python311\Lib\site-packages\paramiko\pkey.py:421, in PKey.from_private_key_file(cls, filename, password)
400 @classmethod
401 def from_private_key_file(cls, filename, password=None):
402 """
403 Create a key object by reading a private key file. If the private
404 key is encrypted and ``password`` is not ``None``, the given password
(...)
419 :raises: `.SSHException` -- if the key file is invalid
420 """
--> 421 key = cls(filename=filename, password=password)
422 return key
File ~\AppData\Local\Programs\Python\Python311\Lib\site-packages\paramiko\ed25519key.py:65, in Ed25519Key.__init__(self, msg, data, filename, password, file_obj)
62 pkformat, data = self._read_private_key("OPENSSH", file_obj)
64 if filename or file_obj:
---> 65 signing_key = self._parse_signing_key_data(data, password)
67 if signing_key is None and verifying_key is None:
68 raise ValueError("need a key")
File ~\AppData\Local\Programs\Python\Python311\Lib\site-packages\paramiko\ed25519key.py:114, in Ed25519Key._parse_signing_key_data(self, data, password)
112 pubkey = Message(message.get_binary())
113 if pubkey.get_text() != self.name:
--> 114 raise SSHException("Invalid key")
115 public_keys.append(pubkey.get_binary())
117 private_ciphertext = message.get_binary()
SSHException: Invalid key
So I expect that it will throw the same thing when I try it with real configs.
答案1
得分: 2
Simplest way is to use key_filename
parameter of SSHClient.connect
instead of pkey
.
If you want to use pkey
for whatever reason, you would have to replicate what SSHClient
does internally, when handling the key_filename
. Roughly something like this (untested):
for pkey_class in (RSAKey, DSSKey, ECDSAKey, Ed25519Key):
try:
key = pkey_class.from_private_key_file(key_filename)
break
except Exception as e:
pass
英文:
Simplest way is to use key_filename
parameter of SSHClient.connect
instead of pkey
.
If you want to use pkey
for whatever reason, you would have to replicate what SSHClient
does internally, when handling the key_filename
. Roughly something like this (untested):
for pkey_class in (RSAKey, DSSKey, ECDSAKey, Ed25519Key):
try:
key = pkey_class.from_private_key_file(key_filename)
break
except Exception as e:
pass
答案2
得分: 1
def get_key_class_name(filepath, passphrase):
for pkey_class in (RSAKey, DSSKey, ECDSAKey, Ed25519Key):
try:
key = pkey_class.from_private_key_file(filepath, passphrase)
class_name = str(pkey_class).split('.')[-1].rstrip("'>")
return class_name
except Exception as e:
# print("An exception occurred: {}".format(e))
pass
def proper_method_call(filepath, passphrase):
key_class_name = get_key_class_name(filepath, passphrase)
key_class = getattr(paramiko, key_class_name)
key_method = getattr(key_class, "from_private_key_file")
key = key_method(filepath, passphrase)
return key
rsa_key_with_passphrase = proper_method_call(rsa_key_filename, key_passphrase)
pass it as an argument: `pkey=rsa_key_with_passphrase`
英文:
def get_key_class_name(filepath, passphrase):
for pkey_class in (RSAKey, DSSKey, ECDSAKey, Ed25519Key):
try:
key = pkey_class.from_private_key_file(filepath, passphrase)
class_name = str(pkey_class).split('.')[-1].rstrip(">'")
return class_name
except Exception as e:
# print("An exception occurred: {}".format(e))
pass
def proper_method_call(filepath, passphrase):
key_class_name = get_key_class_name(filepath, passphrase)
key_class = getattr(paramiko, key_class_name)
key_method = getattr(key_class, "from_private_key_file")
key = key_method(filepath, passphrase)
return key
rsa_key_with_passphrase = proper_method_call(rsa_key_filename, key_passphrase)
pass it as an argument: pkey=rsa_key_with_passphrase
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论