Lab: Secure Shell Keys

Steven J Zeil

Last modified: Feb 22, 2021
Contents:

This is a self-assessment activity to give you practice in working with the ssh keys as a way of authenticating yourself with remote systems. Feel free to share your problems, experiences, and choices in the Forum.

1 Background

How do you prove you’re who you say you are when you are online?

How do you prove you’re allowed to access your online account?

This is the problem of authentication, the action of verifying the identity of a user.

So far, we have relied on login credentials – a combination of a login name and a password. Passwords have been the primary form of online authentication (proving you are allowed to take some action) for a long time. But they have their problems. Passwords can be hacked, by brute force or by clever guesswork, and attempts to make them harder to hack also make them harder to remember and to type.

In this lesson, we will look at SSH keys, a component of public key cryptography, as an alternate means of accessing your account.

1.1 Cryptography

1.1.1 Ciphers

Hiding messages in secret codes has a long history. Julius Caesar encoded his private correspondence using a letter-shift technique in which each letter of his text was replaced the a different letter $k$ steps away in the alphabet. If we treat the letters of the alphabet as numbered 0..25,

a b c d e f g h i j k l m n o p q r s t u v w x y z
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

then we are basically replacing a letter whose number is $x$ by the letter numbered $(x + k) \mod 26$. For example, if our “key” $k$=2 e.g., we replace ‘a’ by ‘c’, b’ by ‘d’, ‘c’ by ‘e’ , … ‘x’ by ‘z’, ‘y’ by ‘a’, and ‘z’ by ‘b’. This substitution is easily reversed by the recipient of the message, so long as they know the key value.

This is a special case of what is called a substitution cipher, in which each letter is replaced by another according to some rule shared by the author and the reader of a secret message. Substitution ciphers that always replace the a letter by the same replacement every time it occurs can be broken fairly easily without knowing the replacement rules. In fact, many newspapers still run a daily “cryptogram” puzzle.

To make substitution ciphers unbreakable, we need to continuously vary the substitution rules. For example, we might have a key text such as a passage from Shakespeare,

“Friends, Romans, countrymen, lend me your ears!”

and use the numeric value of each successive letter in the key text as the next value of $k$. So, if our secret message starts with “Aah! An aardvark…”, we would replace the first ‘A’ (1) by letter ‘G’ ($1+5$ , the $5$ coming from the ‘F’ in “Friends”), then replace the second ‘a’ (1) by letter ‘s’ ($1 + 16$, the 16 coming from the ‘r’ in “Friends”), then replace ‘h’ (7) by ‘p’ ($7 + 8$, the 8 coming from the ‘i’ in “Friends”), and so on. If our message is longer than our key text, we simply start over from the start of the key text.

As long as the key text is kept secret and is long enough, this code is virtually uncrackable.

1.1.2 Key Exchange

The weak spot in this approach to secrecy is that both the author and the reader of the message need to have access to the secret key. At some point in time, they needed to exchange the key text between them. As numerous spy novels and films have attested, if you can intercept that exchange of keys, you can decode their messages. You can even craft messages of your own, pretending to be one of them.

1.2 One-Way Functions

The ciphers we have described so far use calculations that are easily reversible. To encode a message $m$, we compute $(m + k) \mod 26$. To decode it, we compute $(m - k) \mod 26$. Most simple, familiar mathematical functions are easily reversible by applying an inverse function, addition’s inverse is subtraction, multiplication’s inverse is division, etc.

Sometimes the inverse of an action is a bit harder to compute than the original. A square and a square root are inverses, but it’s considerably easier to compute $x^2$ than to compute $\sqrt{x}$.

There are some functions for which we can compute the function itself with relative ease, but would need massive arrays of CPUs running for extensive amounts of time to compute the inverse. When the difference in effort is this great, we call these one-way functions.

For example, suppose I give you two prime numbers $m$ and $p$ and ask you to compute their product. Not hard. Even if the numbers are quite large, you can certainly write a computer program to do this calculation in under a millisecond.

But suppose I give you a number $n$ and tell you that it is the product of two primes, and ask you to find those two numbers. If I give you a small, number, say, $n = 33$, you could rapidly tell me that my two prime numbers were $3$ and $11$. But that if I gave you a larger value, say, $n=58183$? That’s surprisingly difficult. 1

And it’s not just difficult for humans, either. If I were to randomly choose a pair of prime numbers that were hundreds or thousands of bits long, computing this inverse function is prohibitively time-consuming, even for the fastest computers.

1.2.1 Public Key Encryption

One-way functions form the basis of modern electronic cryptography. They can be manipulated to allow for public key encryption, in which we have two numbers, one of which can be used to encrypt messages, and one which can be used to decrypt them, but neither number can be feasibly computed from the other number.

This goes a long way towards solving the key exchange problem. We release the encrypting number as a public key, but tell no one the private key number we will use for decrypting. Anyone can encrypt messages to us that only we will be able to read.

We can even turn this around. If we generate another public-private key pair and release the decrypting one publicly, we can send out encrypted messages that anyone can read, but that only we could have written. We can actually prove to people that a message came from us. (This gives rise to the idea of a digital signature.)

And that means, even if we have no particular interest in hiding our messages, we can use this same technology for authentication, to prove that we are who we say we are, and that we are someone who is allowed to log into a particular account on a remote computer.

1.3 Authentication

Public key encryption can be used for the purpose of authentication. An account holder registers or authorizes a copy of a public key on a remote server, and retains the private key on his or her local PC. When that person attempts to log in to the remote machine, the two machines use the keys to verify the identity of the person logging in.

 

One way this can work is illustrated here.

  1. Someone sitting at the local PC tries to log in to the remote server using the login name “jdoe”. The local PC sends this request to the remote server.
  2. The remote server generates some kind of semi-random message (e.g., the login name “jdoe”, the time of day, and a randomly generated string), and uses jdoe’s public key to encrypt that message.
  3. The remote server sends that encrypted message back to the local PC.
  4. The local PC uses the private key to attempt to decrypt the message.
  5. The local PC sends the decrypted message back to the remote server, asking to be authenticated.
  6. The remote server compares the decrypted message to the one it had originally generated. If they match, the remote server knows that the local PC has access to jdoe’s private key, and accepts that as proof of that the person trying to log in really does own the jdoe account.

Conventional password authentication bases its security on

  1. the user’s ability to remember (or crack) the password.

Public key authentication bases its security on

  1. Physical possession of a device containing the private key.
  2. The ability to log into that device.
    • This is probably based on passwords or fingerprint sensors, but, in general, we have to be a little bit suspicious of how well people protect their personal devices.
  3. In addition, the form of public key authentication that we will use, SSH keys, adds a passphrase control to the private key itself, for another layer of security.

2 ssh Isn’t Just for Terminal Sessions

You should already be familiar with ssh even if you are used to invoking it through PuTTY.

In this lab, you will be working with ssh from a command line. You will be working with

Let’s start with just the basics of using ssh from the command line.

  1. Try opening a remote session on your server machine by issuing the following command on your client machine:

    ssh yourCSLoginName@linux.cs.odu.edu

    • You can omit the “yourCSLoginName@” if your current terminal session is under a user name identical to your CS Dept login name.

    This opens up what should be a familiar text-mode command session on the remote machine. Issue a few commands to verify that everything is familiar, and then log out of the remote machine.

  2. Now give the same command, but append a command string to the end:

    ssh yourCSLoginName@linux.cs.odu.edu ls -l

ssh is useful for issuing all sorts of commands to a remote machine. The “default” is to issue the command to open a login shell, but you can issue any command you want.

ssh has other tricks to offer as well.

We’ll be making heavy use of both of these features of ssh in the coming semester. However, these will call for a more sophisticated approach to identifying ourselves than explicitly typing in our login names and passwords for every connection to a network service.

3 SSH Keys

SSH keys are a form of public key authentication designed for use when logging in to SSH servers. They are useful enough, however, that they crop up in a lot of web-based applications as well.

3.1 Creating an SSH Key Pair

The first step in using SSH keys is to create an SSH key pair. It’s a “pair” because you will wind up with two files, one containing the public key and the other containing the private key. At the end of the process, you will put the public key on one or more remote machines, but keep your only copies of the private key on devices that you physically own and control access to.

It’s important to keep this straight: * The public key goes onto the remote server that you are connecting to. * The private key stays on your (local) client that you are connecting from.

In theory, you can create the key pair on your local PC and then transfer the public key to a remote server, or create the key pair on the remote machine and transfer the private key back to your own local PC, deleting it from the remote machine. Either way you wind up with the public key on the remote machine, and the private key on your local PC.

For simplicity, I’m going to describe the latter approach, because it avoids issues that can arise from competing formats used for public keys.

  1. To generate a key pair, log in to a CS SSH server and give the commands:

    mkdir ~/.ssh   # if you don't already have this directory
    chmod 700 ~/.ssh
    ssh-keygen -b 2048 -t rsa
    
    • You can change the name of the generated files if you like. (I keep different key pairs for different client machines and name them accordingly, e.g., “officePC”, “homePC”, etc.). Do keep it in your ~/.ssh directory, however.

    • You will be prompted for a passphrase. This is used to protect your private key in case someone gains access to the machine/account where you have it stored.

      • Do choose one. Even though the command prompt from ssh-keygen says it’s optional, you don’t want to have an unprotected private key around.

      • Most people use much longer passphrases than a typical password, but generally place less emphasis on odd character substitutions that make the phrase harder to type.

  2. Now look in your ~/.ssh directory. You should see your new keys. One file has the extension “.pub”. That’s the public part of the key.

  3. You can do a quick test of your key pair by giving the command

     
    ssh-keygen -y -f path/to/your/private-key
    

    This command will prompt you for your passphrase and, if you are successful in providing it, will then print the corresponding public key.

    Compare the output of that to

    cat path/to/your/public-key
    

    They should match except possibly for the little “usr@machine” comment at the end indicating the machine on which you created the key.

    Remember, the idea is that you want the public key to be on the remote server and the private key on your local client.

    Since you have just created both keys on one machine, one of them is currently out of place.

  4. Transfer the private key to a convenient directory on your local PC, and delete the original private key from the remote server.

    Where should you put the private key on your local PC? I recommend emulating the Linux practice and storing it in ~/.ssh/, creating that directory if it does not already exist.

    • For Windows users, you can’t use ~ directly, but remember that ~ is a shorthand for your home directory, which in Windows is known as %HOME% and is found in C:\Users\yourWindowsLoginName\. So put your private keys in C:\Users\yourWindowsLoginName\.ssh\.
  5. If you are a Windows PuTTY user, you will need to convert the private key to a different format. The program that you will use for this is PuTTYgen. You probably installed this when you installed PuTTY. Check your Start menu, under the PuTTY heading. If you don’t have it, return to the place where you obtained PuTTY and download and install the full PuTTY package.

    • Run PuTTYgen.
    • From the Conversions menu, select Import key and navigate to the directory with your private key.
    • You will be prompted to enter the pass-phrase for your key.
    • Click Save private key to store your key as a .ppk file in that same directory.

3.2 Authorizing Keys

At this point, you should have

But just having the public key on the remote server isn’t enough. We have to authorize that key before it will do anything for us.

  1. Let’s authorize this key as one that you can use to log in to your CS Linux account.

    On Linux servers, you do this by adding the public key to ~/.ssh/authorized_keys. authorized_keys is simply a text file that contains a list of public keys.

    There’s at least two ways to do this.

    • You can simply attach it to the end of the authorized_keys file:

      cd ~/.ssh
      touch authorized_keys
      cp -a authorized_keys authorized_keys.bak
      cat path/to/your/publicKeyFile >> authorized_keys
      

      This works the first time you install a key. But if you need to replace or modify a key, it’s not useful, because you would wind up with multiple copies of the same key in your authorized_keys file, leading to unpredictable results.

    • Alternatively, log in to the server, open ~/.ssh/authorized_keys in your favorite text editor and add the public key to the end. (This is also how you can clean up this file later if you want to remove a key, e.g., because the key doesn’t work properly or if you lost the private key file or forgot its passphrase.)

      • If you are replacing a key, be sure to delete the old entry for that key. If you have entries in there for keys you aren’t using, remove those as well.

  2. Now, back on your client machine,

    • If you are a PuTTY user, run it. In the configuration screen, enter your usual host name info, but don’t hit Open just yet.

      • In the Category area, expand Connection if necessary. Then expand SSH and click on Auth.

       

      • Use the Browse... button to select your .ppk private key.

      • Click Open to launch your PuTTY session.

        • Instead of the usual login/password sequence, you should see something like this:

         

        asking you for your key’s passphrase instead of your account password.

    • if you use a command-line ssh client (not PuTTY), try connecting to the server:

      ssh -i path/to/your/PrivateKey -o "IdentitiesOnly=yes" yourCSLogin@linux.cs.odu.edu
      
      • Instead of your usual password prompt, you should instead be prompted with:
      Enter passphrase for key...
      

3.3 Troubleshooting

If you run into problems with the final step or with the ones that follow, …

  1. Review your steps. Make sure that you have the correct files on the correct machines.

    Remember, the public key goes on the remote server inside the authorized_keys file.

    The private key goes on your local client machine.

    That means that when you are giving a path to your private key in an ssh command, it must be a path on your local machine.

  2. Check your permissions on the server very carefully. You should have the equivalent of:

      chmod 701 ~
      chmod 700 ~/.ssh
      chmod 600 ~/.ssh/authorized_keys
    
  3. You may also need to check the permissions on your private key on your local client. Some versions of ssh will refuse to use a private key that is readable by anyone other than you (the owner of the file).

In particular, if you get a message complaining that your private key permissions are too open, change the permissions on that key!

chmod 600 path-to-your-private-key

* If you are using the WSL, you may find that `chmod`
  commands like the one above are ignored.  (Do an "`ls -l` ..." to
  check.)

    * You may be able to make `chmod` work by using an editor:

            sudo nano /etc/wsl.conf

        and adding the lines

            [automount]
            options="metadata"

        Then close all open bash instances, reboot Windows, and see if your
        `chmod` commands now work.

     * If not, try moving your keys to a directory somewhere under your
       WSL home directory (`~`). It need not be in that
       directory immediately -- any directory under your home should
       do.

4 Key Agents

Wait, did we just make your life harder?

Now, you may wonder what good that was. Every time you try to log in to a CS Linux machine, you will be prompted for that passphrase, which is probably much longer than your old password.

 

But usually, we don’t activate the private key for a one-shot login. Instead, we run a key agent on our client machine. We tell it to activate our private key (giving it the passphrase to prove that we are its owner). It then watches for subsequent SSH connection attempts and offers up the activated private key.

4.1 Starting an Agent on Windows 10 Machines with the PuTTY Client

If you are a PuTTY user, the key agent you will want to use is Pageant. You probably installed this when you installed PuTTY. Check your Start menu, under the PuTTY heading. If you don’t have it, return to the place where you obtained PuTTY and download and install the full PuTTY package.

  1. Run Pageant.

    You will see a small icon appear in the System Tray area of your TaskBar.

    This indicates that the agent is running. Now you need to tell it what key(s) it should manage.

  2. Right-click on that icon and select “Add key”.

  3. Navigate to your key directory and select your private key (.ppk file).

  4. You will be prompted for your key’s passphrase.

Now your agent is running and monitoring that key. You should be able to launch PuTTY without needing to type your password or passphrase.

  1. Run PuTTY. In the configuration screen, enter your usual host name info, but don’t hit Open just yet.
    • In the Category area, expand Connection if necessary. Then expand SSH and click on Auth.

    • Make sure that there is a checkmark next to Attempt authentication using Pageant.

    • Click Open to launch your PuTTY session.

    • After entering your login name, you should see a message

      Authenticating with public key ... from agent`
      

      and you will find yourself logged in without needing to re-enter your passphrase.

    1. Run PuTTY again, following the same procedure. Enjoy the experience of not needing to type in your password or passphrase.

4.2 Starting an Agent on Windows 10 Machines with the OpenSSH Client

If you are running Windows and use the OpenSSH client, you must start the agent as a Windows service. This means that it will run each time you log in on that PC.

  1. In the taskbar search area, type “powershell”. Select the “Run as administrator” option.

  2. In the Powershell window, give these commands:

    Start-Service ssh-agent
    ssh-add path-to-your-private-key
    

    Unlike when you are in a cmd window, Powershell recognizes ~ as a shortcut for your home directory, so your path could start with ~\\.ssh\.

    You might be asked immediately for your passphrase, or you might be asked for it the first time you try to use that key to connect to a remote server.

  3. Now try logging in to a remote server:

    ssh yourCSLogin@linux.cs.odu.edu date
    ssh yourCSLogin@linux.cs.odu.edu pwd
    ssh yourCSLogin@linux.cs.odu.edu ls
    

    All of these should work, but you should only be prompted for your passphrase on the first one.

4.3 Starting an Agent for Command-Line ssh

If you are running a command-line SSH client other than the Windows 10 OpenSSH, you create an agent using the ssh-agent command.

 
  1. On your local client PC, give the command

    eval `ssh-agent`
    

    This launches a new key agent and tells it to watch for incoming requests to validate that private key. You will be prompted for the passphrase for your key

  2. On your client machine, add a key to your agent: In Linux/MacOS/CygWin/PowerShell:

        ssh-add path-to-your-PrivateKey
    
  3. Now try logging in to a remote server:

    ssh yourCSLogin@linux.cs.odu.edu date
    ssh yourCSLogin@linux.cs.odu.edu pwd
    ssh yourCSLogin@linux.cs.odu.edu ls
    

    All of these should work, but you should only be prompted for your passphrase on the first one.

5 Creating Special-Purpose Key Pairs

Suppose that you wanted to allow someone else to try out a program that you had written and that is sitting somewhere in your account area. Let’s also suppose that you don’t want to make this program available to the entire world. For example, perhaps you are working on a programming assignment in one of my courses and you want me to take a look at your running program.

But we can actually adapt that second approach by limiting the key pair to running only a specific command or program when anyone uses it to log in.

  1. On the remote server, edit the line in the ~/.ssh/authorized_keys file containing the public key that you created in the earlier steps.

    The key is typically written as a single long line ending in _yourLoginName@machineName_, reflecting where you created the key. To the front of that line, add

    command="/usr/bin/env",no-port-forwarding
    

    and a blank space, right in front of the “ssh-rsa”.

    (If you aren’t familiar with the env command in Linux, run it to see what it does.)

  2. Add that edited public key to your authorized keys list on the server as you did earlier. (Remove the old copy of that key if it was previously authorized.)

  3. You should still have a key agent running on your client. If not, restart it and add your private key.

    Now try logging in via that key by giving the following commands on the client:

    ssh yourCSLogin@linux.cs.odu.edu
    ssh yourCSLogin@linux.cs.odu.edu pwd
    

    Notice that, whether you give a specific command for ssh to execute on the remote server or omit it, trying for a normal login session, what actually happens is that you are logged in to your account, the env command is run, and then you are logged out.

So, to return to our earlier example, if you were working on a programming assignment in one of my courses and you want me to take a look at your running program, you could create a special-purpose key pair to run that program (and do nothing else), and then give me the private key and passphrase, knowing that I would be limited to using your account for that single purpose.

5.1 Playing with Fire: Password-Free Key Pairs

Now we are going to do something a bit dangerous.

  1. Shut down the key agent on your client machine with the command

    ssh-agent -k

    (On Windows using OpenSSH, use the Services program to stop the Authentication Agent service. On Windows macghines using Pageant, shut down Pageant.)

  2. Create a new key pair, giving them a different name from the ones you set up earlier.

    This time, however, when prompted for a pass phrase, just hit Enter to create a key pair without a pass phrase.

  3. Test your keys via the commands

    ssh-keygen -y -fpath/to/your/privateKey
    cat path/to/your/publicKey
    

    Again, the printed public keys should match except for the trailing comment. This time, however, you should not be prompted for a passphrase.

  4. If you created these keys on the remote server, transfer the private key to your local client machine and delete the original.

    If you created these keys on your local client, transfer the public key to your ~/.ssh/ directory on the remote server.

  5. Now, adding that key to your authorized key list would be risky, because anyone who got a copy of the private key would be able to log into your account using no passphrase at all.

    So, to keep this safe, immediately edit that public key. This time add

    command="pwd",no-port-forwarding,no-agent-forwarding
    

    to the beginning of the public key.

  6. Add that edited public key to your authorized key list on the server.

  7. On the client, give the command

    ssh -i path/To/Your/New/Private/Key yourCSLoginName@linux.cs.odu.edu

    You should see that the command is executed without your being prompted for a password or pass phrase.

    If you are still prompted for a password, there may be remnants of your ssh-agent interfering. Try

    ssh -o IdentitiesOnly=yes -F /dev/null -i path/To/Your/New/Private/Key yourCSLoginName@linux.cs.odu.edu

  8. When you have succeeded in completing the above steps, remove the keys you have added to your ~/.ssh/authorized_keys file on the server.


1: In case you’re wondering $58183 = 83 * 701$.