OpenSSH for Windows 中基于密钥的身份验证

Windows 环境中的大多数身份验证都是使用用户名-密码对完成的,这非常适用于共享公共域的系统。 在跨域(例如本地和云托管系统之间)工作时,这种类型的身份验证容易受到暴力破解入侵的影响。

相比之下,Linux 环境通常使用公钥/私钥对来驱动不需要使用可猜密码的身份验证。 OpenSSH 包括有助于支持基于密钥的身份验证的工具,具体来说:

  • 用于生成安全密钥的 ssh-keygen
  • ssh-agentssh-add ,用于安全地存储私钥。
  • scpsftp 在初始使用服务器期间安全地复制公钥文件。

本文档概述了如何在 Windows 上使用这些工具开始在安全外壳(SSH)中使用基于密钥的身份验证。 如果不熟悉 SSH 密钥管理,我们强烈建议查看 NIST 文档 IR 7966,标题为“使用安全外壳进行交互式访问和自动访问管理的安全性”。

密钥对

密钥对指的是由特定的身份验证协议使用的公钥和私钥文件。

SSH 公钥身份验证使用非对称加密算法来生成两个密钥文件,一个 是专用 文件,另一个 是公共文件。 每个私钥文件等效于密码,在所有情况下都应保持保护。 如果有人获取了你的私钥,则他们可以像你一样登录到你有权登录的任何 SSH 服务器。 公钥位于 SSH 服务器上,可以共享,而不会损害私钥。

SSH 服务器和客户端可以使用基于密钥的身份验证将提供的用户名与私钥进行比较。 如果无法依据客户端私钥验证服务器端公钥,则身份验证失败。

生成密钥对时,可以通过输入密码对来实现多重身份验证。 有关详细信息,请参阅 用户密钥生成。 在身份验证期间,用户会被提示输入口令。 在 SSH 客户端上,将密码短语与私钥结合使用以对用户进行身份验证。

重要

通过基于密钥的身份验证打开的远程会话没有关联的用户凭据。 因此,会话无法以用户身份进行外部身份验证。 此行为是设计造成的。

主机密钥生成

公钥具有特定的访问控制列表(ACL)要求,即在 Windows 上,仅允许访问管理员和系统用户。 首次使用 sshd 服务时,主机的密钥对自动生成。

重要

需要先安装 OpenSSH 服务器,然后才能运行本文中的命令。 有关详细信息,请参阅 OpenSSH for Windows 入门

默认情况下,需要手动启动 sshd 。 若要将其配置为在每次重新启动服务器时自动启动,请从服务器上提升的 PowerShell 提示符运行以下命令:

# Set the sshd service to be started automatically.
Get-Service -Name sshd | Set-Service -StartupType Automatic

# Start the sshd service.
Start-Service sshd

由于没有与 sshd 服务关联的用户,因此主机密钥存储在 C:\ProgramData\ssh 下。

用户密钥生成

若要使用基于密钥的身份验证,首先需要为客户端生成公钥/私钥对。 可用于 ssh-keygen.exe 生成密钥文件,并且可以指定以下密钥生成算法:

  • 数字签名算法 (DSA)
  • Rivest-Shamir–Adleman (RSA)
  • 椭圆曲线数字签名算法 (ECDSA)
  • 编号:ED25519

如果未指定算法,则使用 Ed25519。 应使用强算法和密钥长度,例如此示例中的 ECDSA。

若要使用 ECDSA 算法生成密钥文件,请在客户端上的 PowerShell 或命令提示符窗口中运行以下命令:

ssh-keygen -t ecdsa

命令的输出应类似于以下行,但 username 替换为用户名:

Generating public/private ecdsa key pair.
Enter file in which to save the key (C:\Users\username/.ssh/id_ecdsa):

在提示符下,可以选择 Enter 接受默认文件路径,也可以为生成的密钥指定路径或文件名。

接下来,系统会提示您使用密码短语来加密私钥文件。 一般情况下,我们不建议使用空的通行短语,因为通行短语与密钥文件一起协作提供双重身份验证。 但在此示例中,可以将密码短语留空。

Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in C:\Users\username/.ssh/id_ecdsa.
Your public key has been saved in C:\Users\username/.ssh/id_ecdsa.pub.
The key fingerprint is:
SHA256:OIzc1yE7joL2Bzy8!gS0j8eGK7bYaH1FmF3sDuMeSj8 username@LOCAL-HOSTNAME

The key's randomart image is:
+--[ECDSA 256]--+
|        .        |
|         o       |
|    . + + .      |
|   o B * = .     |
|   o= B S .      |
|   .=B O o       |
|  + =+% o        |
| *oo.O.E         |
|+.o+=o. .        |
+----[SHA256]-----+

现在,指定位置已有一个公共/专用 ECDSA 密钥对。 .pub 文件是公钥,没有扩展名的文件是私钥:

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----         6/3/2021   2:55 PM            464 id_ecdsa
-a----         6/3/2021   2:55 PM            103 id_ecdsa.pub

私钥文件等效于密码,应以保护密码的方式进行保护。 可用于 ssh-agent 在与 Windows 帐户关联的 Windows 安全上下文中安全地存储私钥。 若要将 ssh-agent 服务配置为在每次重启计算机时自动启动,并使用 ssh-add 存储私钥,请在服务器上的提升 PowerShell 提示符处运行以下命令:

# By default, the ssh-agent service is disabled. Configure it to start automatically.
# Run the following command as an administrator.
Get-Service ssh-agent | Set-Service -StartupType Automatic

# Start the service.
Start-Service ssh-agent

# The following command should return a status of Running.
Get-Service ssh-agent

# Load your key files into ssh-agent.
ssh-add $env:USERPROFILE\.ssh\id_ecdsa

将密钥添加到 ssh-agent 客户端上的服务后,该服务 ssh-agent 会自动检索本地私钥并将其传递给 SSH 客户端。

重要

建议将私钥备份到安全位置,然后在将私钥添加到服务ssh-agent将其从本地系统中删除。 使用强算法时,无法从代理检索私钥,例如此示例中的 ECDSA。 如果无法访问私钥,则必须创建新的密钥对并在与之交互的所有系统上更新公钥。

部署公钥

若要使用之前创建的用户密钥,需要在服务器上将公钥(\.ssh\id_ecdsa.pub)的内容放入文本文件中。 文件的名称和位置取决于用户帐户是本地管理员组的成员还是标准用户帐户。 以下部分涵盖标准和管理用户。

标准用户

需要将服务器上公钥 (\.ssh\id_ecdsa.pub) 的内容置于 C:\Users\username\.ssh\ 中名为 的文本文件中authorized_keys。 可以使用 OpenSSH scp 安全文件传输实用工具或使用 PowerShell 将密钥写入文件来复制公钥。

可以使用以下代码将公钥复制到服务器。 在最后一行中,将 username 替换为您的用户名。 最初,系统会提示输入服务器用户帐户的密码。

# Get the public key file generated previously on your client.
$authorizedKey = Get-Content -Path $env:USERPROFILE\.ssh\id_ecdsa.pub

# Generate the PowerShell command to run remotely that copies the public key file generated previously on your client to the authorized_keys file on your server.
$remotePowershell = "powershell New-Item -Force -ItemType Directory -Path $env:USERPROFILE\.ssh; Add-Content -Force -Path $env:USERPROFILE\.ssh\authorized_keys -Value '$authorizedKey'"

# Connect to your server and run the PowerShell command by using the $remotePowerShell variable.
ssh username@domain1@contoso.com $remotePowershell

管理用户

需要将您的公钥 (\\.ssh\id_ecdsa.pub\) 的内容放入位于 \administrators_authorized_keys 下的一个名为 \ 的文本文件中。 可以使用 OpenSSH scp 安全文件传输实用工具或使用 PowerShell 将密钥写入文件来复制公钥。 需要将此文件的 ACL 配置为仅允许访问管理员和系统用户。

可以使用以下代码将公钥复制到服务器并配置 ACL。 在最后一行中,将 username 替换为您的用户名。 最初,系统会提示输入服务器用户帐户的密码。

注意

此示例演示了创建 administrators_authorized_keys 文件的步骤。 此文件仅适用于管理员帐户。 必须使用它,而不是用户个人资料位置中的用户特定文件。

# Get the public key file generated previously on your client.
$authorizedKey = Get-Content -Path $env:USERPROFILE\.ssh\id_ecdsa.pub

# Generate the PowerShell command to run remotely that copies the public key file generated previously on your client to the authorized_keys file on your server.
$remotePowershell = "powershell Add-Content -Force -Path $env:ProgramData\ssh\administrators_authorized_keys -Value '''$authorizedKey''';icacls.exe ""$env:ProgramData\ssh\administrators_authorized_keys"" /inheritance:r /grant ""Administrators:F"" /grant ""SYSTEM:F"""

# Connect to your server and run the PowerShell command by using the $remotePowerShell variable.
ssh username@domain1@contoso.com $remotePowershell

对于作系统的非英语本地化版本,需要修改脚本以相应地反映组名称。 若要防止授予组名称权限时可能发生的错误,可以使用安全标识符(SID)代替组名称。 可以通过运行 Get-LocalGroup | Select-Object Name, SID来检索 SID。 使用 SID 代替组名时,它前面必须有星号(*)。 在以下示例中,Administrators 组使用 SID S-1-5-32-544

$remotePowershell = "powershell Add-Content -Force -Path $env:ProgramData\ssh\administrators_authorized_keys -Value '''$authorizedKey''';icacls.exe ""$env:ProgramData\ssh\administrators_authorized_keys"" /inheritance:r /grant ""*S-1-5-32-544:F"" /grant ""SYSTEM:F"""

这些步骤完成了对 Windows 上的 OpenSSH 使用基于密钥的身份验证所需的配置。 运行这些 PowerShell 命令后,可以从具有私钥的任何客户端连接到 sshd 主机。