Pivit 是一个用于管理存储在支持 PIV applet 的智能卡(Yubikey)上的 x509 证书的命令行工具,并使用这些证书对数据进行签名和验证。
它与 git 命令行调用外部程序来签名和验证提交与标签的方式完全兼容。
brew install pivit
go install github.com/cashapp/pivit/cmd/pivit@latest
要设置 git 使用 pivit 来签名和验证签名,请运行以下命令:
git config --(local|global) gpg.format x509
git config --(local|global) gpg.x509.program pivit
pivit --reset
重置 Yubikey 的 PIV applet,并创建一个新的 PIN 以访问它。
pivit --generate [--p256] [--self-sign | --no-csr] [--assume-yes] [--pin-policy] [--touch-policy]
在 Yubikey 的卡认证(card authentication)槽位中生成一个新的密钥对。
该命令还会为生成的密钥生成并存储一个由 Yubico 签名的 x509 证书。
如果提供了 --p256 选项,则使用椭圆曲线 P-256 生成密钥对。
否则,使用 P-384 曲线。
添加 --self-sign 标志以生成自签名证书;该证书将使用新生成的密钥进行签名。
系统会提示你确认确实需要自签名证书,然后提示输入 PIN,最后提示触摸你的 Yubikey。
输出末尾将包含 3 个 CERTIFICATE 块,而不是一个 CERTIFICATE_REQUEST(示例输出见下文)。
此选项主要适用于测试目的。
--assume-yes 标志可以与 --self-sign 选项结合使用,以禁用其 y/n 提示。
添加 --no-csr 标志可跳过打印证书签名请求。在这种情况下,系统不会提示你触摸 Yubikey。
如果你不需要将生成的密钥纳入现有 PKI,此选项很有用。
你仍然可以在此使用 Yubico 的证书来验证该密钥的证书。
--pin-policy 标志控制访问生成的密钥时何时提示输入 PIN。
设置为 never、once 或 always 之一(默认为 never)。
--touch-policy 标志控制访问生成的密钥时何时提示物理触摸硬件。
设置为 never、cached 或 always 之一(默认为 always)。
该命令的输出将如下所示:
Printing Yubikey device attestation certificate:
----- BEGIN CERTIFICATE -----
...
----- END CERTIFICATE -----
Printing generated key certificate:
----- BEGIN CERTIFICATE -----
...
----- END CERTIFICATE -----
Printing certificate signing request:
----- BEGIN CERTIFICATE REQUEST -----
...
----- END CERTIFICATE REQUEST-----
末尾的 CERTIFICATE REQUEST 由新生成的私钥签名,可用于签发由 CA 签名的证书。
如果你选择签发并使用自己的证书,还必须验证以下几点:
Pivit 允许通过环境变量在 CSR 中设置不同的属性。
以下证书选项将按如下方式设置:
Subject.CommonName 将设置为 PIVIT_EMAIL 环境变量的值。Subject.Organization 将设置为 PIVIT_ORG 环境变量的值。Subject.OrganizationalUnit 将设置为 PIVIT_ORG_UNIT 环境变量的值。此外,证书请求中将包含以下主题备用名称(SAN):
PIVIT_EMAILPIVIT_CERT_URIS 环境变量中指定的所有 URL(URI 之间以空格分隔)PIV 模块支持多个可存储密钥和证书的槽位。
可用槽位 - 9a、9c、9d 和 9e。
9e 是“卡认证”(Card Authentication)槽位。9a 是“认证”(Authentication)槽位。用于系统登录等操作。9c 是“数字签名”(Digital Signature)槽位。用于文档签名,或对文件和可执行文件进行签名。9d 是“密钥管理”(Key Management)槽位。用于加密电子邮件或文件等以保密的用途。更多信息
pivit 允许使用 -w 标志选择槽位。
对于每个命令,如果未指定槽位,则默认使用 9e。
例如:
证书生成
pivit --generate [-w slot]
签名
pivit -s -u userid [-w slot]
打印证书
pivit --print [-w slot]
默认情况下,pivit 期望只连接一个 Yubikey。如果系统有多个读卡器或 Yubikey,或者只是想确保 pivit 正在与预期的 Yubikey 通信,请通过 PIVIT_YK_SERIAL 环境变量指定序列号。
示例:
PIVIT_YK_SERIAL=13078292 pivit --print
这可以与任何命令一起使用。
pivit --import [--first-pem] [file]
从 file 导入证书。
给定的文件名应包含一个以 PEM 块编码的序列化 x509 证书。
此操作会提示输入 Yubikey PIN。
添加 --first-pem 以从 file 导入第一个 PEM 块,忽略其余部分。如果你使用的 CA 以链或捆绑包形式提供其签发的证书,并且终端实体证书位于最前面(这是惯例),则该选项很有帮助。
pivit --print
打印存储在 Yubikey 卡认证槽位中的证书及其指纹。
例如:
> pivit --print
bad126c47dc90e90e0c7ec90ec682b1717e52757
-----BEGIN CERTIFICATE-----
MIICczCCAVugAwIBAgIRANATKTxzlHOJgMdV30XRz5kwDQYJKoZIhvcNAQELBQAw
ITEfMB0GA1UEAwwWWXViaWNvIFBJViBBdHRlc3RhdGlvbjAgFw0xNjAzMTQwMDAw
MDBaGA8yMDUyMDQxNzAwMDAwMFowJTEjMCEGA1UEAwwaWXViaUtleSBQSVYgQXR0
ZXN0YXRpb24gOWUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT1l76Mvo6dJ4CwX2QQ
HImYRWbxLqt1kCVsn0PUn20DYtSKLuq8kCnMAn+kdneqjtaaBmWuPJH+g7LMyI18
QDQp2w+qSOf9ZWrOupMAGM8EpWXNTSNKoE50JhKXFbudBByjTjBMMBEGCisGAQQB
gsQKAwMEAwUBAjAUBgorBgEEAYLECgMHBAYCBACDq1IwEAYKKwYBBAGCxAoDCAQC
AQIwDwYKKwYBBAGCxAoDCQQBBDANBgkqhkiG9w0BAQsFAAOCAQEAGSmu0PUMDfEb
PkmkJ/rKYvpOKhbmpryjXuZ7ENJ3jGq3foyLSN+aBbd3M1im2ejC6/upthcp1N5M
Mg2SN6MBRS4crtt43DVHepBPt2W07vW8h4uFosJX29HdhK0dP+RnBGDdWYo0nq7y
7q1Y1c0BI7oGnaORL0DzSDtdxAJAphjspWsWNCIP2DTWQPhLTg4DqmYd/ahygVsZ
IE64QvJDQC72Dfyad6tIajmtPncqPt857H0hEJv2X58iOoJA/wAeuId4or859cJU
GTDbN7Ke4LWXRbs7StJTgefmNgWmKjm1Q2qs80WGShyQ6OrxALejd8jivtcTwsDz
WvzdmHCOmg==
-----END CERTIFICATE-----
证书的指纹通过对原始证书字节执行 SHA1 校验和,然后将校验和编码为十六进制字符串来计算。
使用证书指纹让 git 知道在签名提交和标签时使用哪个证书:
FINGERPRINT=`pivit --print | head -1`
git config --(local|global) user.signingkey $FINGERPRINT
pivit -s [-a] [-b] [-u userid] [--status-fd=[num]] [-t url] [file]
-a(--armor)- 是否将签名包装在 ASCII armor 中以使其可打印。-b(--detach-sign)- 生成分离签名。-u(--local-user)- 可以是电子邮件地址,也可以是编码为十六进制字符串的密钥指纹。--status-fd - 用于输出状态消息的文件描述符。1 表示 stdout,2 表示 stderr。0 或更低的值表示不会写入状态消息。-t(--timestamp-authority)- 用于时间戳的 RFC3161 时间戳机构 URL。file - 将被签名的文件路径。如果未指定文件名,则使用 stdin。此命令会使 Yubikey 闪烁,并会阻塞直到你触摸它。
当 git 被设置为签名提交和标签时,它将使用以下硬编码参数 -sbau [user.signingkey] --status-fd=1。
user.signingkey 取自 git 的本地/全局配置。
注意:Pivit 旨在支持对 git 提交进行签名。
但是,如果使用的密钥是通过--pin-policy=always创建的,则可能无法完全支持。
目前,Pivit 仅通过命令行提示输入 PIN。如果你使用的是基于 GUI 的 git 客户端(如 GitKraken 或 SourceTree),你可能会错过该提示。
pivit --verify [file ...]
验证在文件参数中指定的已签名数据。
如果未指定文件,则从 stdin 读取。
如果指定了一个文件路径或没有指定文件路径,则假定签名附加在已签名数据上。
否则,假定第一个文件包含签名,第二个文件包含已签名数据。
指定 - 表示已签名数据应从 stdin 读取。