第3章 加密算法的选型与密钥管理:AES、RSA、密钥存储策略、环境变量注入

3.1 本章知识地图

在编译时加密场景中,算法选择直接决定安全强度与性能开销;密钥管理则决定整个加密体系是否可信。本章聚焦两大对称/非对称算法(AES / RSA),对比其适用场景,并落地 Gradle 环境下的密钥存储与注入方案。

知识单元核心要点Gradle 侧适配
AES 对称加密加解密速度快,密钥需安全分发适合加密静态资源、配置文件
RSA 非对称加密公钥加密、私钥解密,管理成本高适合加密小数据(如 AES 密钥)
密钥存储策略.properties / JSON / gradle.properties / 环境变量推荐统一注入方式,避免硬编码
环境变量注入通过系统环境或 gradle.properties 变量传递密钥Task 中读取 System.getenv() 或 project.findProperty

3.2 AES 与 RSA 的原理选型对比

3.2.1 AES(高级加密标准)

  • 算法类型:对称分组密码,密钥长度 128 / 192 / 256 位。
  • Gradle 场景:文件级别加密,适合大块数据(如 SQLite 数据库、配置文件)。
  • 安全性依赖:密钥派生函数(如 PBKDF2)和随机 IV(初始化向量)。
  • 性能:硬件加速下可达数 Gbps,远快于 RSA。

3.2.2 RSA(Rivest‑Shamir‑Adleman)

  • 算法类型:非对称公钥密码,模数 2048 / 4096 位。
  • Gradle 场景:加密 AES 密钥或小体积凭证(< 200 字节)。
  • 安全性依赖:妥善保管私钥,公钥可公开。
  • 缺陷:加密速度慢(约 100 KB/s 量级),不适合大块数据。

选型原则:优先使用 AES‑256 加密业务数据,用 RSA‑2048 加密 AES 密钥(混合加密)。编译时环境变量中注入 RSA 私钥(或 AES 密钥),避免将密钥硬编码到版本库。

3.3 密钥存储策略

3.3.1 不应使用的做法

  • 在 build.gradle.kts / gradle.properties 中直接写入明文密钥(易被提交到 Git)。
  • 在源码 jar 中嵌入密钥文件(反编译即可提取)。

3.3.2 推荐策略

存储位置安全性使用方式Gradle 获取示例
环境变量(CI/CD) 高(不落入文件系统) System.getenv("ENCRYPT_KEY")
val key = System.getenv("AES256_KEY") ?: error("环境变量未设置")
gradle.properties(加密后) 中(需配合 Gradle 加密属性插件) project.findProperty("secretKey")
val key = project.findProperty("mySecretKey") as? String
外部密钥文件(.enc) 中(需 .gitignore 处理) Gradle 构建时读取并解密
val keyBytes = file("secret.aes256").readBytes()

关键原则:永远不要将密钥明文提交到版本控制系统。CI 环境中通过 Pipeline 变量注入环境变量;本地开发使用 local.properties(已 .gitignore)或 OS 密钥链。

3.4 环境变量注入完整流程

以下步骤演示如何在 Gradle 构建中安全注入 AES‑256 密钥,并对资源文件进行编译时加密。

  1. 定义密钥环境变量(本地开发):
    export AES256_KEY="YouR-SecrEt-K3y1234567890AB=="
  2. 编写 Gradle Task 读取密钥(build.gradle.kts):
    import javax.crypto.Cipher
    import javax.crypto.spec.SecretKeySpec
    
    val aesKey by tasks.registering {
        doLast {
            val keyStr = System.getenv("AES256_KEY") 
                ?: project.findProperty("aes256Key") as? String
                ?: error("未找到 AES‑256 密钥")
            println("密钥已加载(长度=${keyStr.length})")
        }
    }
  3. 加密资源文件(示例):
    val encryptConfig by tasks.registering {
        dependsOn(aesKey)
        inputs.file("src/main/resources/config.properties")
        outputs.file(layout.buildDirectory.file("encrypted-config.properties"))
        doLast {
            val keyBytes = (System.getenv("AES256_KEY") ?: error("无密钥")).toByteArray()
            val key = SecretKeySpec(keyBytes.take(32).toByteArray(), "AES")
            val cipher = Cipher.getInstance("AES/GCM/NoPadding")
            cipher.init(Cipher.ENCRYPT_MODE, key)
    
            val plaintext = file("src/main/resources/config.properties").readBytes()
            val ciphertext = cipher.doFinal(plaintext)
            // 将 IV 与密文拼接(无填充模式)
            val iv = cipher.iv
            val encrypted = iv + ciphertext
            outputs.files.singleFile.writeBytes(encrypted)
            println("配置已加密 → ${outputs.files.singleFile}")
        }
    }
  4. 在 CI 中设置环境变量(GitHub Actions 示例):
    jobs:
      build:
        steps:
          - name: 注入密钥
            run: echo "AES256_KEY=${{ secrets.AES256_KEY }}" >> $GITHUB_ENV
          - name: 构建加密资源
            run: ./gradlew :encryptConfig

调试技巧:开发阶段可在 gradle.properties 临时添加 aes256Key=DevKey123...,但务必在提交前移除。使用 --info 参数运行任务可观察密钥加载情况(日志中不应打印完整密钥)。

3.5 密钥生命周期管理

  • 生成:使用 OpenSSL 或 Java KeyGenerator 生成随机 AES 密钥。
  • 轮换:每 90 天更换密钥,通过 CI 变量版本号区分(如 AES_KEY_V1 / V2)。
  • 销毁:废弃密钥立即从所有环境变量、配置文件、Secrets 平台中删除。

3.6 常见错误与稳定性优化

错误场景原因解决方案
Environment variable not found 异常 CI 与本地变量名不一致 统一在 gradle.properties 声明默认占位,强制覆盖
BadPaddingException / AEADBadTagException 密钥长度不符或 IV 不匹配 确保密钥为 32 字节(256 位),AES/GCM 使用随机 IV 存储
密钥硬编码被提交到 Git 开发者疏忽 启用 Git pre‑commit hook 扫描 gradle.properties 敏感字段
RSA 加密数据过长 使用 RSA 加密 > 245 字节数据 改用 AES 加密大数据,RSA 仅加密 AES 密钥

3.7 密钥注入架构示意

密钥注入架构示意 环境变量 System.getenv() CI Secrets GitHub / GitLab gradle.properties 仅开发用 KeyResolver Task 合并读取、校验长度 Encrypt Task AES/RSA 加密 加密资源文件 .enc / 嵌入式 密钥不落磁盘/版本库,仅运行时注入到 Gradle Task 内存

图中展示了三种密钥来源(环境变量、CI Secrets、gradle.properties)统一由 KeyResolver 任务合并,校验长度后传给 Encrypt Task 执行 AES/RSA 加密,最终输出加密产物。CI 环境下仅使用环境变量与 Secrets,避免敏感信息本地持久化。

3.8 本章小结

  • AES‑256 是编译时加密首选算法,配合 GCM 模式可提供认证加密。
  • RSA 仅用于加密小体积的 AES 密钥,构成混合加密体系。
  • 密钥必须通过环境变量或 CI Secrets 注入,禁止硬编码。
  • 使用 Gradle Task 将密钥读取与加密逻辑解耦,便于测试和替换算法。