使用PGP进行签名、加密和SSH

发布于 2020-12-07  2526 次阅读


签名

使用PGP可以对文件进行签名,在签名之后对文件的任何修改都会导致签名失效,可以用来保护文件不被篡改。

使用以下命令可以进行签名:

gpg --sign 文件名

或者简写为:

gpg -s 文件名

如果有多个私钥,并且不想使用默认的签名私钥,则可以添加--local-user参数来指定私钥:

gpg --local-user 私钥名 --sign 文件名
gpg --sign --local-user 私钥名 文件名

上面的命令生成的文件名.gpg文件是二进制储存的,如果想要生成ASCII格式文件,可以将--sign替换为--clearsign命令:

gpg --clear-sign 文件名

生成的文件名.asc文件即为ASCII格式。

如果不想对原文件进行改动,而是生成另一个文件用于签名验证,则需要将命令替换为--detach-sign来进行:

gpg --detach-sign 文件名

这个命令会生成单独的签名二进制文件文件名.sig。如果想生成ASCII的单独签名文件,则加入--armor参数:

gpg --armor --detach-sign 文件名

对签名文件进行验证则使用--verify命令:

gpg --verify 签名文件

如果是分离式签名,可以直接验证,但最好分别指定原文件和签名文件:

gpg --verify 签名文件 原文件

验证成功则会显示签名时间,使用的密钥和用户。

使用以下命令可以测试签名功能:

echo foo | gpg --clearsign

加密

使用--encrypt-e命令进行加密:

gpg --encrypt 文件名

执行后会询问用谁的公钥进行加密,可以为多个用户来进行加密,分别输入用户标识和回车,输入完成后不输入再回车即可加密。

也可以用--recipient-r参数来直接指定加密用户:

gpg --encrypt --recipient 用户标识 文件名
gpg --encrypt --recipient 用户标识1 --recipient 用户标识2 文件名

输出的文件名.gpg文件即为加密文件。

加入--output-o参数可以指定输出文件名:

gpg --output 输出文件 --encrypt --recipient 用户标识 文件名

解密文件时可以只输入gpg,因为gpg的的默认命令就是解密。也可以用--decrypt或者-d命令进行解密:

gpg 加密文件
gpg --decrypt 加密文件

执行后输入私钥密码来进行解密,解密时会显示使用的密钥,并且如果在解密时加密文件没提供原文件的后缀,则会要求输入文件名。

同时进行签名和加密

gpg可以对一个文件同时进行签名和加密,使用以下命令将签名和加密结合在一起:

gpg --sign --encrypt --recipient 解密用户 文件名

简写为:

gpg -se -r 解密用户 文件名

加入--local-user参数来指定签名用户:

gpg --sign --local-user 签名用户 --encrypt --recipient 解密用户 文件名

解密则与解密单独加密的文件没有区别,只会在解密时输出签名信息。

SSH认证

PGP在生成子密钥时默认是不能生成认证子密钥的,需要增加参数才可以生成,并且认证子密钥用途也很少,通常只用于SSH认证使用。

OpenSSH客户端默认使用的ssh代理并不能直接使用PGP密钥进行认证,所以需要让OpenSSH使用gpg提供的gpg-agent来进行认证。

首先,启用gpg-agent的SSH支持,在~/.gnupg/gpg-agent.conf中文件中加入以下内容:

enable-ssh-support

如果是macOS和GPGTool的话,再加入以下内容

pinentry-program /usr/local/MacGPG2/libexec/pinentry-mac.app/Contents/MacOS/pinentry-mac

加入后的文件类似以下内容:

pinentry-program /usr/local/MacGPG2/libexec/pinentry-mac.app/Contents/MacOS/pinentry-mac
enable-ssh-support
write-env-file
use-standard-socket
default-cache-ttl 600
max-cache-ttl 7200

然后获取认证子密钥的指纹:

gpg --with-keygrip --list-keys

或缩写:

gpg --with-keygrip -K

然后将返回的认证子密钥的指纹(keygrip)添加到~/.gnupg/sshcontrol文件中。

保存后关闭gpg-agent:

gpgconf --kill gpg-agent

重启gpg-agent:

gpg-agent --daemon --options ~/.gnupg/gpg-agent.conf

然后将gpg-agent添加至环境变量。可以直接使用启动gpg-agent时返回的命令来设置环境变量。但是这个命令并不能永久生效,如果不想每次都输入这个命令,需要将其加入Shell的启动配置文件中。

根据使用的Shell的不同,需要在不同的配置文件中配置。例如,Ubuntu中,可以将上述命令直接加入.bashrc文件中。

使用macOS上的zsh时,也可以在~/.zshrc文件中添加添加以下内容:

# on OS X with GPGTools, comment out the next line:
eval $(gpg-agent --daemon)
GPG_TTY=$(tty)
export GPG_TTY
if [ -f "${HOME}/.gpg-agent-info" ]; then
    . "${HOME}/.gpg-agent-info"
    export GPG_AGENT_INFO
    export SSH_AUTH_SOCK
    export SSH_AGENT_PID
fi

如果gpg-agent已经可以自启动(例如使用GPGTools)则可以注释第二行,如果无法自启动,则保留第二行。

在macOS卡特琳娜之后的版本中,以上内容可能不管用。但是使用启动gpg-agent时返回的命令仍然可用。

使用以下命令查看环境变量检查是否生效:

echo "$SSH_AGENT_PID"
echo "$SSH_AUTH_SOCK"

最后生成添加到服务器使用的公钥,执行下述命令:

gpg --export-ssh-key 密钥名

或者使用ssh-add命令:

ssh-add -L

最后,有一点非常重要:

[danger]SSH的服务端只认子密钥,不认主密钥![/danger]

和SSH的CA认证不同,使用PGP的认证子密钥的情况下,丢失后不能简单的通过吊销和生成来更换,使用上除了和pgp密钥整合在一起,便于管理以外并无其他优势。更换时需要像普通SSH密钥一样对每个服务端进行更换。这种不方便之处可能也是gpg客户端默认隐藏认证功能的原因之一。