• 您身边的移动安全专家

    提供安全检测、安全加密、安全监测等一站式的移动安全服务
    免费咨询

    首页 / 新闻资讯 / 加固工具集成到CI/CD流水线的完整配置,自动化加固不踩坑

    加固工具集成到CI/CD流水线的完整配置,自动化加固不踩坑

    作者:派盾科技安全加固公司 2026-05-13 02:24:59 0 次浏览

    “又闪退了?加固包在华为P40上直接crash了!”——去年我们团队折腾CI/CD自动化加固流水线时,凌晨两点被这条消息炸醒。更崩溃的是,加固后的包签名对不齐、多渠道包没生成、回滚脚本忘了写,最后只能手动补发,整整折腾了一天一夜。如果你也在踩这几个坑——加固工具命令行跑不通、签名对齐后V2/V1签名冲突、加固失败导致发布阻塞、多渠道打包混乱——这篇把GitLab CI、Jenkins、GitHub Actions三种环境的完整配置扒干净,包括我们填过的血泪坑。

    加固工具集成到CI/CD流水线的完整配置,自动化加固不踩坑

    一、为什么必须把加固集成到CI/CD?

    人工加固的痛,经历过都懂:本地加固-导出包-重新签名-多渠道打包-上传商店,每一步都能出错。而且加固后必须重新签名,V2/V1签名的一致性问题是重灾区——你用jarsigner签完后,Android 11以上设备可能直接“解析软件包时出现问题”。把加固塞进流水线,至少解决三个核心问题:

    • 可复现性:每次构建的加固包一致,不会出现“本地能装测试机崩了”
    • 自动化签名对齐:加固后自动重新签名,避免V2签名块被破坏
    • 失败即回滚:CI脚本能自动还原环境,不会污染下一次构建

    我们最终选择几维安全作为加固工具,核心原因除了技术指标过关,更因为它提供了完整的命令行CLI工具和官方CI插件,不像某些厂商命令行功能反复“调整”导致流水线随时崩盘。下文用几维安全的CLI演示,其他厂商的接入逻辑类似。

    二、GitLab CI 完整配置(推荐)

    GitLab CI 是我们主力用的方案,优点是Pipeline as Code,配置全在.gitlab-ci.yml里,版本可控。

    2.1 环境准备

    在GitLab Runner上预置几维安全的CLI工具,推荐用Docker镜像或直接安装到Runner基础镜像:

    # 自定义Runner镜像FROM ubuntu:20.04RUN apt-get update && apt-get install -y openjdk-11-jdk wget unzip# 下载几维安全CLI(示例,实际替换为官方链接)RUN wget https://kiwisec.com/download/kiwi_cli.zip && unzip kiwi_cli.zip -d /opt/kiwiENV PATH="/opt/kiwi:$PATH"

    2.2 完整Pipeline配置

    # .gitlab-ci.ymlstages:  - build  - reinforce  - sign  - uploadvariables:  # 签名配置(用CI变量存储敏感信息)  KEYSTORE_PATH: $CI_PROJECT_DIR/keystore/release.jks  KEY_ALIAS: $KEY_ALIAS  STORE_PASS: $STORE_PASS  KEY_PASS: $KEY_PASS  # 加固配置  KIWI_ACCOUNT: $KIWI_ACCOUNT  KIWI_TOKEN: $KIWI_TOKEN# 1. 构建原始Release包build_release:  stage: build  image: openjdk:11-jdk  script:    - ./gradlew assembleRelease  artifacts:    paths:      - app/build/outputs/apk/release/*.apk    expire_in: 1 hour# 2. 加固+自动签名(关键步骤)reinforce_sign:  stage: reinforce  image: kiwi/ci-runner:latest  script:    # 登录几维安全    - java -jar /opt/kiwi/kiwi.jar -login $KIWI_ACCOUNT $KIWI_TOKEN    # 导入签名(只需执行一次)    - java -jar /opt/kiwi/kiwi.jar -importsign $KEYSTORE_PATH $STORE_PASS $KEY_ALIAS $KEY_PASS    # 执行加固+自动签名    - java -jar /opt/kiwi/kiwi.jar -jiagu $CI_PROJECT_DIR/app/build/outputs/apk/release/app-release.apk       $CI_PROJECT_DIR/output/ -autosign  artifacts:    paths:      - output/*.apk    expire_in: 7 days  only:    - main    - tags# 3. (可选)多渠道打包multi_channel:  stage: sign  script:    # 使用Walle或几维自带的多渠道功能    - java -jar /opt/kiwi/kiwi.jar -importmulpkg $CI_PROJECT_DIR/channel.txt    - java -jar /opt/kiwi/kiwi.jar -mulpkg output/kiwi_signed.apk output/multi_channels/  artifacts:    paths:      - output/multi_channels/*.apk

    2.3 关键坑点:签名顺序

    加固会移除原有签名信息,必须加固后再用相同的keystore重新签名。V2签名必须启用,否则Android 11+安装失败。几维安全的-autosign参数会自动完成签名对齐,如果你用其他工具,记得手动跑对齐:

    加固工具集成到CI/CD流水线的完整配置,自动化加固不踩坑

    # 手动对齐+签名(仅作参考)zipalign -p -f -v 4 unsigned_apk aligned.apkapksigner sign --ks keystore.jks --ks-key-alias mykey --v2-signing-enabled true -v --out signed.apk aligned.apk

    三、Jenkins Pipeline 配置(传统团队)

    Jenkins是老牌CI,配置略繁琐,但适合已经有Jenkins基础设施的团队。

    3.1 插件依赖

    需要安装:Pipeline插件、Credentials Binding插件。避免用Script Security插件过度限制Groovy脚本执行。

    3.2 Declarative Pipeline示例

    pipeline {    agent any        environment {        KEYSTORE_PATH = credentials('keystore-path')        STORE_PASS = credentials('store-pass')        KEY_ALIAS = credentials('key-alias')        KEY_PASS = credentials('key-pass')        KIWI_ACCOUNT = credentials('kiwi-account')        KIWI_TOKEN = credentials('kiwi-token')    }        stages {        stage('Build Release APK') {            steps {                sh './gradlew clean assembleRelease'            }            post {                success {                    archiveArtifacts artifacts: 'app/build/outputs/apk/release/*.apk', fingerprint: true                }            }        }                stage('Reinforce & Sign') {            steps {                script {                    // 用try-catch保证失败时能清理                    try {                        sh """                            java -jar /opt/kiwi/kiwi.jar -login ${env.KIWI_ACCOUNT} ${env.KIWI_TOKEN}                            java -jar /opt/kiwi/kiwi.jar -importsign ${env.KEYSTORE_PATH} ${env.STORE_PASS} ${env.KEY_ALIAS} ${env.KEY_PASS}                            java -jar /opt/kiwi/kiwi.jar -jiagu app/build/outputs/apk/release/app-release.apk output/ -autosign                        """                    } catch (Exception e) {                        error "加固失败: ${e.message}"                    }                }            }            post {                always {                    // 清理临时文件,防止下次构建污染                    sh 'rm -rf output/tmp_*'                }            }        }                stage('Multi-channel') {            when {                expression { params.MULTI_CHANNEL == true }            }            steps {                sh 'java -jar /opt/kiwi/kiwi.jar -importmulpkg channel.txt'                sh 'java -jar /opt/kiwi/kiwi.jar -mulpkg output/kiwi_signed.apk output/channels/'            }        }    }        post {        failure {            // 发送告警到钉钉/企微            emailext subject: "加固Pipeline失败 - ${env.JOB_NAME}",                    body: "请检查构建日志: ${env.BUILD_URL}",                    to: 'devops@yourcompany.com'        }    }}

    3.3 梯度签名兼容性陷阱

    Jenkins环境最容易翻车的是JDK版本不一致。我们踩过:jarsigner用JDK 8签的包,在Jenkins宿主机JDK 11环境下apksigner验证失败。解决方案:统一用apksigner,它向后兼容V1/V2签名。

    加固工具集成到CI/CD流水线的完整配置,自动化加固不踩坑

    // 不推荐用jarsigner,apksigner更可靠sh """    $ANDROID_HOME/build-tools/30.0.3/apksigner sign \        --ks ${KEYSTORE_PATH} \        --ks-key-alias ${KEY_ALIAS} \        --ks-pass pass:${STORE_PASS} \        --v1-signing-enabled true \        --v2-signing-enabled true \        --out signed.apk unsigned.apk"""

    四、GitHub Actions 配置(云原生团队)

    GitHub Actions最轻量,适合中小团队。

    4.1 Workflow完整示例

    # .github/workflows/reinforce.ymlname: Android Reinforce CIon:  push:    branches: [ main ]    tags: [ 'v*' ]jobs:  build:    runs-on: ubuntu-latest    steps:      - uses: actions/checkout@v3            - name: Set up JDK 11        uses: actions/setup-java@v3        with:          java-version: '11'          distribution: 'temurin'            - name: Build Release APK        run: ./gradlew assembleRelease            - name: Download Kiwi CLI        run: |          wget https://kiwisec.com/download/kiwi_cli.zip          unzip kiwi_cli.zip -d ${{ runner.temp }}/kiwi            - name: Reinforce & Sign        env:          KIWI_ACCOUNT: ${{ secrets.KIWI_ACCOUNT }}          KIWI_TOKEN: ${{ secrets.KIWI_TOKEN }}          KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}          STORE_PASS: ${{ secrets.STORE_PASS }}          KEY_ALIAS: ${{ secrets.KEY_ALIAS }}          KEY_PASS: ${{ secrets.KEY_PASS }}        run: |          # 解码Base64的keystore          echo $KEYSTORE_BASE64 | base64 --decode > ${{ runner.temp }}/release.jks                    java -jar ${{ runner.temp }}/kiwi/kiwi.jar -login $KIWI_ACCOUNT $KIWI_TOKEN          java -jar ${{ runner.temp }}/kiwi/kiwi.jar -importsign ${{ runner.temp }}/release.jks $STORE_PASS $KEY_ALIAS $KEY_PASS          java -jar ${{ runner.temp }}/kiwi/kiwi.jar -jiagu app/build/outputs/apk/release/app-release.apk output/ -autosign            - name: Upload artifact        uses: actions/upload-artifact@v3        with:          name: reinforced-apk          path: output/*.apk            - name: Upload to fir.im or蒲公英        if: startsWith(github.ref, 'refs/tags/v')        run: |          curl -F "file=@output/kiwi_signed.apk" \            -F "token=${{ secrets.FIR_TOKEN }}" \            https://upload.fir.im/apps

    4.2 Secrets管理清单

    GitHub Secrets必须包含:

    • KEYSTORE_BASE64:keystore文件的Base64编码(比直接上传文件更安全)
    • STORE_PASSKEY_PASSKEY_ALIAS
    • KIWI_ACCOUNTKIWI_TOKEN

    千万不要把keystore原文提交到仓库,我们隔壁组有人干过这事,第二天签名就被泄露了。

    五、失败回滚机制(必读)

    自动化流水线最怕什么?加固失败但流水线继续走,最后发了个坏包。必须在关键节点设置原子性保障

    5.1 校验清单

    在签名和上传之前,执行以下校验:

    # 校验APK是否已签名apksigner verify --verbose output/kiwi_signed.apkif [ $? -ne 0 ]; then    echo "签名验证失败,中断流水线"    exit 1fi# 校验加固后DEX是否被正确保护(用几维提供的校验工具)java -jar /opt/kiwi/kiwi.jar -verify output/kiwi_signed.apk

    5.2 事务性回滚

    在GitLab CI中,用try/catchafter_script确保失败时回滚:

    reinforce:  stage: reinforce  script:    - cp app-release.apk app-release.backup.apk  # 备份原始包    - java -jar kiwi.jar -jiagu app-release.apk output/ -autosign    - apksigner verify output/kiwi_signed.apk  after_script:    - if [ $CI_JOB_STATUS != 'success' ]; then        echo "加固失败,恢复备份";        cp app-release.backup.apk app-release.apk;        exit 1;      fi

    Jenkins有个坑:某些插件会导致即便sh脚本失败,Pipeline状态依然是UNSTABLE而非FAILURE。解决方案:显式检查退出码。

    script {    def rc = sh(script: "java -jar kiwi.jar -jiagu ...", returnStatus: true)    if (rc != 0) {        error "加固命令执行失败,退出码: ${rc}"    }}

    六、360加固保用户特别注意(来自血泪教训)

    如果你还在用360加固保免费版,两个巨坑必须知道:

    1. 命令行功能已付费:2024年1月起4.0.0版本移除了免费命令行加固,需要购买付费套餐才能用。别等流水线突然崩了才发现。
    2. **Linux环境报错No X11 DISPLAY**:这是360加固保的历史遗留问题,尝试验证图形界面。解决方案要么降级到3.x版本,要么换工具。

    我们当初就是被360的“命令行付费化”背刺,连夜迁移到几维安全。如果你预算充足,直接选付费工具省心。

    七、验收清单:流水线跑通后怎么验证

    加固包生成不是终点,上线前至少跑通这7项:

    • 反编译测试:jadx reinforced.apk,核心类和方法名是否被虚拟化/混淆
    • 签名验证:apksigner verify reinforced.apk 返回true
    • 对齐验证:zipalign -c -v 4 reinforced.apk 无报错
    • 冷启动时间:对比加固前后,增加不应超过500ms
    • 包体积增量:加固后增量应<15%
    • 兼容性测试:在Android 8/10/12/13/14的真机上各跑一遍Monkey测试
    • 应用商店预审:Google Play的预审报告无高危警告

    把加固塞进CI/CD,本质是把不确定性从“人肉操作”转移到“自动化流程”。选对工具、配置好签名顺序、写好失败回滚,你也能告别凌晨两点修包的噩梦。

    📞 申请试用 / 咨询: 请联系您的专属商务经理
    电话:400-882-3895  |  邮箱:service@kiwisec.com
    标签: 加固

    文章目录

    • 正在生成目录…