对称加密:AES-256-CBC

基本概念:

AES(Advanced Encryption Standard,高级加密标准) 是由美国国家标准与技术研究院(NIST)于2001年发布的对称加密算法,用于替代旧的DES(Data Encryption Standard)算法。AES 支持三种密钥长度:

  • AES-128:128位密钥,10轮加密
  • AES-192:192位密钥,12轮加密
  • AES-256:256位密钥,14轮加密

其中 AES-256 提供最高安全级别。

CBC(Cipher Block Chaining,密码分组链接) 是一种工作模式。在 CBC 模式中,每个明文块在加密前会与前一个密文块进行异或(XOR)运算,从而避免相同明文产生相同密文的问题,提高安全性。

AES-256 密钥:

  • 密钥长度时候 256位,32个字节
  • 会生成15个轮密钥,每个轮密钥长度是128位即16个字节。因为每次加密操作也是以16个字节为基本单位的。

对每16个字节,使用轮密钥加密过程:

对一个 16 字节明文块:

  1. 初始轮(Initial Round)
    • 操作:State = Plaintext ⊕ K₀
    • 不算作“加密轮”,只是初始密钥加(AddRoundKey)
  2. 主加密轮(Rounds 1 到 13) → 共 13 轮 每轮执行:
    • SubBytes(字节替换)
    • ShiftRows(行移位)
    • MixColumns(列混淆)
    • AddRoundKey(Kᵢ)
  3. 最终轮(Round 14) → 第 14 轮 执行(省略 MixColumns):
    • SubBytes
    • ShiftRows
    • AddRoundKey(K₁₄)

✅ 总计:14 轮加密操作 ✅ 使用的轮密钥:K₀, K₁, …, K₁₄ → 共 15 个

IV初始化向量:

  • AES-CBC(Cipher Block Chaining)模式中,IV(初始化向量)只在加密和解密的第一个数据块时直接使用一次,但它对整个消息的安全性有全局影响。

CBC 模式 具体作用机制:

🔐 加密过程
  1. 第一个明文块 P1 与 IV 异或:
    X1 = P1 ⊕ IV

  2. 然后用 AES 加密:
    C1 = AES_Encrypt(X1, key)

  3. 后续块使用前一个密文块作为“链”:
    C2 = AES_Encrypt(P2 ⊕ C1, key)
    C3 = AES_Encrypt(P3 ⊕ C2, key)

🔓 解密过程
  1. 解密第一个密文块:
    X1 = AES_Decrypt(C1, key)

  2. 用 IV 还原明文:
    P1 = X1 ⊕ IV

  3. 后续块用前一个密文块还原:
    P2 = AES_Decrypt(C2, key) ⊕ C1
    P3 = AES_Decrypt(C3, key) ⊕ C2

加密Shell脚本代码,我这里是针对本地配置json

#!/bin/bash

set -euo pipefail  # 严格模式:出错立即退出

# 配置
INPUT_DIR="plaintext_json"
OUTPUT_BUNDLE="encrypted_json.bundle"
KEY_TEXT=""  # 32 字节,对应 AES-256

# 创建输出 bundle
rm -rf "$OUTPUT_BUNDLE"
mkdir -p "$OUTPUT_BUNDLE"

encrypted_count=0

if [ ${#KEY_TEXT} -ne 32 ]; then
    echo "❌ KEY_TEXT 必须正好 32 字节(当前: ${#KEY_TEXT})" >&2
    exit 1
fi

KEY_HEX=$(printf "%s" "$KEY_TEXT" | xxd -p -c 256)

# 遍历所有 JSON 文件
for file in "$INPUT_DIR"/*.json; do
    [ -e "$file" ] || continue
    
    if [ -f "$file" ]; then
        filename=$(basename "$file")
        output_file="$OUTPUT_BUNDLE/$filename.enc"
        
        echo "🔒 Encrypting: $filename (raw key + random IV)"
        
        tmp_cipher=$(mktemp)
        tmp_iv=$(mktemp)
        trap 'rm -f "$tmp_cipher" "$tmp_iv"' EXIT

        openssl rand -out "$tmp_iv" 16
        IV_HEX=$(xxd -p -c 256 "$tmp_iv")

        if ! openssl enc -aes-256-cbc \
            -K "$KEY_HEX" \
            -iv "$IV_HEX" \
            -in "$file" \
            -out "$tmp_cipher" 2>/dev/null; then
            echo "❌ Encryption failed for $filename" >&2
            rm -f "$tmp_cipher" "$tmp_iv"
            exit 1
        fi

        cat "$tmp_iv" "$tmp_cipher" > "$output_file"
        rm -f "$tmp_cipher" "$tmp_iv"
        trap - EXIT

        if [ -s "$output_file" ]; then
            echo "✅ Success: $filename$filename.enc"
            ((encrypted_count++))
        else
            echo "❌ Error: Output is empty! $output_file" >&2
            exit 1
        fi
    fi
done

if [ "$encrypted_count" -eq 0 ]; then
    echo "⚠️ No JSON files processed. Check if $INPUT_DIR exists and contains .json files." >&2
    exit 1
fi

echo "🔒 All JSON encrypted to $OUTPUT_BUNDLE (Total: $encrypted_count files)"

Swift 解密代码:

class JSONDecryptor {
    // 必须与脚本中的 KEY 完全一致(32 字节 UTF-8 字符串)
    private static let KEY = "".data(using: .utf8)!
    
    static func decryptJSON(named filename: String) -> Data? {
        // 1. 加载 .bundle 中的加密文件
        guard let bundleURL = Bundle.main.url(forResource: "encrypted_json", withExtension: "bundle") else {
            print("❌ 未找到 encrypted_json.bundle")
            return nil
        }
        
        let encryptedDataURL = bundleURL.appendingPathComponent("\(filename).enc")
        guard let encryptedData = try? Data(contentsOf: encryptedDataURL),
              encryptedData.count >= kCCBlockSizeAES128 else {
            print("❌ 文件未找到或太小: \(filename)")
            return nil
        }
        
        // 2. 分离 IV (前16字节) 和密文
        let iv = encryptedData.prefix(16)
        let ciphertext = encryptedData.dropFirst(16)
        
        // 3. 直接解密(关键:跳过 PBKDF2!)
        return aesDecrypt(ciphertext, key: KEY, iv: iv)
    }
    
    private static func aesDecrypt(_ data: Data, key: Data, iv: Data) -> Data? {
        let bufferSize = data.count + kCCBlockSizeAES128
        var buffer = Data(count: bufferSize)
        var numBytesDecrypted = 0
        
        let status = key.withUnsafeBytes { keyBytes in
            iv.withUnsafeBytes { ivBytes in
                data.withUnsafeBytes { dataBytes in
                    buffer.withUnsafeMutableBytes { bufferBytes in
                        CCCrypt(
                            CCOperation(kCCDecrypt),
                            CCAlgorithm(kCCAlgorithmAES),
                            CCOptions(kCCOptionPKCS7Padding),
                            keyBytes.baseAddress!,
                            key.count,
                            ivBytes.baseAddress!,
                            dataBytes.baseAddress!,
                            data.count,
                            bufferBytes.baseAddress!,
                            bufferSize,
                            &numBytesDecrypted
                        )
                    }
                }
            }
        }
        
        guard status == kCCSuccess else {
            print("❌ 解密失败 (状态码: \(status))")
            return nil
        }
        
        buffer.removeSubrange(numBytesDecrypted..<buffer.count)
        return buffer
    }
}

总结:

可以利用AES-256-CBC对本地配置文件做加解密,但是密钥如果是在本地的话,还是能被逆向的,可以通过拆分字符串或者扰动函数提高被逆向的门槛。不过最好的方案密钥还是通过服务端下发。