Generate Ssh Keypairs (Private/Public) Without Ssh-Keygen

Generate SSH Keypairs (private/public) without ssh-keygen

Turns out this was much more complicated than I anticipated. I ended up writing the SSHKey gem to pull it off (source code on GitHub). SSH Public keys are encoded totally differently from the RSA public key provided. Data type encoding for SSH keys are defined in section #5 of RFC #4251.

How using ssh-keygen generate public key that would have ---- BEGIN SSH2 PUBLIC KEY ---- at the top?

Figured out myself

I had to add -e (export) option

Full command looks following:

ssh-keygen -b 2048 -C "email@gmail.com" -f $HOME/.ssh/id -e

ssh-keygen and openssl gives two different public keys

It's the same key but different representations. OpenSSL uses X.509 SubjectPublicKeyInfo in ASN.1, usually (including here) wrapped in PEM; OpenSSH (except 'rsa1' keys for SSHv1 which is broken and you shouldn't use) uses the XDR-like SSH wire format, in base64.

Dupe or neardupe:

Convert pem key to ssh-rsa format

RSA Public Key format

Convert RSA public key to RSA DER

Converting an OpenSSL generated RSA public key to OpenSSH format (PHP)

How to convert RSA key to ssh-rsa

How to store/retrieve RSA public/private key (buried in the middle)

and less obvious cross-stack https://security.stackexchange.com/questions/42268/how-do-i-get-the-rsa-bit-length-with-the-pubkey-and-openssl

What is the proper way to generate, store, and configure an SSH public/private key pair for git repository usage on Assembla?

Since you are just entering gitrep, it's just saving it in your current directory (which is apparently your home directory, judging from your example above).

Check and see if ~/gitrep and ~/gitrep.pub exist. You'll need to copy the contents of the gitrep.pub file to the destination when it asks you for your public key.

Run ssh-keygen in Ruby to generate VCS deploy keys?

Using Kernel Calls to OpenSSH Utilities

This is one of those things that in my opinion should not be ported directly to Ruby. While the OpenSSH binaries and source are routinely audited, lightly- or rarely-used Ruby gems or FFI wrappers wouldn't get the same level of scrutiny. Instead, you should use the Kernel#system or Kernel#` calls or the %x() subshell literal, depending on your use case.

Calling External SSH Utilities from Inside Ruby

For example:

    # Note that 3072 is currently the default size for RSA keys in
# OpenSSH. Also note that you can pass `-f path/to/keyfile` or
# `-P ""` for an empty passphrase if you don't use the `-A` flag.
system %(ssh-keygen -A -b 2048 -t rsa -C "mystring")

Without the -f flag, your RSA keys will be placed into ~/.ssh/id_rsa and ~/.ssh/id_rsa.pub. You can then use standard Ruby methods for reading the files if you really need to, although again I can't think of many reasons why you'd need to do this.

For example, to read in your public key into a Ruby variable:

public_key = File.read "#{ENV['HOME']/.ssh/id_rsa.pub"

Using the SSH-Agent

Note that if you are already inside a running program, your biggest challenge will be using your SSH agent if you're using one, since the environment variables and key material are unlikely to be available at this point. However, you can start an agent and load your key file within your current session if you like. For example:

ssh_agent = %x(eval 'ssh-agent -s')

# assumes a passwordless key
system("ssh-add") && %x(ssh-add -l)

# Your agent's SSH_AUTH_SOCK is now exported by your current Ruby
# environment.
ENV['SSH_AUTH_SOCK']

# For some reason, SSH_AGENT_PID isn't exported properly. This may be
# user error on my part. Luckily, you can easily parse it out of the
# *ssh_agent* variable if needed.
ENV['SSH_AGENT_PID'] =
ssh_agent.match(/SSH_AGENT_PID=\d+/).to_s.split(?=).last

Security Note

If you're running on macOS or a Linux system with keychain installed, you're better off using a password stored in your login keychain or prompted for when starting ssh-agent. Passwordless keys have their place, but there are more-secure options that are just as easy to manage on most modern systems. YMMV based on your exact use case.

See Also

There's a net-ssh gem that also contains ssh-agent support. Whether or not this is suitable for your needs or sufficiently audited for your use case is up to you. However, other visitors who don't want to roll their own or call out to external utilities should be aware of this solution, and there are likely to be others as well. Again, your mileage may vary.

How to execute ssh-keygen without prompt

We need to accomplish two steps automatically:

  1. Enter a passphrase. Use the -N flag (void string for this example):

    ssh-keygen -t rsa -N ''

  2. Overwrite the key file:

Use -f to enter the path (in this example id_rsa) plus a here-string to answer yes to the following question:

ssh-keygen -q -t rsa -N '' -f ~/.ssh/id_rsa <<<y >/dev/null 2>&1

Or, under a bash like shell, If you certainly want to overwrite the previous one, use just a here-string to feed the command with all the need input:

ssh-keygen -q -t rsa -N '' <<< $'\ny' >/dev/null 2>&1

From ssh-keygen man page:

  -N new_passphrase provides the new passphrase.
-q silence ssh-keygen.
-f filename specifies the filename of the key file.

Step by step explanation

$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/klashxx/.ssh/id_rsa):

1) To avoid entering the key use -f:

$ ssh-keygen -t rsa -f ~/.ssh/id_rsa
Generating public/private rsa key pair.
/home/klashxx/.ssh/id_rsa already exists.
Overwrite (y/n)?

ATTENTION: If you don't care about the RSA file name and certainly want to overwrite the previous one, check the instructions below point four.

2) Now we need to answer "y" automatically to the overwrite question (let's use a here-string for that job):

$ ssh-keygen -t rsa -f ~/.ssh/id_rsa <<< y
Generating public/private rsa key pair.
/home/klashxx/.ssh/id_rsa already exists.
Overwrite (y/n)? Enter passphrase (empty for no passphrase):

3) Finally we're going to use the -N flag to enter a void pass:

$ ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa <<< y
Generating public/private rsa key pair.
/home/klashxx/.ssh/id_rsa already exists.
Overwrite (y/n)? Your identification has been saved in /home/klashxx/.ssh/id_rsa.
Your public key has been saved in /home/klashxx/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:Xo0t6caMB/8TSsigxfY28JIfqYjyqxRZrFrPncx5yiU klashxx@server
The key's randomart image is:
+---[RSA 2048]----+
| |
| . |
| o . |
| + * = |
| +. + BSo= o |
|...o.+o+XO... |
|.. .o.E==+B. . |
|o . ...=.o... |
|.+o. o .. |
+----[SHA256]-----+

4) Extra ball, cleanup the output, just check the return code:

$ ssh-keygen -q -t rsa -N '' -f ~/.ssh/id_rsa <<<y >/dev/null 2>&1
$ echo $?
0

An alternative path to overwrite the previous RSA file (no -f flag needed)

NOTE: Only bash like shells.

If you don't care about the RSA name and just want to overwrite it, we need to answer these two questions automatically:

  1. Enter file in which to save the key: /example/path/.ssh/id_rsa already exists.

  2. Overwrite (y/n)?

If we do this by hand, for the first question we just need to hit enter, and for the second, type y and press enter.

We can simulate these actions by using the following here-string:

$'\ny'

From the bash man page:

Words of the form $'string' are treated specially. The word expands to
"string", with backslash-escaped characters replaced as specified by
the ANSI C standard.

\n new line

So, if we use od to analyze our string:

cat - <<< $'\ny' | od -c
0000000 \n y \n

We see that we're getting just what we need to answer the questions.

Points 1 and 2 can be summarized into:

ssh-keygen -q -t rsa  <<< $'\ny'

And the final command will be:

$ ssh-keygen -q -t rsa -N '' <<< $'\ny' >/dev/null 2>&1
$ echo $?
0

Kudos

@lukasz-dynowski, @redochka, @mellow-yellow, @yeti and the rest of the folks in this thread.



Related Topics



Leave a reply



Submit