You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

向OAuth授权的GitHub仓库注入Secret时的密钥及加密库问题

GitHub仓库Secret注入问题及修复方案

问题描述

尝试向已通过OAuth 2.0授权的用户仓库注入GitHub Secret变量(已获取用户access token),使用Octokit创建4096位Deploy Key,再按文档要求用Sodium库加密Secret值,但遇到以下问题:

  • 若部署密钥设为32字节,GitHub报错密钥过短;
  • 若设为4096位,加密时提示密钥长度错误(应为32字节);
  • 使用其他加密库时,GitHub提示必须使用Sodium,不支持其他库。

原问题代码

async function generatePublicKey():Promise<string> {
    return new Promise(
        (resolve, reject) => {
            generateKeyPair('rsa', {
                modulusLength: 4096,
                publicKeyEncoding: {
                    type: 'pkcs1',
                    format: 'pem'
                },
                privateKeyEncoding: {
                    type: 'pkcs8',
                    format: 'pem',
                }
            }, (err, publicKey, privateKey) => {
                if(err){
                    reject(err)
                }
                else{
                    const pemKey = sshpk.parseKey(publicKey, 'pem');
                    const sshRsa = pemKey.toString('ssh');
                    console.log("ssh key is : ",sshRsa);
                    resolve(sshRsa);
                    
                }
            });
          
    }
    );
  }

async injectSecretIntoRepository(repository: string, owner: string, accessToken: string, secretName: string, secretValue: string) {
    const octokit = new Octokit({ auth: accessToken });
    console.debug("finding deploy key")
    const deployKeysResponse = await octokit.rest.repos.listDeployKeys({ owner, repo: repository });
    // Find the deploy key with write access
    let deployKey = deployKeysResponse.data.find((key) =>( key.read_only === false ));
    console.warn(`deploy key:  ${JSON.stringify( deployKeysResponse.data)}`)
    if (!deployKey) {
        console.warn(`Creating a new deploy key: `)
        try{
            const key = await generatePublicKey()
                deployKey =( await octokit.rest.repos.createDeployKey({
                owner,
                repo: repository,
                title: 'Componly deploy key',
                key,
                read_only: false,
              })).data;
              console.warn(`Deploy key created ${JSON.stringify(deployKey)}`)

        }catch(err){
            console.warn(`failed to create a new deploy key`+err)
            throw new InternalServerErrorException(err)
        }
     
    }
    
    try {
    const sodium = await SodiumPlus.auto()
    //@ts-ignore
    const secretKey = CryptographyKey.from(deployKey.key)
    const nonce = await sodium.randombytes_buf(sodium.CRYPTO_BOX_NONCEBYTES)
    //@ts-ignore
        const encryptedValue =  await  sodium.crypto_secretbox (secretValue,nonce,secretKey)
        // Create or update the secret with the encrypted value
        await octokit.rest.actions.createOrUpdateRepoSecret({
            owner,
            repo: repository,
            secret_name: secretName,
            encrypted_value:encryptedValue.toString("base64")

        });

        console.log("Secret injected successfully.");
    } catch (err) {
        console.error("Error injecting secret:", err);
        throw new InternalServerErrorException("Failed to inject secret into the repository.");
    }
}

核心错误原因

混淆了Deploy Key和GitHub仓库加密公钥的用途:

  • Deploy Key是用于SSH拉取/推送仓库代码的RSA密钥,长度要求4096位,和Secret加密无关;
  • 加密GitHub Secret需要使用GitHub为每个仓库生成的专属非对称加密公钥,而非自行生成的Deploy Key,该公钥是32字节的Curve25519格式,符合Sodium的加密要求。

修复方案

步骤1:获取仓库的加密公钥

调用Octokit的actions.getRepoPublicKey接口,获取GitHub提供的仓库公钥及对应的key_id。

步骤2:使用Sodium加密Secret值

用crypto_box_seal方法(非对称加密,无需nonce)对Secret值加密,该方法专门用于用公钥加密只有对应私钥能解密的内容。

步骤3:更新Secret接口调用

传入获取到的key_id和加密后的base64值到createOrUpdateRepoSecret接口。

修正后的代码

async injectSecretIntoRepository(repository: string, owner: string, accessToken: string, secretName: string, secretValue: string) {
    const octokit = new Octokit({ auth: accessToken });

    try {
        // 1. 获取仓库的加密公钥(核心:不是Deploy Key)
        const { data: publicKeyData } = await octokit.rest.actions.getRepoPublicKey({
            owner,
            repo: repository
        });

        const sodium = await SodiumPlus.auto();
        // 解析GitHub提供的公钥(base64格式)
        const publicKey = await sodium.crypto_box_seal_open_publickey(Buffer.from(publicKeyData.key, 'base64'));
        // 2. 加密Secret值:用crypto_box_seal,无需nonce
        const encryptedBuffer = await sodium.crypto_box_seal(secretValue, publicKey);
        const encryptedValue = encryptedBuffer.toString('base64');

        // 3. 创建/更新Secret,传入key_id
        await octokit.rest.actions.createOrUpdateRepoSecret({
            owner,
            repo: repository,
            secret_name: secretName,
            encrypted_value: encryptedValue,
            key_id: publicKeyData.key_id
        });

        console.log("Secret injected successfully.");
    } catch (err) {
        console.error("Error injecting secret:", err);
        throw new InternalServerErrorException("Failed to inject secret into the repository.");
    }
}

额外说明

  • 无需自行生成任何密钥(包括Deploy Key),除非你需要用SSH操作仓库代码;
  • crypto_box_seal是GitHub官方推荐的加密方式,完全符合Sodium库要求,不会出现密钥长度不匹配的问题;
  • 确保你的access token拥有repo权限(用于操作仓库Secret)。

内容的提问来源于stack exchange,提问作者dark man

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 18:14:56