Ejemplo n.º 1
0
    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')
            ])
Ejemplo n.º 2
0
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())
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
 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
Ejemplo n.º 5
0
    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())
Ejemplo n.º 6
0
    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)
Ejemplo n.º 7
0
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
Ejemplo n.º 8
0
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"
Ejemplo n.º 9
0
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())
Ejemplo n.º 10
0
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)
Ejemplo n.º 11
0
    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)
Ejemplo n.º 12
0
    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
Ejemplo n.º 13
0
    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
Ejemplo n.º 14
0
    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)
Ejemplo n.º 15
0
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)
Ejemplo n.º 16
0
    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
Ejemplo n.º 17
0
    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())
Ejemplo n.º 18
0
    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'),
        ]
Ejemplo n.º 19
0
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')
Ejemplo n.º 20
0
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() +
Ejemplo n.º 21
0
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')
Ejemplo n.º 22
0
    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
Ejemplo n.º 23
0
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 )
Ejemplo n.º 24
0
Archivo: AcmeN.py Proyecto: CBPJ/AcmeN
    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
Ejemplo n.º 25
0
 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
Ejemplo n.º 26
0
def decode_csr(b64der):
    return x509.load_der_x509_csr(josepy.json_util.decode_b64jose(b64der))
Ejemplo n.º 27
0
 def process_result_value(self, value: bytes,
                          dialect) -> x509.CertificateSigningRequest:
     return x509.load_der_x509_csr(value, default_backend())
Ejemplo n.º 28
0
    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
Ejemplo n.º 29
0
    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)
Ejemplo n.º 30
0
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')