Paramiko -- using encrypted private key file on OS X

houbysoft picture houbysoft · Mar 22, 2013 · Viewed 20.4k times · Source

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?

Answer

Jukka Suomela picture Jukka Suomela · Jul 18, 2013

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.