iOS应用签名
何为代码签名
代码签名,就是对可执行的文件或者脚本进行数字签名。
主要用来确认软件是否被认可,且在签名后未被修改或者损坏的措施。
与数字签名的原理一样,只不过签名的数据是代码而已
签名流程
通过将源代码进行散列Hash后的Hash值进行私钥M加密并与源代码一起打包成软件包。
在使用端中使用公钥M进行解密得到Hash值,并通过相同的散列Hash对源代码进行Hash,将得到的结果进行匹配,如果一致,说明源代码没有被篡改过,且是正版应用。
苹果双层签名验证原理
首先先分析下Apple应用的需求
- 针对开发情况安装包不需要上传至App Store,就可以直接安装至手机上
- Apple为了保证系统的安全性,又必须对安装的App有绝对的控制权
- 经过Apple允许才能安装
- 不能被滥用导致非开发的App也能被安装
为了实现这一需求,Apple开始提出了双层签名的iOS签名方式。
双层签名逻辑
在Apple服务器中存在着一个私钥A
,而每一部Apple移动设备中存在着公钥A
。这样一来就能确保每个应用都是Apple允许的。
但是,在开发阶段我们需要频繁的进行App的调试,因此Apple新增加了一层签名,来解决App安装到设备上的安全性。
而这一层签名是怎么实现的呢?
初期
- 通过Mac的钥匙串访问生成请求的CSR(Certificate Signing Request)文件
创建CSR文件时,Mac会生成一对公私钥M
,并将包含有公钥M
的CSR文件发送给Apple服务器。 - Apple服务器接收到请求后,利用
私钥A
对公钥M
进行数字签名,生成证书。 - 针对每个App生成对应对
描述文件
。
这个描述文件中包含了相应的信息和权限,以及对应的证书(公钥M
的数字签名的) - 开发初期会通过Apple服务器下载相应App的
描述文件
,并与Mac上的私钥M
进行关联。
App编译安装
- 通过IDE(Xcode)编译生成的App中包含了
Mach-O可执行文件
、App的签名
和描述文件
。
App的签名
就是通过私钥M进行的签名操作生成的。 - 当进行App安装时,手机上的
公钥A
会去验证你的安装行为是否合法。
通过公钥A
去校验证书信息,如果能够解密且对应的Hash校验没有问题的话,说明是合法可安装的,如果无法解密或者说Hash校验有误则不合法不能安装。 - 当运行时,
公钥A
解密得到的公钥M
去验证App的签名
,如果合法就能运行,反之不允许运行。
描述文件
为了解决应用滥用的问题,Apple又添加了两个限制
第一 限制在Apple服务器注册过的设备才能安装
第二 限制签名只能针对某个具体的App应用
- 且Apple还控制着App中的
iCloud
/Push
/后台运行
等权限,所以Apple把这些开关统一称为Entitlements
(授权文件),并将这个文件放到了Provisioning Profile
(描述文件)文件中。- 描述文件是在AppleDevelop网站上创建的,Xcode运行时会打包进App内。
- 开发时,编译完一个App后,用本地的
私钥M
对这个App进行签名,同时把系统服务器获取到的描述文件
打包进App中,文件名为embedded.mobileprovision
,再把App安装至手机上让系统做验证。
App重签名
codesign重签名
针对已砸壳
的ipa包,我们可以手动进行重签名,以便与我们了解其应用的内部。
MacOS中,为我们提供了codesign的终端命令行,让我们对ipa包中对文件进行替换签名。
利用 codesign 重签名步骤
- 删除插件(Plugin)和带有插件的.app包(比如Watch)
- 对Frameworks里面的库进行重签名
- 给可执行文件 +x(可执行)权限
- 添加描述文件(新建工程,真机编译获取)
- 替换被签名BundleID
- 通过描述文件创建权限文件.plist
- <key>application-identifier</key>
- <key>keychain-access-groups</key>
- <key>get-task-allow</key>
- <key>com.apple.developer.team-identifier</key>
- 通过授权文件(Entilements)重签.app包
在重签名过程中涉及到的终端命令:
- $security find-identity -v -p codesigning (查找并列出钥匙串中可用于签名的证书)
- $codesign -fs "证书串" 文件名(进行签名的替换)
- $chmod +x 可执行文件(给可执行文件添加可执行权限)
- $security cms -D -i ../embedded.mobileprovision(查看描述文件)
- $codesign -fs "证书串" --no-strict --entitlements=权限文件.plist App包(App包重签)
- $zip -ry 输出文件 输入文件(将输入文件压缩为输出文件,进行.ipa打包)
Xcode重签名
- 创建一个
同名工程
,生成.app包 - 替换.app包
- 删除插件(Plugin)和带有插件的.app包(比如Watch)
- 对Frameworks里面的库进行重签名
- 运行项目
利用Xcode进行Script重签名
利用脚本重签名,其实就是利用了
codesign签名
,只是将一个一个的步骤通过脚本指令自动化
的去执行。
# ${SRCROOT} 它是工程文件所在的目录
TEMP_PATH="${SRCROOT}/Temp"
#资源文件夹,我们提前在工程目录下新建一个APP文件夹,里面放ipa包
ASSETS_PATH="${SRCROOT}/APP"
#目标ipa包路径
TARGET_IPA_PATH="${ASSETS_PATH}/*.ipa"
#清空Temp文件夹
rm -rf "${SRCROOT}/Temp"
mkdir -p "${SRCROOT}/Temp"
#----------------------------------------
# 1. 解压IPA到Temp下
unzip -oqq "$TARGET_IPA_PATH" -d "$TEMP_PATH"
# 拿到解压的临时的APP的路径
TEMP_APP_PATH=$(set -- "$TEMP_PATH/Payload/"*.app;echo "$1")
# echo "路径是:$TEMP_APP_PATH"
#----------------------------------------
# 2. 将解压出来的.app拷贝进入工程下
# BUILT_PRODUCTS_DIR 工程生成的APP包的路径
# TARGET_NAME target名称
TARGET_APP_PATH="$BUILT_PRODUCTS_DIR/$TARGET_NAME.app"
echo "app路径:$TARGET_APP_PATH"
rm -rf "$TARGET_APP_PATH"
mkdir -p "$TARGET_APP_PATH"
cp -rf "$TEMP_APP_PATH/" "$TARGET_APP_PATH"
#----------------------------------------
# 3. 删除extension和WatchAPP.个人证书没法签名Extention
rm -rf "$TARGET_APP_PATH/PlugIns"
rm -rf "$TARGET_APP_PATH/Watch"
#----------------------------------------
# 4. 更新info.plist文件 CFBundleIdentifier
# 设置:"Set : KEY Value" "目标文件路径"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $PRODUCT_BUNDLE_IDENTIFIER" "$TARGET_APP_PATH/Info.plist"
#----------------------------------------
# 5. 给MachO文件上执行权限
# 拿到MachO文件的路径WeChat
APP_BINARY=`plutil -convert xml1 -o - $TARGET_APP_PATH/Info.plist|grep -A1 Exec|tail -n1|cut -f2 -d\>|cut -f1 -d\<`
#上可执行权限
chmod +x "$TARGET_APP_PATH/$APP_BINARY"
#----------------------------------------
# 6. 重签名第三方 FrameWorks
TARGET_APP_FRAMEWORKS_PATH="$TARGET_APP_PATH/Frameworks"
if [ -d "$TARGET_APP_FRAMEWORKS_PATH" ];
then
for FRAMEWORK in "$TARGET_APP_FRAMEWORKS_PATH/"*
do
#签名
/usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" "$FRAMEWORK"
done
fi