Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 78ca64f

Browse files
committed
Add mdmcert.download code
1 parent c36fb5a commit 78ca64f

6 files changed

Lines changed: 219 additions & 8 deletions

File tree

commandment/app.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
from flask import Flask
77
from .mdm import mdm_app
88
from .admin import admin_app
9+
from .mdmcert import admin_mdmcert_app
910

1011
def create_app():
1112
app = Flask(__name__)
1213

1314
app.register_blueprint(mdm_app)
1415
app.register_blueprint(admin_app, url_prefix='/admin')
16+
app.register_blueprint(admin_mdmcert_app, url_prefix='/admin/mdmcert')
1517

1618
from .database import db_session
1719

commandment/mdmcert.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
'''
2+
Copyright (c) 2015 Jesse Peterson
3+
Licensed under the MIT license. See the included LICENSE.txt file for details.
4+
'''
5+
6+
from flask import Blueprint, Response, render_template, request, make_response
7+
from flask import redirect
8+
# Response, request, current_app, abort, make_response
9+
from .pki.ca import get_ca, PushCertificate
10+
from .pki.x509 import CertificateRequest
11+
from .models import (Certificate as DBCertificate,
12+
PrivateKey as DBPrivateKey,
13+
CertificateRequest as DBCertificateRequest)
14+
from .database import db_session
15+
import json
16+
from base64 import b64encode
17+
import urllib2
18+
from M2Crypto import BIO, SMIME, X509, m2
19+
import datetime
20+
21+
MDMCERT_REQ_URL = 'https://mdmcert.download/api/v1/signrequest'
22+
23+
# PLEASE! Do not take this key and use it for another product/project. It's
24+
# only for Commandment's use. If you'd like to get your own (free!) key
25+
# contact the mdmcert.download administrators and get your own key for your
26+
# own project/product. We're trying to keep statistics on which products are
27+
# requesting certs (per Apple T&C). Don't force Apple's hand and
28+
# ruin it for everyone!
29+
MDMCERT_API_KEY = 'b742461ff981756ca3f924f02db5a12e1f6639a9109db047ead1814aafc058dd'
30+
31+
CERT_REQ_TYPE = 'mdmcert.pushcert'
32+
33+
def submit_mdmcert_request(email, pem_csr, pem_enc):
34+
mdmcert_dict = {
35+
'csr': b64encode(pem_csr),
36+
'email': email,
37+
'key': MDMCERT_API_KEY,
38+
'encrypt': b64encode(pem_enc),
39+
}
40+
41+
req = urllib2.Request(
42+
MDMCERT_REQ_URL,
43+
json.dumps(mdmcert_dict),
44+
{'Content-Type': 'application/json',
45+
'User-Agent': 'coMmanDMent/0.1'})
46+
47+
f = urllib2.urlopen(req)
48+
resp = f.read()
49+
f.close()
50+
51+
return json.loads(resp)
52+
53+
54+
class FixedLocationResponse(Response):
55+
# override Werkzeug default behaviour of "fixing up" once-non-compliant
56+
# relative location headers. now permitted in rfc7231 sect. 7.1.2
57+
autocorrect_location_header = False
58+
59+
admin_mdmcert_app = Blueprint('admin_mdmcert_app', __name__)
60+
61+
@admin_mdmcert_app.route('/')
62+
def index():
63+
return render_template('admin/mdmcert/new.html')
64+
65+
@admin_mdmcert_app.route('/submit', methods=['POST'])
66+
def submit():
67+
mdm_ca = get_ca()
68+
69+
csr, csr_pk = CertificateRequest.with_new_private_key(CN='mdmcert.download')
70+
71+
db_csr = DBCertificateRequest.from_x509(csr, CERT_REQ_TYPE)
72+
db_pk = DBPrivateKey.from_x509(csr_pk)
73+
74+
db_pk.certificate_requests.append(db_csr)
75+
76+
db_session.add(db_csr)
77+
db_session.add(db_pk)
78+
79+
db_session.commit()
80+
81+
email = request.form.get('email')
82+
83+
res = submit_mdmcert_request(email, csr.to_pem(), mdm_ca.get_cacert().to_pem())
84+
print res.get('result')
85+
if res.get('result') != 'success':
86+
return 'Error! ' + res.get('reason', '')
87+
88+
return 'Submitted!'
89+
90+
from binascii import unhexlify
91+
92+
@admin_mdmcert_app.route('/upload_encr', methods=['POST'])
93+
def upload_encr():
94+
mdm_ca = get_ca()
95+
96+
upl_encrypt = unhexlify(request.files['upload_encr_file'].stream.read())
97+
98+
s = SMIME.SMIME()
99+
100+
bio = BIO.MemoryBuffer(upl_encrypt)
101+
102+
s.load_key_bio(
103+
BIO.MemoryBuffer(mdm_ca.get_private_key().to_pem()),
104+
BIO.MemoryBuffer(mdm_ca.get_cacert().to_pem()))
105+
106+
p7 = SMIME.PKCS7(m2.pkcs7_read_bio_der(bio._ptr()))
107+
108+
out = s.decrypt(p7)
109+
110+
response = make_response(out)
111+
response.headers['Content-Type'] = 'application/octet-stream'
112+
response.headers['Content-Disposition'] = 'attachment; filename=mdm_signed_request.%s.plist.b64' % datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
113+
return response
114+
115+
@admin_mdmcert_app.route('/upload_cert', methods=['POST'])
116+
def upload_cert():
117+
pcert = PushCertificate.from_pem(request.files['upload_cert_file'].stream.read())
118+
119+
cert_modulus = pcert._get_pubkey().get_modulus()
120+
121+
q = db_session.query(DBCertificateRequest).filter(DBCertificateRequest.req_type == CERT_REQ_TYPE)
122+
for cr in q:
123+
req = cr.to_x509()
124+
if req._get_pubkey().get_modulus() == cert_modulus:
125+
db_cert = DBCertificate.from_x509(pcert, 'mdm.pushcert')
126+
127+
db_pk = cr.privatekeys[0]
128+
129+
db_pk.certificates.append(db_cert)
130+
131+
db_session.add(db_cert)
132+
133+
db_session.commit()
134+
135+
return redirect('/admin/certificates', Response=FixedLocationResponse)
136+
137+
138+
return 'no matching CSR found'

commandment/models.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
from .database import JSONEncodedDict, Base, or_, and_
1212
from profiles.mdm import MDM_AR__ALL
1313

14-
from .pki.x509 import (Certificate as X509Certificate, PrivateKey as X509PrivateKey)
14+
from .pki.x509 import (Certificate as X509Certificate,
15+
PrivateKey as X509PrivateKey,
16+
CertificateRequest as X509CertificateRequest)
1517

1618
CERT_TYPES = {
1719
'mdm.pushcert': {
@@ -45,13 +47,19 @@
4547
Column('privatekey_id', Integer, ForeignKey('rsa_private_key.id')),
4648
)
4749

50+
certreq_private_key_assoc = Table('certreq_private_key', Base.metadata,
51+
Column('certreq_id', Integer, ForeignKey('certificate_request.id')),
52+
Column('privatekey_id', Integer, ForeignKey('rsa_private_key.id')),
53+
)
54+
4855
class PrivateKey(Base):
4956
__tablename__ = 'rsa_private_key'
5057

5158
id = Column(Integer, primary_key=True)
5259
pem_key = Column(Text, nullable=False)
5360

5461
certificates = relationship('Certificate', secondary=certificate_private_key_assoc, backref='privatekeys')
62+
certificate_requests = relationship('CertificateRequest', secondary=certreq_private_key_assoc, backref='privatekeys')
5563

5664
def to_x509(self, key_type=X509PrivateKey):
5765
return key_type.from_pem(self.pem_key)
@@ -70,10 +78,16 @@ class CertificateRequest(Base):
7078
subject = Column(Text, nullable=True)
7179
pem_request = Column(Text, nullable=False)
7280

73-
certreq_private_key_assoc = Table('certreq_private_key', Base.metadata,
74-
Column('certreq_id', Integer, ForeignKey('certificate_request.id')),
75-
Column('privatekey_id', Integer, ForeignKey('rsa_private_key.id')),
76-
)
81+
def to_x509(self, request_type=X509CertificateRequest):
82+
return request_type.from_pem(self.pem_request)
83+
84+
@classmethod
85+
def from_x509(cls, req, req_type):
86+
newcls = cls()
87+
newcls.subject = req.get_subject_text()
88+
newcls.req_type = req_type
89+
newcls.pem_request = req.to_pem()
90+
return newcls
7791

7892
class Certificate(Base):
7993
__tablename__ = 'certificate'

commandment/pki/x509.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,13 @@ def from_der(cls, data):
8686
assert newcls._req.verify(newcls._req.get_pubkey()) == 1
8787
return newcls
8888

89+
@classmethod
90+
def from_pem(cls, data):
91+
newcls = cls.__new__(cls)
92+
newcls._req = X509.load_request_string(str(data), format=X509.FORMAT_PEM)
93+
assert newcls._req.verify(newcls._req.get_pubkey()) == 1
94+
return newcls
95+
8996
def to_pem(self):
9097
return self._req.as_pem()
9198

@@ -114,6 +121,9 @@ def get_pubkey_fingerprint(self, digest=None):
114121
def _m2_req(self):
115122
return self._req
116123

124+
def get_subject_text(self):
125+
return self._get_subject().as_text()
126+
117127
class CertificatePolicy(object):
118128
ca = False
119129

commandment/templates/admin/certificates/index.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
<h1>Admin - Certificates</h1>
66

7-
<p>
8-
<a href="/admin/certificates/new">[ Generate New ]</a>
9-
</p>
7+
<ul>
8+
<li><a href="/admin/certificates/new">[ Generate New ]</a></li>
9+
<li><a href="/admin/mdmcert/">[ Generate New mdmcert.download Request ]</a></li>
10+
</ul>
1011

1112
<table border="1">
1213

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{% set section = 'cert' %}
2+
{% extends "admin/layout.html" %}
3+
{% block content %}
4+
5+
<a href="https://mdmcert.download/instructions">See instructions here</a>
6+
7+
<h1>
8+
(Step 5) Generate New mdmcert.download Request
9+
</h1>
10+
11+
<form id="new" action="submit" method="POST">
12+
13+
<p>Make sure you've <em>already</em> registered on the <a href="https://mdmcert.download/">mdmcert.download</a> site!</p>
14+
<br>
15+
<label for="email">Email address</label>
16+
<input type="input" name="email" id="email" form="new">
17+
<button type="submit" form="new" value="Submit">Submit</button>
18+
19+
</form>
20+
21+
<h1>
22+
(Step 7) Upload MDM Push Cert from Identity.Apple.com
23+
</h1>
24+
<form id="upload_encr" action="upload_encr" method="POST" enctype="multipart/form-data">
25+
26+
<label for="upload_encr_widget">mdmcert Encrypted Cert Req (from email) - *.plist.b64.p7 file</label>
27+
<input type="file" id="upload_encr_widget" form="upload_encr" name="upload_encr_file">
28+
29+
<button type="submit" form="upload_encr" value="Submit">Upload Encrypted Request</button>
30+
31+
</form>
32+
33+
34+
<h1>
35+
(Step 9) Upload MDM Push Cert from Identity.Apple.com
36+
</h1>
37+
<form id="upload_cert" action="upload_cert" method="POST" enctype="multipart/form-data">
38+
39+
<label for="upload_cert_widget">mdmcert Push Certificate (from identity.apple.com)</label>
40+
<input type="file" form="upload_cert" id="upload_cert_widget" name="upload_cert_file">
41+
42+
<button type="submit" form="upload_cert" value="Submit">Upload Push Cert</button>
43+
44+
</form>
45+
46+
{% endblock %}

0 commit comments

Comments
 (0)