是否可以使用 ssh 密钥对文件进行签名?

是否可以使用 ssh 密钥对文件进行签名?

我使用 SSH(准确地说是 Linux 上的 OpenSSH 5.5p1)。我有一个密钥,上面有一个密码。我使用它来进行常规的计算机登录操作。

我也可以用它来签署文件吗?

据我了解,SSH 密钥是 RSA(或 DSA)密钥,在 SSH 登录过程中,它用于对发送到服务器的消息进行签名。因此,从原则上和实践上讲,它可用于对事物进行签名 - 事实上,这是它的唯一用途。

但据我所知,没有办法使用密钥对任意文件进行签名(例如,像使用 PGP 那样)。有办法做到这一点吗?

答案1

我在寻找同样的东西时偶然发现了这篇旧帖子。事实证明,ssh-keygenOpenSSH 工具现在可以直接使用现有的 SSH 密钥生成和验证签名。这已在OpenSSH 8.1(发布日期 2019-10-09)。

总结

用于ssh-keygen签署和验证签名:

echo "Hello, World!" | ssh-keygen -Y sign -n file -f id_rsa > content.txt.sig
echo "Hello, World!" | ssh-keygen -Y check-novalidate -n file -f id_rsa.pub -s content.txt.sig

ssh-keygen 的参数

由于我没有找到ssh-keygen手册页特别有用(目前),这里概述了一些有用的命令及其签署内容所需的参数。

签约内容:

ssh-keygen 
-Y sign        # The 'sign' signature operation
-n <namespace> # Signature namespace (e.g. file or email)
-f <file>      # Path to the private key to sign the content with
<files>        # Paths to one or more files you want to sign (optional, by default reads from stdin)

检查签名内容的签名:

ssh-keygen 
-Y check-novalidate # The 'check' signature operation
-n <namespace>      # Namespace the signature was generated in
-f <file>           # Path to the public key to validate the signature with
-s <file>           # Path to the signature file

检查签名并验证签名者是否有权限签署内容:

ssh-keygen 
-Y verify      # The 'verify' signature operation
-n <namespace> # Namespace the signature was generated in
-f <file>      # Path to the ALLOWED SIGNERS file
-s <file>      # Path to the signature file
-I <principal> # The expected identity principal used to generate the signature

在 ALLOWED SIGNERS 文件中搜索适用于指定签名的任何主体:

ssh-keygen 
-Y find-principals # The 'find principals' signature operation
-f <file>          # Path to the ALLOWED SIGNERS file
-s <file>          # Path to the signature file

显示密钥的主体(通常<username>@<hostname>从生成密钥时开始):

ssh-keygen 
-l        # Print the key's fingerprint
-f <file> # Path to the public key 

从证书颁发机构 (CA) SSH 密钥生成用户证书:

ssh-keygen 
-I <cert_id>    # The certificate identity
-s <file>       # Path to the CA's private key
-n <principals> # Identity principals allowed by the generated certificate
<files>         # Path to one or more public keys to generate a certificate for

允许签名者文件

此文件包含允许对内容进行签名的身份列表,因此用于确定签名是否来自授权来源。其格式与众所周知的文件类似authorized_keys,并在允许签名者手册页中的部分。

<princpal>[,<principal>,..] [cert-authority] [namespaces="<namespace>[,<namespace>,..]"] <public-key>

例子:

user1@server,user2@server ssh-rsa AAAAX1...
*@server cert-authority ssh-rsa AAAAX1...

使用签名操作命令

生成签名并验证签名的最简单方法:

# Generate a new SSH key
ssh-keygen -f id_rsa -N ''

# Create the original content to be signed
echo "Trust me, the author!" > content.txt

# Sign the content
cat content.txt | ssh-keygen -Y sign -n file -f id_rsa > content.txt.sig

# Check the signature of the content
cat content.txt | ssh-keygen -Y check-novalidate -f id_rsa.pub -n file -s content.txt.sig

现在尝试检查具有无效内容的签名:

# Pretend the content has been tampered with by a malicious party
echo "Trust ME, the hacker!" > content-tampered.txt

# Check the signature of the content again (which will fail)
cat content-tampered.txt | ssh-keygen -Y check-novalidate -f id_rsa.pub -n file -s content.txt.sig

请注意,已使用签名操作验证了签名。当您想验证签名是否与内容匹配时,check-novalidate这部分听起来有点吓人。但是,当此检查成功时,它novalidate意味着签名与原始内容相符。您只是不知道签名者是否被授权签署该内容。

当您拥有一个值得信任的公钥并希望用它来签署内容时,您可以使用 ALLOWED SIGNERS 文件:

# Export your key's principal
ssh-keygen -l -f id_rsa.pub | cut -d ' ' -f3 > identity

# Create an ALLOWED SIGNERS file
echo "$(cat identity) $(cat id_rsa.pub)" > allowed_signers

# Check signature and verify the signer 
cat content.txt | ssh-keygen -Y verify -f allowed_signers -I $(cat identity) -n file -s content.txt.sig

正如您可能在上面的概述中看到的,OpenSSH 现在还能够签署其他 SSH 密钥。这意味着我们可以使用 SSH 密钥充当证书颁发机构 (CA),这非常酷!

这样,您在签名时只需要在 ALLOWED SIGNERS 文件中指定 CA 的公钥,而所有由 CA 签名的 SSH 密钥都能够签署有效内容:

# Generate a new CA key
ssh-keygen -f ca -C 'SSH Certificate Authority' -N ''

# Replace the ALLOWED SIGNERS file contents with just the CA's public key
echo "$(cat identity) cert-authority $(cat ca.pub)" > allowed_signers

# Sign your public key using the CA, creating a user certificate
ssh-keygen -I $(cat identity) -s ca -n $(cat identity) id_rsa

# Sign the content by specifying the user certificate (which uses the private key)
cat content.txt | ssh-keygen -Y sign -n file -f id_rsa-cert.pub > content.txt.sig

# Validate the signature using the CA's public key
cat content.txt | ssh-keygen -Y verify -f allowed_signers -I $(cat identity) -n file -s content.txt.sig

现在我们有了 CA,我们还可以利用自定义的任意主体,因为它们嵌入在证书中

# Sign your public key using the CA, adding a custom principal
ssh-keygen -I $(cat identity) -s ca -n $(cat identity),trusted-authors id_rsa

# Replace the ALLOWED SIGNERS file contents, allowing only signatures generated by keys with the trusted-authors principal
echo "trusted-authors cert-authority $(cat ca.pub)" > allowed_signers

# Sign the content by specifying the user certificate 
cat content.txt | ssh-keygen -Y sign -n file -f id_rsa-cert.pub > content.txt.sig

# Validate the signature using the CA's public key and the expected custom principal
cat content.txt | ssh-keygen -Y verify -f allowed_signers -I trusted-authors -n file -s content.txt.sig

生成的文件

如果您按照上述命令操作,那么您的目录中最终会出现以下文件:

allowed_signers      # List of principal(s) and key combinations authorized to sign the content 
ca                   # Private key of the Certificate Authority
ca.pub               # Public key of the Certificate Authority
content-tampered.txt # The content that got tampered with
content.txt          # The original content
content.txt.sig      # A signature of the original content
id_rsa               # Our private key - used to generate the signature
id_rsa-cert.pub      # Certificate signed by the CA - used to check and verify the signature
id_rsa.pub           # Our public key  - used to check the signature
identity             # Helper file which contains the identity principal of our private key

希望能帮助到你!

答案2

仅使用 OpenSSH 工具可能无法做到这一点。

但使用 OpenSSL 工具可以很容易地完成此操作。事实上,至少有两种方法可以做到这一点。以下示例中,~/.ssh/id_rsa是您的私钥。

一种方法是使用数据表

openssl dgst -sign ~/.ssh/id_rsa some-file

另一个是使用鍵盤

openssl pkeyutl -sign -inkey ~/.ssh/id_rsa -in some-file

这两者都将二进制签名写入标准输出。数据表选择-hex此选项将打印文本表示,其中包含有关签名形式的一些详细信息。鍵盤采用了-hexdump不太有用的选项。两者都将接受 RSA 和 DSA 密钥。我不知道输出的格式是什么。这两个命令产生不同的格式。我得到的印象是鍵盤被认为比数据表

要验证这些签名:

openssl dgst -verify $PUBLIC_KEY_FILE -signature signature-file some-file

和:

openssl pkeyutl -verify -inkey $PUBLIC_KEY_FILE -sigfile signature-file -in some-file

这里的问题是$PUBLIC_KEY_FILE。OpenSSL 无法读取 OpenSSH 的公钥格式,因此您不能只使用id_rsa.pub。您有几个选择,但没有一个是理想的。

如果您拥有 OpenSSH 5.6 或更高版本,您显然可以这样做:

ssh-keygen -e -f ~/.ssh/id_rsa.pub -m pem

这会将公钥以 PEM 格式写入标准输出,OpenSSL 可以读取。

如果您有私钥,并且它是 RSA 密钥,那么您可以从中提取公钥(我假设 PEM 编码的私钥文件包含公钥的副本,因为不可能从私钥本身派生出公钥),然后使用它:

openssl rsa -in ~/.ssh/id_rsa -pubout

我不知道是否有 DSA 等效方法。请注意,这种方法需要私钥所有者的配合,他们必须提取公钥并将其发送给可能的验证者。

最后,你可以使用一个叫 Lars 的家伙编写的 Python 程序将公钥从 OpenSSH 格式转换为 OpenSSL 格式。

答案3

@Tom 的回答帮助我开始了,但并没有起到开箱即用的作用。

这些命令将与以下命令一起使用:

  • OpenSSL 1.0.1 2012 年 3 月 14 日
  • OpenSSH_5.9p1

使用 pkeyutl

# openssl pkeyutl -sign -inkey ~/.ssh/id_sample -in $1 > $1.sig
# ssh-keygen -e -f ~/.ssh/id_sample.pub -m PKCS8 > pub
# openssl pkeyutl -verify -pubin -inkey pub -in $1 -sigfile $1.sig
Signature Verified Successfully

使用 dgst

# openssl dgst -sign ~/.ssh/id_sample $1 > $1.sig
# ssh-keygen -e -f ~/.ssh/id_sample.pub -m PKCS8 > pub
# openssl dgst -verify pub -signature $1.sig $1
Verified OK

pkeyutl 版本只能对小型文件进行签名。而 dgst 可以对任意大的文件进行签名,因为它在对结果进行签名之前会进行摘要。

答案4

有人注意到,我们在签名时使用“allowed_signers”文件中的公钥?但是我们使用签名,因此,相反,有必要对此类文件保密。如果我错了,请纠正我。

相关内容