;;
;; ~ropmur-bicbus
;;

Managing SSH keys in emacs

emacs

Lots of stuff that I do in emacs depends on having an SSH key available, like interacting with git repos. Using ssh-agent makes it convenient to manage SSH keys securely because I don't have to type passphrases often. But it's annoying to have to remember to add the key to the agent!

You can use the following snippet to add an SSH key in emacs, and use emacs to enter the passphrase:

(defvar rb/ssh-default-key "~/.ssh/id_ed25519"
  "My default SSH key.")

(defun rb/ssh-add (&optional arg)
  "Add the default ssh-key if it's not present.

With a universal argument, prompt to specify which key."
  (interactive "P")
  (when (or arg
            (not (rb/ssh-agent-has-keys-p)))
    (rb/ssh-add-in-emacs
     (if (not arg)
         rb/ssh-default-key
       (read-file-name
        "Add key: \n" "~/.ssh" nil 't nil
        (lambda (x)
          (not (or (string-suffix-p ".pub" x)
                   (string= "known_hosts" x))))))))))

(defun rb/ssh-agent-has-keys-p ()
  "Return t if the ssh-agent has a key."
  (when (not
         (string-match-p
          "No identities"
          (shell-command-to-string "ssh-add -l")))
    t))

(defun rb/ssh-add-in-emacs (key-file)
  "Run ssh-add to add a key to the running SSH agent."
  (let ((process-connection-type t)
        process)
    (unwind-protect
        (progn
          (setq process
                (start-process
                 "ssh-add" nil "ssh-add"
                 (expand-file-name key-file)))
          (set-process-filter
           process 'rb/ssh-add-process-filter)
          (while (accept-process-output process)))
      (if (eq (process-status process) 'run)
          (kill-process process)))))

(defun rb/ssh-add-process-filter (process string)
  (save-match-data
    (if (string-match ":\\s *\\'" string)
        (process-send-string process
                             (concat
                              (read-passwd string)
                              "\n"))
      (message "ssh-add: %s" string))))

I add hooks in convenient places that depend on ssh keys, like so:

(use-package magit
  :hook (magit-status-mode . rb/ssh-add))