Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have a db-driven application that needs to communicate with another server via ssh. The web app can generate a keypair and give the user the pubkey, or it can accept the unencrypted private key in a field on a web form. It then encrypts the key with reversible encryption and stores it in its database.

When the web app goes to communicate with the ssh server, I need to unencrypt the private key and provide it to ssh some way or another. I could create a secure temporary file with mktemp, point ssh at it, and then destroy the file, but that's extra work that exposes the key in a new way that seems like it shouldn't be necessary. I've looked at man ssh, man ssh-agent and man ssh-add, but there doesn't seem to be any way to use the key without adding it to a file first. What am I missing?

share|improve this question
If you want to use the standard tools you have to provide the key via a file. Of course you can also implement your own ssh client/SSH agent and directly decrypt the key from the database – Ulrich Dangel Feb 14 '13 at 16:45
@UlrichDangel would you be so kind as to point me to documentation on how to implement an SSH agent? – kojiro Feb 14 '13 at 16:48
No as I don't have one but there should be a description of the SSH agent protocol svnweb.freebsd.org/base/vendor-crypto/openssh/dist/… – Ulrich Dangel Feb 14 '13 at 16:55
Better than reimplementing everything a simple patch for an existing implementation should do the trick. – peterph Feb 14 '13 at 17:04
@peterph That's a good idea, but I still like to have a spec to work from. – kojiro Feb 14 '13 at 17:05
up vote 3 down vote accepted

If you want to use the existing tools like ssh/ssh-agent you have to provide the key as a file.

Another, maybe more feasible solution is to either directly implement a ssh client within your application and depend on third party libraries such as JSch or extend either ssh/ssh-agent to directly receive and decrypt the key from your database.

share|improve this answer

Sorry, not an answer but too long for a comment, which I think is in place.

Important thing is what you are trying to achieve by storing the key in the database encrypted.

An attacker that is able to access the temporary private key file will be able to read the ssh client process' memory as well (and hence get to the key data anyway), because both of these data must have basically the same access permissions - trying to hide the file doesn't seem to make it any more secure.

If you are using different users for db, webapp and ssh connection, by storing the key in the db, decrypting it in the webapp and feeding it to ssh, you are just opening potential attack vectors by spreading the key all over the process. If you are using just one user for all of these (db, app, ssh), you are gaining nothing except for code complexity.

The only advantage seems to be an easier transfer of the system to a different host and potential gain in case the db data gets stolen but the webapp (which AFAIU contains the decryption algorithm and password) not. But is that likely?

That said, if you want to protect the key, you can also use the ssh's internal encryption of the keys and load them into an ssh-agent when starting your service (remember to remove it when it stops!). But again remember that the ssh-agent keeps the keys in memory unencrypted, so it can potentially be read. Yet again: is this the problem you are trying to solve?

If you just need to protect communication between two machines, stunnel might be of better service than ssh.

And finally for the question: as far as OpenSSH (portable) is concerned, writing a simple patch that would allow ssh to read the key from another process as sshd does with the AuthorizedKeysCommand option should be reasonably straightforward.

share|improve this answer
I appreciate what you're saying, and you're right to challenge my assumptions and ask what problem am I solving; however, your answer is too specific to the environment around my question (the webapp and db) and (as you say) doesn't really answer the question itself. – kojiro Feb 14 '13 at 19:11
Sure, I just wanted to write this for future reference. Rather often I've seen people (mis)use ssh in situations where something else would have been better option (I'm not saying this is your case). And security-wise one often forgets what should be his/her major concerns. – peterph Feb 14 '13 at 21:32

Files are the main way to exchange data between applications. It doesn't have to be a disk file. It can be a file on temporary storage or a pipe. You can feed a key file to ssh-add on its standard input, as long as the key file isn't password-protected¹ (and since the key file was stored encrypted, there's no reason for it to be password-protected on top).

Alternatively, don't do anything fancy and write the file to a memory-backed filesystem such as /run (or /dev/shm or /tmp or whatever your distribution offers).

Alternatively, keep the private key file around with no external encryption, but put a password on it. Use a long, randomly-generated passsword, and store that password in the database.

¹ Experimentally, ssh-add chokes on password-protected key files that aren't seekable, such as named pipes.

share|improve this answer

I was trying to solve this problem as I was generating keypairs and storing them in a database with Python. Below are some of the steps that I ended up using, namely starting ssh-agent and interacting with it. It's in Python, but presumably you could translate it to whatever else. Another feature is that none of these use shell=True, so they're safer from injection attacks.

import subprocess
import os
sockfile = '/some/place.sock'

agent = subprocess.Popen(['ssh-agent',
          '-D', # foreground mode
          '-a', sockfile, # bind address (socket file)
           '-D', # delete all identities from the agent
      env={'SSH_AUTH_SOCK': sockfile},

That fires up an ssh-agent that's just for our program and clears it out (I believe it is loading my user keys into it, not sure though.) The environment variable SSH_AUTH_SOCK ordinarily set by ssh-agent (but that we did manually), we're feeding to other programs.

Next we use ssh-add to feed it the private key (here it's stored in dr.private_key as a string, but I need to serialize it, so I encode it as ASCII).

adder = subprocess.Popen(['ssh-add', 
        '-', # read key from stdin
     env={'SSH_AUTH_SOCK': sockfile}, 
retval = adder.wait()
assert retval == 0 # added successfully

Now whenever I use some SSH-using application, e.g. Git, I refer it to use the same SSH agent socket:

gitp = subprocess.Popen(
        ['git', '-C', os.path.abspath('../some-repo') ,'fetch'], 
        env={'SSH_AUTH_SOCK': sockfile}, 

and it works just fine. The only "temporary file" is the socket used by ssh-agent, but it's created so only the owner has permissions to it, and it's removed when you terminate the agent.

share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.