跳转至内容

SSH 密钥

来自 ArchWiki
(重定向自 Ssh-keygen)

本文章或章节需要扩充。

原因: 引言和背景部分忽略了服务器视角。(在 Talk:SSH keys#Server perspective is ignored 中讨论)

SSH 密钥可以通过 公钥加密质询-响应认证 的方式向 SSH 服务器标识您的身份。基于密钥的认证的主要优势在于,与密码认证相比,它不容易受到 暴力破解攻击,并且在服务器被泄露时,您不会暴露有效的凭据 (参见 RFC 4251 9.4.4)。

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

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

本文档假定您已对 安全 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 实现不再支持它们。Dropbear 2022.83 禁用了 DSA 密钥支持,而 OpenSSH 10.0 和 libssh 0.11.0 完全删除了对 DSA 密钥的支持。因此,密码系统的选择在于 RSA 或两种 ECC 类型之一。

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

注意 这些密钥仅用于验证您的身份;选择更强的密钥不会增加通过 SSH 传输数据的 CPU 负载。
提示 为了保护 SSH 密钥免遭从机器中泄露,可以将它们存储在 FIDO/U2F 硬件身份验证器受信任平台模块 中。
  • 通过在生成密钥时使用特殊的“security key”密钥类型,可以将 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 开发人员关于其工作原理的博客文章

它已在 许多应用程序和库 中实现,并且是 ssh-keygen(1)dropbearkey(1) 中的默认密钥类型。

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. 政治担忧,在揭露 NSA 故意在软件、硬件组件和已发布标准中插入后门之后,对 NIST 生成的曲线的信任度受到了质疑;著名的密码学家 表达 怀疑 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 的“Suite B Cryptography”情况说明书最新版本建议 RSA 的最小模数为 3072 位,同时“[为] 准备迎接即将到来的抗量子算法过渡”。[5]

FIDO/U2F

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

需要 libfido2 来支持硬件令牌。

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

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

$ ssh-keygen -t ed25519-sk

通常需要输入 PIN 码和/或触摸令牌以确认生成。

resident

默认情况下,生成的 SSH 密钥由两部分组成:磁盘上的密钥句柄,以及每个安全密钥独有的私钥。为了方便在机器之间移动 FIDO 密钥,可以使用 ssh-keygen(1) § resident 选项 -O resident 生成密钥。这表示“密钥句柄应存储在 FIDO 身份验证器本身上”。[6]

$ ssh-keygen -O resident -t ed25519-sk

之后,在新机器上,可以使用 ssh-keygen(1) § K 下载密钥:

$ ssh-keygen -K
no-touch-required

连接服务器通常需要触摸令牌,除非在生成时使用了 -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。[7]

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

~/.ssh/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 运行时,它会分叉到后台并打印必要的环境变量。例如:

$ 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 连接而无需输入密码。

提示 为了让所有 ssh 客户端(包括 Git)在首次使用时存储密钥到代理中,请将配置设置 AddKeysToAgent yes 添加到 ~/.ssh/config。有关其他可能值,请参阅 ssh_config(5) § AddKeysToAgent

为了自动启动代理并确保一次只运行一个 ssh-agent 进程,请在您的 ~/.bashrc 中创建 touch $XDG_RUNTIME_DIR/ssh-agent.env 并添加以下内容:

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 用户服务启动 ssh-agent

如果您希望您的 SSH 代理在您登录时运行,而不管 X 是否在运行,那么自 9.4p1-3 版本起,openssh 中包含一个方便的 ssh-agent.service,可以将其 启用用户单元

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

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

转发 ssh-agent

本文或本节需要在语言、wiki 语法或风格方面进行改进。请参阅 Help:Style 获取参考。

原因: 这不是 ssh-agent 特有的,例如 gpg-agent 使用相同的环境变量:GnuPG#Forwarding gpg-agent and ssh-agent to remote (在 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 而不是 ssh-agent startx,因为 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 用户单元。

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

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 选项会产生意想不到的副作用,导致 keychain 在重新登录时切换到一个新生成的 ssh-agent(至少在使用 GNOME 的系统上),迫使您重新添加所有以前注册的密钥。

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

~/.bashrc
eval $(keychain --eval --quiet id_ed25519 id_rsa ~/.keys/my_custom_key)
注意 使用 ~/.bashrc 而不是上游建议的 ~/.bash_profile,因为在 Arch 上它会被登录和非登录 shell 所包含,因此适用于文本和图形环境。有关两者之间差异的更多信息,请参阅 Bash#Invocation

在上面的示例中:

  • --eval 开关会输出要由打开的 eval 命令进行求值的行;这会为 SSH 客户端设置必要的环境变量,以便找到您的代理。
  • --eval 开关会为 SHELL 环境变量中指定的 shell 生成命令。在 keychain 命令之前指定它,例如使用 SHELL=/bin/bash,可能会为您省去一些麻烦。
  • --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 密钥。对于 keychain 版本 2.9.0 及更早版本,默认尝试仅启动 ssh-agent,但您可以使用 --agents 选项修改此行为,例如 --agents ssh,gpg。请参阅 keychain(1)
  • 从 Keychain 2.9.0 开始,--agents 选项已弃用。请参阅 keychain(1)
  • 如果您使用的是 Wayland,您可能需要添加 --inherit any-once,根据 keychain issue 148

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 私钥的用户仍然能够登录。这样,对于没有 SSH 私钥的用户来说,pam_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

有关解释,请参阅 [8]

pam_ssh 的已知问题

pam_ssh 项目的工作不频繁,提供的文档也很 sparse。您应该意识到它的一些在软件包本身未提及的限制。

  • pam_ssh 版本 2.0 之前的版本不支持使用较新选项 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 keys

使用 Kwallet 存储 SSH 密钥

有关如何使用 kwallet 存储 SSH 密钥的说明,请参阅 KDE Wallet#Using the KDE Wallet to store ssh key passphrases

KeePass2 配合 KeeAgent 插件

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

  • 支持 PuTTY 和 OpenSSH 私钥格式。
  • 在 Linux/Mac 上与原生 SSH 代理配合使用,在 Windows 上与 PuTTY 配合使用。

请参阅 KeePass#Plugin installation in KeePass安装 keepass-plugin-keeagent 包。

此代理可以直接使用,通过匹配 KeeAgent 套接字:KeePass -> Tools -> Options -> KeeAgent -> Agent mode socket file -> %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
对于远程机器,还要检查目标用户的家目录是否具有正确的权限(它对组和其他用户不能是可写的)。
$ chmod go-w ~target_user
  • 如果问题仍未解决,您可以尝试在 /etc/ssh/sshd_config 中将 StrictModes 临时设置为 no。如果使用 StrictModes off 进行身份验证成功,那么很可能是文件权限问题仍然存在。
  • 确保 ~/.ssh/authorized_keys 中的密钥输入正确,并且只使用单行。
  • 确保远程机器支持您正在使用的密钥类型:有些服务器不支持 ECDSA 密钥,尝试改用 RSA 密钥,请参阅 #Generating an SSH key pair
  • 您可能希望使用调试模式并监控连接时的输出。
# /usr/bin/sshd -d
  • 如果您为密钥指定了其他名称,例如 id_rsa_server,则需要使用 -i 选项进行连接。
$ ssh -i id_rsa_server user@server

代理拒绝操作

如果您的私钥需要密码(或者,例如,您有一个带有 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 等认证器密钥时,会触发“代理拒绝操作”错误。为避免此问题,请在 ssh 命令中使用 -o IdentityAgent=none -o IdentitiesOnly=yes 选项,或将其添加到适用于相关主机的 ssh_config 文件中。

Host myserver.tld
    IdentityAgent none
    IdentitiesOnly yes

参见