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(管理员)身份操作。

申请步骤

  1. 访问苹果开发者联系页面:https://developer.apple.com/contact/submit.php
  2. 使用企业账号的 Agent 身份登录。
  3. 提交申请,说明希望成为 MDM Vendor,描述你的 MDM 业务场景。
  4. 苹果通常在 1 个工作日内处理,通过后会给 Agent 发邮件。
  5. 申请通过后,登录开发者中心,在 Certificates 页面会出现 MDM CSR 选项。

申请被拒的常见原因:业务描述不清晰、没有真实的 MDM 产品。建议详细描述你的 MDM 解决方案和目标客户。


三、第二步:生成 Vendor 私钥和 CSR

一句话原理

CSR(Certificate Signing Request,证书签名请求)是包含你的公钥和身份信息的文件,提交给苹果后,苹果用它生成证书。同时钥匙串里会生成对应的私钥,私钥必须妥善保管(丢失后证书无法使用)。

用钥匙串访问生成 CSR

  1. 打开 钥匙串访问(Keychain Access)。
  2. 菜单栏:钥匙串访问 → 证书助理 → 从证书颁发机构请求证书。
  3. 填写信息:
    • 用户电子邮件:注册的 Apple ID。
    • 常用名称:自定义名称,如 MDM Vendor(记住这个名称,后续签名要用)。
    • CA 电子邮件地址:留空。
    • 请求的内容:选择"存储到磁盘"。
  4. 点击继续,保存为 MdmVendor.certSigningRequest。
  5. 此时钥匙串中会生成一对公私钥,私钥在钥匙串中。

导出私钥为 p12

  1. 在钥匙串中找到刚生成的私钥(名称和常用名称一致)。
  2. 右键 → 导出。
  3. 保存为 vendor.p12,设置密码(记住这个密码)。

用 openssl 转换为 key 文件

# 将 p12 中的私钥提取为 PEM 格式的 key 文件
openssl pkcs12 -in vendor.p12 -nocerts -out vendor.key

过程中会提示输入三次密码:

  1. vendor.p12 的密码(导出时设置的)。
  2. vendor.key 的新密码(PEM 私钥的加密密码)。
  3. 再次确认 vendor.key 的密码。

vendor.key 是后续签名 Customer CSR 时需要的私钥,必须和 MDM 证书配套使用。


四、第三步:提交 CSR 生成 MDM 证书

一句话原理

把 CSR 上传给苹果,苹果用它的私钥签名,生成MDM Vendor 证书,这个证书是 Vendor 身份的证明,后续用来给 Customer 的 CSR 签名。

操作步骤

  1. 登录 苹果开发者网站。
  2. 进入 Certificates, Identifiers & Profiles。
  3. 点击 Certificates → 右上角 + 号。
  4. 选择 MDM CSR(申请通过后才会出现这个选项)。
  5. 上传第二步生成的 MdmVendor.certSigningRequest。
  6. 点击 Continue,苹果生成证书。
  7. 下载得到 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 服务器向设备发送唤醒推送。

操作步骤

  1. Customer 用 Apple ID 登录 苹果 Push 证书门户。
  2. 点击 Create a Certificate。
  3. 上传第六步生成的 plist_encoded 文件。
  4. 苹果验证通过后,生成 APNS 证书,下载得到 .pem 文件(重命名为 push_cert.pem)。
  5. 双击 push_cert.pem,将证书安装到钥匙串中。
  6. 在钥匙串中找到该证书,右键导出为 .p12 格式,设置密码。
  7. 这个 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 证书,用于管理设备。
  • 六步流程:
    1. 申请 MDM Vendor(企业账号 + 苹果审核)。
    2. 生成 Vendor 私钥和 CSR。
    3. 提交 CSR 获取 MDM 证书。
    4. 转换 PEM 证书链。
    5. Customer 生成 CSR → Vendor 签名 → 生成 plist。
    6. 上传 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 传输加密、最小权限访问。

posted @ 2022-03-28 19:03  Mr.陳  阅读(2687)  评论(1)    收藏  举报