跳转到内容

SSH 密钥

来自 Arch Linux 中文维基

这篇文章的某些内容需要扩充。

原因:The intro and Background section ignore the server perspective. (在 Talk:SSH 密钥 中讨论)

对于使用公钥加密质询-应答式认证的 SSH 服务器,SSH 密钥可以作为证明身份的方法。基于密钥的认证主要优点在于,与密码认证相比,它不易遭受暴力破解攻击,且在服务器被攻破的情况下也不会泄露您的有效凭证(详细信息请参考 RFC 4251 9.4.4)。

不仅如此,SSH 密钥认证也会比传统的密码认证更加方便。当与被称作 SSH agent 的程序共用时,SSH 密钥可以让您无需记住或输入每个系统的密码,就能够连接到一个或多个服务器。

基于密钥的认证并非没有缺点,可能也并非适用于一切环境,但在很多情况下可以提供一些有力的优势。对 SSH 密钥如何工作的概略理解会帮助您决定如何以及何时使用它们,以满足您的需求。

本文假定您已经对于安全外壳协议有了基本理解,并安装openssh 软件包。

背景[编辑 | 编辑源代码]

SSH 密钥都是成对生成的,其一称为公钥,另一则称为私钥。私钥只由您所知,必须安全保管。相对地,公钥可以向您想连接的任何 SSH 服务器自由地共享。

如果一个 SSH 服务器在文件中存有您的公钥,并收到了您的连接请求,就会使用您的公钥构建一个质询问题并发送给您。这一质询问题是一条加密信息,必须得到正确应答,服务器才能允许您访问。这条编码信息特别安全是在于,只有私钥持有者才能理解它。公钥可以用来加密信息,但不能用来解密同一条信息。只有您,私钥的持有者,能够正确理解这一质询问题并产生合适的应答。

这一质询-应答过程发生在后台,对用户不可见。只要您持有私钥(一般存放在 ~/.ssh/ 目录下),您的 SSH 客户端就应当能够向服务器回复正确的应答。

私钥是受保护的秘密,因此建议以加密形式将其存储在磁盘上。当需要被加密的私钥时,为了解密它,需要先输入密码。虽然这在表面上可能像是您在向 SSH 服务器提供登录密码,但该密码只用于解密本地系统上的私钥。该密码并未通过网络传输。

生成密钥对[编辑 | 编辑源代码]

通过运行 ssh-keygen 命令可以生成密钥对,具体“一般被认为充足”且应当兼容于几乎所有客户端和服务器的选项请参考 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 rounds 数量。

也可以用 -C 开关对公钥添加可选的注释栏,从而更容易在 ~/.ssh/known_hosts~/.ssh/authorized_keys 以及 ssh-add -L 输出等处进行辨识。例如:

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

会添加一条注释,说明是哪个用户何时在哪台机器上创建的密钥。

选择认证密钥类别[编辑 | 编辑源代码]

OpenSSH(对于认证密钥)支持数种签名算法,按照所采用的数学性质可分为两类:

  1. Ed25519 以及 ECDSA,依赖于椭圆曲线离散对数问题(ECDLP);(例子
  2. RSA,依赖于对两个大质数之积进行分解的实际困难度

椭圆曲线密码学(ECC)算法是对于公钥密码体制的较新发展。其主要优势之一是以较小密钥提供同等安全性的能力,使得计算密集操作更少(也就是密钥创建、加密及解密更快)且存储及传输的要求更低。

由于安全问题,DSA 密钥已被废弃,且 SSH 实现正逐渐将其移除。OpenSSH 9.8 构建默认不带 DSA 密钥支持,Dropbear 2022.83 禁用了 DSA 密钥支持,libssh 0.11.0 完全移除了 DSA 密钥支持。因此密码体制的选择范围就限定在了 RSA 或两种 ECC 之一当中。

#RSA 密钥会提供最大的可移植性,而 #Ed25519 会提供最佳的安全性,但需要新版本的客户端与服务器[1]。. #ECDSA 兼容性可能比 Ed25519 更好(虽然还是不如 RSA),但存在对于其安全性的怀疑(见下文)。

默认的 Ed25519 会提供最佳安全性和不错的性能。ECDSA 比 Ed25519 更慢,但比 RSA 快,在安全性上存在些疑问(具体参见下文)。RSA 密钥提供了对旧服务器的最佳兼容性,但需要更大的密钥才能提供足够的安全性。

注意:这些密钥只用于认证您的身份;选择更强的密钥并不会在通过 SSH 传输数据时加重 CPU 负担。
提示:为防止 SSH 密钥从设备上外泄,可以将密钥储存在 FIDO/U2F 实体认证器可信平台模块中。
  • 在生成密钥时,可以使用特殊的“安全密钥”类型将 Ed25519 或 ECDSA 密钥储存在 FIDO/U2F 实体认证器上。具体信息请参考 #FIDO/U2F
  • 可信平台模块支持 ECDSA 和 RSA,因此可以将 SSH 密钥封装在 TPM 内。具体信息请参考 可信平台模块#SSH

Ed25519[编辑 | 编辑源代码]

Ed25519 在2014年1月被引入 OpenSSH 6.5:“Ed25519 是一种能够提供比 ECDSA 及 DSA 更佳安全性及良好性能的椭圆曲线签名体系”。其主要长处在于速度、时间恒定运行时(从而能够对抗侧信道攻击)以及无需晦暗不明的硬编码常数。[2] 关于其工作方式,参见由一位 Mozilla 开发者撰写的博客文章

它已经在众多应用及库中得到实现,并且在 OpenSSH 中是默认的密钥交换算法(与密钥签名不同)。

Ed25519 密钥对生成方法如下:

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

需要明白的是,使用更长的密钥存在收益递减。[3][4] GnuPG 常见问答记载:“如果你需要的安全性比 RSA-2048 所提供的更强,应该做的是改用椭圆曲线密码学——而不是接着使用 RSA。”[5]

另一方面,最新版本的 NSA Fact Sheet Suite B Cryptography 建议对于 RSA 使用最少 3072 位的模,同时“为即将到来的抗量子算法迁移(做准备)”。[6]

FIDO/U2F[编辑 | 编辑源代码]

这篇文章的某些内容需要扩充。

原因:OpenSSH 8.2 版本添加了 FIDO2 resident 密钥支持,可以将 SSH 密钥存放在实体令牌上。 (在 Talk:SSH 密钥 中讨论)

OpenSSH 8.2 版本添加了 FIDO/U2F 实体认证器支持,可以使用上文描述的两种椭圆曲线签名方案。这使得可以将连接的实体令牌作为多重验证方案与私钥搭配使用。

实体令牌需要用到 libfido2

注意:
  • 客户端和服务器都需要支持 ed25519-skecdsa-sk 密钥类型。
  • OpenSSH 使用中间件库来与实体令牌通信,其带有支持 USB 令牌的内部中间件。如果要使用其它中间件,可以通过sshd_config(5) § SecurityKeyProvider 配置项或为 ssh-keygenssh-add 配置 SSH_SK_PROVIDER 环境变量来实现。

连接兼容的 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,需要 5.2.3 固件版本才能使用 ed25519-sk 类型密钥。[7]

另外,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 的密钥对可通过 ecdsa-sk 密钥类型生成,但同样会有 #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 的内存当中;密码学操作在令牌自身上进行。密码学令牌还有一点好处是,它不与单台电脑绑定;可以将其从一台电脑上轻松取下,四处携带之后在其他电脑上使用。

硬件令牌的例子可参考:

将公钥复制到远程服务器上[编辑 | 编辑源代码]

这篇文章的某些内容需要扩充。

原因:强制公钥认证的情况下如何实现? (在 Talk:SSH 密钥 中讨论)

创建好密钥对之后,您需要将公钥上传到远程服务器上,以便用于 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

如果远程服务器监听端口不是默认的 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 连接时,将会提供不少便利。

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 agent 而不论 X 是否在运行的话,从 9.4p1-3 版本开始 openssh 提供了 ssh-agent.service,可以作为用户单元启用

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

注意:如果您使用 GNOME,该环境变量默认会被覆盖。见 GNOME/Keyring#禁用钥匙环的守护进程组件

转发 ssh-agent[编辑 | 编辑源代码]

本文或本章节的语言、语法或风格需要改进。参考:帮助:风格

原因:This is not specific to ssh-agent, e.g. gpg-agent uses the same environment variable: GnuPG#Forwarding gpg-agent and ssh-agent to remote(在Talk:SSH 密钥讨论)

When forwarding a local ssh-agent to remote (e.g., through command-line argument ssh -A remote or through ForwardAgent yes in the configuration file), it is important for the remote machine not to overwrite the environment variable SSH_AUTH_SOCK. So if the remote machine uses a systemd unit shown previously to start the agent, SSH_AUTH_SOCK must not be set in the environment when a user is logged in through SSH. Otherwise, the forwarding may fail, and you may see errors (for example: The agent has no identities) when checking the existing keys with ssh-add -l on the remote machine.

For example, if using bash, the .bashrc could be something like:

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

In this way, SSH_AUTH_SOCK is only set when the current session is not a SSH login. And when this is a SSH session, SSH_AUTH_SOCK on the remote machine is then set by the local machine to make the forwarding work.

作为包装程序运行 ssh-agent[编辑 | 编辑源代码]

加州大学伯克利分校实验室的这份 ssh-agent 教程还介绍了一种随每个 X 会话启动 ssh-agent 的方法。基本用法是,如果您使用命令 startx 正常启动 X,您可以改成像这样在前面加上 ssh-agent

$ ssh-agent startx

您也可以在 .bash_aliases 或等同文件中设置别名,就不用再费心了:

alias startx='ssh-agent startx'

这样做可以避免多个会话中存在多余的 ssh-agent 进程。整个 X 会话中只会有一个 ssh-agent 在运行。

注意:ssh-askpass 需要 DISPLAYWAYLAND_DISPLAY 环境变量才能正常工作,所以你可能会需要在 ~/.xinitrc 中运行 ssh-agent 以配置 DISPLAY 选项,而不是使用 ssh-agent startx(如将 exec ssh-agent dbus-launch i3 添加到 ~/.xinitrc)。或者也可以将 eval $(ssh-agent) 添加到 ~/.xinitrc 以使用 ssh-agent 作为包装程序运行。

下文有关协同使用 x11-ssh-askpass 与 ssh-add 的说明了解如何立即将密钥添加到 agent。

OpenPGP card ssh-agent[编辑 | 编辑源代码]

This ssh-agent specializes on OpenPGP card integration. It uses private keys that are stored in OpenPGP card authentication slots.

注意: When also using GnuPG while running this agent, it is required to use it with pcscd and configure shared access with pcscd.

Install openpgp-card-ssh-agent and enable and start the openpgp-card-ssh-agent.socket user unit.

Afterwards add the relevant environment variable for this agent:

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

User PIN handling[编辑 | 编辑源代码]

The user PIN for the OpenPGP card is persisted via an org.freedesktop.secrets provider (such as GNOME Keyring, KeePassXC or KDE Wallet) by default. The PIN storage backend is configurable and extendable.

The user PIN needs to be persisted only once for each OpenPGP card. Prior to the first SSH connection with this agent, list the available SSH public keys and add their respective card identifiers:

$ ssh-add -L
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJUz6VnFprMe33G88Pq8NLw3wnIKOsBg0CDrwFeUVrU6 FFFE:01234567
$ ssh-add -s FFFE:01234567
Enter passphrase for PKCS#11:
注意: The prompt mentions PKCS#11 as it is a hardcoded message in ssh-add(1). However, in this context the OpenPGP card user PIN must be entered.

GnuPG Agent[编辑 | 编辑源代码]

gpg-agent 模拟了 OpenSSH 的代理协议,必要配置请参考 GnuPG#SSH 代理

Keychain[编辑 | 编辑源代码]

Keychain 是一个用来方便管理 SSH 密钥对的程序,它能尽最大努力去减少对用户的打扰。实际上,它就是一个 shell 脚本,驱动 ssh-agent 或者 gpg-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#调用

上例当中,

  • --eval 开关可以输出供 eval 命令执行的行;这样可以设置必要的环境变量,让 SSH 客户端能够找到您的 agent。
  • --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)
注意:
  • Keychain 能够以同样的方式管理 GPG 密钥。它默认只会尝试启动 ssh-agent,但使用 --agents 选项可以改变这一行为,例如 --agents ssh,gpg。详细信息请参考 keychain(1)
  • 根据 keychain issue 148,在使用 Wayland 时可能需要添加 --inherit any-once 参数。

x11-ssh-askpass[编辑 | 编辑源代码]

The x11-ssh-askpass package provides a graphical dialog for entering your passhrase when running an X session. x11-ssh-askpass depends only on the libx11 and libxt libraries, and the appearance of x11-ssh-askpass is customizable. While it can be invoked by the ssh-add program, which will then load your decrypted keys into ssh-agent, the following instructions will, instead, configure x11-ssh-askpass to be invoked by the aforementioned Keychain script.

Install the keychain and x11-ssh-askpass packages.

Edit your ~/.xinitrc file to include the following lines, replacing the name and location of your private key if necessary. Be sure to place these commands before the line which invokes your window manager.

~/.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

In the above example, the first line invokes keychain and passes the name and location of your private key. If this is not the first time keychain was invoked, the following two lines load the contents of $HOSTNAME-sh and $HOSTNAME-sh-gpg, if they exist. These files store the environment variables of the previous instance of keychain.

Calling x11-ssh-askpass with ssh-add[编辑 | 编辑源代码]

The ssh-add manual page specifies that, in addition to needing the DISPLAY or WAYLAND_DISPLAY variable defined, you also need SSH_ASKPASS set to the name of your askpass program (in this case x11-ssh-askpass). It bears keeping in mind that the default Arch Linux installation places the x11-ssh-askpass binary in /usr/lib/ssh/, which will not be in most people's PATH. This is a little annoying, not only when declaring the SSH_ASKPASS variable, but also when theming. You have to specify the full path everywhere. Both inconveniences can be solved simultaneously by symlinking:

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

This is assuming that ~/bin is in your PATH. So now in your .xinitrc, before calling your window manager, one just needs to export the SSH_ASKPASS environment variable:

$ export SSH_ASKPASS=ssh-askpass

and your X resources will contain something like:

ssh-askpass*background: #000000

Doing it this way works well with the above method on using ssh-agent as a wrapper program. You start X with ssh-agent startx and then add ssh-add to your window manager's list of start-up programs.

Theming[编辑 | 编辑源代码]

The appearance of the x11-ssh-askpass dialog can be customized by setting its associated X resources. Some examples are the .ad files at https://github.com/sigmavirus24/x11-ssh-askpass. See x11-ssh-askpass(1) for full details.

Alternative passphrase dialogs[编辑 | 编辑源代码]

There are other passphrase dialog programs which can be used instead of x11-ssh-askpass. The following list provides some alternative solutions.

pam_ssh[编辑 | 编辑源代码]

pam_ssh 项目为 SSH 私钥提供了一个可插拔认证模块(PAM),该模块可以为 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 模块会在用户输入登录密码后要求输入私钥密码。optional 选项可以保证在没有私钥时用户也能正常登录系统。这样,没有 SSH 私钥的用户对 pam_ssh 是无感的。

如果你使用 X11 显示管理器(如 SLiMXDM)等其它方式进行登录,必须仿照如上配置编辑 PAM 配置文件。提供 PAM 支持的软件包通常会将默认配置文件放置在 /etc/pam.d/ 目录下。

Further details on how to use pam_ssh and a list of its options can be found in the pam_ssh(8) man page.

使用不同密码解锁 SSH 密钥[编辑 | 编辑源代码]

If you want to unlock the SSH keys or not depending on whether you use your key's passphrase or the (different!) login password, you can modify /etc/pam.d/system-auth to

/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

For an explanation, see [8].

pam_ssh 已知问题[编辑 | 编辑源代码]

pam_ssh 项目并不活跃,其提供的文档也比较少。你需要注意该软件未提及的一些使用限制,比如:

  • 2.0 版本前的 pam_ssh 不支持 ECDSA(椭圆曲线)SSH 密钥,需要使用旧版本的必须使用 RSA 密钥。
  • pam_ssh 调用的 ssh-agent 进程仅限于当前用户登录,无法在多次登录间持久化。如果你在登录期间保留了一个 GNU Screen 会话,会发现重新连接 screen 会话后无法与 ssh-agent 进行通信。这是因为 GNU Screen 环境和其子进程仍在引用调用 screen 时存在的 ssh-agent,但其已随上次登出被关闭。上文提到的 Keychain 前端可以在多次登录间保留 ssh-agent 进程,从而避免该问题。

pam_exec-ssh[编辑 | 编辑源代码]

As an alternative to pam_ssh you can use pam_exec-ssh-gitAUR. It is a shell script that uses pam_exec. Help for configuration can be found upstream.

GNOME Keyring[编辑 | 编辑源代码]

GNOME Keyring 可用作 ssh-agent 的封装,并提供图形界面和/或密钥自动解锁功能。详情请参考 GNOME Keyring#SSH密钥

使用 Kwallet 存储 SSH 密钥[编辑 | 编辑源代码]

For instructions on how to use kwallet to store your SSH keys, see KDE Wallet#Using the KDE Wallet to store ssh key passphrases.

KeePass2 搭配 KeeAgent 插件[编辑 | 编辑源代码]

KeeAgent is a plugin for KeePass that allows SSH keys stored in a KeePass database to be used for SSH authentication by other programs.

  • Supports both PuTTY and OpenSSH private key formats.
  • Works with native SSH agent on Linux/Mac and with PuTTY on Windows.

See KeePass#Plugin installation in KeePass or install the keepass-plugin-keeagent package.

This agent can be used directly, by matching KeeAgent socket: KeePass -> Tools -> Options -> KeeAgent -> Agent mode socket file -> %XDG_RUNTIME_DIR%/keeagent.socket- and environment variable: export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR"'/keeagent.socket'.

KeePassXC[编辑 | 编辑源代码]

The KeePassXC fork of KeePass can act as a client for an existing SSH agent. SSH keys stored in its database can be automatically (or manually) added to the agent. It is also compatible with KeeAgent's database format.

疑难排解[编辑 | 编辑源代码]

密钥被服务器忽略[编辑 | 编辑源代码]

如果您的 SSH 服务器忽略了您的 SSH 密钥对,您需要检查一下相关文件的权限是否正确。

本地机器上:
$ chmod 700 ~/.ssh
$ chmod 600 ~/.ssh/key
服务器上:
$ chmod 700 ~/.ssh
$ chmod 600 ~/.ssh/authorized_keys

如果这样还不能解决您的问题,您可以试试将 /etc/ssh/sshd_config 中的 StrictModes 设为 no。如果使用 StrictModes off 就能够顺利认证的话,说明相关文件的权限还没有改对。

  • 确保 ~/.ssh/authorized_keys 中的密钥正确并只有一行。
  • 确保远程设备支持使用的密钥类型:有些服务器不支持 ECDSA 密钥,可以试试 RSA 密钥,具体请参考 #生成密钥对
  • 你可能会想在连接时启用调试模式并监控输出:
# /usr/bin/sshd -d
  • 如果你的密钥使用其它名字(如 id_rsa_server),需要使用 -i 选项进行连接:
$ ssh -i id_rsa_server user@server

代理拒绝操作[编辑 | 编辑源代码]

If your private key requires a password (or, for instance, you have a hardware key with a PIN) but ssh-agent is not provided with one, ssh will fail:

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

One potential cause for this is ssh-agent being unable to prompt for a password. Ensure that ssh-agent has access to either a display server (via the DISPLAY environment variable) or a TTY.

Another cause, if using a hardware authenticator, could be the key malfunctioning or being unplugged.

There is currently an open bug that triggers with the "agent refused operation" error when using authenticator keys like ED25519-sk and ECDSA-SK that were created with the option -O verify-required. To avoid this issue, use the -o IdentityAgent=none -o IdentitiesOnly=yes option for the ssh command or add it to your ssh_config file for the relevant hosts:

Host myserver.tld
    IdentityAgent none
    IdentitiesOnly yes

参考[编辑 | 编辑源代码]