SSH 密钥

出自 ArchWiki
(重定向自 Ssh-keygen

本文或章节需要扩充。

原因: 导言和背景章节忽略了服务器的角度。(在 Talk:SSH keys 中讨论)

SSH 密钥可以用作一种身份验证方式,通过公钥密码学挑战-响应认证向 SSH 服务器验证您的身份。基于密钥的身份验证的主要优点是,与密码身份验证相比,它不易受到暴力破解攻击,并且如果服务器已被入侵,您不会暴露有效的凭据(参见RFC 4251 9.4.4)。

此外,SSH 密钥身份验证可能比更传统的密码身份验证更方便。当与称为 SSH 代理的程序一起使用时,SSH 密钥可以让您连接到一台或多台服务器,而无需记住或为每个系统输入密码。

基于密钥的身份验证并非没有缺点,可能不适用于所有环境,但在许多情况下,它可以提供一些强大的优势。对 SSH 密钥工作原理的一般理解将帮助您决定如何以及何时使用它们来满足您的需求。

本文假设您已经对Secure Shell协议有基本的了解,并且已经安装openssh软件包。

背景

SSH 密钥总是成对生成,一个称为私钥,另一个称为公钥。私钥仅为您所知,应妥善保管。相比之下,公钥可以与您希望连接的任何 SSH 服务器自由共享。

如果 SSH 服务器在文件中有您的公钥,并且看到您请求连接,它将使用您的公钥构建并向您发送一个挑战。此挑战是一条加密消息,必须以适当的响应来满足它,服务器才会授予您访问权限。使此编码消息特别安全的原因是它只能被私钥持有者理解。虽然公钥可以用来加密消息,但它不能用来解密同一条消息。只有您,私钥的持有者,才能正确理解挑战并产生正确的响应。

这个挑战-响应阶段发生在幕后,对用户是不可见的。只要您持有私钥(通常存储在 ~/.ssh/ 目录中),您的 SSH 客户端应该能够向服务器回复适当的响应。

私钥是一个受保护的秘密,因此建议以加密形式将其存储在磁盘上。当需要加密的私钥时,必须首先输入密码以解密它。虽然这表面上看起来像是您正在向 SSH 服务器提供登录密码,但密码仅用于解密本地系统上的私钥。密码不会通过网络传输。

生成 SSH 密钥对

可以通过运行 ssh-keygen 命令来生成 SSH 密钥对,请参阅 ssh-keygen(1) 手册页,了解什么是“普遍认为足够”并且应该与几乎所有客户端和服务器兼容的密钥类型

$ ssh-keygen
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/username/.ssh/id_ed25519):
Created directory '/home/username/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/username/.ssh/id_ed25519
Your public key has been saved in /home/username/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:RLy4JBv7jMK5qYhRKwHB3af0rpMKYwE2PBhALCBV3G8 username@hostname
The key's randomart image is:
+--[ED25519 256]--+
|%oooo. ..        |
|== ..o.o.        |
|==  . +o..       |
|+ o o.ooE        |
|...  *.oS        |
| o..o ..         |
|o=.. +o          |
|+o*..+o          |
|+.o+. .          |
+----[SHA256]-----+

randomart 图像OpenSSH 5.1 中引入,作为一种更简单的可视化识别密钥指纹的方法。

注意: 您可以使用 -a 开关来指定密码加密的 KDF 轮数。

您还可以使用 -C 开关向公钥添加可选的注释字段,以便在 ~/.ssh/known_hosts~/.ssh/authorized_keysssh-add -L 输出等位置更轻松地识别它。例如

$ ssh-keygen -C "$(whoami)@$(uname -n)-$(date -I)"

将添加一条注释,说明哪个用户在哪个机器上以及何时创建了密钥。

选择认证密钥类型

OpenSSH 支持几种签名算法(用于身份验证密钥),这些算法可以根据它们利用的数学属性分为两组

  1. Ed25519ECDSA,它们依赖于椭圆曲线离散对数问题 (ECDLP)。(例子)
  2. RSA,它依赖于分解两个大素数乘积的实际难度

椭圆曲线密码学 (ECC) 算法是公钥密码系统的较新添加。它们的主要优点之一是它们能够以更小的密钥提供相同级别的安全性,这使得计算密集型操作更少(更快的密钥创建、加密和解密),并减少了存储和传输要求。

DSA 密钥由于其安全弱点而已被弃用,并且 SSH 实现正在删除对它们的支持。OpenSSH 9.8 构建时默认不支持 DSA 密钥,Dropbear 2022.83 禁用了 DSA 密钥支持,libssh 0.11.0 完全删除了对 DSA 密钥的支持。因此,密码系统的选择在于 RSA 或两种类型的 ECC 之一。

默认的 Ed25519 将为您提供最佳的安全性和良好的性能。ECDSA 比 Ed25519 慢,但比 RSA 快;对其安全性存在担忧(见下文)。RSA 密钥将为您提供与旧服务器的最佳兼容性,但它需要更大的密钥尺寸才能提供足够的安全性。

注意: 这些密钥仅用于验证您的身份;选择更强的密钥不会增加通过 SSH 传输数据时的 CPU 负载。
提示: 为了保护 SSH 密钥免受机器泄露,可以将它们存储在 FIDO/U2F 硬件认证器可信平台模块中。
  • Ed25519 和 ECDSA 密钥可以通过在使用密钥生成时使用特殊的“安全密钥”密钥类型存储在 FIDO/U2F 硬件认证器中。请参阅 #FIDO/U2F
  • 可信平台模块支持 ECDSA 和 RSA,从而可以将 SSH 密钥密封在 TPM 内部。请参阅 Trusted Platform Module#SSH

Ed25519

Ed25519 在 2014 年 1 月的 OpenSSH 6.5 中引入:“Ed25519 是一种椭圆曲线签名方案,它比 ECDSA 和 DSA 提供更好的安全性以及良好的性能”。它的主要优点是其速度、恒定时间运行时(以及抵抗侧信道攻击的能力)以及缺乏模糊的硬编码常量。[1] 另请参阅 Mozilla 开发者关于其工作原理的这篇博文

它在许多应用程序和库中实现,并且是 OpenSSH 中的默认密钥交换算法(这与密钥签名不同)。

ssh-keygen(1) 默认使用 Ed25519,因此无需使用 -t ed25519 选项指定它。密钥对可以简单地通过以下方式生成

$ ssh-keygen

无需设置密钥大小,因为所有 Ed25519 密钥都是 256 位的。

请记住,旧的 SSH 客户端和服务器可能不支持这些密钥。

ECDSA

椭圆曲线数字签名算法 (ECDSA) 是从 OpenSSH 5.7 (2011-01-24) 到 OpenSSH 6.5 (2014-01-30) 的首选身份验证算法(密钥交换算法)。

关于它有两种担忧

  1. 政治担忧,NIST 产生的曲线的可信度受到质疑,此前有揭露称 NSA 愿意在软件、硬件组件和已发布标准中插入后门;著名的密码学家 已经 表达了 疑问,关于 NIST 曲线是如何设计的,并且自愿污染已经在过去 证明
  2. 技术担忧,关于正确实现标准的难度以及 速度慢和设计缺陷,这降低了预防措施不足的实现中的安全性。

这些担忧都最好在 libssh curve25519 介绍中进行了总结。虽然政治担忧仍然存在争议,但有一个明确的共识,即 #Ed25519 在技术上更优越,因此应该优先选择。

ECDSA 密钥对可以使用以下命令生成

$ ssh-keygen -t ecdsa

ECDSA 密钥支持三种椭圆曲线大小:256、384 和 521 位。默认值为 256 位。如果您希望生成更强的 ECDSA 密钥对,只需指定 -b 选项

$ ssh-keygen -t ecdsa -b 384

RSA

RSA 提供所有算法中最佳的兼容性,但需要更大的密钥尺寸才能提供足够的安全性。最小密钥尺寸为 1024 位,默认值为 3072(参见 ssh-keygen(1)),最大值为 16384。

RSA 密钥对可以使用以下命令生成

$ ssh-keygen -t rsa

如果您希望生成更强的 RSA 密钥对(例如,为了防范尖端或未知的攻击以及更复杂的攻击者),只需使用比默认值更高的位值指定 -b 选项

$ ssh-keygen -t rsa -b 4096

但请注意,使用更长的密钥会产生边际效益递减。[2][3] GnuPG FAQ 写道:“如果您需要的安全性高于 RSA-2048 提供的安全性,那么应该转向椭圆曲线密码学 — 而不是继续使用 RSA。”[4]

另一方面,NSA Fact Sheet Suite B Cryptography 的最新迭代建议 RSA 的最小 3072 位模数,同时“[准备]迎接即将到来的抗量子算法过渡”。[5]

FIDO/U2F

本文或章节需要扩充。

原因: OpenSSH 8.2 版本 添加了对 FIDO2 驻留密钥的支持,允许将 SSH 密钥存储在硬件令牌上。(在 Talk:SSH keys 中讨论)

OpenSSH 8.2 版本 中,为上述两种椭圆曲线签名方案添加了 FIDO/U2F 硬件认证器支持。它允许通过 USB 或其他方式连接的硬件令牌充当私钥的第二因素。

硬件令牌支持需要 libfido2

注意
  • 客户端和服务器都必须支持 ed25519-skecdsa-sk 密钥类型。
  • OpenSSH 使用中间件库与硬件令牌通信,并附带一个支持 USB 令牌的内部中间件。其他中间件可以通过 sshd_config(5) § SecurityKeyProvider 指令或 SSH_SK_PROVIDER 环境变量为 ssh-keygenssh-add 指定。

连接兼容的 FIDO 密钥后,可以使用以下命令生成密钥对

$ ssh-keygen -t ed25519-sk

通常需要您输入 PIN 和/或点击令牌以确认生成。连接到服务器通常需要点击您的令牌,除非在生成期间使用了 -O no-touch-required 命令行选项,并且在服务器上设置了 sshd(8) § no-touch-required authorized_keys 选项。

要创建不需要触摸事件的密钥,请使用 no-touch-required 选项生成密钥对。例如

$ ssh-keygen -O no-touch-required -t ed25519-sk
注意: 并非所有硬件令牌都支持此选项。如果您使用的是 YubiKey,则 ed25519-sk 密钥类型需要固件版本 5.2.3。[6]

此外,sshd 默认情况下拒绝 no-touch-required 密钥。要允许使用此选项生成的密钥,请在 authorized_keys 文件中为单个密钥启用它,

no-touch-required sk-ssh-ed25519@openssh.com AAAAInN... user@example.com

或者对于整个系统,通过使用以下内容编辑 /etc/ssh/sshd_config

PubkeyAuthOptions none
提示: GitHubGitLab 不支持 no-touch-required

也可以使用 ecdsa-sk 密钥类型生成基于 ECDSA 的密钥对,但上述 #ECDSA 部分中的相关担忧仍然适用。

$ ssh-keygen -t ecdsa-sk

选择密钥位置和密码

在发出 ssh-keygen 命令后,系统将提示您输入所需的私钥名称和位置。默认情况下,密钥存储在 ~/.ssh/ 目录中,并根据使用的加密类型命名。建议您接受默认名称和位置,以便本文后面的代码示例能够正常工作。

当提示输入密码时,如果您考虑到私钥的安全性,请选择一些难以猜测的密码。更长、更随机的密码通常会更强大,并且如果落入坏人之手,则更难破解。

也可以创建不带密码的私钥。虽然这可能很方便,但您需要意识到相关的风险。在没有密码的情况下,您的私钥将以未加密的形式存储在磁盘上。任何访问您的私钥文件的人都将能够在使用基于密钥的身份验证连接的任何 SSH 服务器上冒充您的身份。此外,在没有密码的情况下,您还必须信任 root 用户,因为他们可以绕过文件权限,并且可以随时访问您的未加密私钥文件。

注意: 以前,私钥密码以不安全的方式编码:仅使用单轮 MD5 哈希。OpenSSH 6.5 及更高版本支持一种新的、更安全的格式来编码您的私钥。自 OpenSSH 7.8 版本以来,此格式为默认格式。Ed25519 密钥始终使用新的编码格式。要升级到新格式,只需更改密钥的密码,如下一节所述。

在不更改密钥的情况下更改私钥密码

如果最初选择的 SSH 密钥密码不理想或必须更改,可以使用 ssh-keygen 命令更改密码,而无需更改实际密钥。这也可以用于将密码编码格式更改为新标准。

$ ssh-keygen -f ~/.ssh/id_rsa -p

管理多个密钥

如果您有多个 SSH 身份,您可以通过在配置中使用 HostIdentityFile 指令来设置不同的密钥用于不同的主机或远程用户

~/.ssh/config
Host SERVER1
   IdentitiesOnly yes
   IdentityFile ~/.ssh/id_rsa_IDENTITY1

Host SERVER2 SERVER3
   IdentitiesOnly yes
   IdentityFile ~/.ssh/id_ed25519_IDENTITY2

有关这些选项的完整描述,请参阅 ssh_config(5)

在硬件令牌上存储 SSH 密钥

SSH 密钥也可以存储在安全令牌上,例如智能卡或 USB 令牌。这样做的好处是私钥安全地存储在令牌上,而不是存储在磁盘上。当使用安全令牌时,敏感的私钥也永远不会出现在 PC 的 RAM 中;加密操作在令牌本身上执行。加密令牌的另一个优点是它不绑定到单台计算机;它可以轻松地从计算机上移除并随身携带,以便在其他计算机上使用。

硬件令牌的示例在以下位置描述

将公钥复制到远程服务器

本文或章节需要扩充。

原因: 如果您强制公钥身份验证,如何执行此操作?(在 Talk:SSH keys 中讨论)

生成密钥对后,您需要将公钥复制到远程服务器,以便它将使用 SSH 密钥身份验证。公钥文件与私钥文件共享相同的名称,只是附加了 .pub 扩展名。请注意,私钥不共享,并保留在本地计算机上。

简单方法

如果您的密钥文件是 ~/.ssh/id_rsa.pub,您可以简单地输入以下命令。

$ ssh-copy-id remote-server.org

如果您的用户名在远程计算机上有所不同,请务必在服务器名称前加上用户名和 @ 符号。

$ ssh-copy-id username@remote-server.org

如果您的公钥文件名不是默认的 ~/.ssh/id_rsa.pub,您将收到错误消息 /usr/bin/ssh-copy-id: ERROR: No identities found。在这种情况下,您必须显式提供公钥的位置。

$ ssh-copy-id -i ~/.ssh/id_ed25519.pub username@remote-server.org

如果 ssh 服务器正在侦听默认端口 22 以外的端口,请务必将其包含在主机参数中。

$ ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 221 username@remote-server.org

手动方法

默认情况下,对于 OpenSSH,公钥需要与 ~/.ssh/authorized_keys 连接。首先将公钥复制到远程服务器。

$ scp ~/.ssh/id_ecdsa.pub username@remote-server.org:

上面的示例通过 scp 将公钥 (id_ecdsa.pub) 复制到远程服务器上的主目录。不要忘记在服务器地址的末尾包含 :。另请注意,您的公钥名称可能与给定的示例不同。

在远程服务器上,如果 ~/.ssh 目录尚不存在,您需要创建它,并将您的公钥附加到 authorized_keys 文件中。

$ ssh username@remote-server.org
username@remote-server.org's password:
$ install -dm700 ~/.ssh
$ cat ~/id_ecdsa.pub >> ~/.ssh/authorized_keys
$ rm ~/id_ecdsa.pub
$ chmod 600 ~/.ssh/authorized_keys

最后两个命令从服务器中删除公钥文件,并将 authorized_keys 文件的权限设置为仅对您(所有者)可读写。

SSH 代理

如果您的私钥使用密码加密,则每次尝试使用公钥身份验证连接到 SSH 服务器时,都必须输入此密码。sshscp 的每个单独调用都需要密码才能解密您的私钥,然后才能继续进行身份验证。

SSH 代理是一个程序,它缓存您解密的私钥,并代表您将其提供给 SSH 客户端程序。在这种安排中,您只需提供一次密码,即在将私钥添加到代理的缓存时。当频繁进行 SSH 连接时,此功能可能非常方便。

代理通常配置为在登录时自动运行,并在您的登录会话期间持续存在。存在各种代理、前端和配置来实现此效果。本节概述了许多不同的解决方案,可以对其进行调整以满足您的特定需求。

ssh-agent

ssh-agent 是 OpenSSH 附带的默认代理。它可以直接使用,也可以用作本节稍后提到的几个前端解决方案的后端。当 ssh-agent 运行时,它会 fork 到后台并打印必要的环境变量。例如

$ ssh-agent
SSH_AUTH_SOCK=/tmp/ssh-vEGjCM2147/agent.2147; export SSH_AUTH_SOCK;
SSH_AGENT_PID=2148; export SSH_AGENT_PID;
echo Agent pid 2148;

要使用这些变量,请通过 eval 命令运行该命令。如果使用 fish shell,请改用 ssh-agent -c

$ eval $(ssh-agent)
Agent pid 2157

一旦 ssh-agent 正在运行,您将需要将您的私钥添加到其缓存

$ ssh-add ~/.ssh/id_ed25519
Enter passphrase for /home/user/.ssh/id_ed25519:
Identity added: /home/user/.ssh/id_ed25519 (/home/user/.ssh/id_ed25519)

如果您的私钥已加密,ssh-add 将提示您输入密码。一旦您的私钥已成功添加到代理,您就可以进行 SSH 连接,而无需输入密码。

提示: 为了使包括 git 在内的所有 ssh 客户端在首次使用时将密钥存储在代理中,请将配置设置 AddKeysToAgent yes 添加到 ~/.ssh/config。其他可能的值为 confirmaskno(默认值)。

为了自动启动代理并确保一次只运行一个 ssh-agent 进程,请将以下内容添加到您的 ~/.bashrc

if ! pgrep -u "$USER" ssh-agent > /dev/null; then
    ssh-agent -t 1h > "$XDG_RUNTIME_DIR/ssh-agent.env"
fi
if [ ! -f "$SSH_AUTH_SOCK" ]; then
    source "$XDG_RUNTIME_DIR/ssh-agent.env" >/dev/null
fi

这将运行一个 ssh-agent 进程(如果尚不存在),并保存其输出。如果已有一个进程在运行,我们将检索缓存的 ssh-agent 输出并对其求值,这将设置必要的环境变量。解锁密钥的有效期设置为 1 小时。

也存在许多 ssh-agent 的前端和替代代理,将在本节稍后描述,它们可以避免这个问题。

使用 systemd user 启动 ssh-agent

如果您希望 ssh 代理在您登录时运行,无论 X 是否正在运行,ssh-agent.service 服务单元已包含在 openssh 软件包中,自 9.4p1-3 版本起,可以将其启用用户单元

然后设置环境变量 SSH_AUTH_SOCK$XDG_RUNTIME_DIR/ssh-agent.socket

注意: 如果您使用 GNOME,此环境变量默认会被覆盖。请参阅 GNOME/Keyring#Disabling

转发 ssh-agent

此文章或章节需要语言、wiki 语法或风格改进。请参阅 Help:Style 以获取参考。

原因: 这并非 ssh-agent 特有,例如 gpg-agent 使用相同的环境变量:GnuPG#将 gpg-agent 和 ssh-agent 转发到远程 (在 Talk:SSH keys 中讨论)

转发本地 ssh-agent 到远程(例如,通过命令行参数 ssh -A remote 或通过配置文件中的 ForwardAgent yes),对于远程机器来说,重要的是不要覆盖环境变量 SSH_AUTH_SOCK。因此,如果远程机器使用先前显示的 systemd 单元来启动代理,则当用户通过 SSH 登录时,SSH_AUTH_SOCK 不得在环境中设置。否则,转发可能会失败,并且当在远程机器上使用 ssh-add -l 检查现有密钥时,您可能会看到错误(例如:The agent has no identities)。

例如,如果使用 bash,.bashrc 可能如下所示

~/.bashrc
...
if [[ -z "${SSH_CONNECTION}" ]]; then
    export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent.socket"
fi
...

这样,SSH_AUTH_SOCK 仅在当前会话不是 SSH 登录时设置。当这是一个 SSH 会话时,远程机器上的 SSH_AUTH_SOCK 然后由本地机器设置,以使转发工作。

ssh-agent 作为包装程序

启动 ssh-agent 的另一种方法(例如,在每个 X 会话中)在 UC Berkeley Labs 的 ssh-agent 教程中描述。一个基本用例是,如果您通常使用 startx 命令启动 X,您可以改为在其前面加上 ssh-agent,如下所示

$ ssh-agent startx

因此,您甚至无需考虑它,您可以在 .bash_aliases 文件或等效文件中放置一个别名

alias startx='ssh-agent startx'

以这种方式执行可以避免在登录会话之间漂浮着无关的 ssh-agent 实例的问题。只有一个实例会随着整个 X 会话的生命周期而存在和消亡。

注意: ssh-askpass 需要 DISPLAYWAYLAND_DISPLAY 环境变量才能工作,因此您可能需要在 ~/.xinitrc 而不是 ssh-agent startx 中运行 ssh-agent,在 ~/.xinitrc 中设置了 DISPLAY。例如,您可以将 exec ssh-agent dbus-launch i3 添加到 ~/.xinitrc。或者,作为使用 ssh-agent 作为包装程序的替代方法,您可以将 eval $(ssh-agent) 添加到 ~/.xinitrc

请参阅关于将 x11-ssh-askpass 与 ssh-add 一起使用的说明,了解如何立即将您的密钥添加到代理。

OpenPGP 卡 ssh-agent

此 ssh-agent 专门用于 OpenPGP 卡集成。它使用存储在 OpenPGP 卡身份验证槽中的私钥。

注意: 当在运行此代理时也使用 GnuPG 时,需要将其与 pcscd 一起使用,并配置与 pcscd 的共享访问

安装 openpgp-card-ssh-agent启用启动 openpgp-card-ssh-agent.socket 用户单元。

之后,为此代理添加相关的环境变量

export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/openpgp-card/ssh-agent.sock"

用户 PIN 处理

OpenPGP 卡的用户 PIN 默认通过 org.freedesktop.secrets 提供程序(例如 GNOME KeyringKeePassXCKDE Wallet)持久化。PIN 存储后端是可配置和可扩展的

每个 OpenPGP 卡的用户 PIN 只需持久化一次。在与此代理的第一次 SSH 连接之前,列出可用的 SSH 公钥并添加它们各自的卡标识符

$ ssh-add -L
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJUz6VnFprMe33G88Pq8NLw3wnIKOsBg0CDrwFeUVrU6 FFFE:01234567
$ ssh-add -s FFFE:01234567
Enter passphrase for PKCS#11:
注意: 提示信息中提到了 PKCS#11,因为它是在 ssh-add(1) 中的硬编码消息。但是,在此上下文中,必须输入 OpenPGP 卡用户 PIN。

GnuPG Agent

gpg-agent 具有 OpenSSH Agent 协议模拟功能。请参阅 GnuPG#SSH agent 以获取必要的配置。

Keychain

Keychain 是一个旨在帮助您以最少的用户交互轻松管理 SSH 密钥的程序。它作为一个 shell 脚本实现,驱动 ssh-agentssh-add。Keychain 的一个显著特点是它可以跨多个登录会话维护单个 ssh-agent 进程。这意味着您只需在每次启动本地机器时输入一次密码。

安装

安装 keychain 软件包。

配置

警告: 截至 2015-09-26,-Q, --quick 选项具有意外的副作用,即在重新登录时(至少在使用 GNOME 的系统上)使 keychain 切换到新生成的 ssh-agent,迫使您重新添加所有先前注册的密钥。

将类似于以下行的内容添加到您的shell配置文件中,例如,如果使用Bash

~/.bashrc
eval $(keychain --eval --quiet id_ed25519 id_rsa ~/.keys/my_custom_key)
注意: 使用 ~/.bashrc 而不是上游建议的 ~/.bash_profile,因为在 Arch 上,它由登录和非登录 shell 共同加载,使其适用于文本和图形环境。有关它们之间差异的更多信息,请参阅 Bash#调用

在上面的例子中,

  • --eval 开关输出要由打开的 eval 命令评估的行;这设置了必要的环境变量,以便 SSH 客户端能够找到您的代理。
  • --quiet 将输出限制为警告、错误和用户提示。

可以在命令行中指定多个密钥,如示例所示。默认情况下,keychain 将在 ~/.ssh/ 目录中查找密钥对,但对于非标准位置的密钥可以使用绝对路径。您还可以使用 --confhost 选项通知 keychain 在 ~/.ssh/config 中查找为特定主机定义的 IdentityFile 设置,并使用这些路径来定位密钥。

有关为其他 shell 设置 keychain 的详细信息,请参阅 keychain --helpkeychain(1)

要测试 Keychain,只需打开一个新的终端模拟器或注销并重新登录您的会话。它应该提示您输入指定的私钥的密码(如果适用),可以使用 $SSH_ASKPASS 中设置的程序或在终端上输入。

由于 Keychain 在连续登录时重用相同的 ssh-agent 进程,因此您下次登录或打开新终端时应该不必再次输入密码。您只需在每次机器重启时输入一次密码。

提示

  • keychain 期望公钥文件与其私钥文件位于同一目录中,并带有 .pub 扩展名。如果私钥是符号链接,则可以在符号链接旁边或与符号链接目标相同的目录中找到公钥(此功能需要系统上提供 readlink 命令)。
  • 要禁用图形提示并始终在终端上输入密码,请使用 --nogui 选项。这允许从密码管理器复制粘贴长密码等。
  • 如果您不想立即提示解锁密钥,而是等到需要时再解锁,请使用 --noask 选项。
注意
  • Keychain 能够以相同的方式管理 GPG 密钥。默认情况下,它仅尝试启动 ssh-agent,但您可以使用 --agents 选项修改此行为,例如 --agents ssh,gpg。请参阅 keychain(1)
  • 如果您在 Wayland 上,您可能需要根据 keychain issue 148 添加 --inherit any-once

x11-ssh-askpass

x11-ssh-askpass 软件包为在运行 X 会话时输入密码提供了一个图形对话框。x11-ssh-askpass 仅依赖于 libx11libxt 库,并且 x11-ssh-askpass 的外观是可定制的。虽然它可以由 ssh-add 程序调用,然后将解密的密钥加载到 ssh-agent 中,但以下说明将配置 x11-ssh-askpass 以由前面提到的 Keychain 脚本调用。

安装 keychainx11-ssh-askpass 软件包。

编辑您的 ~/.xinitrc 文件以包含以下行,如有必要,替换您的私钥的名称和位置。请务必将这些命令放置在调用您的窗口管理器的行之前

~/.xinitrc
keychain ~/.ssh/id_ecdsa
[ -f ~/.keychain/$HOSTNAME-sh ] && . ~/.keychain/$HOSTNAME-sh 2>/dev/null
[ -f ~/.keychain/$HOSTNAME-sh-gpg ] && . ~/.keychain/$HOSTNAME-sh-gpg 2>/dev/null
...
exec openbox-session

在上面的示例中,第一行调用 keychain 并传递您的私钥的名称和位置。如果这不是第一次调用 keychain,则以下两行加载 $HOSTNAME-sh$HOSTNAME-sh-gpg 的内容(如果存在)。这些文件存储先前 keychain 实例的环境变量。

使用 x11-ssh-askpass 调用 ssh-add

ssh-add 手册页指定,除了需要定义 DISPLAYWAYLAND_DISPLAY 变量外,还需要将 SSH_ASKPASS 设置为您的 askpass 程序的名称(在本例中为 x11-ssh-askpass)。请记住,默认的 Arch Linux 安装将 x11-ssh-askpass 二进制文件放在 /usr/lib/ssh/ 中,这不会在大多数人的 PATH 中。这有点烦人,不仅在声明 SSH_ASKPASS 变量时,而且在主题化时也是如此。您必须在任何地方指定完整路径。通过符号链接可以同时解决这两个不便之处

$ ln -sv /usr/lib/ssh/x11-ssh-askpass ~/bin/ssh-askpass

这是假设 ~/bin 在您的 PATH 中。因此,现在在您的 .xinitrc 中,在调用您的窗口管理器之前,只需要导出 SSH_ASKPASS 环境变量

$ export SSH_ASKPASS=ssh-askpass

并且您的 X 资源将包含类似的内容

ssh-askpass*background: #000000

以这种方式执行效果很好,配合上述使用 ssh-agent 作为包装程序的方法。您使用 ssh-agent startx 启动 X,然后将 ssh-add 添加到窗口管理器的启动程序列表中。

主题化

可以通过设置与其关联的 X 资源来自定义 x11-ssh-askpass 对话框的外观。一些示例是 https://github.com/sigmavirus24/x11-ssh-askpass 上的 .ad 文件。有关完整详细信息,请参阅 x11-ssh-askpass(1)

替代密码对话框

还有其他密码对话框程序可以代替 x11-ssh-askpass 使用。以下列表提供了一些替代解决方案。

pam_ssh

pam_ssh 项目旨在为 SSH 私钥提供一个可插拔身份验证模块 (PAM)。此模块可以为您的 SSH 连接提供单点登录行为。登录时,可以输入您的 SSH 私钥密码短语来代替或补充您的传统系统密码。一旦您通过身份验证,pam_ssh 模块将生成 ssh-agent 以在会话期间存储您解密的私钥。

要在 tty 登录提示符处启用单点登录行为,请安装非官方的 pam_sshAUR 软件包。

注意: pam_ssh 2.0 现在要求在身份验证过程中使用的所有私钥都位于 ~/.ssh/login-keys.d/ 下。

创建一个指向您的私钥文件的符号链接,并将其放在 ~/.ssh/login-keys.d/ 中。在下面的示例中,将 id_rsa 替换为您自己的私钥文件的名称。

$ mkdir ~/.ssh/login-keys.d/
$ cd ~/.ssh/login-keys.d/
$ ln -s ../id_rsa

编辑 /etc/pam.d/login 配置文件,以包含以下示例中以粗体突出显示的文本。这些行出现的顺序很重要,可能会影响登录行为。

警告: 错误配置 PAM 可能会使系统处于所有用户都被锁定的状态。在进行任何更改之前,您应该了解 PAM 配置的工作原理,并拥有访问 PAM 配置文件的备份方法,例如 Arch Live CD,以防您被锁定并且需要恢复任何更改。IBM developerWorks 上有一篇文章解释了 PAM 配置的更多细节。
/etc/pam.d/login
#%PAM-1.0

auth       required     pam_securetty.so
auth       requisite    pam_nologin.so
auth       include      system-local-login
auth       optional     pam_ssh.so        try_first_pass
account    include      system-local-login
session    include      system-local-login
session    optional     pam_ssh.so

在上面的示例中,登录身份验证最初像往常一样进行,用户将被提示输入其用户密码。添加到身份验证堆栈末尾的附加 auth 身份验证规则然后指示 pam_ssh 模块尝试使用先前输入的用户密码解密在 ~/.ssh/login-keys.d 目录中找到的任何私钥。try_first_pass 选项传递给 pam_ssh 模块,指示它首先尝试使用先前输入的用户密码解密任何 SSH 私钥。如果用户的私钥密码短语和用户密码相同,则应成功,并且不会提示用户输入两次相同的密码。在用户的私钥密码短语用户密码不同的情况下,pam_ssh 模块将在输入用户密码后提示用户输入 SSH 密码短语。optional 控制值确保没有 SSH 私钥的用户仍然可以登录。通过这种方式,pam_ssh 的使用对于没有 SSH 私钥的用户来说是透明的。

如果您使用另一种登录方式,例如像 SLiMXDM 这样的 X11 显示管理器,并且您希望它提供类似的功能,您必须以类似的方式编辑其关联的 PAM 配置文件。提供 PAM 支持的软件包通常在 /etc/pam.d/ 目录中放置默认配置文件。

有关如何使用 pam_ssh 及其选项列表的更多详细信息,请参阅 pam_ssh(8) 手册页。

使用不同的密码来解锁 SSH 密钥

如果您想根据您使用的是密钥的密码短语还是(不同的!)登录密码来解锁 SSH 密钥,您可以修改 /etc/pam.d/system-auth

/etc/pam.d/system-auth
#%PAM-1.0

auth      [success=1 new_authtok_reqd=1 ignore=ignore default=ignore]  pam_unix.so     try_first_pass nullok
auth      required  pam_ssh.so      use_first_pass
auth      optional  pam_permit.so
auth      required  pam_env.so

account   required  pam_unix.so
account   optional  pam_permit.so
account   required  pam_time.so

password  required  pam_unix.so     try_first_pass nullok sha512 shadow
password  optional  pam_permit.so

session   required  pam_limits.so
session   required  pam_unix.so
session   optional  pam_permit.so
session   optional  pam_ssh.so

有关解释,请参阅 [7]

pam_ssh 的已知问题

pam_ssh 项目的工作不频繁,提供的文档也很稀疏。您应该了解其某些在软件包本身中未提及的限制。

  • 2.0 之前的 pam_ssh 版本不支持使用更新的 ECDSA(椭圆曲线)加密选项的 SSH 密钥。如果您使用的是早期版本的 pam_ssh,则必须使用 RSA 密钥。
  • pam_ssh 生成的 ssh-agent 进程不会在用户登录之间持久存在。如果您希望在登录之间保持 GNU Screen 会话处于活动状态,您可能会注意到,当重新附加到您的 screen 会话时,它无法再与 ssh-agent 通信。这是因为 GNU Screen 环境及其子进程仍将引用在调用 GNU Screen 时存在但在之前的注销中被随后杀死的 ssh-agent 实例。Keychain 前端通过在登录之间保持 ssh-agent 进程的活动状态来避免此问题。

pam_exec-ssh

作为 pam_ssh 的替代方案,您可以使用 pam_exec-ssh-gitAUR。它是一个使用 pam_exec 的 shell 脚本。有关配置的帮助,请参阅 上游

GNOME Keyring

GNOME Keyring 工具可以充当 ssh-agent 的包装器,提供 GUI 和/或自动密钥解锁。有关更多详细信息,请参阅 GNOME Keyring#SSH 密钥

使用 Kwallet 存储 SSH 密钥

有关如何使用 kwallet 存储 SSH 密钥的说明,请参阅 KDE Wallet#使用 KDE Wallet 存储 ssh 密钥密码短语

KeePass2 与 KeeAgent 插件

KeeAgentKeePass 的一个插件,允许将存储在 KeePass 数据库中的 SSH 密钥用于其他程序的 SSH 身份验证。

  • 支持 PuTTY 和 OpenSSH 私钥格式。
  • 适用于 Linux/Mac 上的本机 SSH 代理和 Windows 上的 PuTTY。

请参阅 KeePass#在 KeePass 中安装插件安装 keepass-plugin-keeagent 软件包。

此代理可以直接使用,通过匹配 KeeAgent 套接字:KeePass -> 工具 -> 选项 -> KeeAgent -> 代理模式套接字文件 -> %XDG_RUNTIME_DIR%/keeagent.socket - 和环境变量:export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR"'/keeagent.socket'

KeePassXC

KeePass 的分支 KeePassXC 可以充当现有 SSH 代理的客户端。存储在其数据库中的 SSH 密钥可以自动(或手动)添加到代理。它也与 KeeAgent 的数据库格式兼容。

故障排除

密钥被服务器忽略

  • 如果 SSH 服务器似乎忽略了您的密钥,请确保您已在所有相关文件上设置了正确的权限。
对于本地机器
$ chmod 700 ~/.ssh
$ chmod 600 ~/.ssh/key
对于远程机器
$ chmod 700 ~/.ssh
$ chmod 600 ~/.ssh/authorized_keys
对于远程机器,还要检查目标用户的 home 目录是否具有正确的权限(它必须不可被组和其他用户写入)
$ chmod go-w ~target_user
  • 如果这不能解决问题,您可以尝试暂时将 StrictModes 设置为 no/etc/ssh/sshd_config 中。如果使用 StrictModes off 进行身份验证成功,则可能是文件权限问题仍然存在。
  • 确保 ~/.ssh/authorized_keys 中的密钥输入正确,并且仅使用单行。
  • 确保远程机器支持您正在使用的密钥类型:某些服务器不支持 ECDSA 密钥,请尝试改用 RSA 密钥,请参阅 #生成 SSH 密钥对
  • 您可能想要使用调试模式并在连接时监视输出
# /usr/bin/sshd -d
  • 如果您为密钥指定了另一个名称,例如 id_rsa_server,则需要使用 -i 选项连接
$ ssh -i id_rsa_server user@server

agent refused operation (代理拒绝操作)

如果您的私钥需要密码(或者,例如,您有一个带有 PIN 码的硬件密钥),但 ssh-agent 未提供密码,则 ssh 将失败

sign_and_send_pubkey: signing failed for ECDSA-SK user@host from agent: agent refused operation

此问题的一个潜在原因是 ssh-agent 无法提示输入密码。确保 ssh-agent 可以访问显示服务器(通过 DISPLAY 环境变量)或 TTY。对于某些图形环境,您可能只需要安装 x11-ssh-askpass,对于其他设置,也请遵循 #x11-ssh-askpass 说明。

另一个原因,如果使用硬件身份验证器,可能是密钥故障或已拔出。

当前有一个打开的 bug,当使用使用 -O verify-required 选项创建的身份验证器密钥(如 ED25519-sk 和 ECDSA-SK)时,会触发“agent refused operation”错误。要避免此问题,请为 ssh 命令使用 -o IdentityAgent=none -o IdentitiesOnly=yes 选项,或将其添加到您的 ssh_config 文件中以用于相关主机

Host myserver.tld
    IdentityAgent none
    IdentitiesOnly yes

另请参阅