SSH commands - the Secure SHell commands




Generate / manage / convert / SSH keys


Flag Usage
-a numberRounds When saving a private key, this option specifies the number of KDF rounds used.
Higher numbers result in slower passphrase verification and increased resistance to brute-force password cracking (should the keys be stolen).
-b numberBitsKey Specifies the number of bits in the key to create, which depends on the key type.
-C comment Will append comment at the end of the public key ( :
ssh-rsa AAAAB3NzaCDvrOAYDtfSbKsIzDt4fOKQ1yc2EAUadaPsKA2ofpo95n/Altv9t4mu1Ob/YHaZcdX0aCzS7WVQ== comment
ssh-ed25519 AAAACxOtLlwWTzud97ZBIpcQ3NzaC1lZDI1NTE5IHyXmUPVMe+qyxB5II3CeXx9k comment
comment defaults to currentUserName@currentHostName
-f keyFile Specifies the filename of the key file.
-F host
-F [host]:port
Search host in the specified key file
If ~/.ssh/known_hosts has an entry such as :
[]:443 keyType base64PublicHostKey
the syntax that may return it is :
ssh-keygen -F '[]:443' -f ~/.ssh/known_hosts
-N newPassphrase Specifies the new passphrase
-o save private keys using the new OpenSSH format rather than the more compatible PEM format. The new format has increased resistance to brute-force password cracking but is not supported by versions of OpenSSH prior to 6.5 (check it : dpkg -l | grep openssh).
ed25519 keys always use the new private key format.
-p Requests changing the passphrase of a private key file instead of creating a new private key. The program will prompt for the file containing the private key, for the old passphrase, and twice for the new passphrase.
-R host Removes all keys belonging to host from a known_hosts file (example)
-t type Specifies the type of key to create :


Create a ED25519 key :

The default OpenSSH key encryption is worse than plaintext :

Not sure how it could actually be "worse" than plaintext, but it sounds rather poor anyway (see gory technical details below). To workaround this :
  • replace old keys with keys using the new private key format :
    ssh-keygen -t ed25519
  • update private keys to the new storage format :
    1. privateKey=~/.ssh/id_rsa; oldPrivateKey="$privateKey.old"; cp "$privateKey" "$oldPrivateKey"; ssh-keygen -p -o -f "$privateKey"
    2. make sure the key pair still works (just in case...)
    3. rm -i "$oldPrivateKey"
  • This applies only to private keys having a passphrase. Commands will work on passwordless keys but are merely useless.
  • Compatibility with servers is not a concern, because the private key never leaves your machine.

Technical details :

Make a private key without passphrase (i.e. unencrypted private key, source) :
myKey='/run/shm/myKey'; ssh-keygen -N '' -f "$myKey"; head -5 "$myKey"; rm "$myKey"*
MIIEoAIBAAKCAQEA0Fcqiy4b2hoYLdy2jxgJ4n4Ek+S5rKCwfkbMTarq5WsSJ4Rf	MII... : the base64 DER clue that an RSA key follows

The private key is an ASN.1 data structure, serialized to a byte string using DER, and then Base64-encoded.

View the ASN.1 structure (source):
myKey='/run/shm/myKey'; ssh-keygen -N '' -f "$myKey"; openssl asn1parse -in "$myKey"; rm "$myKey"*
Outputs the ASN.1 structure, made of 9 integers.
Make a private key with a passphrase (source) :
myKey='/run/shm/myKey'; ssh-keygen -N 'secret' -f "$myKey"; head -5 "$myKey"; rm "$myKey"*
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,D634E4857AF0E3B1EF0CA8518425C70E	"D634E4857AF0E3B1EF0CA8518425C70E" is the Initialization Vector (IV)

Try to view its ASN.1 structure :
myKey='/run/shm/myKey'; ssh-keygen -N 'secret' -f "$myKey"; openssl asn1parse -in "$myKey"; rm "$myKey"*
fails because not in ASN.1 format anymore : it's encrypted
Generate an encrypted key, then decode it (source):
password='123456'; myKey='/run/shm/myKey'; ssh-keygen -N "$password" -f "$myKey"; openssl rsa -text -in "$myKey" -passin "pass:$password"; head -5 "$myKey"; rm "$myKey"*
==> displays the decoded key.
Trying to do it manually :

password='123456'; myKey='/run/shm/myKey'; ssh-keygen -N "$password" -f "$myKey"; head -5 "$myKey"
	Proc-Type: 4,ENCRYPTED
	DEK-Info: AES-128-CBC,DD75D306F2CEADFCD9F5B0AA88E72368


echo -n 123456DD75D306 | md5sum

tail -n +5 "$myKey" | grep -v 'END ' | base64 -d | openssl aes-128-cbc -d -iv DD75D306F2CEADFCD9F5B0AA88E72368 -K 500d7daf9dac3883117c63b073996144 | openssl asn1parse -inform DER
	==> fails :-(((

openssl rsa -text -in "$myKey" -passin "pass:$password";
rm "$myKey"*

...doesn't work 

Long story short, the AES encryption key (used to encrypt the private key) is built with :

md5(passphrase + 8 first bytes of the IV)
  • MD5 is used in the process of encrypting the private key with the passphrase.
  • It is used only once
  • both AES and MD5 functions are cheap in terms of CPU and memory
this key is prone to brute-forcing, especially if the passphrase is short/poor quality




ssh-copy-id is a script used to copy your SSH public key to the specified host. If necessary, it can also create the remote :


Flag Usage
-i identityFile
  • use only the key corresponding to identityFile (i.e. do not try other existing keys)
  • .pub will be appended to identityFile if missing : identityFile is actually the public part of the key pair


ssh-copy-id -i ~/.ssh/myProject/ mySshServer
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/stuart/.ssh/myProject/"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys

Password: t-o-t-a-l-e-m-e-n-t i-n-t-e-r-d-i-t

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'mySshServer'"
and check to make sure that only the key(s) you wanted were added.



There is a lot to say about SSH, since there are :


Flag Usage
-A Enable forwarding of the authentication agent connection (see ssh-agent, example)
You can enable this behavior in ~/.ssh/config with ForwardAgent yes.
-b bindAddress Use bindAddress on the local machine as the source address of the connection
-C Compress all data
This may be counterproductive on fast networks.
-e escapeCharacter Sets the escape character for sessions with a pty
  • default value : ~
  • none disables any escapes and makes the session fully transparent
-f Requests SSH to go to background just before command execution. Implies -n.
-F configFile Specifies an alternative per-user configuration file
to ignore your config file : -F /dev/null
-i privateKey identity file (aka private key)
defaults to ~/.ssh/id_rsa for RSA
-J jumpHost connect to targetHost via jumpHost :
ssh -J jumpHost targetHost
This is equivalent to using the ProxyJump client configuration directive.
-M Enable SSH Master mode allowing connection sharing (for details, see ControlMaster) (See -S)
-n Redirects stdin from /dev/null (actually, prevents reading from stdin).
-N Do Not execute a remote command, just forward ports (protocol v2 only).
-o option=value give options in the format used in the configuration file
-p port port number on which sshd will listen (default is 22).
-q quiet mode. Causes most warning and diagnostic messages to be suppressed (may be overzealous)
-S path/to/controlSocket
  • Specifies the path to the control socket for connection sharing —aka ControlPath
  • to be used with -M
-t Force pseudo-tty allocation (and fix the sudo: sorry, you must have a tty to run sudo error). This can be used to :
  • transmit signals between the SSH process and the remote one
  • allow sudo'ed commands to prompt for a password (example)
-T Disable pseudo-tty allocation. This proves useful only in scripts.
-v verbose mode. Repeat to increase verbosity : -vvv
-W host:port Requests that standard input and output on the client be forwarded to host on port over the secure channel. See ProxyCommand.
-X enable X11 forwarding
-Y enable trusted X11 forwarding. These are not subjected to the X11 SECURITY extension controls.


Send command to SSH host :

  • ssh kevin@myServer 'command'
  • ssh -t haproxyServer 'sudo /usr/sbin/haproxy -c -V -f /etc/haproxy/conf.d/myConfig.cfg'
  • for server in server1 server2; do ssh "$server" 'df -h /home'; done

If the SSH session is interrupted (with CTRL-c or anything else) the remote process receives no signal and continues executing. To prevent this, you may allocate a pseudo-tty to SSH with -t. (source)