Android 的签名过程:
1. 基本概念:
// 签名文件结构
class KeystoreExample {
val keystore = mapOf(
"别名" to "应用标识",
"密钥" to "私钥内容",
"证书" to "公钥证书",
"有效期" to "25年"
)
}
2. 生成签名文件:
# 使用 keytool 生成密钥库
keytool -genkey \
-alias myapp \ # 别名
-keyalg RSA \ # 算法
-keysize 2048 \ # 密钥长度
-validity 9125 \ # 有效期(25年)
-keystore release.keystore # 输出文件
3. Gradle 配置签名:
android {
signingConfigs {
release {
storeFile file("release.keystore")
storePassword "store123"
keyAlias "myapp"
keyPassword "key123"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
4. 签名过程:
class SigningProcess {
fun explain() {
// 1. 计算 APK 内容摘要
val manifest = calculateDigest("AndroidManifest.xml")
val classes = calculateDigest("classes.dex")
val resources = calculateDigest("resources.arsc")
// 2. 生成 MANIFEST.MF
writeToFile("MANIFEST.MF", """
Manifest-Version: 1.0
Built-By: Android Gradle
Name: AndroidManifest.xml
SHA-256-Digest: ${manifest.base64()}
Name: classes.dex
SHA-256-Digest: ${classes.base64()}
Name: resources.arsc
SHA-256-Digest: ${resources.base64()}
""")
// 3. 生成 CERT.SF
val sfContent = calculateDigest("MANIFEST.MF")
writeToFile("CERT.SF", sfContent)
// 4. 使用私钥签名
val signature = sign(sfContent, privateKey)
writeToFile("CERT.RSA", signature)
}
}
5. 签名验证过程:
class SignatureVerification {
fun verify(apk: File) {
// 1. 验证 CERT.RSA
val cert = extractCertificate(apk)
if (!cert.verify()) throw SecurityException()
// 2. 验证 CERT.SF
val sf = extractSignatureFile(apk)
if (!sf.matchesManifest()) throw SecurityException()
// 3. 验证 MANIFEST.MF
val manifest = extractManifest(apk)
if (!manifest.matchesFiles()) throw SecurityException()
}
}
MANIFEST.MF 和 CERT.SF
1. MANIFEST.MF:
# MANIFEST.MF 文件内容示例
Manifest-Version: 1.0
Created-By: Android Gradle 4.0.1
Name: AndroidManifest.xml
SHA-256-Digest: 8WU2AJ678BXZN+K1YvUj789HN...
Name: res/layout/activity_main.xml
SHA-256-Digest: 9KL3QW5TY7VBXM+P2ZXCV456N...
Name: classes.dex
SHA-256-Digest: MNBV4X3ZLK9P0QWERTY12345...
功能:
- 记录 APK 中每个文件的摘要值
- 确保文件内容完整性
- 防止文件被篡改
2. CERT.SF:
# CERT.SF 文件内容示例
Signature-Version: 1.0
Created-By: Android Gradle 4.0.1
SHA-256-Digest-Manifest: QWERTY12345... # 整个 MANIFEST.MF 的摘要
Name: AndroidManifest.xml
SHA-256-Digest: ASDFGH67890... # 对应 MANIFEST.MF 中该条目的摘要
Name: res/layout/activity_main.xml
SHA-256-Digest: ZXCVBN45678...
Name: classes.dex
SHA-256-Digest: POIUYT98765...
功能:
- 对 MANIFEST.MF 文件进行二次摘要
- 防止 MANIFEST.MF 被篡改
- 提供签名保护
3. 验证流程:
class SignatureVerification {
fun verify() {
// 1. 验证 CERT.SF
val sfFile = extractFile("META-INF/CERT.SF")
val manifestFile = extractFile("META-INF/MANIFEST.MF")
// 验证 MANIFEST.MF 的完整性
val manifestDigest = calculateDigest(manifestFile)
if (manifestDigest != sfFile.manifestDigest) {
throw SecurityException("MANIFEST.MF 被篡改")
}
// 2. 验证 MANIFEST.MF
val actualFiles = extractAllFiles()
manifestFile.entries.forEach { entry ->
val fileDigest = calculateDigest(actualFiles[entry.name])
if (fileDigest != entry.digest) {
throw SecurityException("文件被篡改: ${entry.name}")
}
}
}
}
4. 双重保护机制:
graph TD
A[APK文件] --> B[计算文件摘要]
B --> C[MANIFEST.MF]
C --> D[计算MANIFEST.MF摘要]
D --> E[CERT.SF]
E --> F[私钥签名]
F --> G[CERT.RSA]
5. 实际应用示例:
class SigningExample {
fun explainProcess() {
// 1. 生成 MANIFEST.MF
val manifest = StringBuilder().apply {
appendLine("Manifest-Version: 1.0")
appendLine("Created-By: Android Gradle")
appendLine()
// 为每个文件生成摘要
files.forEach { file ->
appendLine("Name: ${file.name}")
appendLine("SHA-256-Digest: ${calculateDigest(file)}")
appendLine()
}
}.toString()
// 2. 生成 CERT.SF
val sf = StringBuilder().apply {
appendLine("Signature-Version: 1.0")
appendLine("Created-By: Android Gradle")
appendLine("SHA-256-Digest-Manifest: ${calculateDigest(manifest)}")
appendLine()
// 为 MANIFEST.MF 中的每个条目生成摘要
manifest.entries.forEach { entry ->
appendLine("Name: ${entry.name}")
appendLine("SHA-256-Digest: ${calculateDigest(entry.toString())}")
appendLine()
}
}.toString()
}
}
总结:
-
MANIFEST.MF 特点:
- 记录所有文件摘要
- 一级完整性保护
- 文件级别验证
-
CERT.SF 特点:
- 对 MANIFEST.MF 再次摘要
- 二级完整性保护
- 整体性验证
-
保护机制:
- 双重摘要保护
- 层层签名验证
- 完整性链条
-
验证过程:
- 先验证 CERT.SF
- 再验证 MANIFEST.MF
- 最后验证实际文件
-
安全保证:
- 防止文件篡改
- 防止摘要篡改
- 确保完整性
这种双重保护机制确保了 APK 文件的完整性和安全性。
6. 流程图:
[生成签名文件]
↓
[准备 APK 文件]
↓
[计算文件摘要]
↓
[生成 MANIFEST.MF]
↓
[生成 CERT.SF]
↓
[使用私钥签名]
↓
[生成 CERT.RSA]
↓
[打包进 APK]
↓
[安装时验证]
7. 详细流程图:
┌─────────────────┐
│ APK 文件内容 │
└────────┬────────┘
↓
┌─────────────────┐ ┌─────────────┐
│ 计算文件摘要 │ → │ MANIFEST.MF │
└────────┬────────┘ └──────┬──────┘
↓ ↓
┌─────────────────┐ ┌─────────────┐
│ 生成签名文件 │ → │ CERT.SF │
└────────┬────────┘ └──────┬──────┘
↓ ↓
┌─────────────────┐ ┌─────────────┐
│ 私钥签名 │ → │ CERT.RSA │
└────────┬────────┘ └──────┬──────┘
↓ ↓
┌─────────────────────────────────────┐
│ 最终 APK 文件 │
└─────────────────────────────────────┘
总结:
-
签名过程:
- 生成密钥库
- 计算文件摘要
- 生成清单文件
- 生成签名文件
- 使用私钥签名
-
文件说明:
- MANIFEST.MF:文件摘要清单
- CERT.SF:清单文件签名
- CERT.RSA:包含公钥证书和签名
-
验证过程:
- 验证证书有效性
- 验证签名完整性
- 验证文件一致性
-
安全保证:
- 完整性保护
- 来源认证
- 防篡改机制
-
注意事项:
- 密钥安全保管
- 签名文件备份
- 版本升级考虑
- 签名算法选择
这就是 Android 应用签名的完整过程。签名机制确保了应用的完整性和来源可信性。
数字签名与不对称加密的区别
数字签名和加密是不同的过程。在 APK 签名中,使用的是私钥签名,公钥验证的机制。让我详细解释:
1. 签名过程:
class SigningProcess {
fun sign() {
// 1. 计算 CERT.SF 的摘要
val sfDigest = calculateDigest(certSF)
// 2. 使用私钥对摘要进行签名
val signature = privateKey.sign(sfDigest)
// 3. CERT.RSA 包含:
val certRSA = CertRSA(
publicKeyCertificate = certificate, // 公钥证书
signature = signature // 签名数据
)
}
}
2. 验证过程:
class VerificationProcess {
fun verify() {
// 1. 从 CERT.RSA 提取公钥证书
val certificate = certRSA.certificate
val publicKey = certificate.publicKey
// 2. 计算 CERT.SF 的摘要
val actualDigest = calculateDigest(certSF)
// 3. 使用公钥验证签名
val isValid = publicKey.verify(
signature = certRSA.signature,
digest = actualDigest
)
}
}
3. 对比加密和签名:
class Comparison {
fun explain() {
// 加密通信
val encrypted = publicKey.encrypt(message) // 公钥加密
val decrypted = privateKey.decrypt(encrypted) // 私钥解密
// 数字签名
val signature = privateKey.sign(message) // 私钥签名
val isValid = publicKey.verify(signature) // 公钥验证
}
}
4. 流程图:
graph TD
subgraph 签名过程
A[CERT.SF文件] --> B[计算摘要]
B --> C[私钥签名]
C --> D[生成CERT.RSA]
E[公钥证书] --> D
end
subgraph 验证过程
F[CERT.RSA文件] --> G[提取公钥证书]
F --> H[提取签名]
I[CERT.SF文件] --> J[计算摘要]
G --> K[验证签名]
H --> K
J --> K
end
5. 关键区别:
class KeyDifferences {
fun explain() {
// 1. 加密通信(保密性)
val secret = "机密信息"
val encrypted = publicKey.encrypt(secret) // 公钥加密
val decrypted = privateKey.decrypt(encrypted)// 私钥解密
// 2. 数字签名(完整性、认证)
val message = "重要文件"
val signature = privateKey.sign(message) // 私钥签名
val authentic = publicKey.verify(signature) // 公钥验证
}
}
总结:
-
数字签名目的:
- 确保完整性
- 验证来源
- 防止篡改
-
加密通信目的:
- 保护隐私
- 确保机密性
- 防止窃听
-
主要区别:
- 签名:私钥签名,公钥验证
- 加密:公钥加密,私钥解密
-
APK 签名特点:
- 不是加密 APK 内容
- 只是验证完整性
- 确保来源可信
-
安全保证:
- 只有持有私钥者才能签名
- 任何人都能用公钥验证
- 签名不可伪造
这就是为什么 APK 签名使用私钥签名,而验证时使用公钥的原因。这是数字签名的标准做法,与加密通信的过程是不同的。
总体流程图
graph TD
A[APK文件] --> B[计算文件摘要]
B --> C[生成 MANIFEST.MF]
C --> D[生成 CERT.SF]
D --> E[使用私钥签名]
E --> F[生成 CERT.RSA]
F --> G[最终签名APK]
%% 签名文件生成过程
H[Keystore生成] --> |keytool| I[密钥对]
I --> |私钥| E
I --> |公钥证书| F
%% 验证过程
G --> J[安装验证]
J --> K[验证 CERT.RSA]
K --> L[验证 CERT.SF]
L --> M[验证 MANIFEST.MF]
M --> N[验证通过]
更详细的版本:
flowchart TD
subgraph 准备阶段
A[生成Keystore] --> B[配置Gradle签名]
B --> C[准备APK文件]
end
subgraph 签名过程
C --> D[计算APK文件摘要]
D --> E[生成MANIFEST.MF]
E --> F[生成CERT.SF]
F --> G[私钥签名]
G --> H[生成CERT.RSA]
end
subgraph META-INF目录
E --> I[MANIFEST.MF]
F --> J[CERT.SF]
H --> K[CERT.RSA]
end
subgraph 验证过程
L[安装APK] --> M[验证证书]
M --> N[验证签名]
N --> O[验证文件完整性]
O --> P{验证通过?}
P -->|是| Q[安装完成]
P -->|否| R[安装失败]
end
状态流转图:
stateDiagram-v2
[*] --> 准备密钥
准备密钥 --> 计算摘要
计算摘要 --> 生成清单
生成清单 --> 签名文件
签名文件 --> 打包APK
打包APK --> 验证安装
验证安装 --> [*]
state 验证安装 {
[*] --> 验证证书
验证证书 --> 验证签名
验证签名 --> 验证完整性
验证完整性 --> [*]
}
时序图:
sequenceDiagram
participant A as APK文件
participant B as 构建工具
participant C as 签名工具
participant D as 安装器
A->>B: 准备打包
B->>C: 计算文件摘要
C->>C: 生成MANIFEST.MF
C->>C: 生成CERT.SF
C->>C: 私钥签名
C->>C: 生成CERT.RSA
C->>A: 写入签名文件
A->>D: 请求安装
D->>D: 验证签名
D-->>A: 安装结果
这些图表从不同角度展示了 Android 签名的过程,包括:
- 基本流程
- 详细步骤
- 状态转换
- 时序关系
选择合适的图表可以更好地理解整个签名过程。