背景

为了提高APK的安全等级,要求APK使用高级加固方式进行加固。了解了市面上的主流加固方案发现都是收费的而且费用不低!最后选择了免费的OPPO加固。APK在加固后需要进行重新签名,签名流程分为好几个步骤并且涉及到配置android sdk、签名文件地址、apk地址等信息。为了提高工作效率需要生成一个脚本进行一键签名。

实践

分别用DeepSeek、TraeCursor进行生成脚本,最后进行比较发现Cursor生成的最好
生成脚本描述文本:

生成一个可以在win11电脑上运行的给加固后的apk进行签名的脚本

以下是Cursor生成的信息:


这里给你一个可在 Windows 11 上直接使用的 PowerShell 签名脚本(支持加固后 APK)
将下面内容保存为 sign-apk.ps1,PowerShell 7 或 Windows PowerShell 都可运行。脚本会自动查找 Android SDK Build-Tools(或可手动指定),可选跳过 zipalign,支持选择签名方案 v1/v2/v3/v4。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
param(
[Parameter(Mandatory = $true)]
[string]$ApkPath, # 待签名的APK路径

[Parameter(Mandatory = $true)]
[string]$KeystorePath, # keystore 文件路径(.jks/.keystore)

[Parameter(Mandatory = $true)]
[string]$Alias, # key别名

[Parameter(Mandatory = $true)]
[string]$KeystorePass, # keystore 密码

[string]$KeyPass, # key 密码(如不填默认与 keystore 密码相同)

[string]$BuildToolsDir, # 指定 Android build-tools 目录(可选,如 C:\Android\sdk\build-tools\35.0.0)
[string]$OutDir, # 输出目录(可选,默认与 APK 同目录)
[switch]$SkipZipalign, # 跳过 zipalign(部分加固包要求跳过时可用)
[switch]$Overwrite, # 输出存在时覆盖
[ValidateSet("v1","v2","v3","v4","all")]
[string]$Schemes = "all" # 指定签名方案
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

function Write-Info($msg) { Write-Host "[INFO] $msg" -ForegroundColor Cyan }
function Write-Warn($msg) { Write-Host "[WARN] $msg" -ForegroundColor Yellow }
function Write-Err ($msg) { Write-Host "[ERROR] $msg" -ForegroundColor Red }

# 规范化路径
$ApkPath = (Resolve-Path -LiteralPath $ApkPath).Path
$KeystorePath = (Resolve-Path -LiteralPath $KeystorePath).Path
if ([string]::IsNullOrWhiteSpace($OutDir)) {
$OutDir = Split-Path -Parent $ApkPath
} else {
$OutDir = (Resolve-Path -LiteralPath $OutDir).Path
}
if ([string]::IsNullOrWhiteSpace($KeyPass)) { $KeyPass = $KeystorePass }

# 寻找 build-tools
function Get-LatestBuildToolsDir {
$candidates = @()
foreach ($envVar in @("ANDROID_HOME","ANDROID_SDK_ROOT")) {
$sdk = [Environment]::GetEnvironmentVariable($envVar, "User")
if (-not $sdk) { $sdk = [Environment]::GetEnvironmentVariable($envVar, "Machine") }
if ($sdk) {
$bt = Join-Path $sdk "build-tools"
if (Test-Path $bt) { $candidates += $bt }
}
}
foreach ($root in $candidates) {
$subdirs = Get-ChildItem -Path $root -Directory -ErrorAction SilentlyContinue | Sort-Object Name -Descending
if ($subdirs -and $subdirs.Count -gt 0) {
return $subdirs[0].FullName
}
}
return $null
}

if ([string]::IsNullOrWhiteSpace($BuildToolsDir)) {
$BuildToolsDir = Get-LatestBuildToolsDir
if (-not $BuildToolsDir) {
Write-Err "未找到 Android build-tools,请安装 Android SDK 并设置 ANDROID_HOME/ANDROID_SDK_ROOT,或通过 -BuildToolsDir 指定。"
exit 1
}
}

# 定位工具(Windows 下 apksigner 为 .bat)
$zipalign = Join-Path $BuildToolsDir "zipalign.exe"
$apksigner = Join-Path $BuildToolsDir "apksigner.bat"
if (-not (Test-Path $zipalign)) { Write-Warn "未找到 zipalign:$zipalign,将无法对齐。建议安装完整 build-tools 或使用 -SkipZipalign。" }
if (-not (Test-Path $apksigner)) { Write-Err "未找到 apksigner:$apksigner"; exit 1 }

# 生成输出文件名
$baseName = [IO.Path]::GetFileNameWithoutExtension($ApkPath)
$alignedApk = Join-Path $OutDir ($baseName + "_aligned.apk")
$signedApk = Join-Path $OutDir ($baseName + "_signed.apk")

# 覆盖处理
if ($Overwrite) {
foreach ($f in @($alignedApk,$signedApk)) {
if (Test-Path $f) { Remove-Item -LiteralPath $f -Force -ErrorAction SilentlyContinue }
}
} else {
foreach ($f in @($alignedApk,$signedApk)) {
if (Test-Path $f) {
Write-Err "输出文件已存在:$f。使用 -Overwrite 覆盖或删除后重试。"
exit 1
}
}
}

# 可选 zipalign
$inputForSign = $ApkPath
if (-not $SkipZipalign) {
if (Test-Path $zipalign) {
Write-Info "执行 zipalign..."
& $zipalign -v -p 4 "$ApkPath" "$alignedApk" | Write-Host
if ($LASTEXITCODE -ne 0 -or -not (Test-Path $alignedApk)) {
Write-Err "zipalign 失败。可尝试使用 -SkipZipalign 跳过或检查 APK。"
exit 1
}
$inputForSign = $alignedApk
} else {
Write-Warn "找不到 zipalign,跳过对齐。"
}
} else {
Write-Info "已选择跳过 zipalign。"
}

# 签名方案配置
$enableV1 = $false; $enableV2 = $false; $enableV3 = $false; $enableV4 = $false
switch ($Schemes) {
"all" { $enableV1=$true; $enableV2=$true; $enableV3=$true; $enableV4=$false } # V4 常用于增量分发,通常不必
"v1" { $enableV1=$true }
"v2" { $enableV2=$true }
"v3" { $enableV3=$true }
"v4" { $enableV4=$true } # 如需 V4,请同时确保下游支持
}

# 组装 apksigner 参数
$signArgs = @(
"sign",
"--ks", "$KeystorePath",
"--ks-key-alias", "$Alias",
"--ks-pass", "pass:$KeystorePass",
"--key-pass", "pass:$KeyPass",
"--v1-signing-enabled", ($enableV1.ToString().ToLower()),
"--v2-signing-enabled", ($enableV2.ToString().ToLower()),
"--v3-signing-enabled", ($enableV3.ToString().ToLower()),
"--v4-signing-enabled", ($enableV4.ToString().ToLower()),
"--out", "$signedApk",
"$inputForSign"
)

Write-Info "执行签名..."
& $apksigner @signArgs | Write-Host
if ($LASTEXITCODE -ne 0 -or -not (Test-Path $signedApk)) {
Write-Err "签名失败。"
exit 1
}

# 验证签名
Write-Info "验证签名..."
& $apksigner verify --verbose --print-certs "$signedApk" | Write-Host
if ($LASTEXITCODE -ne 0) {
Write-Err "签名验证失败。"
exit 1
}

Write-Host ""
Write-Host "签名完成:" -ForegroundColor Green
Write-Host " $signedApk" -ForegroundColor Green

使用示例
基本用法(自动查找最新 build-tools,v1+v2+v3,先 zipalign 再签名):

pwsh .\sign-apk.ps1 -ApkPath D:\app\reinforced.apk -KeystorePath D:\keys\release.jks -Alias myalias -KeystorePass 你的Keystore密码

指定 build-tools 目录与输出目录,并覆盖已有输出:

pwsh .\sign-apk.ps1 -ApkPath D:\app\reinforced.apk -KeystorePath D:\keys\release.jks -Alias myalias -KeystorePass 你的Keystore密码 -KeyPass 你的Key密码 -BuildToolsDir “C:\Android\sdk\build-tools\35.0.0” -OutDir D:\out -Overwrite

某些加固包要求跳过对齐:

pwsh .\sign-apk.ps1 -ApkPath D:\app\reinforced.apk -KeystorePath D:\keys\release.jks -Alias myalias -KeystorePass 你的Keystore密码 -SkipZipalign

只启用 v2 签名(极少数场景需要):

pwsh .\sign-apk.ps1 -ApkPath D:\app\reinforced.apk -KeystorePath D:\keys\release.jks -Alias myalias -KeystorePass 你的Keystore密码 -Schemes v2

以上方法都需要每次输入Keystore密码和alias,也可以提前设置在脚本中配置好这些信息
修改脚本最上面的param配置信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
param(
[Parameter(Mandatory = $true)]
[string]$ApkPath, # 待签名的APK

[Parameter(Mandatory = $false)]
[string]$KeystorePath = '签名文件路径', # keystore 文件路径(.jks/.keystore)

[Parameter(Mandatory = $false)]
[string]$Alias = 'key别名', # key别名

[Parameter(Mandatory = $false)]
[string]$KeystorePass = '密码', # keystore 密码

[string]$KeyPass = '密码', # key 密码(如不填默认与 keystore 密码相同)

[string]$BuildToolsDir, # 指定 Android build-tools 目录(可选,如 C:\Android\sdk\build-tools\35.0.0)
[string]$OutDir = '输出目录', # 输出目录(可选,默认与 APK 同目录)
)

这样就可以简化运行脚本为:

pwsh 脚本路径(例如:.\cursor-sign1.ps1) -ApkPath 加固后的apk路径(例如:D:\work\xxx.apk)

前置条件

  1. 已安装 Android SDK,并在 build-tools 中包含 apksigner.bat 和 zipalign.exe。
  2. 或通过 -BuildToolsDir 明确指定 build-tools 路径。

如需同时产出未对齐签名、v4 签名或引入渠道信息,可告诉我你的要求,我可调整脚本。


脚本验证结论

进行一次简单的配置sdk、签名路径,之后每次加固后进行签名只用在PowerShell中执行脚本就可生成对应的签名后的apk文件。