对称加密: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 字节明文块:
- 初始轮(Initial Round)
- 操作:
State = Plaintext ⊕ K₀ - 这不算作“加密轮”,只是初始密钥加(AddRoundKey)
- 操作:
- 主加密轮(Rounds 1 到 13) → 共 13 轮
每轮执行:
- SubBytes(字节替换)
- ShiftRows(行移位)
- MixColumns(列混淆)
- AddRoundKey(Kᵢ)
- 最终轮(Round 14) → 第 14 轮
执行(省略 MixColumns):
- SubBytes
- ShiftRows
- AddRoundKey(K₁₄)
✅ 总计:14 轮加密操作 ✅ 使用的轮密钥:K₀, K₁, …, K₁₄ → 共 15 个
IV初始化向量:
- 在 AES-CBC(Cipher Block Chaining)模式中,IV(初始化向量)只在加密和解密的第一个数据块时直接使用一次,但它对整个消息的安全性有全局影响。
CBC 模式 具体作用机制:
🔐 加密过程
-
第一个明文块 P1 与 IV 异或:
X1 = P1 ⊕ IV -
然后用 AES 加密:
C1 = AES_Encrypt(X1, key) -
后续块使用前一个密文块作为“链”:
C2 = AES_Encrypt(P2 ⊕ C1, key)
C3 = AES_Encrypt(P3 ⊕ C2, key)
…
🔓 解密过程
-
解密第一个密文块:
X1 = AES_Decrypt(C1, key) -
用 IV 还原明文:
P1 = X1 ⊕ IV -
后续块用前一个密文块还原:
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对本地配置文件做加解密,但是密钥如果是在本地的话,还是能被逆向的,可以通过拆分字符串或者扰动函数提高被逆向的门槛。不过最好的方案密钥还是通过服务端下发。