如何用Go实现Github Action Secrets API仓库秘钥加密?
问题分析与修正方案
你的代码存在三个核心问题,导致GitHub无法正确解密秘钥:
- 固定全零Nonce:GitHub要求加密必须使用随机生成的24字节nonce,原代码中
nonce := new([24]byte)生成的全零nonce会导致解密失败。 - 缺少临时公钥:GitHub的加密格式要求将临时生成的公钥(ephemeral public key)放在加密数据最前面,原代码未拼接该公钥,导致GitHub无法用仓库私钥解密原始内容。
- 错误处理缺失:忽略了base64解码、密钥生成等步骤的错误,可能引发静默失败。
正确的加密实现
import ( "crypto/rand" "encoding/base64" "fmt" "golang.org/x/crypto/nacl/box" ) func EncodeWithPublicKey(text string, publicKey string) (string, error) { // 解码仓库公钥并验证长度 publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKey) if err != nil { return "", fmt.Errorf("解码公钥失败: %w", err) } if len(publicKeyBytes) != 32 { return "", fmt.Errorf("公钥长度必须为32字节,当前为%d字节", len(publicKeyBytes)) } // 将公钥转换为nacl/box要求的数组格式 var repoPublicKey [32]byte copy(repoPublicKey[:], publicKeyBytes) // 生成临时密钥对(每次加密都需要新的密钥对) ephemeralPubKey, ephemeralPrivKey, err := box.GenerateKey(rand.Reader) if err != nil { return "", fmt.Errorf("生成临时密钥对失败: %w", err) } // 生成随机nonce(必须24字节) var nonce [24]byte if _, err := rand.Read(nonce[:]); err != nil { return "", fmt.Errorf("生成nonce失败: %w", err) } // 加密明文:box.Seal会将nonce作为前缀拼接在密文前 encrypted := box.Seal(nonce[:], []byte(text), &nonce, &repoPublicKey, ephemeralPrivKey) // 拼接临时公钥到加密数据头部(GitHub API要求的格式) finalEncrypted := append(ephemeralPubKey[:], encrypted...) // 编码为base64字符串返回 return base64.StdEncoding.EncodeToString(finalEncrypted), nil }
关键流程说明
- 公钥处理:解码仓库提供的base64公钥,确保长度为32字节(nacl/box要求的Curve25519密钥长度)。
- 临时密钥对:每次加密生成新的临时密钥对,用于和仓库公钥完成端到端加密。
- 随机Nonce:生成24字节随机nonce,保证加密的唯一性和安全性。
- 加密与格式拼接:使用
box.Seal生成nonce+密文的组合,再将临时公钥拼接在最前面,形成GitHub要求的临时公钥(32字节)+nonce(24字节)+密文格式。 - 错误处理:对所有可能失败的步骤添加错误捕获与返回,便于排查问题。
内容的提问来源于stack exchange,提问作者UDESHYA DHUNGANA
相关产品推荐
相关产品推荐

