go-piv/piv-go

GitHub: go-piv/piv-go

一个用纯 Go 实现的 YubiKey PIV 智能卡操作库,提供密钥生成、证书管理与数字签名等功能。

Stars: 433 | Forks: 80

这不是官方支持的 Google 产品 # 一个 Go YubiKey PIV 实现 [![Go Reference](https://pkg.go.dev/badge/github.com/go-piv/piv-go/v2/piv.svg)](https://pkg.go.dev/github.com/go-piv/piv-go/v2/piv) YubiKey 实现了 PIV 规范,用于管理智能卡证书。 对于在 YubiKey 上管理非对称密钥,此 applet 是 GPG 的一个更简单的替代方案。 本包是 Paul Tagliamonte 的 [go-ykpiv](https://github.com/paultag/go-ykpiv) 的替代方案, 后者是 YubiKey 的 ykpiv.h C 库的一个封装。本包旨在提供: * 更好的错误消息 * 符合 Go 惯用的 API * 现代特性,例如受 PIN 保护的管理密钥 ``` import "github.com/go-piv/piv-go/v2/piv" ``` ## 示例 * [签名](#signing) * [PIN](#pins) * [证书](#certificates) * [证明](#attestation) ### 签名 piv-go 包可用于生成密钥并在 YubiKey 上存储证书。它使用管理密钥在 applet 上生成新密钥,并使用 PIN 进行签名操作。本包提供了默认的 PIN 值。如果 YubiKey 上的 PIV 凭据未被修改,以下代码会在智能卡上生成一个新的 EC 密钥,并提供一个签名接口: ``` // List all smartcards connected to the system. cards, err := piv.Cards() if err != nil { // ... } // Find a YubiKey and open the reader. var yk *piv.YubiKey for _, card := range cards { if strings.Contains(strings.ToLower(card), "yubikey") { if yk, err = piv.Open(card); err != nil { // ... } break } } if yk == nil { // ... } // Generate a private key on the YubiKey. key := piv.Key{ Algorithm: piv.AlgorithmEC256, PINPolicy: piv.PINPolicyAlways, TouchPolicy: piv.TouchPolicyAlways, } pub, err := yk.GenerateKey(piv.DefaultManagementKey, piv.SlotAuthentication, key) if err != nil { // ... } auth := piv.KeyAuth{PIN: piv.DefaultPIN} priv, err := yk.PrivateKey(piv.SlotAuthentication, pub, auth) if err != nil { // ... } // Use private key to sign or decrypt. ``` ### PIN PIV applet 有三个独特的凭据: * 管理密钥(3DES 密钥),用于在 YubiKey 上生成新密钥。 * YubiKey 固件 5.4.0+ 增加了对 AES128/192/256 密钥的支持 * PIN(最多 8 位数字,通常为 6 位),用于访问签名操作。 * PUK(最多 8 位数字),用于解锁 PIN。通常设置一次后即丢弃,或由管理员进行管理。 piv-go 实现了受 PIN 保护的管理密钥,以将管理密钥存储在 YubiKey 上。这允许用户仅提供 PIN 即可访问管理功能。 以下代码为 YubiKey 生成新的随机凭据: ``` newPINInt, err := rand.Int(rand.Reader, big.NewInt(1_000_000)) if err != nil { // ... } newPUKInt, err := rand.Int(rand.Reader, big.NewInt(100_000_000)) if err != nil { // ... } newKey := make([]byte, 24) if _, err := io.ReadFull(rand.Reader, newKey); err != nil { // ... } // Format with leading zeros. newPIN := fmt.Sprintf("%06d", newPINInt) newPUK := fmt.Sprintf("%08d", newPUKInt) // If you want to change PIN/PUK retries, it's recommended to do it BEFORE changing // the PIN/PUK, as SetRetries will reset PIN/PUK to their default values. if err := yk.SetRetries(piv.DefaultManagementKey, piv.DefaultPIN, 5, 4); err != nil { // ... } // Set all values to a new value. if err := yk.SetManagementKey(piv.DefaultManagementKey, newKey); err != nil { // ... } if err := yk.SetPUK(piv.DefaultPUK, newPUK); err != nil { // ... } if err := yk.SetPIN(piv.DefaultPIN, newPIN); err != nil { // ... } // Store management key on the YubiKey. m := piv.Metadata{ManagementKey: &newKey} if err := yk.SetMetadata(newKey, m); err != nil { // ... } fmt.Println("Credentials set. Your PIN is: %s", newPIN) ``` 用户稍后可以使用 PIN 获取管理密钥: ``` m, err := yk.Metadata(pin) if err != nil { // ... } if m.ManagementKey == nil { // ... } key := *m.ManagementKey ``` ### 证书 PIV applet 还可以在 YubiKey 上存储 X.509 证书: ``` cert, err := x509.ParseCertificate(certDER) if err != nil { // ... } if err := yk.SetCertificate(managementKey, piv.SlotAuthentication, cert); err != nil { // ... } ``` 该证书稍后可以与私钥结合使用。例如,用于处理 TLS 流量: ``` cert, err := yk.Certificate(piv.SlotAuthentication) if err != nil { // ... } priv, err := yk.PrivateKey(piv.SlotAuthentication, cert.PublicKey, auth) if err != nil { // ... } s := &http.Server{ TLSConfig: &tls.Config{ Certificates: []tls.Certificate{ { Certificate: [][]byte{cert.Raw}, PrivateKey: priv, }, }, }, Handler: myHandler, } ``` ### 证明 YubiKey 可以证明某个特定密钥是在智能卡上生成的,并且它被设置了特定的 PIN 和触摸策略。客户端生成一个密钥,然后要求 YubiKey 对一个证明证书进行签名: ``` // Get the YubiKey's attestation certificate, which is signed by Yubico. yubiKeyAttestationCert, err := yk.AttestationCertificate() if err != nil { // ... } // Generate a key on the YubiKey and generate an attestation certificate for // that key. This will be signed by the YubiKey's attestation certificate. key := piv.Key{ Algorithm: piv.AlgorithmEC256, PINPolicy: piv.PINPolicyAlways, TouchPolicy: piv.TouchPolicyAlways, } if _, err := yk.GenerateKey(managementKey, piv.SlotAuthentication, key); err != nil { // ... } slotAttestationCertificate, err := yk.Attest(piv.SlotAuthentication) if err != nil { // ... } // Send certificates to server. ``` 然后,CA 可以验证该证明,从而证明密钥是在卡上生成的,并执行相应的策略: ``` // Server receives both certificates, then proves a key was generated on the // YubiKey. a, err := piv.Verify(yubiKeyAttestationCert, slotAttestationCertificate) if err != nil { // ... } if a.TouchPolicy != piv.TouchPolicyAlways { // ... } // Record YubiKey's serial number and public key. pub := slotAttestationCertificate.PublicKey serial := a.Serial ``` ## 安装 在 MacOS 上,piv-go 不需要任何额外的包。 要在 Linux 上构建,piv-go 需要 PCSC lite。要在基于 Debian 的发行版上安装,请运行: ``` sudo apt-get install libpcsclite-dev ``` 在 Fedora 上: ``` sudo yum install pcsc-lite-devel ``` 在 CentOS 上: ``` sudo yum install 'dnf-command(config-manager)' sudo yum config-manager --set-enabled PowerTools sudo yum install pcsc-lite-devel ``` 在 FreeBSD 上: ``` sudo pkg install pcsc-lite ``` 在 Windows 上: 无需任何先决条件。Microsoft 提供的默认驱动程序支持单元测试涵盖的所有功能。但是,如果您遇到问题,请尝试官方的 [YubiKey Smart Card Minidriver](https://www.yubico.com/products/services-software/download/smart-card-drivers-tools/)。Yubico 在其网站上声明,该驱动程序增加了[_额外的智能卡功能_](https://www.yubico.com/authentication-standards/smart-card/)。 请注意以下事项: ## 非 YubiKey 智能卡 由于缺乏测试硬件,实现 PIV 标准的非 YubiKey 智能卡未获得官方支持。但是,我们欢迎修复与其他智能卡集成问题的 PR,并且 piv-go 会尽量不破坏这种支持。 ## 测试 测试会自动查找已连接且可用的 YubiKey,但如果没有 `--wipe-yubikey` 标志,就不会修改智能卡。要让测试修改您 YubiKey 的 PIV applet,请运行: ``` go test -v ./piv --wipe-yubikey ``` 可以使用 `--test.short` 标志跳过较长的测试。 ``` go test -v --short ./piv --wipe-yubikey ``` ## 为什么? YubiKey 的 C PIV 库 ykpiv 很脆弱。它的错误消息并不友好,而且虽然它有调试选项,但通过它们进行排查既不惯用也不方便。 ykpiv 封装了 Windows、Mac 和 Linux 上可用的 PC/SC API。它并没有规定必须用任何特定的语言来编写。作为 [pault.ag/go/ykpiv][go-ykpiv] 的替代方案,本包使用 Go 重新实现了 ykpiv,而不是直接调用它。 ## 替代方案
标签:EVTX分析, Go, PIV, Ruby工具, YubiKey, 密码学, 手动系统调用, 数字证书管理, 日志审计, 硬件安全模块