def test_works_with_lowercase_attr_type_shortname(self, generator): principal = { 'uid': ['testuser'], 'mail': ['*****@*****.**'], } template_env = { 'ipacertificatesubjectbase': [ 'o=DOMAIN.EXAMPLE.COM' # lower-case attr type shortname ], } config = generator.csr_config(principal, template_env, 'userCert') key = rsa.generate_private_key( public_exponent=65537, key_size=2048, backend=default_backend(), ) adaptor = csrgen.OpenSSLAdaptor(key=key) reqinfo = bytes( csrgen_ffi.build_requestinfo( config.encode('utf-8'), adaptor.get_subject_public_key_info())) csr_der = adaptor.sign_csr(reqinfo) csr = x509.load_der_x509_csr(csr_der, default_backend()) assert (csr.subject.get_attributes_for_oid( x509.NameOID.COMMON_NAME) == [ x509.NameAttribute(x509.NameOID.COMMON_NAME, u'testuser') ]) assert (csr.subject.get_attributes_for_oid( x509.NameOID.ORGANIZATION_NAME) == [ x509.NameAttribute(x509.NameOID.ORGANIZATION_NAME, u'DOMAIN.EXAMPLE.COM') ])
def sign_csr_builder( session: PivSession, slot: SLOT, public_key: Union[rsa.RSAPublicKey, ec.EllipticCurvePublicKey], builder: x509.CertificateSigningRequestBuilder, hash_algorithm: Type[hashes.HashAlgorithm] = hashes.SHA256, ) -> x509.CertificateSigningRequest: """Sign a CSR.""" key_type = KEY_TYPE.from_public_key(public_key) dummy_key = _dummy_key(key_type) csr = builder.sign(dummy_key, hash_algorithm(), default_backend()) seq = Tlv.parse_list(Tlv.unpack(0x30, csr.public_bytes(Encoding.DER))) # Replace public key pub_format = (PublicFormat.PKCS1 if key_type.algorithm == ALGORITHM.RSA else PublicFormat.SubjectPublicKeyInfo) dummy_bytes = dummy_key.public_key().public_bytes(Encoding.DER, pub_format) pub_bytes = public_key.public_bytes(Encoding.DER, pub_format) seq[0] = Tlv(seq[0].replace(dummy_bytes, pub_bytes)) sig = session.sign( slot, key_type, seq[0], hash_algorithm(), padding.PKCS1v15(), # Only used for RSA ) # Replace signature, add unused bits = 0 seq[2] = Tlv(seq[2].tag, b"\0" + sig) # Re-assemble sequence der = Tlv(0x30, b"".join(seq)) return x509.load_der_x509_csr(der, default_backend())
def parse_csr( csr: Union[x509.CertificateSigningRequest, str, bytes], csr_format: Encoding ) -> x509.CertificateSigningRequest: """Parse a CSR in the given format. .. deprecated:: 1.18.0 This function is no longer useful and will be removed in django-ca 1.20.0. Parameters ---------- csr : str or bytes or :py:class:`~cg:cryptography.x509.CertificateSigningRequest` The CSR to parse. csr_format : :py:class:`~cg:cryptography.hazmat.primitives.serialization.Encoding` The format that the CSR is in. """ if isinstance(csr, x509.CertificateSigningRequest): # pragma: no cover # not used since 1.18.0 return csr if csr_format == Encoding.PEM: return x509.load_pem_x509_csr(force_bytes(csr), default_backend()) if csr_format == Encoding.DER: return x509.load_der_x509_csr(force_bytes(csr), default_backend()) raise ValueError("Unknown CSR format passed: %s" % csr_format)
def generate_csr(self, private_key, subject_name, extensions=None): common_name = subject_name.get_attributes_for_oid( NameOID.COMMON_NAME)[0].value info = CertificationRequestInfo({ 'version': 0, 'subject': Name.build({ 'country_name': 'US', 'state_or_province_name': 'North Carolina', 'organization_name': 'Hyperledger', 'organizational_unit_name': 'Fabric', 'common_name': common_name }), 'subject_pk_info': PublicKeyInfo.load(encode_ec_public_key(private_key.public_key)), 'attributes': CRIAttributes([]) }) hash = hashlib.sha256(info.dump()).digest() signature = private_key.private_key.sign(hash, mechanism=Mechanism.ECDSA) csr = CertificationRequest({ 'certification_request_info': info, 'signature_algorithm': { 'algorithm': 'sha256_ecdsa', 'parameters': None }, 'signature': encode_ecdsa_signature(signature) }) der = csr.dump() result = x509.load_der_x509_csr(der, default_backend()) return result
def sign_csr_builder(self, slot, public_key, builder, touch_callback=None): algorithm = ALGO.from_public_key(public_key) dummy_key = _dummy_key(algorithm) csr = builder.sign(dummy_key, hashes.SHA256(), default_backend()) seq = parse_tlvs(Tlv(csr.public_bytes(Encoding.DER)).value) # Replace public key pub_format = PublicFormat.PKCS1 if algorithm.name.startswith('RSA') \ else PublicFormat.SubjectPublicKeyInfo dummy_bytes = dummy_key.public_key().public_bytes( Encoding.DER, pub_format) pub_bytes = public_key.public_bytes(Encoding.DER, pub_format) seq[0] = seq[0].replace(dummy_bytes, pub_bytes) if touch_callback is not None: touch_timer = Timer(0.500, touch_callback) touch_timer.start() sig = self.sign(slot, algorithm, seq[0]) if touch_callback is not None: touch_timer.cancel() # Replace signature, add unused bits = 0 seq[2] = Tlv(seq[2].tag, b'\0' + sig) # Re-assemble sequence der = Tlv(0x30, b''.join(seq)) return x509.load_der_x509_csr(der, default_backend())
def parse_csr(self, csr, csr_format): if isinstance(csr, x509.CertificateSigningRequest): return csr elif csr_format == Encoding.PEM: return x509.load_pem_x509_csr(force_bytes(csr), default_backend()) elif csr_format == Encoding.DER: return x509.load_der_x509_csr(force_bytes(csr), default_backend()) raise ValueError('Unknown CSR format passed: %s' % csr_format)
def check_csr_and_return_cert(csr_der, order): """ validate CSR and pass to backend Checks that the CSR only contains domains from previously validated challenges and get a signed certificate from the backend. Args: csr_der (bytes): client's CSR in DER encoding order (Order): the order object that the CSR belongs to Returns: bytes: the signed certificate and chain in PKCS#7 format (DER encoded) Raises: ACMEError: CSR was rejected (by us) or the backend refused to sign it. """ csr = x509.load_der_x509_csr(csr_der, x509_backend()) try: alt_names = csr.extensions.get_extension_for_oid( x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME ).value.get_values_for_type(x509.DNSName) except: alt_names = [] try: common_name = csr.subject.get_attributes_for_oid( x509.oid.NameOID.COMMON_NAME)[0].value except IndexError: # certbot does not set Subject Name, only SANs # https://github.com/certbot/certbot/issues/4922 common_name = alt_names[0] if not common_name in alt_names: # chrome ignores CN, so write CN to SAN alt_names.insert(0, common_name) # since we pass the CN and SANs to the backend, make sure the client only # specified those that we verified before: order_identifiers = {ident.value for ident in order.identifiers} csr_identifiers = {*alt_names} # convert list to set if order_identifiers != csr_identifiers: raise ACMEError(f"{order_identifiers} != {csr_identifiers}", 400, "badCSR") csr_der = csr.public_bytes(serialization.Encoding.DER) email = order.account.contact subject_dn = csr.subject.rfc4514_string() if config["forceTemplateDN"] or not subject_dn: subject_dn = config["subjectNameTemplate"].format(SAN=alt_names, MAIL=email) certificate, error = backend.sign(csr_der, subject_dn, alt_names, email) if error: raise ACMEError(error, 400, "badCSR") return certificate
def test_build_csr() -> None: for key in [ ecdsa.TextbookEccPrivateKey(ecc.NIST_P_256, 12345), rsa.TextbookRsaPrivateKey(65537, [2**521 - 1, 2**607 - 1]), ]: subject = [("country_name", PrintableString("QY")), ("common_name", UTF8String("cryptokey"))] csr = run(build_csr(key, subject)) req = x509.load_der_x509_csr(csr, backend) assert req.is_signature_valid assert req.subject.rfc4514_string() == "C=QY,CN=cryptokey"
def parse_acme_csr(value: str) -> x509.CertificateSigningRequest: """Convert the CSR as received via ACMEv2 into a valid CSR. ACMEv2 sends the CSR as a base64url encoded string of its DER /ASN.1 representation. Returns ------- :py:class:`~cg:cryptography.x509.CertificateSigningRequest` The CSR as used by cryptography. """ decoded = jose.decode_b64jose(value) return x509.load_der_x509_csr(decoded, default_backend())
def decode_csr(b64der): """ Decode JOSE Base-64 DER-encoded CSR. :param str b64der: The encoded CSR. :rtype: `cryptography.x509.CertificateSigningRequest` :return: The decoded CSR. """ try: return x509.load_der_x509_csr(decode_b64jose(b64der), default_backend()) except ValueError as error: raise DeserializationError(error)
def __init__(self, subject: dict): try: req_obj = x509.load_pem_x509_csr(subject['bytes'], default_backend()) except: try: req_obj = x509.load_der_x509_csr(subject['bytes'], default_backend()) except: raise ValueError('request must be wrong') logger.debug('Certificate signing request is:\n{}'.format( subject['bytes'])) _Request.__init__(self, req_obj)
def load(self, raw, encoding='PEM'): """Load a CSR and return a cryptography CSR object """ csr = None try: if encoding == 'PEM': csr = x509.load_pem_x509_csr(raw, backend=self.__backend) elif encoding in ['DER', 'PFX', 'P12']: csr = x509.load_der_x509_csr(raw, backend=self.__backend) else: raise NotImplementedError( 'Unsupported certificate request encoding') except Exception as err: raise Exception(err) return csr
def set_signature(self, signature: bytes): """ Modify the signature of a CSR. :param bytes signature: the new signature for a CSR. :rtype: None """ der_csr = self.public_bytes(serialization.Encoding.DER) asn1_csr, _ = asn1_decode( der_csr, asn1Spec=CertificationRequest(), ) asn1_csr.setComponentByName("signature", BitString.fromOctetString(signature)) self._x509_req = x509.load_der_x509_csr( asn1_encode(asn1_csr))._x509_req
def test_rsa_cert(self): """Generates a PKCS#7 renewal request from an rsa certificate""" cert, key = _generate_self_signed_cert('rsa') csr = pkcs7csr.create_pkcs7csr(cert, key) verify_result, raw_inner_csr = _verify_pkcs7_signature(csr) inner_csr = x509.load_der_x509_csr(raw_inner_csr, default_backend()) decoded_inner_csr = decoder.decode(raw_inner_csr, asn1Spec=rfc2314.CertificationRequest()) encoded_inner_csr = encoder.encode(decoded_inner_csr[0]['certificationRequestInfo'] ['attributes'][0]['vals'][0]) self.assertEqual(verify_result, 0) self.assertEqual(inner_csr.is_signature_valid, True) self.assertEqual( key.public_key().public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo), inner_csr.public_key().public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)) self.assertEqual(cert.public_bytes(Encoding.DER), encoded_inner_csr)
def parse_csr(csr: Union[x509.CertificateSigningRequest, str, bytes], csr_format: Encoding) -> x509.CertificateSigningRequest: """Parse a CSR in the given format. Parameters ---------- csr : str or bytes or :py:class:`~cg:cryptography.x509.CertificateSigningRequest` The CSR to parse. csr_format : :py:class:`~cg:cryptography.hazmat.primitives.serialization.Encoding` The format that the CSR is in. """ if isinstance(csr, x509.CertificateSigningRequest): return csr if csr_format == Encoding.PEM: return x509.load_pem_x509_csr(force_bytes(csr), default_backend()) if csr_format == Encoding.DER: return x509.load_der_x509_csr(force_bytes(csr), default_backend()) raise ValueError('Unknown CSR format passed: %s' % csr_format)
def parse(self, raw, encoding='PEM'): """Parse CSR data (PEM default) and return dict with values """ data = dict({}) try: if encoding == 'PEM': csr = x509.load_pem_x509_csr(raw, backend=self.__backend) elif encoding in ['DER', 'PFX', 'P12']: csr = x509.load_der_x509_csr(raw, backend=self.__backend) else: raise NotImplementedError( 'Unsupported certificate request encoding') except Exception as err: raise Exception(err) data['subject'] = csr.subject data['digest'] = csr.signature_hash_algorithm data['signature'] = csr.signature return data
def sign_csr_builder(self, slot, public_key, builder, touch_callback=None): algorithm = ALGO.from_public_key(public_key) dummy_key = _dummy_key(algorithm) cert = builder.sign(dummy_key, hashes.SHA256(), default_backend()) message = cert.tbs_certrequest_bytes if algorithm in (ALGO.RSA1024, ALGO.RSA2048): message = _pkcs1_15_pad(algorithm, message) dummy_bytes = dummy_key.public_key().public_bytes( Encoding.DER, PublicFormat.PKCS1) pub_bytes = public_key.public_bytes(Encoding.DER, PublicFormat.PKCS1) elif algorithm in (ALGO.ECCP256, ALGO.ECCP384): h = hashes.Hash(hashes.SHA256(), default_backend()) h.update(message) message = h.finalize() dummy_bytes = dummy_key.public_key().public_bytes( Encoding.DER, PublicFormat.SubjectPublicKeyInfo) pub_bytes = public_key.public_bytes( Encoding.DER, PublicFormat.SubjectPublicKeyInfo) if touch_callback is not None: touch_timer = Timer(0.500, touch_callback) touch_timer.start() sig = self.sign_raw(slot, algorithm, message) if touch_callback is not None: touch_timer.cancel() seq = parse_tlvs(Tlv(cert.public_bytes(Encoding.DER)).value) # Replace public key seq[0] = seq[0].replace(dummy_bytes, pub_bytes) # Replace signature, add unused bits = 0 seq[2] = Tlv(seq[2].tag, b'\0' + sig) # Re-assemble sequence der = Tlv(0x30, b''.join(seq)) return x509.load_der_x509_csr(der, default_backend())
def test_public_bytes_der(self, backend): # Load an existing CSR. request = _load_cert(os.path.join("x509", "requests", "rsa_sha1.pem"), x509.load_pem_x509_csr, backend) # Encode it to DER and load it back. request = x509.load_der_x509_csr( request.public_bytes(encoding=serialization.Encoding.DER, ), backend) # We should recover what we had to start with. assert isinstance(request.signature_hash_algorithm, hashes.SHA1) public_key = request.public_key() assert isinstance(public_key, rsa.RSAPublicKey) subject = request.subject assert isinstance(subject, x509.Name) assert list(subject) == [ x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'), x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA'), x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), ]
def main(): # Create argument parser to document script use parser = argparse.ArgumentParser(description='Provisions the kit by requesting a CSR and returning signed certificates.') args = parser.parse_args() kit_info = read_kit_info() print('\nOpening AWS Zero-touch Kit Device') device = MchpAwsZTKitDevice(hid.device()) #device = MchpAwsZTKitDevice(SimMchpAwsZTHidDevice()) device.open() print('\nInitializing Kit') resp = device.init() print(' ATECC508A SN: %s' % resp['deviceSn']) print(' ATECC508A Public Key:') int_size = int(len(resp['devicePublicKey']) / 2) print(' X: %s' % resp['devicePublicKey'][:int_size]) print(' Y: %s' % resp['devicePublicKey'][int_size:]) kit_info['device_sn'] = resp['deviceSn'] save_kit_info(kit_info) print('\nLoading root CA certificate') if not os.path.isfile(ROOT_CA_CERT_FILENAME): raise AWSZTKitError('Failed to find root CA certificate file, ' + ROOT_CA_CERT_FILENAME + '. Have you run ca_create_root first?') with open(ROOT_CA_CERT_FILENAME, 'rb') as f: print(' Loading from ' + f.name) root_ca_cert = x509.load_pem_x509_certificate(f.read(), crypto_be) print('\nLoading signer CA key') if not os.path.isfile(SIGNER_CA_KEY_FILENAME): raise AWSZTKitError('Failed to find signer CA key file, ' + SIGNER_CA_KEY_FILENAME + '. Have you run ca_create_signer_csr first?') with open(SIGNER_CA_KEY_FILENAME, 'rb') as f: print(' Loading from ' + f.name) signer_ca_priv_key = serialization.load_pem_private_key( data=f.read(), password=None, backend=crypto_be) print('\nLoading signer CA certificate') if not os.path.isfile(SIGNER_CA_CERT_FILENAME): raise AWSZTKitError('Failed to find signer CA certificate file, ' + SIGNER_CA_CERT_FILENAME + '. Have you run ca_create_signer first?') with open(SIGNER_CA_CERT_FILENAME, 'rb') as f: print(' Loading from ' + f.name) signer_ca_cert = x509.load_pem_x509_certificate(f.read(), crypto_be) if 'endpointAddress' not in kit_info: raise AWSZTKitError('endpointAddress not found in %s. Have you run aws_register_signer yet?' % KIT_INFO_FILENAME) if 'wifi_ssid' not in kit_info: raise AWSZTKitError('wifi_ssid not found in %s. Have you run kit_set_wifi yet?' % KIT_INFO_FILENAME) if 'wifi_password' not in kit_info: raise AWSZTKitError('wifi_password not found in %s. Have you run kit_set_wifi yet?' % KIT_INFO_FILENAME) print('\nRequesting device CSR') resp = device.gen_csr() device_csr = x509.load_der_x509_csr(binascii.a2b_hex(resp['csr']), crypto_be) if not device_csr.is_signature_valid: raise AWSZTKitError('Device CSR has invalid signature.') with open(DEVICE_CSR_FILENAME, 'wb') as f: print(' Saving to ' + f.name) f.write(device_csr.public_bytes(encoding=serialization.Encoding.PEM)) print('\nGenerating device certificate from CSR') # Build certificate builder = x509.CertificateBuilder() builder = builder.issuer_name(signer_ca_cert.subject) builder = builder.not_valid_before(datetime.datetime.now(tz=pytz.utc).replace(minute=0,second=0)) # Device cert must have minutes and seconds set to 0 builder = builder.not_valid_after(datetime.datetime(3000, 12, 31, 23, 59, 59)) # Should be year 9999, but this doesn't work on windows builder = builder.subject_name(device_csr.subject) builder = builder.public_key(device_csr.public_key()) # Device certificate is generated from certificate dates and public key builder = builder.serial_number(device_cert_sn(16, builder)) # Add in extensions specified by CSR for extension in device_csr.extensions: builder = builder.add_extension(extension.value, extension.critical) # Subject Key ID is used as the thing name and MQTT client ID and is required for this demo builder = builder.add_extension( x509.SubjectKeyIdentifier.from_public_key(builder._public_key), critical=False) issuer_ski = signer_ca_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier) builder = builder.add_extension( x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(issuer_ski), critical=False) # Sign certificate device_cert = builder.sign( private_key=signer_ca_priv_key, algorithm=hashes.SHA256(), backend=crypto_be) # Find the subject key ID for use as the thing name is_subject_key_id_found = False for extension in device_cert.extensions: if extension.oid._name != 'subjectKeyIdentifier': continue # Not the extension we're looking for, skip kit_info['thing_name'] = binascii.b2a_hex(extension.value.digest).decode('ascii') save_kit_info(kit_info) is_subject_key_id_found = True if not is_subject_key_id_found: raise RuntimeError('Could not find the subjectKeyIdentifier extension in the device certificate.') # Save certificate for reference with open(DEVICE_CERT_FILENAME, 'wb') as f: print(' Saving to ' + f.name) f.write(device_cert.public_bytes(encoding=serialization.Encoding.PEM)) print('\nProvisioning device with AWS IoT credentials') pub_nums = root_ca_cert.public_key().public_numbers() pubkey = pub_nums.x.to_bytes(32, byteorder='big', signed=False) pubkey += pub_nums.y.to_bytes(32, byteorder='big', signed=False) device.save_credentials( host_name=kit_info['endpointAddress'], device_cert=device_cert.public_bytes(encoding=serialization.Encoding.DER), signer_cert=signer_ca_cert.public_bytes(encoding=serialization.Encoding.DER), signer_ca_public_key=pubkey) print('\nUpdating WiFi settings') print(' SSID: %s' % kit_info['wifi_ssid']) disp_password = '******' if kit_info['wifi_password'] is not None: disp_password = '******'*len(kit_info['wifi_password']) print(' Password: %s' % disp_password) device.set_wifi(ssid=kit_info['wifi_ssid'], psk=kit_info['wifi_password']) print('\nDone')
from cryptography.x509.oid import NameOID from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.serialization import load_der_private_key import datetime import sys with open("./key_self_sign.der", "rb") as f: key_self_sign = load_der_private_key(f.read(), password=b"passphrase", backend=default_backend()) with open("./certificate.der", "rb") as f: ca_cert = x509.load_der_x509_certificate(f.read(), default_backend()) with open("../csr.der", "rb") as f: csr = x509.load_der_x509_csr(f.read(), default_backend()) subject = issuer = x509.Name([ x509.NameAttribute(NameOID.COUNTRY_NAME, u"PT"), x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Braga"), x509.NameAttribute(NameOID.LOCALITY_NAME, u"Gualtar"), x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"UMinho"), x509.NameAttribute(NameOID.COMMON_NAME, u"CA"), ]) cert = x509.CertificateBuilder().subject_name(csr.subject).issuer_name( ca_cert.subject).public_key(csr.public_key()).serial_number( x509.random_serial_number()).not_valid_before( datetime.datetime.utcnow()).not_valid_after( # Our certificate will be valid for 10 days datetime.datetime.utcnow() +
def scep(): op = request.args.get('operation') if storage.exists(): g.ca = CertificateAuthority(storage) else: subject = x509.Name([ x509.NameAttribute(x509.NameOID.COMMON_NAME, app.config['CA_X509_CN']), x509.NameAttribute(x509.NameOID.ORGANIZATION_NAME, app.config['CA_X509_O']), x509.NameAttribute(x509.NameOID.COUNTRY_NAME, app.config['CA_X509_C']) ]) g.ca = CertificateAuthority.create(storage, subject) ca = g.ca if op == 'GetCACert': certs = [ca.certificate] if len(certs) == 1 and not app.config.get('FORCE_DEGENERATE_FOR_SINGLE_CERT', False): return Response(certs[0].public_bytes(Encoding.DER), mimetype='application/x-x509-ca-cert') elif len(certs): raise ValueError('cryptography cannot produce degenerate pkcs7 certs') # p7_degenerate = degenerate_pkcs7_der(certs) # return Response(p7_degenerate, mimetype='application/x-x509-ca-ra-cert') elif op == 'GetCACaps': return '\n'.join(CACAPS) elif op == 'PKIOperation': if request.method == 'GET': msg = request.args.get('message') # note: OS X improperly encodes the base64 query param by not # encoding spaces as %2B and instead leaving them as +'s msg = b64decode(msg.replace(' ', '+')) elif request.method == 'POST': # workaround for Flask/Werkzeug lack of chunked handling if 'chunked' in request.headers.get('Transfer-Encoding', ''): msg = request.environ['body_copy'] else: msg = request.data req = SCEPMessage.parse(msg) app.logger.debug('Received SCEPMessage, details follow') req.debug() if req.message_type == MessageType.PKCSReq or req.message_type == MessageType.RenewalReq: app.logger.debug('received {} SCEP message'.format(MessageType(req.message_type))) cakey = ca.private_key cacert = ca.certificate der_req = req.get_decrypted_envelope_data( cacert, cakey, ) cert_req = x509.load_der_x509_csr(der_req, backend=default_backend()) req_info_bytes = cert_req.tbs_certrequest_bytes # Check the challenge password (unless it is a renewal) if 'SCEP_CHALLENGE' in app.config and req.message_type == MessageType.PKCSReq: req_info = CertificationRequestInfo.load(req_info_bytes) for attr in req_info['attributes']: if attr['type'].native == 'challenge_password': assert len(attr['values']) == 1 challenge_password = attr['values'][0].native if challenge_password != app.config['SCEP_CHALLENGE']: app.logger.warning('Client did not send the correct challenge') signer = Signer(cacert, cakey) reply = PKIMessageBuilder().message_type( MessageType.CertRep ).transaction_id( req.transaction_id ).pki_status( PKIStatus.FAILURE, FailInfo.BadRequest ).recipient_nonce( req.sender_nonce ).add_signer(signer).finalize() return Response(reply.dump(), mimetype='application/x-pki-message') else: break app.logger.warning('Client did not send any challenge password, but there was one configured') signer = Signer(cacert, cakey) reply = PKIMessageBuilder().message_type( MessageType.CertRep ).transaction_id( req.transaction_id ).pki_status( PKIStatus.FAILURE, FailInfo.BadRequest ).recipient_nonce( req.sender_nonce ).add_signer(signer).finalize() return Response(reply.dump(), mimetype='application/x-pki-message') # CA should persist all signed certs itself new_cert = ca.sign(cert_req) degenerate = create_degenerate_certificate(new_cert) # with open('/tmp/degenerate.der', 'wb') as fd: # fd.write(degenerate.dump()) envelope, _, _ = PKCSPKIEnvelopeBuilder().encrypt(degenerate.dump(), 'aes').add_recipient( req.certificates[0]).finalize() signer = Signer(cacert, cakey, 'sha512') reply = PKIMessageBuilder().message_type( MessageType.CertRep ).transaction_id( req.transaction_id ).pki_status( PKIStatus.SUCCESS ).recipient_nonce( req.sender_nonce ).pki_envelope( envelope ).certificates(new_cert).add_signer(signer).finalize() # res = SCEPMessage.parse(reply.dump()) # app.logger.debug('Reply with CertRep, details follow') # res.debug() # with open('/tmp/reply.bin', 'wb') as fd: # fd.write(reply.dump()) return Response(reply.dump(), mimetype='application/x-pki-message') else: app.logger.error('unhandled SCEP message type: %d', req.message_type) return '' else: abort(404, 'unknown SCEP operation')
def sign_cert(self, ca, csr, expires=None, algorithm=None, subject=None, cn_in_san=True, csr_format=Encoding.PEM, subject_alternative_name=None, key_usage=None, extended_key_usage=None, tls_feature=None, ocsp_no_check=False, issuer_url=None, crl_url=None, ocsp_url=None, issuer_alternative_name=None, extra_extensions=None, password=None): """Create a signed certificate from a CSR. **PLEASE NOTE:** This function creates the raw certificate and is usually not invoked directly. It is called by :py:func:`Certificate.objects.init() <django_ca.managers.CertificateManager.init>`, which passes along all parameters unchanged and saves the raw certificate to the database. Parameters ---------- ca : :py:class:`~django_ca.models.CertificateAuthority` The certificate authority to sign the certificate with. csr : str or :py:class:`~cg:cryptography.x509.CertificateSigningRequest` A valid CSR. If not already a :py:class:`~cg:cryptography.x509.CertificateSigningRequest`, the format is given by the ``csr_format`` parameter. expires : datetime, optional Datetime for when this certificate will expire, defaults to the ``CA_DEFAULT_EXPIRES`` setting. algorithm : str or :py:class:`~cg:cryptography.hazmat.primitives.hashes.HashAlgorithm`, optional Hash algorithm used when signing the certificate, passed to :py:func:`~django_ca.utils.parse_hash_algorithm`. The default is the value of the :ref:`CA_DIGEST_ALGORITHM <settings-ca-digest-algorithm>` setting. subject : dict or str or :py:class:`~django_ca.subject.Subject` Subject string, e.g. ``"/CN=example.com"`` or ``Subject("/CN=example.com")``. The value is actually passed to :py:class:`~django_ca.subject.Subject` if it is not already an instance of that class. If this value is not passed or if the value does not contain a CommonName, the first value of the ``subject_alternative_name`` parameter is used as CommonName. cn_in_san : bool, optional Wether the CommonName should also be included as subjectAlternativeName. The default is ``True``, but the parameter is ignored if no CommonName is given. This is typically set to ``False`` when creating a client certificate, where the subjects CommonName has no meaningful value as subjectAlternativeName. csr_format : :py:class:`~cg:cryptography.hazmat.primitives.serialization.Encoding`, optional The format of the CSR. The default is ``PEM``. subject_alternative_name : list of str or :py:class:`~django_ca.extensions.SubjectAlternativeName`, optional A list of alternative names for the certificate. The value is passed to :py:class:`~django_ca.extensions.SubjectAlternativeName` if not already an instance of that class. key_usage : str or dict or :py:class:`~django_ca.extensions.KeyUsage`, optional Value for the ``keyUsage`` X509 extension. The value is passed to :py:class:`~django_ca.extensions.KeyUsage` if not already an instance of that class. extended_key_usage : str or dict or :py:class:`~django_ca.extensions.ExtendedKeyUsage`, optional Value for the ``extendedKeyUsage`` X509 extension. The value is passed to :py:class:`~django_ca.extensions.ExtendedKeyUsage` if not already an instance of that class. tls_feature : str or dict or :py:class:`~django_ca.extensions.TLSFeature`, optional Value for the ``TLSFeature`` X509 extension. The value is passed to :py:class:`~django_ca.extensions.TLSFeature` if not already an instance of that class. ocsp_no_check : bool, optional Add the OCSPNoCheck flag, indicating that an OCSP client should trust this certificate for it's lifetime. This value only makes sense if you intend to use the certificate for an OCSP responder, the default is ``False``. See `RFC 6990, section 4.2.2.2.1 <https://tools.ietf.org/html/rfc6960#section-4.2.2.2>`_ for more information. issuer_url : ``str`` or ``bool``, optional Pass a custom issuer URL overriding the value configured in the CA or pass ``False`` to disable getting any issuer_url from the CA (e.g. to pass a custom extension in ``extra_extensions``). crl_url : ``str`` or ``bool``, optional Pass a custom CRL URL overriding the value configured in the CA or pass ``False`` to disable getting any issuer_url from the CA (e.g. to pass a custom extension in ``extra_extensions``). ocsp_url : ``str`` or ``bool``, optional Pass a custom OCSP URL overriding the value configured in the CA or pass ``False`` to disable getting any issuer_url from the CA (e.g. to pass a custom extension in ``extra_extensions``). issuer_alternative_name : ``str`` or ``bool``, optional Pass a custom issuer alternative name URL overriding the value configured in the CA or pass ``False`` to disable getting any issuer_url from the CA (e.g. to pass a custom extension in ``extra_extensions``). extra_extensions : list of :py:class:`cg:cryptography.x509.Extension` or \ :py:class:`django_ca.extensions.Extension`, optional An optional list of additional extensions to add to the certificate. password : bytes, optional Password used to load the private key of the certificate authority. If not passed, the private key is assumed to be unencrypted. Returns ------- cryptography.x509.Certificate The signed certificate. """ # TODO: This function does not check the expiry of the parent CA yet (manage.py sign_cert does) ######################## # Normalize parameters # ######################## if subject is None: subject = Subject() # we need a subject instance so we can possibly add the CN elif not isinstance(subject, Subject): subject = Subject(subject) if 'CN' not in subject and not subject_alternative_name: raise ValueError("Must name at least a CN or a subjectAlternativeName.") algorithm = parse_hash_algorithm(algorithm) # Normalize extensions to django_ca.extensions.Extension subclasses if key_usage and not isinstance(key_usage, KeyUsage): key_usage = KeyUsage(key_usage) if extended_key_usage and not isinstance(extended_key_usage, ExtendedKeyUsage): extended_key_usage = ExtendedKeyUsage(extended_key_usage) if tls_feature and not isinstance(tls_feature, TLSFeature): tls_feature = TLSFeature(tls_feature) if not subject_alternative_name: subject_alternative_name = SubjectAlternativeName([]) elif not isinstance(subject_alternative_name, SubjectAlternativeName): subject_alternative_name = SubjectAlternativeName(subject_alternative_name) # use first SAN as CN if CN is not set if 'CN' not in subject: subject['CN'] = subject_alternative_name.value[0].value elif cn_in_san and 'CN' in subject: # add CN to SAN if cn_in_san is True (default) try: cn_name = parse_general_name(subject['CN']) except idna.IDNAError: raise ValueError('%s: Could not parse CommonName as subjectAlternativeName.' % subject['CN']) else: if cn_name not in subject_alternative_name: subject_alternative_name.insert(0, cn_name) if issuer_url is None: issuer_url = ca.issuer_url if crl_url is None: crl_url = ca.crl_url if ocsp_url is None: ocsp_url = ca.ocsp_url if issuer_alternative_name is None: issuer_alternative_name = ca.issuer_alt_name ################ # Read the CSR # ################ if isinstance(csr, x509.CertificateSigningRequest): req = csr elif csr_format == Encoding.PEM: req = x509.load_pem_x509_csr(force_bytes(csr), default_backend()) elif csr_format == Encoding.DER: req = x509.load_der_x509_csr(force_bytes(csr), default_backend()) else: raise ValueError('Unknown CSR format passed: %s' % csr_format) ######################### # Send pre-issue signal # ######################### pre_issue_cert.send(sender=self.model, ca=ca, csr=csr, expires=expires, algorithm=algorithm, subject=subject, cn_in_san=cn_in_san, csr_format=csr_format, subject_alternative_name=subject_alternative_name, key_usage=key_usage, extended_key_usage=extended_key_usage, tls_featur=tls_feature, issuer_url=issuer_url, crl_url=crl_url, ocsp_url=ocsp_url, issuer_alternative_name=issuer_alternative_name, extra_extensions=extra_extensions, password=password) ####################### # Generate public key # ####################### public_key = req.public_key() builder = get_cert_builder(expires) builder = builder.public_key(public_key) builder = builder.issuer_name(ca.x509.subject) builder = builder.subject_name(subject.name) # Add extensions builder = builder.add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True) builder = builder.add_extension( x509.SubjectKeyIdentifier.from_public_key(public_key), critical=False) # Get authorityKeyIdentifier from subjectKeyIdentifier from signing CA builder = builder.add_extension(ca.get_authority_key_identifier(), critical=False) for critical, ext in self.get_common_extensions(issuer_url, crl_url, ocsp_url): builder = builder.add_extension(ext, critical=critical) if subject_alternative_name: builder = builder.add_extension(**subject_alternative_name.for_builder()) if key_usage: builder = builder.add_extension(**key_usage.for_builder()) if extended_key_usage: builder = builder.add_extension(**extended_key_usage.for_builder()) if tls_feature: builder = builder.add_extension(**tls_feature.for_builder()) if issuer_alternative_name: issuer_alt_name = IssuerAlternativeName(issuer_alternative_name) builder = builder.add_extension(**issuer_alt_name.for_builder()) if ocsp_no_check: builder = builder.add_extension(**OCSPNoCheck().for_builder()) if extra_extensions: builder = self._extra_extensions(builder, extra_extensions) ################### # Sign public key # ################### cert = builder.sign(private_key=ca.key(password), algorithm=algorithm, backend=default_backend()) return cert, req
from cryptography import x509 from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes if __name__ == "__main__": csrDER = open("/tmp/Signed-PKC-CSR.der", "rb").read() csr = x509.load_der_x509_csr(csrDER, default_backend()) print(csr) #isinstance( csr.signature_hash_algorithm, hashes.SHA256 )
def get_cert_by_csr(self, csr: typing.Union[str, bytes], challenge_handler: ChallengeHandlerBase, output_name: str = None) -> bytes: """Get certificate by csr. :param csr: The path to the csr file or the content of a csr file. :param challenge_handler: The challenge handler to handle challenge. :param output_name: The output certificate name. If an empty string ('') is provided, {Common Name}.{Timestamp}.crt will be used. If None is provided, the certificate will not be written to a file. :raises RuntimeError: If the order status is not valid after finalization. :return: The bytes representing the certificate. """ # read csr self.__log.info('Loading CSR.') if isinstance(csr, str) and csr.startswith('-----BEGIN CERTIFICATE REQUEST-----'): csr = csr.encode() if isinstance(csr, str): with open(csr, 'rb') as file: csr = file.read() try: self.__log.debug('Trying to loading CSR using der format.') csr = x509.load_der_x509_csr(csr, backends.default_backend()) except ValueError: self.__log.debug('CSR is not in der format, trying to loading CSR using pem format.') csr = x509.load_pem_x509_csr(csr, backends.default_backend()) # get domains from csr domains = set() cn = csr.subject.get_attributes_for_oid(x509.oid.NameOID.COMMON_NAME)[0].value domains.add(cn) # add commonName self.__log.debug(f'commonName: {cn}') # add SAN if existed try: san = csr.extensions.get_extension_for_class(x509.SubjectAlternativeName).value for i in san: domains.add(i.value) self.__log.debug(f'subjectAlternativeName: {i.value}') except x509.extensions.ExtensionNotFound: self.__log.debug('No subjectAlternativeName found in CSR.') pass # process order self.__log.info(f'All domains in CSR: {str(domains)}') r_order = self.process_order(domains, challenge_handler) # finalize order by sending csr to server. self.__log.info('Finalizing order.') csr = base64.urlsafe_b64encode(csr.public_bytes(serialization.Encoding.DER)).decode().rstrip('=') r_order = self.__netio.send_request({'csr': csr}, AcmeAction.VariableUrlAction, r_order.content['finalize']) # poll order status retry_counter = 5 while r_order.content['status'] == 'processing' and retry_counter > 0: time.sleep(int(r_order.headers.get('Retry-After', '5'))) self.__log.debug('Order is processing, polling order status.') r_order = self.__netio.send_request('', AcmeAction.VariableUrlAction, r_order.headers['Location']) retry_counter -= 1 if r_order.content['status'] != 'valid': raise RuntimeError(f'Order status is not valid after finalization. {r_order.headers["Location"]}, ' f'status: {r_order.content["status"]}.') # download certificate and write it to a file. self.__log.info('Certificate is issued.') # TODO: send download-certificate request using 'Accept: application/pem-certificate-chain' header. r_cert = self.__netio.send_request('', AcmeAction.VariableUrlAction, r_order.content['certificate'], False) if output_name is not None: output_name = output_name or f'{cn}.{str(int(time.time()))}.crt' with open(output_name, 'wb') as file: file.write(r_cert.content) return r_cert.content
def loaded(self) -> x509.CertificateSigningRequest: """This CSR as :py:class:`cg:cryptography.x509.CertificateSigningRequest`.""" if self._loaded is None: self._loaded = x509.load_der_x509_csr(self._bytes, default_backend()) return self._loaded
def decode_csr(b64der): return x509.load_der_x509_csr(josepy.json_util.decode_b64jose(b64der))
def process_result_value(self, value: bytes, dialect) -> x509.CertificateSigningRequest: return x509.load_der_x509_csr(value, default_backend())
def sign_cert(self, ca, csr, expires, algorithm, subject=None, cn_in_san=True, csr_format=Encoding.PEM, subjectAltName=None, keyUsage=None, extendedKeyUsage=None, tls_features=None, password=None): """Create a signed certificate from a CSR. X509 extensions (`key_usage`, `ext_key_usage`) may either be None (in which case they are not added) or a tuple with the first value being a bool indicating if the value is critical and the second value being a byte-array indicating the extension value. Example:: (True, b'value') Parameters ---------- ca : :py:class:`~django_ca.models.CertificateAuthority` The certificate authority to sign the certificate with. csr : str A valid CSR. The format is given by the ``csr_format`` parameter. expires : int When the certificate should expire (passed to :py:func:`~django_ca.utils.get_cert_builder`). algorithm : {'sha512', 'sha256', ...} Algorithm used to sign the certificate. The default is the CA_DIGEST_ALGORITHM setting. subject : dict, optional The Subject to use in the certificate. The keys of this dict are the fields of an X509 subject, that is `"C"`, `"ST"`, `"L"`, `"OU"` and `"CN"`. If ommited or if the value does not contain a `"CN"` key, the first value of the `subjectAltName` parameter is used as CommonName (and is obviously mandatory in this case). cn_in_san : bool, optional Wether the CommonName should also be included as subjectAlternativeName. The default is `True`, but the parameter is ignored if no CommonName is given. This is typically set to `False` when creating a client certificate, where the subjects CommonName has no meaningful value as subjectAltName. csr_format : :py:class:`~cryptography:cryptography.hazmat.primitives.serialization.Encoding`, optional The format of the CSR. The default is ``PEM``. subjectAltName : list of str, optional A list of values for the subjectAltName extension. Values are passed to :py:func:`~django_ca.utils.parse_general_name`, see function documentation for how this value is parsed. keyUsage : tuple or None Value for the `keyUsage` X509 extension. See description for format details. extendedKeyUsage : tuple or None Value for the `extendedKeyUsage` X509 extension. See description for format details. tls_features : tuple Value for the `TLS Feature` X509 extension. See description for format details. password : bytes, optional Password used to load the private key of the certificate authority. If not passed, the private key is assumed to be unencrypted. Returns ------- cryptography.x509.Certificate The signed certificate. """ if subject is None: subject = {} if not subject.get('CN') and not subjectAltName: raise ValueError("Must name at least a CN or a subjectAltName.") if subjectAltName: subjectAltName = [ parse_general_name(san) for san in subjectAltName ] else: subjectAltName = [] # so we can append the CN if requested if not subject.get('CN'): # use first SAN as CN if CN is not set subject['CN'] = subjectAltName[0].value elif cn_in_san and subject.get( 'CN'): # add CN to SAN if cn_in_san is True (default) try: cn_name = parse_general_name(subject['CN']) except idna.IDNAError: raise ValueError( '%s: Could not parse CommonName as subjectAltName.' % subject['CN']) else: if cn_name not in subjectAltName: subjectAltName.insert(0, cn_name) if csr_format == Encoding.PEM: req = x509.load_pem_x509_csr(force_bytes(csr), default_backend()) elif csr_format == Encoding.DER: req = x509.load_der_x509_csr(force_bytes(csr), default_backend()) else: raise ValueError('Unknown CSR format passed: %s' % csr_format) public_key = req.public_key() builder = get_cert_builder(expires) builder = builder.public_key(public_key) builder = builder.issuer_name(ca.x509.subject) builder = builder.subject_name(x509_name(subject)) # Add extensions builder = builder.add_extension(x509.BasicConstraints( ca=False, path_length=None), critical=True) builder = builder.add_extension( x509.SubjectKeyIdentifier.from_public_key(public_key), critical=False) # Get authorityKeyIdentifier from subjectKeyIdentifier from signing CA ca_subject_key_id = ca.x509.extensions.get_extension_for_oid( ExtensionOID.SUBJECT_KEY_IDENTIFIER) auth_key_id = x509.AuthorityKeyIdentifier( key_identifier=ca_subject_key_id.value.digest, authority_cert_issuer=None, authority_cert_serial_number=None) builder = builder.add_extension(auth_key_id, critical=False) for critical, ext in self.get_common_extensions( ca.issuer_url, ca.crl_url, ca.ocsp_url): builder = builder.add_extension(ext, critical=critical) if subjectAltName: builder = builder.add_extension( x509.SubjectAlternativeName(subjectAltName), critical=False) if keyUsage: critical, values = keyUsage params = {v: False for v in KEY_USAGE_MAPPING.values()} for value in [KEY_USAGE_MAPPING[k] for k in values.split(',')]: params[value] = True builder = builder.add_extension(x509.KeyUsage(**params), critical=critical) if extendedKeyUsage: critical, usages = extendedKeyUsage usages = [EXTENDED_KEY_USAGE_MAPPING[u] for u in usages.split(',')] builder = builder.add_extension(x509.ExtendedKeyUsage(usages), critical=critical) if tls_features: critical, features = tls_features features = [TLS_FEATURE_MAPPING[f] for f in features.split(',')] builder = builder.add_extension(TLSFeature(features), critical=critical) if ca.issuer_alt_name: builder = builder.add_extension(x509.IssuerAlternativeName( [parse_general_name(ca.issuer_alt_name)]), critical=False) return builder.sign(private_key=ca.key(password), algorithm=algorithm, backend=default_backend()), req
def handle( # type: ignore[override] # pylint: disable=too-many-arguments,too-many-locals self, ca: CertificateAuthority, subject: typing.Optional[Subject], expires: timedelta, watch: typing.List[str], password: typing.Optional[bytes], encoding: typing.Optional[Encoding], cn_in_san: bool, csr_path: str, profile: typing.Optional[str], out: typing.Optional[str], **options: typing.Any ) -> None: if ca.expires < timezone.now(): raise CommandError("Certificate Authority has expired.") if ca.revoked: raise CommandError("Certificate Authority is revoked.") if encoding is not None: warnings.warn( "--csr-format option is deprecated and will be removed in django-ca 1.20.0.", category=RemovedInDjangoCA120Warning, ) self.test_options(ca=ca, expires=expires, password=password, **options) subject = subject or Subject() # get list of watchers watchers = [Watcher.from_addr(addr) for addr in watch] # get extensions based on profiles extensions: typing.List[Extension[x509.ExtensionType, typing.Any, typing.Any]] = [] for ext in self.sign_extensions: if options[ext.key]: extensions.append(options[ext.key]) if "CN" not in subject and not options[SubjectAlternativeName.key]: raise CommandError("Must give at least a CN in --subject or one or more --alt arguments.") # Read the CSR if csr_path == "-": self.stdout.write("Please paste the CSR:") csr_bytes = b"" while True: csr_bytes += sys.stdin.buffer.read(1) # COVERAGE NOTE: mock function always returns the full string, so we always break right away if csr_bytes.strip().endswith(b"-----END CERTIFICATE REQUEST-----"): # pragma: no branch break else: with open(csr_path, "rb") as csr_stream: csr_bytes = csr_stream.read() if csr_bytes.startswith(b"-----BEGIN CERTIFICATE REQUEST-----"): csr = x509.load_pem_x509_csr(csr_bytes, default_backend()) else: csr = x509.load_der_x509_csr(csr_bytes, default_backend()) try: cert = Certificate.objects.create_cert( ca, csr, profile=profiles[profile], cn_in_san=cn_in_san, # TODO: since expires option has a default, it currently overrides profile values expires=expires, extensions=extensions, password=password, subject=subject, ) except Exception as ex: raise CommandError(ex) from ex cert.watchers.add(*watchers) if out: with open(out, "w") as stream: stream.write(cert.pub.pem) else: self.stdout.write(cert.pub.pem)
def scep(): storage = FileStorage(current_app.config['SCEPY_CA_ROOT']) op = request.args.get('operation') current_app.logger.info("Operation: %s, From: %s, User-Agent: %s", op, request.remote_addr, request.user_agent) dump_dir = current_app.config.get('SCEPY_DUMP_DIR', None) if dump_dir is not None and not os.path.exists(dump_dir): current_app.logger.debug("Creating dir for request dumps: %s", dump_dir) os.mkdir(dump_dir) dump_filename_prefix = "request-{}".format( datetime.datetime.now().timestamp()) if storage.exists(): g.ca = CertificateAuthority(storage) else: subject = x509.Name([ x509.NameAttribute(x509.NameOID.COMMON_NAME, current_app.config['SCEPY_CA_X509_CN']), x509.NameAttribute(x509.NameOID.ORGANIZATION_NAME, current_app.config['SCEPY_CA_X509_O']), x509.NameAttribute(x509.NameOID.COUNTRY_NAME, current_app.config['SCEPY_CA_X509_C']) ]) g.ca = CertificateAuthority.create(storage, subject) ca = g.ca if op == 'GetCACert': certs = [ca.certificate] if len(certs) == 1 and not current_app.config.get( 'SCEPY_FORCE_DEGENERATE_FOR_SINGLE_CERT', False): return Response(certs[0].public_bytes(Encoding.DER), mimetype='application/x-x509-ca-cert') elif len(certs): raise ValueError( 'cryptography cannot produce degenerate pkcs7 certs') # p7_degenerate = degenerate_pkcs7_der(certs) # return Response(p7_degenerate, mimetype='application/x-x509-ca-ra-cert') elif op == 'GetCACaps': return Response('\n'.join(CACAPS), mimetype='text/plain') elif op == 'PKIOperation': if request.method == 'GET': msg = request.args.get('message') # note: OS X improperly encodes the base64 query param by not # encoding spaces as %2B and instead leaving them as +'s msg = b64decode(msg.replace(' ', '+')) elif request.method == 'POST': # workaround for Flask/Werkzeug lack of chunked handling if 'chunked' in request.headers.get('Transfer-Encoding', ''): msg = request.environ['body_copy'] else: msg = request.data if dump_dir is not None: filename = "{}.bin".format(dump_filename_prefix) current_app.logger.debug('Dumping request to {}'.format( os.path.join(dump_dir, filename))) with open(os.path.join(dump_dir, filename), 'wb') as fd: fd.write(msg) req = SCEPMessage.parse(msg) current_app.logger.debug('Message Type: %s', req.message_type) print(req.debug()) if req.message_type == MessageType.PKCSReq or req.message_type == MessageType.RenewalReq: cakey = ca.private_key cacert = ca.certificate der_req = req.get_decrypted_envelope_data( cacert, cakey, ) if dump_dir is not None: filename = os.path.join(dump_dir, '{}.csr'.format(dump_filename_prefix)) current_app.logger.debug( 'Dumping CertificateSigningRequest to {}'.format( os.path.join(dump_dir, filename))) with open(filename, 'wb') as fd: fd.write(der_req) cert_req = x509.load_der_x509_csr(der_req, backend=default_backend()) req_info_bytes = cert_req.tbs_certrequest_bytes # Check the challenge password (unless it is a renewal) if 'SCEPY_CHALLENGE' in current_app.config and req.message_type == MessageType.PKCSReq: req_info = CertificationRequestInfo.load(req_info_bytes) for attr in req_info['attributes']: if attr['type'].native == 'challenge_password': assert len(attr['values']) == 1 challenge_password = attr['values'][0].native if challenge_password != current_app.config[ 'SCEPY_CHALLENGE']: current_app.logger.warning( 'Client did not send the correct challenge') signer = Signer(cacert, cakey, 'sha512') reply = PKIMessageBuilder().message_type( MessageType.CertRep).transaction_id( req.transaction_id).pki_status( PKIStatus.FAILURE, FailInfo.BadRequest).recipient_nonce( req.sender_nonce).add_signer( signer).finalize() return Response( reply.dump(), mimetype='application/x-pki-message') else: current_app.logger.debug( 'Client sent correct challenge') break current_app.logger.warning( 'Client did not send any challenge password, but there was one configured' ) signer = Signer(cacert, cakey, 'sha1') reply = PKIMessageBuilder().message_type( MessageType.CertRep).transaction_id( req.transaction_id).pki_status( PKIStatus.FAILURE, FailInfo.BadRequest).recipient_nonce( req.sender_nonce).add_signer( signer).finalize() return Response(reply.dump(), mimetype='application/x-pki-message') new_cert = ca.sign(cert_req, 'sha1') degenerate = create_degenerate_pkcs7(new_cert, ca.certificate) if dump_dir is not None: filename = os.path.join( dump_dir, '{}-degenerate.bin'.format(dump_filename_prefix)) with open(filename, 'wb') as fd: fd.write(degenerate.dump()) envelope, _, _ = PKCSPKIEnvelopeBuilder().encrypt( degenerate.dump(), 'aes256').add_recipient(req.certificates[0]).finalize() signer = Signer(cacert, cakey, 'sha1') reply = PKIMessageBuilder().message_type( MessageType.CertRep).transaction_id( req.transaction_id).pki_status( PKIStatus.SUCCESS).recipient_nonce( req.sender_nonce).sender_nonce().pki_envelope( envelope).add_signer(signer).finalize() if dump_dir is not None: filename = os.path.join( dump_dir, '{}-reply.bin'.format(dump_filename_prefix)) with open(filename, 'wb') as fd: fd.write(reply.dump()) current_app.logger.debug("Sending CertRep") return Response(reply.dump(), mimetype='application/x-pki-message') else: current_app.logger.error('unhandled SCEP message type: %d', req.message_type) return '' else: abort(404, 'unknown SCEP operation')