iOS开发基础100-MDM Vendor 申请与证书生成
iOS MDM Vendor 申请与证书生成
MDM(Mobile Device Management,移动设备管理)是企业管理 iOS 设备的核心技术。成为 MDM Vendor 并正确生成证书是搭建 MDM 系统的第一步。本文从 MDM 基本原理讲起,完整梳理从申请 MDM Vendor 到生成最终 APNS 证书的全流程,深入解释每一步的底层原理(证书链、签名、Vendor 与 Customer 的关系),补充常见错误排查和证书管理要点。
一、MDM 概述
一句话原理
MDM 是企业通过服务器远程管理 iOS 设备的技术(安装 App、配置策略、锁定/擦除设备等),核心流程是:MDM 服务器通过 APNS 唤醒设备 → 设备主动连接 MDM 服务器 → 服务器下发指令 → 设备执行并返回结果。
MDM 工作流程
MDM 服务器 苹果 APNS 服务器 iOS 设备
│ │ │
│── 1. 发送唤醒通知 ─────────→│ │
│ │── 2. APNS 推送唤醒 ─────→│
│ │ │
│←── 3. 设备主动连接 MDM ─────────────────────────────────│
│ │ │
│── 4. 下发管理指令(安装App/配置策略/锁定设备)─────────→│
│ │ │
│←── 5. 返回执行结果 ─────────────────────────────────────│
MDM Vendor 与 Customer
| 角色 | 说明 |
|---|---|
| MDM Vendor | MDM 服务商,拥有苹果颁发的 MDM 证书,负责为客户签名证书 |
| MDM Customer | 使用 MDM 的企业客户,需要 Vendor 帮其签名,生成自己的 APNS 证书 |
| 关系 | Customer 生成 CSR → Vendor 用 MDM 证书签名 → Customer 上传签名后的文件到苹果获取 APNS 证书 |
为什么需要 Vendor 签名?苹果不直接给每个企业颁发 MDM 证书,而是通过授权的 Vendor 来管理,Vendor 对 Customer 的身份和用途负责。
整体证书链
Apple Root CA(根证书)
└── Apple WWDR CA(中间证书)
└── MDM Vendor Certificate(MDM 证书,苹果颁发给 Vendor)
└── Vendor 签名 Customer CSR → 生成 plist
└── 上传苹果 Push 门户 → APNS 证书(Customer 用)
└── 导出 p12 → MDM 服务器用于连接 APNS
二、第一步:申请成为 MDM Vendor
一句话原理
MDM 功能不是企业开发者账号默认就有的,需要向苹果单独申请开通,审核通过后才能在证书页面看到 MDM CSR 选项。
先决条件
- 必须拥有 iOS Developer Enterprise Program(企业开发者账号,$299/年)。
- 个人账号和公司账号($99/年)不能申请 MDM Vendor。
- 需要以企业账号的 Agent(管理员)身份操作。
申请步骤
- 访问苹果开发者联系页面:
https://developer.apple.com/contact/submit.php - 使用企业账号的 Agent 身份登录。
- 提交申请,说明希望成为 MDM Vendor,描述你的 MDM 业务场景。
- 苹果通常在 1 个工作日内处理,通过后会给 Agent 发邮件。
- 申请通过后,登录开发者中心,在 Certificates 页面会出现 MDM CSR 选项。
申请被拒的常见原因:业务描述不清晰、没有真实的 MDM 产品。建议详细描述你的 MDM 解决方案和目标客户。
三、第二步:生成 Vendor 私钥和 CSR
一句话原理
CSR(Certificate Signing Request,证书签名请求)是包含你的公钥和身份信息的文件,提交给苹果后,苹果用它生成证书。同时钥匙串里会生成对应的私钥,私钥必须妥善保管(丢失后证书无法使用)。
用钥匙串访问生成 CSR
- 打开 钥匙串访问(Keychain Access)。
- 菜单栏:钥匙串访问 → 证书助理 → 从证书颁发机构请求证书。
- 填写信息:
- 用户电子邮件:注册的 Apple ID。
- 常用名称:自定义名称,如
MDM Vendor(记住这个名称,后续签名要用)。 - CA 电子邮件地址:留空。
- 请求的内容:选择"存储到磁盘"。
- 点击继续,保存为
MdmVendor.certSigningRequest。 - 此时钥匙串中会生成一对公私钥,私钥在钥匙串中。
导出私钥为 p12
- 在钥匙串中找到刚生成的私钥(名称和常用名称一致)。
- 右键 → 导出。
- 保存为
vendor.p12,设置密码(记住这个密码)。
用 openssl 转换为 key 文件
# 将 p12 中的私钥提取为 PEM 格式的 key 文件
openssl pkcs12 -in vendor.p12 -nocerts -out vendor.key
过程中会提示输入三次密码:
vendor.p12的密码(导出时设置的)。vendor.key的新密码(PEM 私钥的加密密码)。- 再次确认
vendor.key的密码。
vendor.key是后续签名 Customer CSR 时需要的私钥,必须和 MDM 证书配套使用。
四、第三步:提交 CSR 生成 MDM 证书
一句话原理
把 CSR 上传给苹果,苹果用它的私钥签名,生成MDM Vendor 证书,这个证书是 Vendor 身份的证明,后续用来给 Customer 的 CSR 签名。
操作步骤
- 登录 苹果开发者网站。
- 进入 Certificates, Identifiers & Profiles。
- 点击 Certificates → 右上角 + 号。
- 选择 MDM CSR(申请通过后才会出现这个选项)。
- 上传第二步生成的
MdmVendor.certSigningRequest。 - 点击 Continue,苹果生成证书。
- 下载得到
MDM.cer文件。
注意事项
- MDM 证书有有效期(通常 1 年),到期前需要更新。
- 证书必须和第二步生成的私钥配套使用(私钥在钥匙串或 vendor.key 中)。
- 如果私钥丢失,需要重新生成 CSR 并申请新证书。
五、第四步:生成 PEM 证书链
一句话原理
苹果返回的 MDM.cer 是 DER 格式的二进制证书,需要转换成 PEM 格式(Base64 文本),并和苹果的根证书、中间证书组合成完整证书链,供后续签名使用。
下载苹果根证书和中间证书
从 苹果证书授权页面 下载:
AppleIncRootCertificate.cer(苹果根证书)AppleWWDRCA.cer(苹果全球开发者关系中间证书)
转换为 PEM 格式
# MDM 证书 → PEM
openssl x509 -inform der -in MDM.cer -out mdm.pem
# 中间证书 → PEM
openssl x509 -inform der -in AppleWWDRCA.cer -out intermediate.pem
# 根证书 → PEM
openssl x509 -inform der -in AppleIncRootCertificate.cer -out root.pem
为什么需要完整证书链
证书是链式信任的:
- 设备信任苹果根证书。
- 根证书信任中间证书。
- 中间证书信任 MDM 证书。
- 验证时需要从 MDM 证书一路追溯到根证书,所以需要完整的证书链。
六、第五步:生成 Customer 证书并签名
一句话原理
每个 MDM 客户(Customer)需要生成自己的 CSR,Vendor 用自己的 MDM 证书和私钥对 Customer 的 CSR 签名,生成一个 plist 文件,Customer 用这个 plist 去苹果换取自己的 APNS 证书。
1. Customer 生成 CSR
Customer 用同样的方式(钥匙串访问)生成一个新的 CSR,常用名称区分开,如 MDMCustomer,保存为 MDMCustomer.certSigningRequest。
2. 转换 CSR 为 DER 格式
# PEM 格式的 CSR → DER 格式
openssl req -inform pem -outform der -in MDMCustomer.csr -out customer.der
签名工具需要 DER 格式的 CSR,所以这一步是格式转换。
3. Vendor 对 customer.der 签名
Vendor 用自己的 MDM 证书(mdm.pem)和私钥(vendor.key)对 customer.der 进行签名,生成一个编码后的 plist 文件。
签名工具
常见的签名方式:
- Softthink 的 Java 工具(传统方案,需要 dom4j 等依赖)。
- Python 脚本(更现代,用 cryptography 库)。
- OpenSSL 命令(部分场景可用)。
Python 签名示例(推荐)
# sign_csr.py
# 依赖:pip install cryptography
from cryptography import x509
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.backends import default_backend
import plistlib
import base64
# 1. 加载 MDM 证书
with open('mdm.pem', 'rb') as f:
mdm_cert = x509.load_pem_x509_certificate(f.read(), default_backend())
# 2. 加载私钥
with open('vendor.key', 'rb') as f:
private_key = serialization.load_pem_private_key(
f.read(), password=b'your_key_password', backend=default_backend()
)
# 3. 读取 Customer CSR(DER 格式)
with open('customer.der', 'rb') as f:
csr_data = f.read()
# 4. 用私钥对 CSR 数据签名
signature = private_key.sign(
csr_data,
padding.PKCS1v15(),
hashes.SHA1()
)
# 5. 构造 plist
plist_data = {
'PushCertRequestCSR': csr_data,
'PushCertCertificateChain': mdm_cert.public_bytes(serialization.Encoding.PEM),
'PushCertSignature': signature,
}
# 6. 序列化为 plist 并 Base64 编码
plist_bytes = plistlib.dumps(plist_data, fmt=plistlib.FMT_XML)
plist_encoded = base64.b64encode(plist_bytes)
# 7. 保存
with open('plist_encoded', 'wb') as f:
f.write(plist_encoded)
print("签名完成,生成 plist_encoded 文件")
传统的 Java 方案依赖
sun.misc.BASE64Encoder(Java 8 后被移除),容易遇到编译问题。Python 方案更简洁,推荐使用。
签名后的文件结构
plist_encoded 是 Base64 编码的 plist,解码后包含:
| 字段 | 说明 |
|---|---|
PushCertRequestCSR |
Customer 的 CSR 数据(DER 格式) |
PushCertCertificateChain |
Vendor 的 MDM 证书链(PEM 格式) |
PushCertSignature |
Vendor 用私钥对 CSR 的签名 |
苹果验证这个 plist 时,会用 MDM 证书中的公钥验证签名,确认是合法 Vendor 签名的。
七、第六步:上传生成 APNS 证书并导出 p12
一句话原理
Customer 把签名后的 plist 上传到苹果 Push 证书门户,苹果验证通过后颁发APNS 证书,这个证书用于 MDM 服务器向设备发送唤醒推送。
操作步骤
- Customer 用 Apple ID 登录 苹果 Push 证书门户。
- 点击 Create a Certificate。
- 上传第六步生成的
plist_encoded文件。 - 苹果验证通过后,生成 APNS 证书,下载得到
.pem文件(重命名为push_cert.pem)。 - 双击
push_cert.pem,将证书安装到钥匙串中。 - 在钥匙串中找到该证书,右键导出为
.p12格式,设置密码。 - 这个 p12 文件就是 MDM 服务器连接 APNS 时需要的证书。
APNS 证书的用途
- MDM 服务器用这个证书和苹果 APNS 服务器建立 TLS 连接。
- 通过这个连接发送唤醒通知,让设备主动连接 MDM 服务器。
- 证书有效期通常为 1 年,到期前需要更新(重新走 Customer CSR → Vendor 签名 → 上传流程)。
八、证书链与安全原理
非对称加密在 MDM 中的应用
| 环节 | 加密方式 | 作用 |
|---|---|---|
| CSR 生成 | 非对称(RSA) | 生成公私钥对,公钥在 CSR 中,私钥自己保留 |
| 苹果颁发 MDM 证书 | 数字签名 | 苹果用私钥签名,设备用苹果公钥验证 |
| Vendor 签名 Customer CSR | 数字签名 | Vendor 用私钥签名,苹果用 MDM 证书公钥验证 |
| MDM 服务器连接 APNS | TLS 双向认证 | APNS 证书证明 MDM 服务器身份 |
| 设备与 MDM 服务器通信 | HTTPS | 加密传输管理指令 |
证书生命周期管理
生成 → 分发 → 使用 → 即将过期 → 更新 → 旧证书撤销
│ │
└── 私钥丢失 → 重新生成 ────────────┘
| 阶段 | 注意事项 |
|---|---|
| 生成 | 私钥必须安全保存,不能泄露 |
| 分发 | p12 文件传输时加密,密码单独传递 |
| 使用 | MDM 服务器安全存储证书,限制访问权限 |
| 更新 | 到期前 30 天开始更新流程,避免服务中断 |
| 撤销 | 私钥泄露时立即吊销证书,重新申请 |
九、常见问题与坑
Q1:申请 MDM Vendor 被拒怎么办?
- 确保用的是企业开发者账号($299),不是个人/公司账号($99)。
- 申请描述要详细,说明 MDM 产品的功能、目标客户、部署规模。
- 如果被拒,可以回复邮件补充信息,或重新提交申请。
Q2:证书页面看不到 MDM CSR 选项?
- 确认 MDM Vendor 申请已通过(苹果会发邮件)。
- 确认登录的是企业账号的 Agent 身份。
- 尝试刷新页面或退出重新登录。
Q3:签名后上传 plist 报错?
常见原因:
- CSR 格式不对(必须是 DER 格式,不是 PEM)。
- 证书链不完整(缺少中间证书或根证书)。
- 签名算法不对(MDM 签名用 SHA1withRSA)。
- MDM 证书和私钥不匹配(私钥不是生成 CSR 时的那个)。
- plist 编码格式错误(必须是 Base64 编码的 XML plist)。
Q4:sun.misc.BASE64Encoder 找不到?
这是 Java 8 之后的常见问题,sun.misc.BASE64Encoder 被移除了。解决方案:
- 用
java.util.Base64(Java 8+ 标准库)替代。 - 或改用 Python 签名脚本(推荐,见第六步)。
Q5:MDM 证书和 APNS 证书有什么区别?
| 证书 | 持有者 | 用途 |
|---|---|---|
| MDM 证书 | Vendor | 给 Customer 的 CSR 签名 |
| APNS 证书 | Customer | MDM 服务器连接 APNS,唤醒设备 |
Vendor 不能直接用 MDM 证书管理设备,必须通过 Customer 的 APNS 证书。
Q6:证书过期了怎么办?
- MDM 证书过期:Vendor 需要重新生成 CSR、申请新证书,然后给所有 Customer 重新签名。
- APNS 证书过期:Customer 需要重新生成 CSR、找 Vendor 签名、上传获取新证书,更新 MDM 服务器配置。
- 建议到期前 30 天开始更新,避免服务中断。
Q7:私钥丢失了怎么办?
- 私钥丢失后,对应的证书无法使用(没有私钥无法签名)。
- 需要重新生成 CSR 和私钥,重新申请证书。
- 所以私钥(vendor.p12 / vendor.key)必须安全备份,建议加密存储。
Q8:一个 Vendor 可以给多个 Customer 签名吗?
可以。Vendor 用同一个 MDM 证书给不同 Customer 的 CSR 签名,每个 Customer 得到自己的 APNS 证书。这是 MDM Vendor 的商业模式。
Q9:MDM 可以管理非企业设备吗?
- 企业设备(DEP 注册的)可以零接触管理。
- 个人设备需要用户手动安装 MDM 描述文件并信任。
- 设备用户可以随时移除 MDM 描述文件(除非是受监管设备)。
Q10:测试 MDM 需要什么环境?
- 企业开发者账号 + MDM Vendor 资格。
- 一台或多台 iOS 测试设备。
- 一个可以被设备访问的 MDM 服务器(公网 IP 或域名 + HTTPS)。
- APNS 证书(用于推送唤醒)。
十、证书操作代码(OC/Swift)
读取证书信息(OC)
#import <Security/Security.h>
// 从 p12 文件读取证书和私钥
- (void)loadIdentityFromP12:(NSString *)path password:(NSString *)password {
NSData *p12Data = [NSData dataWithContentsOfFile:path];
NSDictionary *options = @{(__bridge id)kSecImportExportPassphrase: password};
CFArrayRef items = NULL;
OSStatus status = SecPKCS12Import((__bridge CFDataRef)p12Data,
(__bridge CFDictionaryRef)options,
&items);
if (status == errSecSuccess && CFArrayGetCount(items) > 0) {
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identity = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
// 获取证书
SecCertificateRef certificate = NULL;
SecIdentityCopyCertificate(identity, &certificate);
// 获取证书摘要
NSData *summary = (__bridge_transfer NSData *)SecCertificateCopyNormalizedSubjectContent(certificate, NULL);
NSLog(@"证书信息:%@", summary);
CFRelease(certificate);
CFRelease(items);
}
}
读取证书信息(Swift)
import Security
func loadIdentity(from p12Path: String, password: String) {
guard let p12Data = try? Data(contentsOf: URL(fileURLWithPath: p12Path)) else { return }
let options = [kSecImportExportPassphrase as String: password]
var items: CFArray?
let status = SecPKCS12Import(p12Data as CFData, options as CFDictionary, &items)
if status == errSecSuccess, let items = items, CFArrayGetCount(items) > 0 {
let identityDict = CFArrayGetValueAtIndex(items, 0) as! [String: Any]
let identity = identityDict[kSecImportItemIdentity as String] as! SecIdentity
var certificate: SecCertificate?
SecIdentityCopyCertificate(identity, &certificate)
if let cert = certificate {
let summary = SecCertificateCopyNormalizedSubjectContent(cert, nil)
print("证书信息:\(String(describing: summary))")
}
}
}
十一、总结
- MDM 原理:MDM 服务器通过 APNS 唤醒设备 → 设备连接 MDM 服务器 → 下发管理指令。
- 两个角色:
- Vendor:拥有 MDM 证书,给 Customer 的 CSR 签名。
- Customer:用签名后的 plist 换取 APNS 证书,用于管理设备。
- 六步流程:
- 申请 MDM Vendor(企业账号 + 苹果审核)。
- 生成 Vendor 私钥和 CSR。
- 提交 CSR 获取 MDM 证书。
- 转换 PEM 证书链。
- Customer 生成 CSR → Vendor 签名 → 生成 plist。
- 上传 plist 获取 APNS 证书 → 导出 p12。
- 关键文件:
vendor.p12/vendor.key:Vendor 私钥,必须安全保存。MDM.cer/mdm.pem:Vendor 的 MDM 证书。plist_encoded:Vendor 签名后的文件,Customer 上传用。push_cert.pem/ p12:Customer 的 APNS 证书,MDM 服务器用。
- 常见坑:CSR 格式、证书链完整性、签名算法、私钥丢失、证书过期。
- 安全原则:私钥加密存储、证书到期前更新、p12 传输加密、最小权限访问。

浙公网安备 33010602011771号