def verify(self, data, signature, ca_name, detached=True): """Verify detached SMIME signature. Requires CA cert. Returns tuple (data, signer_details_dict). """ sm = self.get_verify_ctx(ca_name) # init data bsign = BIO.MemoryBuffer(signature) if RAW_MESSAGES: pk = load_pkcs7_bio_der(bsign) else: pk = SMIME.load_pkcs7_bio(bsign) # check signature if detached: bdata = BIO.MemoryBuffer(data) data2 = sm.verify(pk, bdata, flags = SMIME.PKCS7_BINARY | SMIME.PKCS7_DETACHED) assert data2 == data elif data: raise Exception('Have data when detached=False') else: data2 = sm.verify(pk, flags = SMIME.PKCS7_BINARY) return data2, self.get_signature_info(sm, pk)
def verify_payload(msg, raw_sig, cert, ca_cert, verify_cert): # Load the public certificate of the signer signer = SMIME.SMIME() signer_key = X509.X509_Stack() signer_key.push(X509.load_cert(cert)) signer.set_x509_stack(signer_key) signer_store = X509.X509_Store() signer_store.load_info(ca_cert) signer.set_x509_store(signer_store) # Extract the pkcs7 signature and the data if raw_sig: raw_sig.strip() sig = "-----BEGIN PKCS7-----\n%s\n-----END PKCS7-----\n" % raw_sig.replace('\r\n', '\n') p7 = SMIME.load_pkcs7_bio(BIO.MemoryBuffer(sig)) data_bio = BIO.MemoryBuffer(msg) else: p7, data_bio = SMIME.smime_load_pkcs7_bio(BIO.MemoryBuffer(msg)) # Verify the signature against the message if verify_cert: signer.verify(p7, data_bio) else: # Don't verify the signer certificate if this flag is set signer.verify(p7, data_bio, SMIME.PKCS7_NOVERIFY)
def test_load_bad(self): s = SMIME.SMIME() with self.assertRaises(EVP.EVPError): s.load_key('tests/signer.pem', 'tests/signer.pem') with self.assertRaises(BIO.BIOError): SMIME.load_pkcs7('nosuchfile-dfg456') with self.assertRaises(SMIME.PKCS7_Error): SMIME.load_pkcs7('tests/signer.pem') with self.assertRaises(SMIME.PKCS7_Error): SMIME.load_pkcs7_bio(BIO.MemoryBuffer(b'no pkcs7')) with self.assertRaises(SMIME.SMIME_Error): SMIME.smime_load_pkcs7('tests/signer.pem') with self.assertRaises(SMIME.SMIME_Error): SMIME.smime_load_pkcs7_bio(BIO.MemoryBuffer(b'no pkcs7'))
def plist_from_pkcs7(pkcs7): """Extract a plist from PKCS7 encoded data.""" # DEP request # base64 encode the DER data, and wrap in a PEM-ish format for SMIME.load_pkcs7_bio() req_data = base64_to_pem('PKCS7', pkcs7) p7_bio = BIO.MemoryBuffer(str(req_data)) pkcs7 = SMIME.load_pkcs7_bio(p7_bio) p7_signers = pkcs7.get0_signers(X509.X509_Stack()) signer = SMIME.SMIME() signer.set_x509_store(X509.X509_Store()) signer.set_x509_stack(p7_signers) # TODO/XXX: not verifying ANY certificates! # # spec says we should verify against the "Apple Root CA" and that this # CMS message contains all intermediates to do that verification. # M2Crypto has no way to get at all the intermediate certificates to # do this manually we'd need to extract all of the certificates and # verify the chain aginst it. Note as of 2016-03-14 on a brand new # iPad Apple was including an expired certificate in this chain. Note # also that at least one of the intermediate certificates had a # certificate purpose apparently not appropraite for CMS/SMIME # verification. For now just verify with no CA and skip any # verification. plist_text = signer.verify(pkcs7, None, flags=SMIME.PKCS7_NOVERIFY) plist = plistlib.readPlistFromString(plist_text) return plist
def verify_pkcs7(pkcs7_sig, document): try: # raw_sig = pkcs7_sig raw_sig = str(pkcs7_sig).encode('ascii') msg = document sm_obj = SMIME.SMIME() x509 = X509.load_cert(os.path.join(config.APP_STATIC, 'AWSpubkey')) # public key cert used by the remote # client when signing the message sk = X509.X509_Stack() sk.push(x509) sm_obj.set_x509_stack(sk) st = X509.X509_Store() st.load_info(os.path.join(config.APP_STATIC, 'AWSpubkey')) # Public cert for the CA which signed # the above certificate sm_obj.set_x509_store(st) # re-wrap signature so that it fits base64 standards cooked_sig = '\n'.join(raw_sig[pos:pos + 76] for pos in xrange(0, len(raw_sig), 76)) # cooked_sig = raw_sig # now, wrap the signature in a PKCS7 block sig = ("-----BEGIN PKCS7-----\n" + cooked_sig + "\n-----END PKCS7-----").encode('ascii') # and load it into an SMIME p7 object through the BIO I/O buffer: buf = BIO.MemoryBuffer(sig) p7 = SMIME.load_pkcs7_bio(buf) # finally, try to verify it: if dict(json.loads(sm_obj.verify(p7))) == dict(json.loads(msg)): return True else: return False except Exception as e: raise Exception("INVALID CLIENT MESSAGE SIGNATURE")
def verify_mdm_signature(mdm_sig, req_data): '''Verify the client's supplied MDM signature and return the client certificate included in the signature.''' pkcs7_pem_sig = base64_to_pem('PKCS7', mdm_sig) p7_bio = BIO.MemoryBuffer(str(pkcs7_pem_sig)) p7 = SMIME.load_pkcs7_bio(p7_bio) p7_signers = p7.get0_signers(X509.X509_Stack()) mdm_ca = get_ca() # can probably directly use m2 certificate here ca_x509_bio = BIO.MemoryBuffer(mdm_ca.get_cacert().get_pem()) ca_x509 = X509.load_cert_bio(ca_x509_bio) cert_store = X509.X509_Store() cert_store.add_x509(ca_x509) signer = SMIME.SMIME() signer.set_x509_store(cert_store) signer.set_x509_stack(p7_signers) # NOTE: may need to do something special if we can't cleanly convert # to string from Unicode. must be byte-accurate as the signature won't # match otherwise data_bio = BIO.MemoryBuffer(req_data) # will raise an exception if verification fails # if no CA certificate we get an: # PKCS7_Error: certificate verify error signer.verify(p7, data_bio) return p7_signers[0].as_pem()
def verify_mdm_signature(mdm_sig, req_data): '''Verify the client's supplied MDM signature and return the client certificate included in the signature.''' p7_bio = BIO.MemoryBuffer(str(mdm_sig)) p7 = SMIME.load_pkcs7_bio(p7_bio) p7_signers = p7.get0_signers(X509.X509_Stack()) mdm_ca = get_ca() # can probably directly use m2 certificate here ca_x509_bio = BIO.MemoryBuffer(mdm_ca.get_cacert().to_pem()) ca_x509 = X509.load_cert_bio(ca_x509_bio) cert_store = X509.X509_Store() cert_store.add_x509(ca_x509) signer = SMIME.SMIME() signer.set_x509_store(cert_store) signer.set_x509_stack(p7_signers) # NOTE: may need to do something special if we can't cleanly convert # to string from Unicode. must be byte-accurate as the signature won't # match otherwise data_bio = BIO.MemoryBuffer(req_data) # will raise an exception if verification fails # if no CA certificate we get an: # PKCS7_Error: certificate verify error signer.verify(p7, data_bio) return p7_signers[0].as_pem()
def test_load_bad(self): s = SMIME.SMIME() with self.assertRaises(EVP.EVPError): s.load_key('tests/signer.pem', 'tests/signer.pem') with self.assertRaises(BIO.BIOError): SMIME.load_pkcs7('nosuchfile-dfg456') with self.assertRaises(SMIME.PKCS7_Error): SMIME.load_pkcs7('tests/signer.pem') with self.assertRaises(SMIME.PKCS7_Error): SMIME.load_pkcs7_bio(BIO.MemoryBuffer('no pkcs7')) with self.assertRaises(SMIME.SMIME_Error): SMIME.smime_load_pkcs7('tests/signer.pem') with self.assertRaises(SMIME.SMIME_Error): SMIME.smime_load_pkcs7_bio(BIO.MemoryBuffer('no pkcs7'))
def verify_sig_cert(sig, sender, local_domain): import certvld p7 = SMIME.load_pkcs7_bio(BIO.MemoryBuffer(sig)) certs = p7.get0_signers(X509.X509_Stack()) if len(certs) == 0: return False for cert in certs: if certvld.verify_cert(cert, local_domain, sender) and certvld.verify_binding(cert, sender, sender.partition('@')[2], True): return True return False
def unlock(self, passphrase): s = SMIME.SMIME() try: s.load_key(settings.RSA_KEYPAIR, settings.RSA_CERTIFICATE, lambda *args: passphrase) except: raise Lockable.InvalidPassphrase( 'The supplied passhrase was incorrect.') p7 = SMIME.load_pkcs7_bio(BIO.MemoryBuffer(self.ciphertext)) plaintext = s.decrypt(p7) self.plaindict = loads(plaintext)
def calculate_difference(FILENAME): signature_date = None certificate_date = None certificate_subject = None try: # open an apk or jar as a ZipFile Z = zipfile.ZipFile(open(FILENAME, 'rb')) except: print 'WARNING: Not a ZipFile', FILENAME return signature_date, certificate_date, certificate_subject for name in Z.namelist(): # certificate file if (name.endswith("DSA") or (name.endswith("RSA"))): try: with Z.open(name) as f: fileBytes = f.read() except: print 'WARNING: Bad ZipFile', name return signature_date, certificate_date, certificate_subject # encode binary data to base64 and wrap it in a PKCS7 envelop sig = '-----BEGIN PKCS7-----\n' + fileBytes.encode( 'base64') + '-----END PKCS7-----\n' # load certificate into a p7 object through BIO I/O buffer: buf = BIO.MemoryBuffer(sig) p7 = SMIME.load_pkcs7_bio(buf) sk = X509.X509_Stack() signers = p7.get0_signers(sk) # get X509 certificate certificate = signers[0] dateString = certificate.get_not_before() certificate_date = datetime.datetime.strptime( str(dateString), '%b %d %H:%M:%S %Y %Z') certificate_subject = certificate.get_subject() # signature file if (name.endswith("SF")): try: signature_date = datetime.datetime( *Z.getinfo(name).date_time[0:6]) except ValueError: signature_date = datetime.datetime( Z.getinfo(name).date_time[0], 1, 1) Z.close() return signature_date, certificate_date, certificate_subject
def _run(self, scanObject, result, depth, args): moduleResult = [] flags = [] buffer = scanObject.buffer cert = None try: #scanObject.addMetadata(self.module_name, key, value) input_bio = BIO.MemoryBuffer(buffer) #Check for PEM or DER if buffer[:1] == "0": #DER p7 = SMIME.PKCS7(m2.pkcs7_read_bio_der(input_bio._ptr()), 1) else: #PEM p7 = SMIME.load_pkcs7_bio(input_bio) certs = p7.get0_signers(X509.X509_Stack()) #some pkcs7's should have more than one certificate in them. openssl can extract them handily. #openssl pkcs7 -inform der -print_certs -in MYKEY.DSA i = 0 for cert in certs: cert_filename = "%x.der" % (cert.get_serial_number()) moduleResult.append( ModuleObject( buffer=cert.as_der(), externalVars=ExternalVars(filename=cert_filename))) i = i + 1 except (QuitScanException, GlobalScanTimeoutError, GlobalModuleTimeoutError): raise except: exc_type, exc_value, exc_traceback = sys.exc_info() logging.exception("Error parsing cert in " + str(get_scanObjectUID(getRootObject(result)))) ugly_error_string = str(exc_value) #nicer_error_string = string.split(string.split(ugly_error_string,":")[4])[0] nicer_error_string = ugly_error_string.split(":")[4].split()[0] scanObject.addFlag("pkcs7:err:" + nicer_error_string) return moduleResult
def _run(self, scanObject, result, depth, args): moduleResult = [] flags = [] buffer = scanObject.buffer cert = None try: #scanObject.addMetadata(self.module_name, key, value) input_bio = BIO.MemoryBuffer(buffer) #Check for PEM or DER if buffer[:1] == "0": #DER p7 = SMIME.PKCS7(m2.pkcs7_read_bio_der(input_bio._ptr()), 1) else: #PEM p7 = SMIME.load_pkcs7_bio(input_bio) certs = p7.get0_signers(X509.X509_Stack()) #some pkcs7's should have more than one certificate in them. openssl can extract them handily. #openssl pkcs7 -inform der -print_certs -in MYKEY.DSA i=0 for cert in certs: cert_filename = "%x.der" % (cert.get_serial_number()) moduleResult.append(ModuleObject(buffer=cert.as_der(),externalVars=ExternalVars(filename=cert_filename))) i = i + 1 except (QuitScanException, GlobalScanTimeoutError, GlobalModuleTimeoutError): raise except: exc_type, exc_value, exc_traceback = sys.exc_info() logging.exception("Error parsing cert in "+str(get_scanObjectUID(getRootObject(result)))) ugly_error_string = str(exc_value) #nicer_error_string = string.split(string.split(ugly_error_string,":")[4])[0] nicer_error_string = ugly_error_string.split(":")[4].split()[0] scanObject.addFlag("pkcs7:err:"+nicer_error_string) return moduleResult
def decrypt(self, ciphtext, receiver_name): """Decrypt message. Requires private key and it's cert. Returns decrypted message. """ sm = self.get_decrypt_ctx(receiver_name) # decrypt bdata = BIO.MemoryBuffer(ciphtext) if RAW_MESSAGES: pk = load_pkcs7_bio_der(bdata) else: pk = SMIME.load_pkcs7_bio(bdata) return sm.decrypt(pk, SMIME.PKCS7_BINARY)
def verify_payload(msg, raw_sig, cert, ca_cert, verify_cert): signer = SMIME.SMIME() signerKey = X509.X509_Stack() signerKey.push(X509.load_cert(cert)) signer.set_x509_stack(signerKey) signerStore = X509.X509_Store() signerStore.load_info(ca_cert) signer.set_x509_store(signerStore) if raw_sig: raw_sig.strip() sig = "-----BEGIN PKCS7-----\n%s\n-----END PKCS7-----\n"%raw_sig.replace('\r\n','\n') p7 = SMIME.load_pkcs7_bio(BIO.MemoryBuffer(sig)) data_bio = BIO.MemoryBuffer(msg) if verify_cert: signer.verify(p7, data_bio) else: signer.verify(p7, data_bio,SMIME.PKCS7_NOVERIFY) else: p7, data_bio = SMIME.smime_load_pkcs7_bio(BIO.MemoryBuffer(msg)) signer.verify(p7, data_bio)
def verify_pkcs7(pkcs7_sig, document): try: # raw_sig = pkcs7_sig raw_sig = str(pkcs7_sig).encode('ascii') msg = document sm_obj = SMIME.SMIME() x509 = X509.load_cert( os.path.join(config.APP_STATIC, 'AWSpubkey')) # public key cert used by the remote # client when signing the message sk = X509.X509_Stack() sk.push(x509) sm_obj.set_x509_stack(sk) st = X509.X509_Store() st.load_info( os.path.join(config.APP_STATIC, 'AWSpubkey')) # Public cert for the CA which signed # the above certificate sm_obj.set_x509_store(st) # re-wrap signature so that it fits base64 standards cooked_sig = '\n'.join(raw_sig[pos:pos + 76] for pos in xrange(0, len(raw_sig), 76)) # cooked_sig = raw_sig # now, wrap the signature in a PKCS7 block sig = ("-----BEGIN PKCS7-----\n" + cooked_sig + "\n-----END PKCS7-----").encode('ascii') # and load it into an SMIME p7 object through the BIO I/O buffer: buf = BIO.MemoryBuffer(sig) p7 = SMIME.load_pkcs7_bio(buf) # finally, try to verify it: if dict(json.loads(sm_obj.verify(p7))) == dict(json.loads(msg)): return True else: return False except Exception as e: raise Exception("INVALID CLIENT MESSAGE SIGNATURE")
def verifySignature(signaturePkcs7, hash, chainOfTrust): # see also in AM: src/crypto-lib/signature.cpp / Signature::verify() s = SMIME.SMIME() bioSignature = BIO.MemoryBuffer(data=base64.decodestring(signaturePkcs7)) signature = SMIME.load_pkcs7_bio(bioSignature) bioHash = BIO.MemoryBuffer(data=hash) certChain = X509.X509_Store() for trustedCert in chainOfTrust: bioCert = BIO.MemoryBuffer(data=trustedCert) while len(bioCert): cert = X509.load_cert_bio(bioCert, X509.FORMAT_PEM) certChain.add_x509(cert) s.set_x509_store(certChain) s.set_x509_stack(X509.X509_Stack()) s.verify(signature, bioHash, SMIME.PKCS7_NOCHAIN)
def verifySignature(signaturePkcs7, hash, chainOfTrust): # see also in AM: src/crypto-lib/signature.cpp / Signature::verify() s = SMIME.SMIME() bioSignature = BIO.MemoryBuffer(data = base64.decodestring(signaturePkcs7)) signature = SMIME.load_pkcs7_bio(bioSignature) bioHash = BIO.MemoryBuffer(data = hash) certChain = X509.X509_Store() for trustedCert in chainOfTrust: bioCert = BIO.MemoryBuffer(data = trustedCert) while len(bioCert): cert = X509.load_cert_bio(bioCert, X509.FORMAT_PEM) certChain.add_x509(cert) s.set_x509_store(certChain) s.set_x509_stack(X509.X509_Stack()) s.verify(signature, bioHash, SMIME.PKCS7_NOCHAIN)
def validate_smime(self, msg_part, ): ''' Validates SMIME certs :param msg_part: email.message :return signer_certs: valid signer certs ''' _dashes = '-----' if self._smime is None: self.__setup_smimesetup() if msg_part.get_content_type() in self.SMIME_CONTENT_TYPES: with TemporaryFile('r+b') as fh: fh.write('%sBEGIN PKCS7%s\n%s\n%sEND PKCS7%s' % ( _dashes, _dashes, msg_part.get_payload(), _dashes, _dashes )) fh.seek(0) pfh = BIO.File(fh) p7 = SMIME.load_pkcs7_bio(pf) sk3 = p7.get0_signers(X509.X509_Stack()) if len(sk3) == 0: raise [] signer_certs = [] for cert in sk3: signer_certs.append( "%sBEGIN CERTIFICATE%s\n%s\n%sEND CERTIFICATE%s" % ( _dashes, _dashes, base64.encodestring(cert.as_der()), _dashes, _dashes )) self._smime.set_x509_stack(sk3) try: v = self._smime.verify(p7) except SMIME.SMIME_Error, e: return [] if data_bio is not None and data != v: raise [] return signer_certs
from M2Crypto import BIO, SMIME pf = BIO.openfile('pkcs7-thawte.pem') p7 = SMIME.load_pkcs7_bio(pf) print p7.type(1)
def PUT(self): global current_command, last_result HIGH='[1;31m' LOW='[0;32m' NORMAL='[0;30m' i = web.data() pl = readPlistFromString(i) if 'HTTP_MDM_SIGNATURE' in web.ctx.environ: raw_sig = web.ctx.environ['HTTP_MDM_SIGNATURE'] cooked_sig = '\n'.join(raw_sig[pos:pos+76] for pos in xrange(0, len(raw_sig), 76)) signature = """ -----BEGIN PKCS7----- %s -----END PKCS7----- """ % cooked_sig #print i #print signature buf = BIO.MemoryBuffer(signature) p7 = SMIME.load_pkcs7_bio(buf) data_bio = BIO.MemoryBuffer(i) try: v = sm_obj.verify(p7, data_bio) if v: print "Client signature verified." except: print "*** INVALID CLIENT MESSAGE SIGNATURE ***" print "%sReceived %4d bytes: %s" % (HIGH, len(web.data()), NORMAL), if pl.get('Status') == 'Idle': print HIGH + "Idle Status" + NORMAL rd = current_command print "%sSent: %s%s" % (HIGH, rd['Command']['RequestType'], NORMAL) # print HIGH, rd, NORMAL elif pl.get('MessageType') == 'TokenUpdate': print HIGH+"Token Update"+NORMAL rd = do_TokenUpdate(pl) print HIGH+"Device Enrolled!"+NORMAL elif pl.get('Status') == 'Acknowledged': print HIGH+"Acknowledged"+NORMAL rd = dict() else: rd = dict() if pl.get('MessageType') == 'Authenticate': print HIGH+"Authenticate"+NORMAL elif pl.get('MessageType') == 'CheckOut': print HIGH+"Device leaving MDM"+ NORMAL else: print HIGH+"(other)"+NORMAL print HIGH, pl, NORMAL log_data(pl) log_data(rd) out = writePlistToString(rd) # print LOW, out, NORMAL q = pl.get('QueryResponses') last_result = pprint.pformat(pl) return out
raw_sig = sign_part.get_payload().replace("\n", "") # re-wrap signature so that it fits base64 standards cooked_sig = '\n'.join(raw_sig[pos:pos + 76] for pos in xrange(0, len(raw_sig), 76)) if sign_type == 'smime': # now, wrap the signature in a PKCS7 block sig = """ -----BEGIN PKCS7----- %s -----END PKCS7----- """ % cooked_sig # and load it into an SMIME p7 object through the BIO I/O buffer: buf = BIO.MemoryBuffer(sig) p7 = SMIME.load_pkcs7_bio(buf) sk = X509.X509_Stack() signers = p7.get0_signers(sk) signing_cert = signers[0] signing_cert.save(os.path.join(CERT_PATH, from_addr)) elif sign_type == 'pgp': # send POST to localost on port 11371 which points to our HTTP registration page sig = cooked_sig payload = {'email': from_addr, 'key': sig} r = requests.post("http://127.0.0.1:11371", data=payload) # format in user-specific data success_msg = file(cfg['smime']['mail_templates'] +
def PUT(self): global sm_obj, device_list HIGH='[1;31m' LOW='[0;32m' NORMAL='[0;39m' i = web.data() pl = readPlistFromString(i) print pl print web.ctx.environ if 'HTTP_MDM_SIGNATURE' in web.ctx.environ: raw_sig = web.ctx.environ['HTTP_MDM_SIGNATURE'] cooked_sig = '\n'.join(raw_sig[pos:pos+76] for pos in xrange(0, len(raw_sig), 76)) signature = '\n-----BEGIN PKCS7-----\n%s\n-----END PKCS7-----\n' % cooked_sig # Verify client signature - necessary? buf = BIO.MemoryBuffer(signature) p7 = SMIME.load_pkcs7_bio(buf) data_bio = BIO.MemoryBuffer(i) try: v = sm_obj.verify(p7, data_bio) if v: print "Client signature verified." except: print "*** INVALID CLIENT MESSAGE SIGNATURE ***" print "%sReceived %4d bytes: %s" % (HIGH, len(web.data()), NORMAL), if pl.get('Status') == 'Idle': print HIGH + "Idle Status" + NORMAL print "*FETCHING CMD TO BE SENT FROM DEVICE:", pl['UDID'] rd = device_list[pl['UDID']].sendCommand() # If no commands in queue, return empty string to avoid infinite idle loop if(not rd): return '' print "%sSent: %s%s" % (HIGH, rd['Command']['RequestType'], NORMAL) elif pl.get('MessageType') == 'TokenUpdate': print HIGH+"Token Update"+NORMAL rd = do_TokenUpdate(pl) print HIGH+"Device Enrolled!"+NORMAL elif pl.get('Status') == 'Acknowledged': global devicesAwaitingConfiguration print HIGH+"Acknowledged"+NORMAL rd = dict() # A command has returned a response # Add the response to the given device print "*CALLING ADD RESPONSE TO CMD:", pl['CommandUUID'] device_list[pl['UDID']].addResponse(pl['CommandUUID'], pl) # If we grab device information, we should also update the device info if pl.get('QueryResponses'): print "DeviceInformation should update here..." p = pl['QueryResponses'] device_list[pl['UDID']].updateInfo(p['DeviceName'], p['ModelName'], p['OSVersion']) if pl.get('RequestType') and 'SetupConfiguration' in pl.get('RequestType'): print HIGH+"Device completed SetupConfiguration command!"+NORMAL print HIGH+"Sending DeviceConfigured command now."+NORMAL queue('DeviceConfigured', pl['UDID']) print HIGH+"Removing device from devicesAwaitingConfiguration list..."+NORMAL devicesAwaitingConfiguration[pl['UDID']]['status'] = 'DeviceConfigured' devicesAwaitingConfiguration[pl['UDID']]['mtime'] = int(datetime.now().strftime('%s')) print HIGH+"Removed device from devicesAwaitingConfiguration list"+NORMAL if pl.get('RequestType') and 'DeviceConfigured' in pl.get('RequestType'): devicesAwaitingConfiguration[pl['UDID']] = { 'status': 'InstallApplication', 'mtime': int(datetime.now().strftime('%s'))} print HIGH+"Sending InstallManagementTools command now."+NORMAL sleep(5) queue('InstallManagementTools', pl['UDID']) sleep(5) if pl.get('RequestType') and 'InstallApplication' in pl.get('RequestType'): devicesAwaitingConfiguration[pl['UDID']] = { 'status': 'AllDone', 'mtime': int(datetime.now().strftime('%s'))} sleep(5) # Update pickle file with new response store_devices() else: rd = dict() if pl.get('MessageType') == 'Authenticate': print HIGH+"Authenticate"+NORMAL elif pl.get('MessageType') == 'CheckOut': print HIGH+"Device leaving MDM"+ NORMAL elif pl.get('Status') == 'Error': print "*CALLING ADD RESPONSE WITH ERROR TO CMD:", pl['CommandUUID'] device_list[pl['UDID']].addResponse(pl['CommandUUID'], pl) elif pl.get('Status') == 'NotNow': print "*CALLING ADD RESPONSE WITH NotNow TO CMD:", pl['CommandUUID'] device_list[pl['UDID']].addResponse(pl['CommandUUID'], pl) now = int(datetime.now().strftime('%s')) mtime = devicesAwaitingConfiguration[pl['UDID']]['mtime'] queue('DeviceInformation', pl['UDID']) if now - mtime > 5: if 'InstallApplication' in devicesAwaitingConfiguration[udid]['status']: print "*Sending command %s again to device %s" % (pl['RequestType'], pl['UDID']) devicesAwaitingConfiguration[pl['UDID']]['mtime'] = now queue('InstallApplication', pl['UDID']) else: print HIGH+"(other)"+NORMAL print HIGH, pl, NORMAL log_data(pl) log_data(rd) out = writePlistToString(rd) #print LOW, out, NORMAL return out
def PUT(self): global sm_obj, device_list HIGH='[1;31m' LOW='[0;32m' NORMAL='[0;39m' i = web.data() pl = readPlistFromString(i) if 'HTTP_MDM_SIGNATURE' in web.ctx.environ: raw_sig = web.ctx.environ['HTTP_MDM_SIGNATURE'] cooked_sig = '\n'.join(raw_sig[pos:pos+76] for pos in xrange(0, len(raw_sig), 76)) signature = '\n-----BEGIN PKCS7-----\n%s\n-----END PKCS7-----\n' % cooked_sig # Verify client signature - necessary? buf = BIO.MemoryBuffer(signature) p7 = SMIME.load_pkcs7_bio(buf) data_bio = BIO.MemoryBuffer(i) try: v = sm_obj.verify(p7, data_bio) if v: print "Client signature verified." except: print "*** INVALID CLIENT MESSAGE SIGNATURE ***" print "%sReceived %4d bytes: %s" % (HIGH, len(web.data()), NORMAL), if pl.get('Status') == 'Idle': print HIGH + "Idle Status" + NORMAL print "*FETCHING CMD TO BE SENT FROM DEVICE:", pl['UDID'] rd = device_list[pl['UDID']].sendCommand() # If no commands in queue, return empty string to avoid infinite idle loop if(not rd): return '' print "%sSent: %s%s" % (HIGH, rd['Command']['RequestType'], NORMAL) elif pl.get('MessageType') == 'TokenUpdate': print HIGH+"Token Update"+NORMAL rd = do_TokenUpdate(pl) print HIGH+"Device Enrolled!"+NORMAL elif pl.get('Status') == 'Acknowledged': print HIGH+"Acknowledged"+NORMAL rd = dict() # A command has returned a response # Add the response to the given device print "*CALLING ADD RESPONSE TO CMD:", pl['CommandUUID'] device_list[pl['UDID']].addResponse(pl['CommandUUID'], pl) # If we grab device information, we should also update the device info if pl.get('QueryResponses'): print "DeviceInformation should update here..." p = pl['QueryResponses'] device_list[pl['UDID']].updateInfo(p['DeviceName'], p['ModelName'], p['OSVersion']) # Update pickle file with new response store_devices() else: rd = dict() if pl.get('MessageType') == 'Authenticate': print HIGH+"Authenticate"+NORMAL elif pl.get('MessageType') == 'CheckOut': print HIGH+"Device leaving MDM"+ NORMAL elif pl.get('Status') == 'Error': print "*CALLING ADD RESPONSE WITH ERROR TO CMD:", pl['CommandUUID'] device_list[pl['UDID']].addResponse(pl['CommandUUID'], pl) else: print HIGH+"(other)"+NORMAL print HIGH, pl, NORMAL log_data(pl) log_data(rd) out = writePlistToString(rd) #print LOW, out, NORMAL return out
def main(): """ The script takes as argument the content of PATH_INFO, that is to say the string after the script name in the query URL. The format is: http://<server>/<path_to_the_script>/<file_id>~<key_id> (note the "~" in between), where: file_id: file to get key_id: identifier of the key to encrypt the file """ # SETTINGS for the key exchange server connection KEY_SERVER_URL = "http://localhost/cgi-bin/" KEY_SCRIPTNAME = "main.py/" # SETTINGS for the server authentication PRIVATE_KEY_FILENAME = "luventur_privkey.pem" # TODO (jblomer): generate a certificate for the server CERT_FILENAME = "luventurx509.cer" # Get the arguments query = os.environ["PATH_INFO"][1:] # remove the first "/" from PATH_INFO argv = query.split("~") file_id = argv[0] key_id = argv[1] # Get the key command = "get/"+key_id pkcs7 = urllib.urlopen(KEY_SERVER_URL + KEY_SCRIPTNAME + command).read() # Decript the key s = SMIME.SMIME() # Prepare an SMIME object def passphrase_fun(self): return "pass" s.load_key(PRIVATE_KEY_FILENAME, CERT_FILENAME, passphrase_fun) p7 = SMIME.load_pkcs7_bio(BIO.MemoryBuffer(pkcs7)) key = s.decrypt(p7) # CHECK IF THIS IS NEEDED # The key and the iv are currently exchanged in ascii format. # Potentially, we could use binary data, but there are compatibility issues between Python and C++. # key = binascii.unhexlify(key) # END CHECK # Get the IV from the file id iv = file_id[-33:-1] # Take 32 chars (128 bits) from the id, excluding the last (could be NaN) # iv = binascii.unhexlify(iv) # Translate to binary (see above) # Get the file # N.B.: the files are stored in the subtree "data/xy", # where xy are the first two hex digits of file_id path = "data/" + file_id[0:2] + "/" + file_id[2:] clear_text = open(path).read() # TODO: open the real file from cvmfs # Encrypt cipher = Cipher('aes_256_cbc', key, iv, op=ENC) # TODO: ask the type of the key ('aes_256_cbc') to the server v = cipher.update(clear_text) v = v + cipher.final() v = b64encode(v) # print "HTTP/1.1 200 OK" print "Content-Type: text/plain" print "Content-Length: " + str(len(v)) print # blank line: end of headers print v return
def test_load_pkcs7_bio(self): f = open(self.filename, 'rb') buf = BIO.MemoryBuffer(f.read()) f.close() assert SMIME.load_pkcs7_bio(buf).type() == SMIME.PKCS7_SIGNED
def enroll(): if request.method == 'POST' and \ request.headers.get('Content-type', '').lower() == \ 'application/pkcs7-signature': # DEP request # base64 encode the DER data, and wrap in a PEM-ish format for SMIME.load_pkcs7_bio() req_data = base64_to_pem('PKCS7', base64.b64encode(request.data)) p7_bio = BIO.MemoryBuffer(str(req_data)) p7 = SMIME.load_pkcs7_bio(p7_bio) p7_signers = p7.get0_signers(X509.X509_Stack()) signer = SMIME.SMIME() signer.set_x509_store(X509.X509_Store()) signer.set_x509_stack(p7_signers) # TODO/XXX: not verifying ANY certificates! # # spec says we should verify against the "Apple Root CA" and that this # CMS message contains all intermediates to do that verification. # M2Crypto has no way to get at all the intermediate certificates to # do this manually we'd need to extract all of the certificates and # verify the chain aginst it. Note as of 2016-03-14 on a brand new # iPad Apple was including an expired certificate in this chain. Note # also that at least one of the intermediate certificates had a # certificate purpose apparently not appropraite for CMS/SMIME # verification. For now just verify with no CA and skip any # verification. plist_text = signer.verify(p7, None, flags=SMIME.PKCS7_NOVERIFY) plist = plistlib.readPlistFromString(plist_text) try: device = db_session.query(Device).filter(or_(Device.serial_number == plist['SERIAL'], Device.udid == plist['UDID'])).one() # assign in case absent (UDID present only - not likely due to spec) device.serial_number = plist['SERIAL'] # assign in case different (e.g. changing serial UDIDs i.e. VM testing) device.udid = plist['UDID'] except NoResultFound: # should never get here, we could take benefit of the doubt and # allow the enrollment anyway, though..? current_app.logger.warn('DEP enrollment attempt but no serial number nor UDID found!') device = Device() device.serial_number = plist['SERIAL'] device.udid = plist['UDID'] # TODO: do we care about PRODUCT, VERSION, or LANGUAGE here? db_session.add(device) db_session.commit() # TODO: except too many results (e.g. perhaps both a UDID and a SERIAL found?) else: device = None mdm_ca = get_ca() config = db_session.query(MDMConfig).first() if not config: abort(500, 'No MDM configuration present; cannot generate enrollment profile') profile = Profile(config.prefix, PayloadDisplayName=config.mdm_name) ca_cert_payload = PEMCertificatePayload(config.prefix + '.mdm-ca', mdm_ca.get_cacert().get_pem(), PayloadDisplayName='MDM CA Certificate') profile.append_payload(ca_cert_payload) # find and include all mdm.webcrt's q = db_session.query(DBCertificate).filter(DBCertificate.cert_type == 'mdm.webcrt') for i, cert in enumerate(q): new_webcrt_profile = PEMCertificatePayload(config.prefix + '.webcrt.%d' % i, str(cert.pem_certificate), PayloadDisplayName='Web Server Certificate') profile.append_payload(new_webcrt_profile) db_push_cert = config.push_cert push_cert = PushCertificate.load(str(db_push_cert.pem_certificate)) topic = push_cert.get_topic() # make new device privkey, certificate then CA sign and persist certificate finally return new Identity object # NOTE: any device requesting enrollment will be generating new CA-signed # certificates. may want to gate the enrollment page by password or other # authentication # NOTE2: at a high-level there are two choices for how to get client # identity certificates onto the device: 1. embeddeding an PKCS12 # identitiy (what we're doing) and 2. using SCEP to hand off certificates. # Apple recommends the latter method but not everyone will have a SCEP # infrastructure and due to complexities we're using this methodology at # the moment new_dev_ident, db_dev_cert = mdm_ca.gen_new_device_identity() if device: # pre-assign for DEP enrollment # TODO: delete previously assigned certificate before assignment? device.certificate = db_dev_cert # random password for PKCS12 payload p12pw = urandom(20).encode('hex') # generate PCKS12 profile payload new_dev_ident_payload = PKCS12CertificatePayload(config.prefix + '.id-cert', new_dev_ident.gen_pkcs12(p12pw), p12pw, PayloadDisplayName='Device Identity Certificate') profile.append_payload(new_dev_ident_payload) new_mdm_payload = MDMPayload( config.prefix + '.mdm', new_dev_ident_payload.get_uuid(), topic, # APNs push topic config.mdm_url, config.access_rights, CheckInURL=config.checkin_url, # we can validate MDM device client certs provided via SSL/TLS. # however this requires an SSL framework that is able to do that. # alternatively we may optionally have the client digitally sign the # MDM messages in an HTTP header. this method is most portable across # web servers so we'll default to using that method. note it comes # with the disadvantage of adding something like 2KB to every MDM # request SignMessage=True, CheckOutWhenRemoved=True, ServerCapabilities=['com.apple.mdm.per-user-connections'], # per-network user & mobile account authentication (OS X extensions) PayloadDisplayName='Device Configuration and Management') profile.append_payload(new_mdm_payload) resp = make_response(profile.generate_plist()) resp.headers['Content-Type'] = PROFILE_CONTENT_TYPE return resp
# Make a MemoryBuffer of the message. # buf = BIO.MemoryBuffer('a sign of our times') buf = BIO.File(open("inputfile.txt")) # Seed the PRNG. #Rand.load_file('/dev/random', 1024) # Instantiate an SMIME object; set it up; sign the buffer. s = SMIME.SMIME() s.load_key_bio(BIO.File(open('signer.key.pem')), BIO.File(open('signer.pem'))) p7 = s.sign(buf, SMIME.PKCS7_DETACHED) out = BIO.MemoryBuffer() p7.write(out) x509 = X509.load_cert('signer.pem') sk = X509.X509_Stack() sk.push(x509) s.set_x509_stack(sk) st = X509.X509_Store() st.load_info('signer.pem') s.set_x509_store(st) p7 = SMIME.load_pkcs7_bio(out) v = s.verify(p7, BIO.File(open("inputfile.txt"))) print "VERIFY", v
def PUT(self): global current_command, last_result HIGH = '[1;31m' LOW = '[0;32m' NORMAL = '[0;30m' i = web.data() pl = readPlistFromString(i) if 'HTTP_MDM_SIGNATURE' in web.ctx.environ: raw_sig = web.ctx.environ['HTTP_MDM_SIGNATURE'] cooked_sig = '\n'.join(raw_sig[pos:pos + 76] for pos in xrange(0, len(raw_sig), 76)) signature = """ -----BEGIN PKCS7----- %s -----END PKCS7----- """ % cooked_sig #print i #print signature buf = BIO.MemoryBuffer(signature) p7 = SMIME.load_pkcs7_bio(buf) data_bio = BIO.MemoryBuffer(i) try: v = sm_obj.verify(p7, data_bio) if v: print "Client signature verified." except: print "*** INVALID CLIENT MESSAGE SIGNATURE ***" print "%sReceived %4d bytes: %s" % (HIGH, len(web.data()), NORMAL), if pl.get('Status') == 'Idle': print HIGH + "Idle Status" + NORMAL rd = current_command print "%sSent: %s%s" % (HIGH, rd['Command']['RequestType'], NORMAL) # print HIGH, rd, NORMAL elif pl.get('MessageType') == 'TokenUpdate': print HIGH + "Token Update" + NORMAL rd = do_TokenUpdate(pl) print HIGH + "Device Enrolled!" + NORMAL elif pl.get('Status') == 'Acknowledged': print HIGH + "Acknowledged" + NORMAL rd = dict() else: rd = dict() if pl.get('MessageType') == 'Authenticate': print HIGH + "Authenticate" + NORMAL elif pl.get('MessageType') == 'CheckOut': print HIGH + "Device leaving MDM" + NORMAL else: print HIGH + "(other)" + NORMAL print HIGH, pl, NORMAL log_data(pl) log_data(rd) out = writePlistToString(rd) # print LOW, out, NORMAL q = pl.get('QueryResponses') if q: redact_list = ('UDID', 'BluetoothMAC', 'SerialNumber', 'WiFiMAC', 'IMEI', 'ICCID', 'SerialNumber') for resp in redact_list: if q.get(resp): pl['QueryResponses'][resp] = '--redacted--' for top in ('UDID', 'Token', 'PushMagic', 'UnlockToken'): if pl.get(top): pl[top] = '--redacted--' last_result = pprint.pformat(pl) return out
def PUT(self): global sm_obj, device_list HIGH = '[1;31m' LOW = '[0;32m' NORMAL = '[0;39m' i = web.data() pl = readPlistFromString(i) if 'HTTP_MDM_SIGNATURE' in web.ctx.environ: raw_sig = web.ctx.environ['HTTP_MDM_SIGNATURE'] cooked_sig = '\n'.join(raw_sig[pos:pos + 76] for pos in xrange(0, len(raw_sig), 76)) signature = '\n-----BEGIN PKCS7-----\n%s\n-----END PKCS7-----\n' % cooked_sig # Verify client signature - necessary? buf = BIO.MemoryBuffer(signature) p7 = SMIME.load_pkcs7_bio(buf) data_bio = BIO.MemoryBuffer(i) try: v = sm_obj.verify(p7, data_bio) if v: print "Client signature verified." except: print "*** INVALID CLIENT MESSAGE SIGNATURE ***" print "%sReceived %4d bytes: %s" % (HIGH, len(web.data()), NORMAL), if pl.get('Status') == 'Idle': print HIGH + "Idle Status" + NORMAL print "*FETCHING CMD TO BE SENT FROM DEVICE:", pl['UDID'] rd = device_list[pl['UDID']].sendCommand() # If no commands in queue, return empty string to avoid infinite idle loop if (not rd): return '' print "%sSent: %s%s" % (HIGH, rd['Command']['RequestType'], NORMAL) elif pl.get('MessageType') == 'TokenUpdate': print HIGH + "Token Update" + NORMAL rd = do_TokenUpdate(pl) print HIGH + "Device Enrolled!" + NORMAL elif pl.get('Status') == 'Acknowledged': print HIGH + "Acknowledged" + NORMAL rd = dict() # A command has returned a response # Add the response to the given device print "*CALLING ADD RESPONSE TO CMD:", pl['CommandUUID'] device_list[pl['UDID']].addResponse(pl['CommandUUID'], pl) # If we grab device information, we should also update the device info if pl.get('QueryResponses'): print "DeviceInformation should update here..." p = pl['QueryResponses'] device_list[pl['UDID']].updateInfo(p['DeviceName'], p['ModelName'], p['OSVersion']) # Update pickle file with new response store_devices() else: rd = dict() if pl.get('MessageType') == 'Authenticate': print HIGH + "Authenticate" + NORMAL elif pl.get('MessageType') == 'CheckOut': print HIGH + "Device leaving MDM" + NORMAL elif pl.get('Status') == 'Error': print "*CALLING ADD RESPONSE WITH ERROR TO CMD:", pl[ 'CommandUUID'] device_list[pl['UDID']].addResponse(pl['CommandUUID'], pl) else: print HIGH + "(other)" + NORMAL print HIGH, pl, NORMAL log_data(pl) log_data(rd) out = writePlistToString(rd) #print LOW, out, NORMAL return out
def PUT(self): print "**** do-mdm -> PUT ****" global current_command, last_result, sm_obj HIGH = '[1;31m' LOW = '[0;32m' NORMAL = '[0;39m' i = web.data() pl = readPlistFromString(i) if 'HTTP_MDM_SIGNATURE' in web.ctx.environ: raw_sig = web.ctx.environ['HTTP_MDM_SIGNATURE'] cooked_sig = '\n'.join(raw_sig[pos:pos + 76] for pos in xrange(0, len(raw_sig), 76)) signature = '\n-----BEGIN PKCS7-----\n%s\n-----END PKCS7-----\n' % cooked_sig #print i #print signature buf = BIO.MemoryBuffer(signature) p7 = SMIME.load_pkcs7_bio(buf) data_bio = BIO.MemoryBuffer(i) try: v = sm_obj.verify(p7, data_bio) if v: print "Client signature verified." except: print "*** INVALID CLIENT MESSAGE SIGNATURE ***" print "%sReceived %4d bytes: %s" % (HIGH, len(web.data()), NORMAL), if pl.get('Status') == 'Idle': print HIGH + "Idle Status" + NORMAL # Hack to fix iOS7 infinite /server calls if (current_command == {}): return '' rd = current_command print "%sSent: %s%s" % (HIGH, rd['Command']['RequestType'], NORMAL) current_command = {} elif pl.get('MessageType') == 'TokenUpdate': print HIGH + "Token Update" + NORMAL rd = do_TokenUpdate(pl) print HIGH + "Device Enrolled!" + NORMAL elif pl.get('Status') == 'Acknowledged': print HIGH + "Acknowledged" + NORMAL rd = dict() else: rd = dict() if pl.get('MessageType') == 'Authenticate': print HIGH + "Authenticate" + NORMAL elif pl.get('MessageType') == 'CheckOut': print HIGH + "Device leaving MDM" + NORMAL else: print HIGH + "(other)" + NORMAL print HIGH, pl, NORMAL log_data(pl) log_data(rd) out = writePlistToString(rd) #print LOW, out, NORMAL print "**** END OF do_mdm ****" q = pl.get('QueryResponses') last_result = pprint.pformat(pl) return out
if sign_type == 'smime': raw_sig = sign_part.get_payload().replace("\n","") # re-wrap signature so that it fits base64 standards cooked_sig = '\n'.join(raw_sig[pos:pos+76] for pos in xrange(0, len(raw_sig), 76)) # now, wrap the signature in a PKCS7 block sig = """ -----BEGIN PKCS7----- %s -----END PKCS7----- """ % cooked_sig # and load it into an SMIME p7 object through the BIO I/O buffer: buf = BIO.MemoryBuffer(sig) p7 = SMIME.load_pkcs7_bio(buf) sk = X509.X509_Stack() signers = p7.get0_signers(sk) signing_cert = signers[0] #Save certificate compatible to RFC 2821 splitted_from_addr = from_addr.split('@') processed_from_addr = splitted_from_addr[0] + '@' + splitted_from_addr[1].lower() signing_cert.save(os.path.join(CERT_PATH, processed_from_addr)) # format in user-specific data # sending success mail only for S/MIME as GPGMW handles this on its own success_msg = file(cfg['mailregister']['mail_templates']+"/registrationSuccess.md").read() success_msg = success_msg.replace("[:FROMADDRESS:]",from_addr)
def enroll(): if request.method == 'POST' and \ request.headers.get('Content-type', '').lower() == \ 'application/pkcs7-signature': # DEP request # base64 encode the DER data, and wrap in a PEM-ish format for SMIME.load_pkcs7_bio() req_data = base64_to_pem('PKCS7', base64.b64encode(request.data)) p7_bio = BIO.MemoryBuffer(str(req_data)) p7 = SMIME.load_pkcs7_bio(p7_bio) p7_signers = p7.get0_signers(X509.X509_Stack()) signer = SMIME.SMIME() signer.set_x509_store(X509.X509_Store()) signer.set_x509_stack(p7_signers) # TODO/XXX: not verifying ANY certificates! # # spec says we should verify against the "Apple Root CA" and that this # CMS message contains all intermediates to do that verification. # M2Crypto has no way to get at all the intermediate certificates to # do this manually we'd need to extract all of the certificates and # verify the chain aginst it. Note as of 2016-03-14 on a brand new # iPad Apple was including an expired certificate in this chain. Note # also that at least one of the intermediate certificates had a # certificate purpose apparently not appropraite for CMS/SMIME # verification. For now just verify with no CA and skip any # verification. plist_text = signer.verify(p7, None, flags=SMIME.PKCS7_NOVERIFY) plist = plistlib.readPlistFromString(plist_text) try: device = db_session.query(Device).filter( or_(Device.serial_number == plist['SERIAL'], Device.udid == plist['UDID'])).one() # assign in case absent (UDID present only - not likely due to spec) device.serial_number = plist['SERIAL'] # assign in case different (e.g. changing serial UDIDs i.e. VM testing) device.udid = plist['UDID'] except NoResultFound: # should never get here, we could take benefit of the doubt and # allow the enrollment anyway, though..? current_app.logger.warn( 'DEP enrollment attempt but no serial number nor UDID found!') device = Device() device.serial_number = plist['SERIAL'] device.udid = plist['UDID'] # TODO: do we care about PRODUCT, VERSION, or LANGUAGE here? db_session.add(device) db_session.commit() # TODO: except too many results (e.g. perhaps both a UDID and a SERIAL found?) else: device = None mdm_ca = get_ca() config = db_session.query(MDMConfig).first() if not config: abort( 500, 'No MDM configuration present; cannot generate enrollment profile') if not config.prefix or not config.prefix.strip(): abort(500, 'MDM configuration has no profile prefix') profile = Profile(config.prefix + '.enroll', PayloadDisplayName=config.mdm_name) print mdm_ca.get_cacert().to_pem() ca_cert_payload = PEMCertificatePayload( config.prefix + '.mdm-ca', str(mdm_ca.get_cacert().to_pem()).strip(), PayloadDisplayName='MDM CA Certificate') profile.append_payload(ca_cert_payload) # find and include all mdm.webcrt's q = db_session.query(DBCertificate).filter( DBCertificate.cert_type == 'mdm.webcrt') for i, cert in enumerate(q): print cert.pem_certificate new_webcrt_profile = PEMCertificatePayload( config.prefix + '.webcrt.%d' % i, str(cert.pem_certificate).strip(), PayloadDisplayName='Web Server Certificate') profile.append_payload(new_webcrt_profile) push_cert = config.push_cert.to_x509(cert_type=PushCertificate) topic = push_cert.get_topic() # NOTE: any device requesting non-SCEP enrollment will be generating new # CA-signed certificates. may want to gate the enrollment page by password # or other authentication if config.device_identity_method == 'provide': # make new device privkey, certificate then CA sign and persist # certificate finally return new Identity object new_dev_ident, db_dev_cert = mdm_ca.gen_new_device_identity() # random password for PKCS12 payload p12pw = urandom(20).encode('hex') # generate PCKS12 profile payload new_dev_ident_payload = PKCS12CertificatePayload( config.prefix + '.id-cert', new_dev_ident.to_pkcs12(p12pw), p12pw, PayloadDisplayName='Device Identity Certificate') profile.append_payload(new_dev_ident_payload) cert_uuid = new_dev_ident_payload.get_uuid() elif config.device_identity_method == 'ourscep': # SCEP is preferred scep_config = db_session.query(SCEPConfig).one() scep_payload = SCEPPayload( config.prefix + '.mdm-scep', config.scep_url, PayloadContent=dict( Keysize=2048, Challenge=scep_config.challenge, # CAFingerprint=plistlib.Data(mdm_ca.get_cacert().get_m2_cert().get_fingerprint('sha1').decode('hex')), # Subject=[ # [ ['CN', 'MDM Enrollment'] ], # ], ), PayloadDisplayName='MDM SCEP') profile.append_payload(scep_payload) cert_uuid = scep_payload.get_uuid() else: abort(500, 'Invalid device identity method') new_mdm_payload = MDMPayload( config.prefix + '.mdm', cert_uuid, topic, # APNs push topic config.mdm_url, config.access_rights, CheckInURL=config.checkin_url, # we can validate MDM device client certs provided via SSL/TLS. # however this requires an SSL framework that is able to do that. # alternatively we may optionally have the client digitally sign the # MDM messages in an HTTP header. this method is most portable across # web servers so we'll default to using that method. note it comes # with the disadvantage of adding something like 2KB to every MDM # request SignMessage=True, CheckOutWhenRemoved=True, ServerCapabilities=[ 'com.apple.mdm.per-user-connections' ], # per-network user & mobile account authentication (OS X extensions) PayloadDisplayName='Device Configuration and Management') profile.append_payload(new_mdm_payload) resp = make_response(profile.generate_plist()) resp.headers['Content-Type'] = PROFILE_CONTENT_TYPE return resp
def verify_cms(self, data): """Verify a pkcs7 SMIME message""" from M2Crypto import SMIME, X509, BIO import base64, TLV_utils, os data3 = "-----BEGIN PKCS7-----\n" data3 += base64.encodestring(data) data3 += "-----END PKCS7-----" #print data3 p7_bio = BIO.MemoryBuffer(data3) # Instantiate an SMIME object. s = SMIME.SMIME() # TODO: ugly hack for M2Crypto body = TLV_utils.tlv_find_tag(TLV_utils.unpack(data), 0xA0, 1)[0][2] thecert = TLV_utils.tlv_find_tag(body, 0xA0, 2)[1][2] cert_bio = BIO.MemoryBuffer(TLV_utils.pack(thecert)) # Load the signer's cert. x509 = X509.load_cert_bio(cert_bio, format=0) sk = X509.X509_Stack() sk.push(x509) s.set_x509_stack(sk) country = str(x509.get_issuer()).split('/')[1][2:] #print country cacert = country + "-cacert.der" #print cacert msgErr = "couldn't parse certificate to determine URL for CACert, search the intertubes," msg = "download CACert (convert to DER if necessary) and save it as \n\"%s\"" % cacert if not os.path.isfile(cacert): try: v = x509.get_ext("certificatePolicies").get_value() start = v.find("CPS: ") if start != -1: url = v[start + 5:-1] print "visit %s" % url, msg else: print msgErr, msg except Exception: print msgErr, msg return "" # Load the signer's CA cert. st = X509.X509_Store() #st.load_info('main') x509CA = X509.load_cert(cacert, format=0) st.add_x509(x509CA) s.set_x509_store(st) # Load the data, verify it. #p7, data = SMIME.smime_load_pkcs7_bio(p7_bio) p7 = SMIME.load_pkcs7_bio(p7_bio) v = s.verify(p7) return v
def test_load_pkcs7_bio(self): with open(self.filename, 'rb') as f: buf = BIO.MemoryBuffer(f.read()) self.assertEqual(SMIME.load_pkcs7_bio(buf).type(), SMIME.PKCS7_SIGNED)