我使用 SSH(准确地说是 Linux 上的 OpenSSH 5.5p1)。我有一个密钥,上面有一个密码。我使用它来进行常规的计算机登录操作。
我也可以用它来签署文件吗?
据我了解,SSH 密钥是 RSA(或 DSA)密钥,在 SSH 登录过程中,它用于对发送到服务器的消息进行签名。因此,从原则上和实践上讲,它可用于对事物进行签名 - 事实上,这是它的唯一用途。
但据我所知,没有办法使用密钥对任意文件进行签名(例如,像使用 PGP 那样)。有办法做到这一点吗?
答案1
我在寻找同样的东西时偶然发现了这篇旧帖子。事实证明,ssh-keygen
OpenSSH 工具现在可以直接使用现有的 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”文件中的公钥?但是我们使用签名,因此,相反,有必要对此类文件保密。如果我错了,请纠正我。