20

I'm trying to use Paramiko to connect to an SSH server from Python. This is what I tried so far:

>>> import paramiko
>>> import os
>>> privatekeyfile = os.path.expanduser('~/.ssh/id_rsa')
>>> mykey = paramiko.RSAKey.from_private_key_file(privatekeyfile)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/paramiko/pkey.py", line 198, in from_private_key_file
    key = cls(filename=filename, password=password)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/paramiko/rsakey.py", line 51, in __init__
    self._from_private_key_file(filename, password)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/paramiko/rsakey.py", line 163, in _from_private_key_file
    data = self._read_private_key_file('RSA', filename, password)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/paramiko/pkey.py", line 280, in _read_private_key_file
    data = self._read_private_key(tag, f, password)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/paramiko/pkey.py", line 323, in _read_private_key
    raise PasswordRequiredException('Private key file is encrypted')
paramiko.PasswordRequiredException: Private key file is encrypted

As you can see, it's failing because my private key is encrypted. However, the password is stored in my OS X login keychain, and when I type ssh host it won't ask for it (rather, it only asks once, then remembers it until the next reboot). Is there a way to make paramiko use the password / fetch it from the keychain, like ssh does?

houbysoft
  • 29,496
  • 22
  • 91
  • 153

3 Answers3

14

The following approach seems to work fine (on OS X, with the usual setup of encrypted private keys that have passphrases stored in the keychain, without any user interaction):

import paramiko

ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.connect(HOST, username=USER, look_for_keys=False)
...
ssh.close()

It seems that look_for_keys=False is not absolutely necessary. However, if you use it you will get much better error messages in the case of an authentication failure ("AuthenticationException" instead of "PasswordRequiredException").


If you really want to use private keys directly, you could do the following:

import os
import paramiko
import keyring

keyfile = os.path.expanduser('~/.ssh/id_rsa')
password = keyring.get_password('SSH', keyfile)
key = paramiko.RSAKey.from_private_key_file(keyfile, password=password)

However, based on my testing, this is not needed. The above solution that uses ssh.connect in a straightforward manner should be sufficient.

Jukka Suomela
  • 11,423
  • 4
  • 32
  • 45
9

The RSAKey.from_private_key_file() is inherited from PKey(); an optional parameter of this method is a password. To quote:

If the private key is encrypted and password is not None, the given password will be used to decrypt the key (otherwise PasswordRequiredException is thrown).

As you're not passing a password and your key is encrypted this exception will always be thrown. There's only one way round this problem, to actually give the method a password. You, therefore, need a way of getting the password out of the OSXKeychain.

You could use the cross-platform Keyring module to do this.

Ben
  • 48,249
  • 32
  • 114
  • 131
  • I ended up just creating a new key pair which did not have a password and using that, but accepting this since this would've worked probably – houbysoft Apr 24 '13 at 22:34
  • That was my own solution when faced with this situation @houbysoft... it just didn't answer the question :-). – Ben Apr 26 '13 at 17:04
  • So, The password is being used as the passphrase die decrypting? because I just tried that and it didn't work! – Sameer Asal Apr 07 '16 at 23:19
  • It's not possible to get the private key from ssh-agent (without memory dump). https://stackoverflow.com/a/2976729/952234 – Yaroslav Nikitenko Jun 10 '19 at 08:34
0

Using encrypted private key in Paramiko is not possible, because ssh-agent doesn't give private key (without memory dump).

The solution would be to use subprocess and call ssh command from that (as any usual command). It didn't me ask for decryption of the private key (it uses ssh agent, you can find that using ssh -vvv).

BTW, I couldn't find benefits of using paramiko. SSH agent seems more developed and more general tool. For example, it's not possible to forward SSH agent in paramiko, one has to resort to subprocess for that. Also note this issue from 2014, "Key handling is terribad" (open):

SSHClient._auth uses a multi-exit strategy combined with storing a single exception to raise at the end of the process. This frequently means the raised exception at auth time is flat out incorrect as to the true cause of the inability to authenticate.

There are many paramiko bugs linked in this thread. It seems actively developed now, and I hope paramiko will fix that, but my advice is: don't rely on one single library, it may not fulfill your demands.

Yes, there is a possibility to provide password to the encrypted key, but that defeats the purpose of that. You either enter password yourself (then you don't need a key for ssh), or store the password on disk (of course not in version control), then you don't need the private key to be encrypted (the idea of that is that if someone gets your HDD, one doesn't get your private keys in plain text).

Yaroslav Nikitenko
  • 1,342
  • 1
  • 18
  • 25