def recipient_info(self, session_key): cipher = self._get_public_key_cipher() if cipher == None: return None encrypted_key = cipher.encrypt(session_key) tbs_cert = self._cert['tbs_certificate'] # TODO: use subject_key_identifier when available return cms.RecipientInfo(name='ktri', value={ 'version': u'v0', 'rid': cms.RecipientIdentifier( name='issuer_and_serial_number', value={ 'issuer': tbs_cert['issuer'], 'serial_number': tbs_cert['serial_number'] }), 'key_encryption_algorithm': { 'algorithm': cipher.algo, 'parameters': cipher.parameters }, 'encrypted_key': encrypted_key })
def recipient_info(self, session_key): cipher = self._get_public_key_cipher() if cipher is None: return None encrypted_key = cipher.encrypt(session_key) tbs_cert = self._cert["tbs_certificate"] # TODO: use subject_key_identifier when available return cms.RecipientInfo( name="ktri", value={ "version": "v0", "rid": cms.RecipientIdentifier( name="issuer_and_serial_number", value={ "issuer": tbs_cert["issuer"], "serial_number": tbs_cert["serial_number"], }, ), "key_encryption_algorithm": { "algorithm": cipher.algo, "parameters": cipher.parameters, }, "encrypted_key": encrypted_key, }, )
def recipient_info(self, cert, session_key): public_key = cert.get_pubkey().to_cryptography_key() encrypted_key = public_key.encrypt(session_key, padding.PKCS1v15()) cert = signer.cert2asn(cert.to_cryptography()) tbs_cert = cert['tbs_certificate'] # TODO: use subject_key_identifier when available return cms.RecipientInfo(name=u'ktri', value={ 'version': u'v0', 'rid': cms.RecipientIdentifier( name=u'issuer_and_serial_number', value={ 'issuer': tbs_cert['issuer'], 'serial_number': tbs_cert['serial_number'] }), 'key_encryption_algorithm': { 'algorithm': u'rsa', }, 'encrypted_key': core.OctetString(encrypted_key) })
def recipient_info(self, cert, session_key, oaep): public_key = cert.public_key() cert = signer.cert2asn(cert) tbs_cert = cert['tbs_certificate'] # TODO: use subject_key_identifier when available if oaep: encrypted_key = public_key.encrypt( session_key, padding.OAEP(mgf=padding.MGF1(hashes.SHA512()), algorithm=hashes.SHA512(), label=None)) kea = cms.KeyEncryptionAlgorithm({ 'algorithm': cms.KeyEncryptionAlgorithmId('rsaes_oaep'), 'parameters': algos.RSAESOAEPParams({ 'hash_algorithm': algos.DigestAlgorithm({'algorithm': 'sha512'}), 'mask_gen_algorithm': algos.MaskGenAlgorithm({ 'algorithm': algos.MaskGenAlgorithmId('mgf1'), 'parameters': { 'algorithm': algos.DigestAlgorithmId('sha512'), } }), 'p_source_algorithm': algos.PSourceAlgorithm({ 'algorithm': algos.PSourceAlgorithmId('p_specified'), 'parameters': b'', }) }) }) else: kea = {'algorithm': 'rsa'} encrypted_key = public_key.encrypt(session_key, padding.PKCS1v15()) result = cms.RecipientInfo(name='ktri', value={ 'version': 'v0', 'rid': cms.RecipientIdentifier( name='issuer_and_serial_number', value={ 'issuer': tbs_cert['issuer'], 'serial_number': tbs_cert['serial_number'] }), 'key_encryption_algorithm': kea, 'encrypted_key': core.OctetString(encrypted_key) }) return result
def on_get(self, req, resp): operation = req.get_param("operation", required=True) if operation.lower() == "getcacert": resp.body = keys.parse_certificate( self.authority.certificate_buf).dump() resp.append_header("Content-Type", "application/x-x509-ca-cert") return # If we bump into exceptions later encrypted_container = b"" attr_list = [ cms.CMSAttribute({ 'type': "message_type", 'values': ["3"] }), cms.CMSAttribute({ 'type': "pki_status", 'values': ["2"] # rejected }) ] try: info = cms.ContentInfo.load( b64decode(req.get_param("message", required=True))) ############################################### ### Verify signature of the outer container ### ############################################### signed_envelope = info['content'] encap_content_info = signed_envelope['encap_content_info'] encap_content = encap_content_info['content'] # TODO: try except current_certificate, = signed_envelope["certificates"] signer, = signed_envelope["signer_infos"] # TODO: compare cert to current one if we are renewing assert signer["digest_algorithm"]["algorithm"].native == "md5" assert signer["signature_algorithm"][ "algorithm"].native == "rsassa_pkcs1v15" message_digest = None transaction_id = None sender_nonce = None for attr in signer["signed_attrs"]: if attr["type"].native == "sender_nonce": sender_nonce, = attr["values"] elif attr["type"].native == "trans_id": transaction_id, = attr["values"] elif attr["type"].native == "message_digest": message_digest, = attr["values"] if hashlib.md5(encap_content.native).digest( ) != message_digest.native: raise SCEPBadMessageCheck() assert message_digest msg = signer["signed_attrs"].dump(force=True) assert msg[0] == 160 # Verify signature try: asymmetric.rsa_pkcs1v15_verify( asymmetric.load_certificate(current_certificate.dump()), signer["signature"].native, b"\x31" + msg[1:], # wtf?! "md5") except SignatureError: raise SCEPBadMessageCheck() ############################### ### Decrypt inner container ### ############################### info = cms.ContentInfo.load(encap_content.native) encrypted_envelope = info['content'] encrypted_content_info = encrypted_envelope[ 'encrypted_content_info'] iv = encrypted_content_info['content_encryption_algorithm'][ 'parameters'].native if encrypted_content_info['content_encryption_algorithm'][ "algorithm"].native != "des": raise SCEPBadAlgo() encrypted_content = encrypted_content_info[ 'encrypted_content'].native recipient, = encrypted_envelope['recipient_infos'] if recipient.native["rid"][ "serial_number"] != self.authority.certificate.serial_number: raise SCEPBadCertId() # Since CA private key is not directly readable here, we'll redirect it to signer socket key = asymmetric.rsa_pkcs1v15_decrypt( self.authority.private_key, recipient.native["encrypted_key"]) if len(key) == 8: key = key * 3 # Convert DES to 3DES buf = symmetric.tripledes_cbc_pkcs5_decrypt( key, encrypted_content, iv) _, _, common_name = self.authority.store_request(buf, overwrite=True) cert, buf = self.authority.sign(common_name, overwrite=True) signed_certificate = asymmetric.load_certificate(buf) content = signed_certificate.asn1.dump() except SCEPError as e: attr_list.append( cms.CMSAttribute({ 'type': "fail_info", 'values': ["%d" % e.code] })) else: ################################## ### Degenerate inner container ### ################################## degenerate = cms.ContentInfo({ 'content_type': "signed_data", 'content': cms.SignedData({ 'version': "v1", 'certificates': [signed_certificate.asn1], 'digest_algorithms': [cms.DigestAlgorithm({'algorithm': "md5"})], 'encap_content_info': { 'content_type': "data", 'content': cms.ContentInfo({ 'content_type': "signed_data", 'content': None }).dump() }, 'signer_infos': [] }) }) ################################ ### Encrypt middle container ### ################################ key = os.urandom(8) iv, encrypted_content = symmetric.des_cbc_pkcs5_encrypt( key, degenerate.dump(), os.urandom(8)) assert degenerate.dump() == symmetric.tripledes_cbc_pkcs5_decrypt( key * 3, encrypted_content, iv) ri = cms.RecipientInfo({ 'ktri': cms.KeyTransRecipientInfo({ 'version': "v0", 'rid': cms.RecipientIdentifier({ 'issuer_and_serial_number': cms.IssuerAndSerialNumber({ 'issuer': current_certificate.chosen["tbs_certificate"] ["issuer"], 'serial_number': current_certificate.chosen["tbs_certificate"] ["serial_number"], }), }), 'key_encryption_algorithm': { 'algorithm': "rsa" }, 'encrypted_key': asymmetric.rsa_pkcs1v15_encrypt( asymmetric.load_certificate( current_certificate.chosen.dump()), key) }) }) encrypted_container = cms.ContentInfo({ 'content_type': "enveloped_data", 'content': cms.EnvelopedData({ 'version': "v1", 'recipient_infos': [ri], 'encrypted_content_info': { 'content_type': "data", 'content_encryption_algorithm': { 'algorithm': "des", 'parameters': iv }, 'encrypted_content': encrypted_content } }) }).dump() attr_list = [ cms.CMSAttribute({ 'type': "message_digest", 'values': [hashlib.sha1(encrypted_container).digest()] }), cms.CMSAttribute({ 'type': "message_type", 'values': ["3"] }), cms.CMSAttribute({ 'type': "pki_status", 'values': ["0"] # ok }) ] finally: ############################## ### Signed outer container ### ############################## attrs = cms.CMSAttributes(attr_list + [ cms.CMSAttribute({ 'type': "recipient_nonce", 'values': [sender_nonce] }), cms.CMSAttribute({ 'type': "trans_id", 'values': [transaction_id] }) ]) signer = cms.SignerInfo({ "signed_attrs": attrs, 'version': "v1", 'sid': cms.SignerIdentifier({ 'issuer_and_serial_number': cms.IssuerAndSerialNumber({ 'issuer': self.authority.certificate.issuer, 'serial_number': self.authority.certificate.serial_number, }), }), 'digest_algorithm': algos.DigestAlgorithm({'algorithm': "sha1"}), 'signature_algorithm': algos.SignedDigestAlgorithm({'algorithm': "rsassa_pkcs1v15"}), 'signature': asymmetric.rsa_pkcs1v15_sign(self.authority.private_key, b"\x31" + attrs.dump()[1:], "sha1") }) resp.append_header("Content-Type", "application/x-pki-message") resp.body = cms.ContentInfo({ 'content_type': "signed_data", 'content': cms.SignedData({ 'version': "v1", 'certificates': [self.authority.certificate], 'digest_algorithms': [cms.DigestAlgorithm({'algorithm': "sha1"})], 'encap_content_info': { 'content_type': "data", 'content': encrypted_container }, 'signer_infos': [signer] }) }).dump()
def on_get(self, req, resp): operation = req.get_param("operation", required=True) if operation == "GetCACert": resp.body = keys.parse_certificate( self.authority.certificate_buf).dump() resp.append_header("Content-Type", "application/x-x509-ca-cert") return elif operation == "GetCACaps": # TODO: return renewal flag based on renewal subnets config option resp.body = "Renewal\nMD5\nSHA-1\nSHA-256\nSHA-512\nDES3\n" return elif operation == "PKIOperation": pass else: raise falcon.HTTPBadRequest("Bad request", "Unknown operation %s" % operation) # If we bump into exceptions later encrypted_container = b"" attr_list = [ cms.CMSAttribute({ 'type': "message_type", 'values': ["3"] }), cms.CMSAttribute({ 'type': "pki_status", 'values': ["2"] # rejected }) ] try: info = cms.ContentInfo.load( b64decode(req.get_param("message", required=True))) ############################################### ### Verify signature of the outer container ### ############################################### signed_envelope = info['content'] encap_content_info = signed_envelope['encap_content_info'] encap_content = encap_content_info['content'] # TODO: try except current_certificate, = signed_envelope["certificates"] signer, = signed_envelope["signer_infos"] # TODO: compare cert to current one if we are renewing digest_algorithm = signer["digest_algorithm"]["algorithm"].native signature_algorithm = signer["signature_algorithm"][ "algorithm"].native if digest_algorithm not in ("md5", "sha1", "sha256", "sha512"): raise SCEPBadAlgo() if signature_algorithm != "rsassa_pkcs1v15": raise SCEPBadAlgo() message_digest = None transaction_id = None sender_nonce = None for attr in signer["signed_attrs"]: if attr["type"].native == "sender_nonce": sender_nonce, = attr["values"] elif attr["type"].native == "trans_id": transaction_id, = attr["values"] elif attr["type"].native == "message_digest": message_digest, = attr["values"] if getattr(hashlib, digest_algorithm)(encap_content.native).digest( ) != message_digest.native: raise SCEPDigestMismatch() if not sender_nonce: raise SCEPBadRequest() if not transaction_id: raise SCEPBadRequest() assert message_digest msg = signer["signed_attrs"].dump(force=True) assert msg[0] == 160 # Verify signature try: asymmetric.rsa_pkcs1v15_verify( asymmetric.load_certificate(current_certificate.dump()), signer["signature"].native, b"\x31" + msg[1:], # wtf?! "md5") except SignatureError: raise SCEPSignatureMismatch() ############################### ### Decrypt inner container ### ############################### info = cms.ContentInfo.load(encap_content.native) encrypted_envelope = info['content'] encrypted_content_info = encrypted_envelope[ 'encrypted_content_info'] iv = encrypted_content_info['content_encryption_algorithm'][ 'parameters'].native if encrypted_content_info['content_encryption_algorithm'][ "algorithm"].native != "des": raise SCEPBadAlgo() encrypted_content = encrypted_content_info[ 'encrypted_content'].native recipient, = encrypted_envelope['recipient_infos'] if recipient.native["rid"][ "serial_number"] != self.authority.certificate.serial_number: raise SCEPBadCertId() key = asymmetric.rsa_pkcs1v15_decrypt( self.authority.private_key, recipient.native["encrypted_key"]) if len(key) == 8: key = key * 3 # Convert DES to 3DES buf = symmetric.tripledes_cbc_pkcs5_decrypt( key, encrypted_content, iv) _, _, common_name = self.authority.store_request(buf, overwrite=True) logger.info( "SCEP client from %s requested with %s digest algorithm, %s signature", req.context["remote_addr"], digest_algorithm, signature_algorithm) cert, buf = self.authority.sign(common_name, profile=config.PROFILES["gw"], overwrite=True) signed_certificate = asymmetric.load_certificate(buf) content = signed_certificate.asn1.dump() except SCEPError as e: attr_list.append( cms.CMSAttribute({ 'type': "fail_info", 'values': ["%d" % e.code] })) logger.info("Failed to sign SCEP request due to: %s" % e.explaination) else: ################################## ### Degenerate inner container ### ################################## degenerate = cms.ContentInfo({ 'content_type': "signed_data", 'content': cms.SignedData({ 'version': "v1", 'certificates': [signed_certificate.asn1], 'digest_algorithms': [cms.DigestAlgorithm({'algorithm': digest_algorithm})], 'encap_content_info': { 'content_type': "data", 'content': cms.ContentInfo({ 'content_type': "signed_data", 'content': None }).dump() }, 'signer_infos': [] }) }) ################################ ### Encrypt middle container ### ################################ key = os.urandom(8) iv, encrypted_content = symmetric.des_cbc_pkcs5_encrypt( key, degenerate.dump(), os.urandom(8)) assert degenerate.dump() == symmetric.tripledes_cbc_pkcs5_decrypt( key * 3, encrypted_content, iv) ri = cms.RecipientInfo({ 'ktri': cms.KeyTransRecipientInfo({ 'version': "v0", 'rid': cms.RecipientIdentifier({ 'issuer_and_serial_number': cms.IssuerAndSerialNumber({ 'issuer': current_certificate.chosen["tbs_certificate"] ["issuer"], 'serial_number': current_certificate.chosen["tbs_certificate"] ["serial_number"], }), }), 'key_encryption_algorithm': { 'algorithm': "rsa" }, 'encrypted_key': asymmetric.rsa_pkcs1v15_encrypt( asymmetric.load_certificate( current_certificate.chosen.dump()), key) }) }) encrypted_container = cms.ContentInfo({ 'content_type': "enveloped_data", 'content': cms.EnvelopedData({ 'version': "v1", 'recipient_infos': [ri], 'encrypted_content_info': { 'content_type': "data", 'content_encryption_algorithm': { 'algorithm': "des", 'parameters': iv }, 'encrypted_content': encrypted_content } }) }).dump() attr_list = [ cms.CMSAttribute({ 'type': "message_digest", 'values': [ getattr( hashlib, digest_algorithm)(encrypted_container).digest() ] }), cms.CMSAttribute({ 'type': "message_type", 'values': ["3"] }), cms.CMSAttribute({ 'type': "pki_status", 'values': ["0"] # ok }) ] finally: ############################## ### Signed outer container ### ############################## attrs = cms.CMSAttributes(attr_list + [ cms.CMSAttribute({ 'type': "recipient_nonce", 'values': [sender_nonce] }), cms.CMSAttribute({ 'type': "trans_id", 'values': [transaction_id] }) ]) signer = cms.SignerInfo({ "signed_attrs": attrs, 'version': "v1", 'sid': cms.SignerIdentifier({ 'issuer_and_serial_number': cms.IssuerAndSerialNumber({ 'issuer': self.authority.certificate.issuer, 'serial_number': self.authority.certificate.serial_number, }), }), 'digest_algorithm': algos.DigestAlgorithm({'algorithm': digest_algorithm}), 'signature_algorithm': algos.SignedDigestAlgorithm({'algorithm': "rsassa_pkcs1v15"}), 'signature': asymmetric.rsa_pkcs1v15_sign(self.authority.private_key, b"\x31" + attrs.dump()[1:], digest_algorithm) }) resp.append_header("Content-Type", "application/x-pki-message") resp.body = cms.ContentInfo({ 'content_type': "signed_data", 'content': cms.SignedData({ 'version': "v1", 'certificates': [self.authority.certificate], 'digest_algorithms': [cms.DigestAlgorithm({'algorithm': digest_algorithm})], 'encap_content_info': { 'content_type': "data", 'content': encrypted_container }, 'signer_infos': [signer] }) }).dump()
def encrypt(certificate, data): # encrypt data with block cipher AES-256-CBC session_key = os.urandom(32) iv = os.urandom(16) algorithm = algorithms.AES(session_key) encryptor = Cipher(algorithm, modes.CBC(iv), backend=default_backend()).encryptor() padder = padding.PKCS7(128).padder() padded_data = padder.update(data) + padder.finalize() encrypted_data = encryptor.update(padded_data) + encryptor.finalize() # load certificate with open(certificate, 'rb') as fp: cert = x509.Certificate.load(pem.unarmor(fp.read())[2]) tbs_cert = cert['tbs_certificate'] # encrypt session key with public key pub = serialization.load_der_public_key(cert.public_key.dump()) encrypted_key = pub.encrypt( session_key, apadding.OAEP(mgf=apadding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)) # encode encrypted key and RSA parameters for recipient recipient_info = cms.RecipientInfo(name='ktri', value={ 'version': 'v0', 'rid': cms.RecipientIdentifier( name='issuer_and_serial_number', value={ 'issuer': tbs_cert['issuer'], 'serial_number': tbs_cert['serial_number'] }), 'key_encryption_algorithm': { 'algorithm': 'rsaes_oaep', 'parameters': algos.RSAESOAEPParams({ 'hash_algorithm': { 'algorithm': 'sha256' }, 'mask_gen_algorithm': { 'algorithm': 'mgf1', 'parameters': { 'algorithm': 'sha256' } } }), }, 'encrypted_key': encrypted_key, }) # wrap up encrypted data along with symmetric encryption parameters # and recipient info enveloped_data = cms.ContentInfo({ 'content_type': 'enveloped_data', 'content': { 'version': 'v0', 'recipient_infos': [recipient_info], 'encrypted_content_info': { 'content_type': 'data', 'content_encryption_algorithm': { 'algorithm': 'aes256_cbc', 'parameters': iv, }, 'encrypted_content': encrypted_data } } }) return enveloped_data