简介
MDM中分为Vendor 和Customer两个角色,即服务商和用户。
如果你有个企业开发者账号,没错就是$299的那个,你既可以是Vendor,也可以是Customer。作为一个Vendor,你可以为Customer的证书请求文件颁发个用于签名的证书。</pre>
如果你仅仅是个Customer也就是没有$299的账号,只能等Vendor给你颁发个.cer证书、签名产生个plist_encoded 文件,然后提交到https://identity.apple.com/pushcert/,如果文件不正确会提示格式错误,若正确会生成一个用于推送的证书mdm.pem。</pre>
以下操作默认你的账号已经被授权了MDM服务功能,下面将以Vendor的角色进行以下操作。
1、生成MDM证书
作为一个vendor,首先要生成用户签名的.cer
证书,具体步骤如下:
打开钥匙串,菜单栏选择
钥匙串访问
->证书助理
->从证书颁发机构请求证书
,然后随便填一个电子邮件,选择存储到磁盘,命名成mdm_vendor.certSigningRequest
登录开发者中心到
Certificates
,选择创建证书,拉到底下Services
,然后选择MDM CSR
.下一步的choose File选择上一步生成的mdm_vendor.certSigningRequest
文件。创建完成后下载。再到钥匙串里找到MDM Vender
开头的刚才那个证书,然后导出p12
文件。这里会要求输入一个密码,后面会用到。这里把密码设置成1,因为这边的p12包含了过期信息,后面要传到监控平台,监控平台的解密码设置成1了-
接下来把
mdm_vendor.p12
转化成mdm_vendor.key
格式openssl pkcs12 -in mdm_vendor.p12 -nocerts -nodes -legacy -out mdm_vendor.key
这里参考教程里使用的是
openssl pkcs12 -in mdm_vendor.p12 -nocerts -out mdm_vendor.key
,但是会报错,因为新版本的openssl移除了一些算法(我用的是3.4.1版本的ssl),需要在命令里显示的调用已经被移除的算法
作为一个customer
- 在终端中生成
customer.csr
:
openssl genrsa -des3 -out customerPrivateKey.pem 2048
再执行:
openssl req -new -key customerPrivateKey.pem -out customer.csr
执行这一步需要输入各种公司信息,可以参考一下信息
Enter pass phrase for root.key: ← 输入前面创建的密码
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter ‘.’, the field will be left blank.
—–
Country Name (2 letter code) [AU]:HK ← 国家代号,美国输入US
State or Province Name (full name) [Some-State]:Hong Kong ← 省的全名,拼音
Locality Name (eg, city) []:Hong Kong ← 市的全名,拼音
Organization Name (eg, company) [Internet Widgits Pty Ltd]: hp ← 公司英文名
Organizational Unit Name (eg, section) []: ← 可以不输入 我输入的公司名称
Common Name (eg, YOUR name) []: ← 此时不输入,和后面的需要输入ip或者域名不一样,这里不能输入,输入了好像证书有问题
Email Address []:xxx@xx.com ← 电子邮箱,可随意填
Please enter the following ‘extra’ attributes
to be sent with your certificate request
A challenge password []: ← 可以不输入
An optional company name []: ← 可以不输入</pre>
对customer.csr
签名
下载
AppleWWDRCA.cer下载 脚本里原来的AppleWWDRCA已经过期了,下载最新的G6会导致后面上传苹果会显示失败,实测这里使用G3是可以正常使用的。下载完成后,把文件名改成AppleWWDRCA.cer,方便后面直接使用命令行
由于mac命令行现在只支持python3,需要安装python2,并且由于 Python 2 已经停止支持,Homebrew 已经移除了
python@2
的官方支持。需要冲从 Python 官网下载 macOS 安装包: https://www.python.org/ftp/python/2.7.18/python-2.7.18-macosx10.9.pkg-
生成
plist_encoded
:这里需要把文章末尾的python代码保存成mdm_vendor_sign.py
到同目录下,然后终端cd到目录然后执行python2 mdm_vendor_sign.py --key mdm_vendor.key --csr customer.csr --mdm mdm.cer --root AppleIncRootCertificate.cer --WWDR AppleWWDRCA.cer
这里遇到了一些问题:
1:路径出现中文可能会导致失败
2:原脚本里验证customer.crs的代码,可能由于openssl升级导致这边验证有问题,所以这里对原脚本做了修正处理。
3:如果后面生成的
plist_encoded
有问题,可以使用命令base64 -D -i plist_encoded -o decoded.plist
把文件转成明文的plist文件查看里面的各种信息是否有问题
到https://identity.apple.com/pushcert/提交生成的
plist_encoded
。如果文件有问题会提示无效的文件,如果一切正常会生成我们最后需要的MDM_Certificate.pem
的证书
对MDM_Certificate.pem证书进行格式化转换
在终端中输入openssl pkcs12 -export -in MDM_Certificate.pem -out MDM_Certificate.p12 -inkey customerPrivateKey.pem
即可得到MDM_Certificate.p12
文件,后续把这个文件丢给服务端就可以了
python脚本
# http://www.softhinker.com/in-the-news/iosmdmvendorcsrsigning
# fuck java
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import argparse
from plistlib import writePlistToString
import os
import subprocess
from base64 import b64encode
import sys
import urllib2
def p(s):
sys.stdout.write(s)
sys.stdout.flush()
def mdm_vendor_sign():
"""
This utility will create a properly encoded certifiate signing request
that you can upload to identity.apple.com/pushcert
"""
parser = argparse.ArgumentParser(description=mdm_vendor_sign.__doc__)
parser.add_argument('--key', help='Private key', required=True)
parser.add_argument('--csr', help='Certificate signing request', required=True)
parser.add_argument('--mdm', help='MDM vendor certificate', required=True)
parser.add_argument('--root', help='AppleIncRootCertificate', required=True)
parser.add_argument('--WWDR', help='AppleWWDRCA', required=True)
parser.add_argument('--out', help='Output filename', required=False)
#.....
cli_args = vars(parser.parse_args())
# Verify CSR
# openssl req -text -noout -verify -in CSR.csr
p('Verifying %s ... ' % cli_args['csr'])
print("aaa", cli_args['csr'])
print("bbb", os.path.abspath(cli_args['csr']))
print("ccc", os.path.exists(cli_args['csr']))
csr_file = open(cli_args['csr']).read()
args = ['openssl', 'req', '-noout', '-verify', '-quiet']
command = subprocess.Popen(args, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT)
output, error = command.communicate(input = csr_file)
print(repr(output))
print("OpenSSL", output)
print("OpenSSL", error)
if output.rstrip().split('\n')[0] == 'Certificate request self-signature verify OK':
p('OK\n')
else:
p('FAILED1\n')
return
# Verify private key
# openssl rsa -in privateKey.key -check
p('Verifying %s ... ' % cli_args['key'])
key_file = open(cli_args['key']).read()
args = ['openssl', 'rsa', '-check', '-noout' ]
command = subprocess.Popen(args, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT)
output, error = command.communicate(input = key_file)
if output.rstrip().split('\n')[0] == 'RSA key ok':
p('OK\n')
else:
p('FAILED2\n\n')
print """If you don't have the plain private key already, you need
to extract it from the pkcs12 file...
First convert to PEM
openssl pkcs12 -in filename.p12 -nocerts -out key.pem
Then export the certificate file from the pfx file
openssl pkcs12 -in filename.pfx -clcerts -nokeys -out cert.pem
Lastly Remove the passphrase from the private key
openssl rsa -in key.pem -out the_private_key.key
"""
return
# Verify MDM vendor certificate
# openssl x509 -noout -in mdm.cer -inform DER
p('Verifying %s ... ' % cli_args['mdm'])
mdm_cert_file = open(cli_args['mdm'],'rb').read() # Binary read
args = ['openssl', 'x509', '-noout', '-inform', 'DER' ]
command = subprocess.Popen(args, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT)
output, error = command.communicate(input = mdm_cert_file)
if len(output) == 0:
p('OK\n')
else:
p('FAILED\n')
return
# Convert CSR to DER format
# openssl req -inform pem -outform der -in customer.csr -out customer.der
p('Converting %s to DER format... ' % cli_args['csr'])
args = ['openssl', 'req', '-inform', 'pem', '-outform', 'der' ]
command = subprocess.Popen(args, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT)
output, error = command.communicate(input = csr_file)
if error:
p('FAILED\n')
return
p('OK\n')
csr_der = output
csr_b64 = b64encode(csr_der)
# Sign the CSR with the private key
# openssl sha1 -sign private_key.key -out signed_output.rsa data_to_sign.txt
p('Signing CSR with private key... ')
args = ['openssl', 'sha1', '-sign', cli_args['key'] ]
command = subprocess.Popen(args, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT)
output, error = command.communicate(input = csr_der)
if error:
p('FAILED\n')
return
p('OK\n')
signature_bytes = output
signature = b64encode(signature_bytes)
def cer_to_pem(cer_data):
# openssl x509 -inform der -in mdm.cer -out mdm.pem
# -in and -out flags are handled by STDIN and STDOUT
args = ['openssl', 'x509', '-inform', 'der' ]
command = subprocess.Popen(args, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT)
output, error = command.communicate(input = cer_data)
if error:
p('Error converting from cer to pem: %s' % error)
return output
# TODO : Probably should verify these too
p('Downloading WWDR intermediate certificate...')
# intermediate_cer = urllib2.urlopen('https://www.apple.com/certificateauthority/AppleWWDRCAG3.cer').read()
# p(' converting to pem...')
# intermediate_pem = cer_to_pem(intermediate_cer)
# p('OK\n')
# p('Downloading Apple Root Certificate...')
# root_cer = urllib2.urlopen('http://www.apple.com/appleca/AppleIncRootCertificate.cer').read()
# p(' converting to pem...')
# root_pem = cer_to_pem(root_cer)
# p('OK\n')
#.....
root_cert_file = open(cli_args['root'],'rb').read()
wwdr_cert_file = open(cli_args['WWDR'],'rb').read()
intermediate_pem = cer_to_pem(wwdr_cert_file)
root_pem = cer_to_pem(root_cert_file)
mdm_pem = cer_to_pem(mdm_cert_file)
print("\n=== MDM Vendor Certificate (PEM) ===")
print(mdm_pem)
print("\n=== Intermediate (WWDR) Certificate (PEM) ===")
print(intermediate_pem)
print("\n=== Root (Apple) Certificate (PEM) ===")
print(root_pem)
p('Finishing...')
plist_dict = dict(
PushCertRequestCSR = csr_b64,
PushCertCertificateChain = mdm_pem + intermediate_pem + root_pem,
PushCertSignature = signature
)
plist_xml = writePlistToString(plist_dict)
plist_b64 = b64encode(plist_xml)
output_filename = cli_args['out'] if cli_args['out'] else 'plist_encoded'
write_path = os.path.join(os.getcwd(), output_filename)
output = open(write_path, 'wb')
output.write(plist_b64)
output.close()
p('DONE\n\nGo upload file \'%s\' to identity.apple.com/pushcert !\n' % output_filename)
if __name__=="__main__":
mdm_vendor_sign()</pre>
参考_