openpubkey/openpubkey

GitHub: openpubkey/openpubkey

OpenPubkey 是一个通过将公钥绑定到 OpenID Connect 身份来实现无 CA 证书签名与验证的协议参考实现。

Stars: 894 | Forks: 73

# OpenPubkey [![Go Coverage](https://static.pigsec.cn/wp-content/uploads/repos/cas/36/36422aaff1b6650271452c11f90b46c413d306816e370b14dd96abc31961ed0b.svg)](https://raw.githack.com/wiki/openpubkey/openpubkey/coverage.html) ## 概述 OpenPubkey 是一个利用 OpenID Provider (OP) 将身份与公钥绑定的协议。它将用户或 workload 生成的公钥添加到 [OpenID Connect (OIDC)](https://openid.net/developers/how-connect-works/) 中,使身份能够在其 OIDC 身份下对消息或制品进行签名。 我们将这种绑定表示为 PK Token。只要验证者信任 OP,该 token 就能证明在特定时间对 OIDC 身份及相关私钥的控制权。换言之,PK Token 提供了与证书颁发机构 (CA) 颁发的证书相同的保证,但关键是,它不需要添加 CA。相反,OP 履行了 CA 的角色。该 token 可以像证书一样随签名一起分发。 除了 OpenID Connect 所需的之外,OpenPubkey 不会增加任何新的受信任方。它与现有的 OpenID Provider(Google、Azure/Microsoft、Okta、OneLogin、Keycloak)完全兼容,无需对 OpenID Provider 进行任何更改。 基于 OpenPubkey 构建的公司包括: * [Docker, Inc](https://www.docker.com/) 正在构建一个公共容器镜像仓库,其中[使用 OpenPubkey 对 Docker 官方镜像进行签名](https://www.docker.com/blog/signing-docker-official-images-using-openpubkey/)。 * [BastionZero](https://www.bastionzero.com/) 使用 OpenPubkey 提供对基础设施的安全远程访问。 OpenPubkey 是一个 Linux Foundation 项目。它是开源的,并在 Apache 2.0 许可证下授权。该项目目前提供了一个 OpenPubkey 客户端和验证器,用于从 Google 的 OP(面向用户)和 GitHub 的 OP(面向 workload)创建和验证 PK Token。 ## 快速开始 让我们来看一个简单的消息签名示例。为了简洁,我们省略了错误处理代码。此示例的完整代码可以在 [./examples/simple/example.go](./examples/simple/example.go) 中找到。 我们首先配置客户端和验证器将要使用的 OP (OpenID Provider)。在此示例中,我们使用 Google 作为我们的 OP。 ``` opOptions := providers.GetDefaultGoogleOpOptions() opOptions.GQSign = signGQ op := providers.NewGoogleOpWithOptions(opOptions) ``` 接下来,我们创建 OpenPubkey 客户端并调用 `opkClient.Auth`: ``` opkClient, err := client.New(op) pkt, err := opkClient.Auth(context.Background()) ``` 函数 `opkClient.Auth` 会打开一个浏览器窗口跳转到 OP(在本例中为 Google),然后提示用户对其身份进行身份验证。如果用户成功通过身份验证,客户端将生成并返回一个 PK Token `pkt`。 随后可以使用 PK Token `pkt` 以及客户端的签名密钥对消息进行签名: ``` msg := []byte("All is discovered - flee at once") signedMsg, err := pkt.NewSignedMessage(msg, opkClient.GetSigner()) ``` 要验证已签名的消息,我们首先验证 PK Token `pkt` 是否由 OP (Google) 签发。然后我们使用 PK Token 验证已签名的消息。 ``` pktVerifier, err := verifier.New(provider) err = pktVerifier.VerifyPKToken(context.Background(), pkt) msg, err := pkt.VerifySignedMessage(signedMsg) ``` 要运行此示例,请输入:`go run .\examples\simple\example.go`。 这将打开一个浏览器窗口跳转到 Google。如果您成功向 Google 进行身份验证,您应该会看到:`Verification successful: anon.author.aardvark@gmail.com (https://accounts.google.com) signed the message 'All is discovered - flee at once'`,其中 `anon.author.aardvark@gmail.com` 是您的 Gmail 地址。 ## OpenPubkey 是如何工作的? OpenPubkey 支持 workload 身份和用户身份。让我们看看它是如何为用户工作的,然后再展示如何将 OpenPubkey 扩展到 workload。 ### OpenPubkey 和用户身份 在 OpenID Connect (OIDC) 中,用户向 OP (OpenID Provider) 进行身份验证,OP 会向用户授予一个 ID Token。这些 ID Token 由 OP 签名,并包含 OP 对用户所作的声明,例如用户的电子邮件地址。对 OpenPubkey 来说,ID Token 中的 `nonce` 声明非常重要。 ID Token 中的 `nonce` 声明是用户客户端在与 OP 进行身份验证期间发送给 OP 的一个随机值。OpenPubkey 遵循与 OP 进行 OpenID Connect 身份验证的协议,但它传输的 `nonce` 值被设置为用户公钥和一个随机值的加密哈希值,因此 `nonce` 在加密上仍然是随机的,但任何使用 OpenPubkey 的方都可以检查 ID Token 是否包含用户的公钥。从 OP 的角度来看,`nonce` 看起来就像一个随机值。 让我们看一个例子:用户 Alice 利用 OpenPubkey 让她的 OpenID Provider (`google.com`) 将她的 OIDC 身份 (`alice@acme.co`) 绑定到她的公钥 `alice-pubkey`。为此,Alice 调用了她的 OpenPubkey 客户端。 1. Alice 的 OpenPubkey 客户端为 Alice 生成了一对全新的密钥对(`alice-pubkey`,`alice-signkey`)以及一个随机值 `rz`。然后,客户端计算 `nonce=crypto.SHA3_256(upk=alice-pubkey, alg=ES256, rz=crypto.Rand())`。值 `alg` 被设置为 Alice 密钥对的算法。 2. Alice 的 OpenPubkey 客户端随后启动与 OP `google.com` 的 OIDC 身份验证流程,并将 `nonce` 发送给 OP。 3. OP 请求 Alice 同意签发 ID Token 并提供凭证(即用户名和密码)以向她的 OP (`Google`) 进行身份验证。 4. 如果 Alice 成功通过身份验证,OP 将构建一个包含有关 Alice 声明的 ID Token。至关重要的是,此 ID Token 包含由 Alice 的客户端生成的 `nonce` 声明,用于承诺 Alice 的公钥。然后,OP 在其签名密钥下对此 ID Token 进行签名,并将该 ID Token 发送给 Alice。 ID Token 是一个 JSON Web Signature (JWS),并遵循如下所示的结构: ``` payload: { "iss": "https://accounts.google.com", "aud": "878305696756-6maur39hl2psmk23imilg8af815ih9oi.apps.googleusercontent.com", "sub": "123456789010", "email": "alice@acme.co", "nonce": 'crypto.SHA3_256(upk=alice-pubkey, alg=ES256, rz=crypto.Rand(), typ="CIC")', "name": "Alice Example", ... } signatures: [ {"protected": {"alg": "RS256", "kid": "1234...", "typ": "JWT"}, "signature": SIGN(google-signkey, (payload, signatures[0].protected))` }, ] ``` 此时,Alice 拥有一个由 `google.com` (OP) 签名的 ID Token。任何人都可以从 `google.com` 著名的 JSON Web Key Set (JWKS) URI ([www.googleapis.com/oauth2/v3/certs]([https://www.googleapis.com/oauth2/v3/cert](https://www.googleapis.com/oauth2/v3/certs))) 下载 OP (`google.com`) 的公钥,并验证这个承诺了 Alice 公钥的 ID Token 确实是由 `google.com` 签名的。如果 Alice 公开 `alice-pubkey`、`alg` 和 `rz` 的值,任何人都可以验证 ID Token 中的 `nonce` 是否为 `upk=alice-pubkey, alg=ES256, rz=crypto.Rand()` 的哈希值。因此,Alice 现在拥有一个由 Google 签名的 ID Token,在密码学上将她的身份 `alice@acme.co` 与她的公钥 `alice-pubkey` 绑定在一起。 ### PK Token PK Token 只是 ID Token 的一种扩展,它将 ID Token 与 ID Token `nonce` 中承诺的值捆绑在一起。由于 ID Token 是 JSON Web Signature (JWS),并且 JWS 可以拥有多个签名,我们通过附加第二个签名/受保护头将 ID Token 扩展为 PK Token。 Alice 只需将她承诺在 `nonce` 中的值设置为 JWS 受保护头,并在其签名密钥 `alice-signkey` 下对 ID Token 载荷和此受保护头进行签名。该签名作为用户知道与公钥对应的秘密签名密钥的密码学证明。 注意下面 PK Token 示例中(与上面的 ID Token 示例相比)多出的签名条目: ``` "payload": { "iss": "https://accounts.google.com", "aud": "878305696756-6maur39hl2psmk23imilg8af815ih9oi.apps.googleusercontent.com", "sub": "123456789010", "email": "alice@acme.co", "nonce": , "name": "Alice Example", ... } "signatures": [ {"protected": {"alg": "RS256", "kid": "1234...", "typ": "JWT"}, "signature": }, {"protected": {"upk": alice-pubkey, "alg": "EC256", "rz": crypto.Rand(), "typ": "CIC"}, "signature": }, ] ``` PK Token 可以提交给 OpenPubkey 验证器,该验证器使用 OIDC 获取 OP 的公钥并验证 ID Token 中 OP 的签名。然后,它使用受保护头中的值来提取用户的公钥。 ### OpenPubkey 和 Workload 身份 与 OpenID Connect 一样,OpenPubkey 同时支持用户身份和 workload 身份。 workload 身份设置与用户身份设置非常相似,但有一个主要区别。Workload OpenID Provider(例如 `github.com`)不包含 ID Token 中的 `nonce` 声明。与用户身份提供者不同,它们允许 workload 指定 `aud`(受众)声明。因此,workload 身份的功能与用户身份类似,但我们使用 `aud` 声明而不是在 `nonce` 中承诺公钥。 ### GQ 签名以防止重放攻击 尽管原始的 [OpenPubkey 论文](https://eprint.iacr.org/2023/296)中没有提到,但现已集成了 GQ 签名,以便可以将 OpenID Provider (OP) 的签名从 ID Token 中剥离,并发布该 OP 签名的证明来代替它。这可以防止 PK Token 中的 ID Token 被用于攻击任何 OIDC 资源提供者,因为原始签名已被移除,且不会损害原始 OP 签名所提供的任何保证。 我们遵循以下论文中指定的方法:[Reducing Trust in Automated Certificate Authorities via Proofs-of-Authentication](https://arxiv.org/abs/2307.08201)。 对于 PK Token 不公开的用户身份场景,不需要 GQ 签名。对于当前所有的 workload 身份用例,都需要 GQ 签名。 ## 如何使用 OpenPubkey OpenPubkey 由其用例驱动。您可以在 [examples 文件夹](./examples/)中找到所有可用的用例。 我们希望这个列表能继续增加(如果您对其他用例有任何想法,请[提交 issue](#file-an-issue),在[社区会议](#get-involved-with-our-community)上提出想法,或者在我们的 [Slack 频道](#join-our-slack)中留言! ## 如何使用 OpenPubkey 进行开发 在我们努力让这个代码库准备 `v 1.0` 版本的同时,您可以查看 [examples 文件夹](./examples/)以获取关于 OpenPubkey 不同用例的更多信息。同时,我们非常希望社区能贡献更多用例。有关加入我们社区的指导,请参阅[下文](#get-involved-with-our-community)。 ### 报告安全问题 要报告安全问题,请遵循我们的[安全披露政策](./SECURITY.md)。 ## 常见问题解答 请参阅 [FAQ](./docs/FAQ.md) 以获取有关 OpenPubkey 的常见问题解答。
标签:CVE, EVTX分析, Go, OpenID Connect, Ruby工具, 公钥基础设施, 数字签名, 日志审计, 零信任架构