[Xcode]iOS代码签名(Code Signing)

article/2025/10/10 13:34:49

在打包过程中让我吃了不少苦头,归根到底是对一些概念和一些原理不清楚不明白,...

代码签名 Code Signing 也叫 App 签名,它是依靠 iOS 证书来进行的,它保证了 App 的合法性、完整性、真实性以及一致性(未被修改)。代码签名的核心是:证书、公钥、私钥。

数字签名(digital signature)

对指定信息使用哈希算法,得到一个固定长度的信息摘要,然后再使用私钥 (注意必须是私钥)对该摘要加密,就得到了数字签名所谓的代码签名就是这个意思。

数字证书(digital certificate)

证书生产

开发者在申请iOS开始证书时,需要通过keychain生产一个CSR文件(CertificateSigningRequest),提交给苹果的证书认证中心进行签名[Apple Worldwide Developer Relations Certification Authority(WWDR)],最后从苹果官网下载并安装使用。这个过程中还会产生一个私钥,证书和私钥在keychain中的位置如图:


iOS证书申请和使用详解

证书组成

经过WWDR数字签名后的数字证书长这个样子:

详情:


其中包含两大部分:

  •     证书本身
包含用户的公钥、用户个人信息、证书颁发机构信息、证书有效期等信息。
  •     证书签名

WWDR将上述证书本身内容的使用哈希算法得到一个固定长度的信息摘要,然后使用自己的私钥对该信息摘要加密生成数字签名,整个过程如图所示:

假如上面的私钥被删除,或者没了,那么就不能对代码进行签名,也就无法使用这个证书了。假如私钥被别人获取了,别人更可以“代替”你来发布 App 了!因此一定要保存好自己的私钥,最好是导出来保存在安全地方!

证书使用

iOS系统原本就持有WWDR的公钥,系统首先会对证书内容通过指定的哈希算法计算得到一个信息摘要;然后使用WWDR的公钥对证书中包含的数字签名解密,从而得到经过WWDR的私钥加密过的信息摘要;最后对比两个信息摘要,如果内容相同就说明该证书可信。整个过程如图所示:

在验证了证书是可信的以后,iOS系统就可以获取到证书中包含的开发者的公钥,并使用该公钥来判断代码签名的可用性了。

证书存在的意义

通过证书使用过程可以看出,证书本身只是一个中间媒介,iOS系统对证书并不关心,它其实只想要证书中包含的开发者的公钥!!
但是开发者怎么才能证明公钥是自己的呢?iOS安全系统怎么才能相信这个公钥就是这个开发者的呢?
不管是哪一个开发者对iOS的安全系统说,这个公钥就是我的,系统是都不相信的,即系统对开发者有着百分之百的不信任感。但是iOS安全系统对自家的WWDR是可信任的,苹果将WWDR的公钥内置在了iOS系统中。有了证书,iOS安全系统只需要通过WWDR的公钥就可以获取到任何一个开发者的可信任的公钥了,这就是证书存在的意义!!

公钥(public key)

公钥被包含在数字证书里,数字证书又被包含在描述文件(Provisioning File)中,描述文件在应用被安装的时候会被拷贝到iOS设备中。
iOS安全系统通过证书就能够确定开发者身份,就能够通过从证书中获取到的公钥来验证开发者用该公钥对应的私钥签名后的代码、资源文件等有没有被更改破坏,最终确定应用能否合法的在iOS设备上合法运行。

私钥(private key)

每个证书(其实是公钥)都对应有一个私钥,私钥会被用来对代码、资源文件等签名。只有开发证书和描述文件是没办法正常调试的,因为没有私钥根本无法签名。

签名相关命令

  • 快捷查看系统中能用来对代码进行签名的证书
可以使用如下命令:security find-identity -v -p codesigning


这就说明当前有10个同时有公钥和私钥的可用证书。
  • 对未签名app手动签名

使用如下命令:codesign -s 'iPhone Developer: Yongjun Ma (5R2CR73PQ7)' TXChatParent.app

: xx.app文件是xx.ipa文件修改扩展名,xx.ipa.zip解压后包含的xx.app文件。

  • 对已签名app重新签名
为了重新设置签名,你必须带上 -f 参数,有了这个参数,codesign 会用你选择的签名替换掉已经存在的那一个:codesign -f -s 'iPhone Developer: Yongjun Ma (5R2CR73PQ7)' TXChatParent.app
  • 查看指定app的签名信息
codesign 还可以为你提供有关一个可执行文件签名状态的信息,这些信息在出现不明错误时会提供巨大的帮助:codesign -vv -d /Users/apple/genPackage/IOS_Parent/Payload/TXChatParent.app


  • 验证签名文件的完整性

检查已签名的文件是否完整可以使用如下命令:
 codesign --verify /Users/apple/genPackage/IOS_Parent/Payload/TXChatParent.app
就像大多数 UNIX 工具一样,没有任何输出代表签名是完好的。如果修改一下这个二进制文件:

和预料中的一样,修改已经签名的应用会导致数字签名验证不通过。

资源文件签名

iOS 和 OS X 的应用和框架则是包含了它们所需要的资源在其中的。这些资源包括图片和不同的语言文件,资源中也包括很重要的应用组成部分例如 XIB/NIB 文件,存档文件(archives),甚至是证书文件。所以为一个程序包设置签名时,这个包中的所有资源文件也都会被设置签名。
为了达到为所有文件设置签名的目的,签名的过程中会在程序包(即Example.app)中新建一个叫做 _CodeSignatue/CodeResources 的文件,这个文件中存储了被签名的程序包中所有文件的签名。你可以自己去查看这个签名列表文件,它仅仅是一个 plist 格式文件。

授权文件(entitlements)

在 iOS 上你的应用能做什么依然是沙盒限制的,这些限制大多情况下都由授权文件(entitlements)来决定。授权机制决定了哪些系统资源在什么情况下允许被一个应用使用,简单的说它就是一个沙盒的配置列表。
运行如下命令:codesign -d --entitlements - /Users/apple/genPackage/IOS_Parent/Payload/TXChatParent.app


在 Xcode 的 Capabilities 选项卡下选择一些选项之后,Xcode 就会生成这样一段 XML。 Xcode 会自动生成一个 .entitlements 文件,然后在需要的时候往里面添加条目。当构建整个应用时,这个文件也会提交给 codesign 作为应用所需要拥有哪些授权的参考。这些授权信息必须都在开发者中心的 App ID 中启用,并且包含在后文介绍的描述文件中。在构建应用时需要使用的授权文件可以在 Xcode build setting 中的 code signing entitlements中设置。
在新版本的 Xcode 6 之后,授权信息列表会以 Example.app.xcent 这样的名字的文件形式包含在应用包中。这么做或许是为了在出现配置错误时提供更加有用的错误信息。

描述文件(provisioning file)

在整个代码签名和沙盒机制中有一个组成部分将签名,授权和沙盒联系了起来,那就是描述文件 (provisioning profiles)。
  • OS X中保存目录
Xcode 将从开发者中心下载的全部配置文件都放在了这里:
~/Library/MobileDevice/Provisioning\ Profiles
  • 文件格式
描述文件并不是一个普通的plist文件,它是一个根据密码讯息语法 (Cryptographic Message Syntax) 加密的文件。
以XML格式查看该文件的命令:security cms -D -i b95d2739-7ae4-4426-8bae-c83d2777a45b.mobileprovision
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict><key>AppIDName</key><string>teacherClient</string><key>ApplicationIdentifierPrefix</key><array><string>LB3ZWQKTZB</string></array><key>CreationDate</key><date>2016-10-19T05:56:17Z</date><key>Platform</key><array><string>iOS</string></array><key>DeveloperCertificates</key><array><data>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</data></array><key>Entitlements</key><dict><key>keychain-access-groups</key><array><string>LB3ZWQKTZB.*</string>		</array><key>get-task-allow</key><false/><key>application-identifier</key><string>LB3ZWQKTZB.com.shangde.ps.teacher</string><key>com.apple.security.application-groups</key><array></array><key>com.apple.developer.team-identifier</key><string>LB3ZWQKTZB</string><key>aps-environment</key><string>production</string><key>beta-reports-active</key><true/></dict><key>ExpirationDate</key><date>2017-10-17T10:37:32Z</date><key>Name</key><string>XC: com.shangde.ps.teacher</string><key>TeamIdentifier</key><array><string>LB3ZWQKTZB</string></array><key>TeamName</key><string>Beijing XieChengShangDe Education Technology Co.,Ltd.</string><key>TimeToLive</key><integer>363</integer><key>UUID</key><string>b95d2739-7ae4-4426-8bae-c83d2777a45b</string><key>Version</key><integer>1</integer>
</dict>
文件内容 :描述文件主要包含以下内容:
  • UUID:每一个配置文件都有它自己的 UUID 。Xcode 会用这个 UUID 来作为标识,记录你在 build settings 中选择了哪一个配置文件。
  • ProvisionedDevices:记录所有可用于调试的设备ID。
  • DeveloperCertificates:包含了可以为使用这个配置文件的应用签名的所有证书。所有的证书都是基于 Base64 编码符合 PEM (Privacy Enhanced Mail, RFC 1848) 格式的。
  • Entitlements:有关前面讲到的配置文件的所有内容都会被保存在这里。

ipa文件的组成

iOS程序最终都会以.ipa文件导出,先来了解一下ipa文件的结构:


事实上,ipa文件只是一个zip包,可以使用如下命令解压:
/usr/bin/unzip -q xxx.ipa -d <destination>
解压后,得到上图的Payload目录,下面是个子目录,其中的内容如下:

  • 资源文件,例如图片、html、等等。
  • _CodeSignature/CodeResources。这是一个plist文件,可用文本查看,其中的内容就是是程序包中(不包括Frameworks)所有文件的签名。注意这里是所有文件。意味着你的程序一旦签名,就不能更改其中任何的东西,包括资源文件和可执行文件本身。iOS系统会检查这些签名。
  • 可执行文件。此文件跟资源文件一样需要签名。
  • 一个mobileprovision文件.打包的时候使用的,从MC上生成的。
  • Frameworks。程序引用的非系统自带的Frameworks,每个Frameworks其实就是一个app,其中的结构应该和app差不多,也包含签名信息CodeResources文件

总结

证书、签名、私钥、描述文件的一句话描述

  • 证书分两种:开发者证书、发布者证书。前者开发时使用,后者发布使用
  • 模拟器调试无需代码签名;真机调试需开发者证书代码签名;发布时需发布证书签名
  • 代码签名需要:证书+私钥,缺一不可
  • 真机调试时要求在设备上安装描述文件(provision profile),该文件包含信息:调试者证书,授权调试设备清单,应用ID。一个应用对应一个描述文件。

团队开发中如何共用证书?共用证书需要:*.p12文件 和 *.mobileprovision
选择 ”个人信息交换(.p12)” 文件格式,导出 .p12 文件!由于 p12 文件包含个人信息,所以必须要密码。

如果选择 “证书(.cer)” 文件格式,则导出证书文件本身!(也可以从 Apple Developer Member Center 下载)

这样,如果想和别人或者其它设备共享证书时,只要把 p12 文件传给他,然后他就可以使用证书里面的私钥对代码进行签名了。


数字签名原理简介


http://chatgpt.dhexx.cn/article/k2fxBnuQ.shtml

相关文章

WordPress给博客文章页添加个性名片

食用教程 本文以子比主题为例 插入HTML代码 进入主题设置 –> 文章&列表 –> 文章页面 –> 文章插入内容 –> 在文章内容后-插入内容 –> 粘贴HTML代码 <div class"business-center"><div class"business-card"><div …

SCTF-2019 Misc wp

SCTF-2019解题纪实 MISC 签到题题目中说’cat \flag ’ in data:image/jpeg;base64,/9j/4QBkRXhpZgAATU0AKgAAAAgABYdpAAQAAAABAAAASgESAAQAAAABAAAAAAEBAAMAAAABAa4AAAEyAAIAAAABAAAAAAEAAAMAAAABAa4AAAAAAAAAAZIIAAQAAAABAAAAAAAAAAD/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYF…

基于 Ubuntu 20.04 系统 部署 NetBox

提示&#xff1a;请注意 信息的时效性、环境的匹配度。侵删&#xff01; 文章目录 前言NetBox 安装一、环境二、安装1.安装 PostgreSQL2.安装 Redis3.安装 Python3.安装 Netbox4.修改 NetBox 配置文件5.安装其他可选的插件或组件6.运行升级脚本6.Python试运行Netbox7.使用 Guni…

从某一点出发沿任意一方向旋转矩阵计算思考与实现

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 上期讲到 绕任一向量旋转矩阵计算思考与实现 点击前往 点击前往 问题提出 之前讲到绕任一向量旋转矩阵实现&#xff0c;原来的向量都是从原点出发&#xff0c;现在把…

绕任一向量旋转矩阵计算思考与实现

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 问题提出 如图所示&#xff0c;在空间中有一向量A&#xff0c;问点O绕A方向逆时针旋转角度α的矩阵如何表示。 问题分析 问题化规 直接去构造一个矩阵是比较困难的。…

echarts 象形柱状图+标注图形

代码&#xff1a; let myChart echarts.init(this.$refs.myChart);let option {graphic: [ // echarts整体的背景图{type: image, // 图形元素类型id: logo, // 更新或删除图形元素时指定更新哪个图形元素&#xff0c;如果不需要用可以忽略。right: center, // 根据父元素进…

【JEECG技术文档】JEECG平台对外接口JWT应用文档V3.7.2

一、 接口方式 接口调用采用http协议&#xff0c;rest请求方式&#xff1b; 二、 接口安全 接口安全采用Json web token (JWT)机制&#xff0c;基于token的鉴权机制. 1. 机制说明 基于token的鉴权机制类似于http协议也是无状态的&#xff0c;它不需要在服务端去保留用户的认证…

轻量级前端MVVM框架avalon - 整体架构

官网提供架构图 单看这个图呢,还木有说明,感觉有点蛋疼,作者的抽象度太高了,还好在前面已经大概分析过了执行流程 如图 左边是View视图,我们就理解html结构,换句话就是说用户能看到的界面,渲染页面&#xff0c;绑定事件&#xff0c;切换类名&#xff0c;什么脏活都揽右边是View…

下载kaggle数据集出现的一系列问题

下载kaggle数据集出现了如下情况 按照下面的帖子安装kaggle库 https://www.cnblogs.com/shuaishuaidefeizhu/p/15170305.html 1.windowsR 打开命令提示符cmd&#xff0c;输入pip install kaggle --user 2.在C:\Users\&#xff08;用户名&#xff09;文件下会生成一个.kaggle文…

CT图像重建算法------射线驱动投影模型

在模拟CT扫描过程中被测物体保持静止不动&#xff0c;射线源与探测器绕Z轴做逆时针旋转&#xff0c;每隔一定角度进行一次投影数据的计算&#xff0c;而计算方法则取决于采用什么样的投影算法。本文主要介绍了投影算法的分类&#xff0c;并详细描述了射线驱动算法中的joseph算法…

尤雨溪告诉你为什么Vue比yox优秀

2019年6月8日来自全球各地的开发者齐聚上海交通大学文治堂&#xff0c;VueConf 2019 在上海成功举办。 VUE 3.0 最新进展 更快 使用 Object.defineProperty -> Proxy 速度 提升了1倍 VUE3.0 将 Virtual DOM 重构 速度 提升 6 倍 牛逼吧 &#xff01;&#xff01;&…

kubernetes的初认识

1.先谈容器。 一个“容器”&#xff0c;实践上是一个由Linux Namespace、Linux Cgroups 和 rootfs 三种技术构建出来的进程的隔离环境。 一个正在运行的linux容器&#xff0c;其实可以被“一分为二”地看待&#xff1a; 一组联合挂载在/var/lib/docker/overlay2下的rootfs&a…

KubeSphere简介,功能介绍,优势,架构说明及应用场景

目录 一、KuberSphere简介 1.1 功能介绍 1.2 Kubernetes 资源管理 1.3 微服务治理 1.4 多租户管理 1.5 DevOps 工程 1.6 Source to Image 1.7 多维度监控 1.8 自研多租户告警系统 1.9 日志查询与收集 1.10 应用管理与编排 1.11 基础设施管理 1.12 多存储类型支持…

云原生钻石课程 | 第4课:Kubernetes存储架构原理深度剖析(上)

点击上方“程序猿技术大咖”&#xff0c;关注并选择“设为星标” 回复“加群”获取入群讨论资格&#xff01; 本篇文章来自《华为云云原生王者之路训练营》钻石系列课程第4课&#xff0c;由云原生存储解决方案Everest2.0的架构设计专家Jabin主讲&#xff0c;详细介绍云原生存储…

1.k8s基本使用(测试加分)

测试为什么要学习容器技术及k8s k8s不是运维的专属技术 随着互联网技术的发展&#xff0c;架构也已经从单体架构发展到容器云( “微服务 k8s” 完美结合) 很多人认为&#xff0c;k8s只是运维需要掌握的技术&#xff0c;讲真&#xff0c;测试和运维、开发都有技术交集 所以&…

Kubernetes 持久化存储(Pod Volumes,PV和PVC)

一、Volumes 介绍 Pod Volumes 首先来看一下 Pod Volumes 的使用场景&#xff1a; 场景一&#xff1a;如果 pod 中的某一个容器在运行时异常退出&#xff0c;被 kubelet 重新拉起之后&#xff0c;如何保证之前容器产生的重要数据没有丢失&#xff1f;场景二&#xff1a;如果…

面试题引出的知识点整理

1、自旋锁&可重复锁&公平锁&共享锁&分段锁你都知道吗? 2、无锁&偏向锁&轻量级锁&重量级锁如何膨胀升级? 3、Lock底层AQS实现与Synchronized底层实现异同&#xff1f; 4、LongAdder的分段CAS优化机制如何设计的&#xff1f; 5、Java多线程内存模型…

云集成,给超大规模K8s运维带来的丝滑体验

根据IDC统计&#xff0c;未来五年容器基础架构软件市场将以超过40%的复合增长率爆发式增长&#xff0c;行业也从互联网向更多的传统行业渗透&#xff0c;比如金融、政府、电信&#xff0c;制造和能源等行业。 近年来&#xff0c;随着云技术的日益普及&#xff0c;越来越多的企…

Spring

目录 1.spring介绍 spring中的两个核心概念 1.1 IoC 1.2 AoP 1.3 Spring的优点 1.4 Spring工程构建 1.4.1 Maven Spring 依赖 1.4.2 Spring核心配置文件编写 1.4.2.1 完成控制反转以及依赖注入 1.4.2.2 测试 1.4.2.3 Spring中的IOC 产生的对象是否是单例模式 1.5 B…

动静分离 与 热点缓存

动静分离 让系统“快”起来&#xff1a;1、提高单次请求的效率2、减少没必要的请求 “动静分离”就是瞄着这个大方向去的。所谓“动静分离”&#xff0c;其实就是把用户请求的数据&#xff08;如HTML页面&#xff09;划分为“动态数据”和“静态数据”。简单来说&#xff0c;“…