예제 #1
0
def return_ocsp_request_object(cert, issuer, algo, nonce=True):
    cert_details = ocsp.CertId({
        'issuer_name_hash':
        getattr(cert.issuer, algo),
        'issuer_key_hash':
        getattr(issuer.public_key, algo),
        'hash_algorithm':
        algos.DigestAlgorithm({'algorithm': algo}),
        'serial_number':
        cert.serial_number,
    })

    request_obj = ocsp.Request({
        'req_cert': cert_details,
    })

    tbs_request_obj = ocsp.TBSRequest(
        {'request_list': ocsp.Requests([request_obj])})

    if nonce:
        nonce_extension = ocsp.TBSRequestExtension({
            'extn_id':
            'nonce',
            'critical':
            True,
            'extn_value':
            core.OctetString(os.urandom(16)),
        })
        tbs_request_obj['request_extensions']: ocsp.TBSRequestExtensions(
            [nonce_extension])

    ocsp_request_obj = ocsp.OCSPRequest({
        'tbs_request': tbs_request_obj,
    })

    return ocsp_request_obj
예제 #2
0
    def update_ocsp(self, context: CertificateContext):
        log.info('Update OCSP Response')
        for item in context:  # type: CertificateItem
            # ignore ocsp if explicitly disabled for this certificate
            if not context.config.ocsp_responder_urls:
                item.ocsp_response = None
                continue

            with log.prefix(f'  - [{item.type.upper()}] '):
                if not item.certificate:
                    log.warning(
                        "certificate not found. Can't update OCSP response")
                    continue

                ocsp_response = item.ocsp_response
                if (ocsp_response
                        and ('good' == ocsp_response.response_status.lower())
                        and (ocsp_response.serial_number
                             == item.certificate.serial_number)):
                    last_update = ocsp_response.this_update
                    log.debug('Have stapled OCSP response updated at %s',
                              last_update.strftime('%Y-%m-%d %H:%M:%S UTC'))
                else:
                    last_update = None

                ocsp_urls = (item.certificate.ocsp_urls
                             or context.config.ocsp_responder_urls)
                if not ocsp_urls:
                    log.warning('No OCSP responder URL and no default set')
                    continue

                chain = item.chain
                issuer_certificate = chain[
                    0] if chain else context.root_certificate(item.type)
                issuer_name = issuer_certificate.x509_certificate.subject.public_bytes(
                    default_backend())
                issuer_key = issuer_certificate.x509_certificate.public_key(
                ).public_bytes(serialization.Encoding.DER,
                               serialization.PublicFormat.PKCS1)
                tbs_request = ocsp.TBSRequest({
                    'request_list': [{
                        'req_cert': {
                            'hash_algorithm': {
                                'algorithm': 'sha1'
                            },
                            'issuer_name_hash':
                            hashlib.sha1(issuer_name).digest(),
                            'issuer_key_hash':
                            hashlib.sha1(issuer_key).digest(),
                            'serial_number': item.certificate.serial_number,
                        },
                        'single_request_extensions': None
                    }],
                    'request_extensions':
                    None  # [{'extn_id': 'nonce', 'critical': False, 'extn_value': os.urandom(16)}]
                    # we don't appear to be getting the nonce back, so don't send it
                })
                ocsp_request = ocsp.OCSPRequest({
                    'tbs_request': tbs_request,
                    'optional_signature': None
                })

                for ocsp_url in ocsp_urls:
                    ocsp_response = OCSP.fetch(ocsp_url, ocsp_request,
                                               last_update)
                    if ocsp_response:
                        if 'successful' != ocsp_response.response_status:
                            log.warning('OCSP request received "%s" from %s',
                                        ocsp_response.response_status,
                                        ocsp_url)
                            continue

                        ocsp_status = ocsp_response.cert_status
                        this_update = ocsp_response.this_update
                        log.debug(
                            'Retrieved OCSP status "%s" from %s updated at %s',
                            ocsp_status.upper(), ocsp_url,
                            this_update.strftime('%Y-%m-%d %H:%M:%S UTC'))
                        if 'good' != ocsp_status.lower():
                            log.warning(
                                'certificate has OCSP status "%s" from %s updated at %s',
                                ocsp_status.upper(), ocsp_url,
                                this_update.strftime('%Y-%m-%d %H:%M:%S UTC'))
                            continue

                        if this_update == last_update:
                            log.debug(
                                'OCSP response from %s has not been updated',
                                ocsp_url)
                            break

                        log.progress('Updating OCSP response from %s',
                                     ocsp_url)
                        item.ocsp_response = ocsp_response
                        break

                    elif ocsp_response is False:
                        log.debug('OCSP response from %s has not been updated',
                                  ocsp_url)
                        break
                else:
                    log.warning('Unable to retrieve OCSP response')
예제 #3
0
def fetch(cert,
          issuer,
          hash_algo='sha1',
          nonce=True,
          user_agent=None,
          timeout=10):
    """
    Fetches an OCSP response for a certificate

    :param cert:
        An asn1cyrpto.x509.Certificate object to get an OCSP reponse for

    :param issuer:
        An asn1crypto.x509.Certificate object that is the issuer of cert

    :param hash_algo:
        A unicode string of "sha1" or "sha256"

    :param nonce:
        A boolean - if the nonce extension should be used to prevent replay
        attacks

    :param user_agent:
        The HTTP user agent to use when requesting the OCSP response. If None,
        a default is used in the format "certvalidation 1.0.0".

    :param timeout:
        The number of seconds after which an HTTP request should timeout

    :raises:
        urllib.error.URLError/urllib2.URLError - when a URL/HTTP error occurs
        socket.error - when a socket error occurs

    :return:
        An asn1crypto.ocsp.OCSPResponse object
    """

    if not isinstance(cert, x509.Certificate):
        raise TypeError(
            'cert must be an instance of asn1crypto.x509.Certificate, not %s' %
            type_name(cert))

    if not isinstance(issuer, x509.Certificate):
        raise TypeError(
            'issuer must be an instance of asn1crypto.x509.Certificate, not %s'
            % type_name(issuer))

    if hash_algo not in set(['sha1', 'sha256']):
        raise ValueError('hash_algo must be one of "sha1", "sha256", not %s' %
                         repr(hash_algo))

    if not isinstance(nonce, bool):
        raise TypeError('nonce must be a bool, not %s' % type_name(nonce))

    if user_agent is None:
        user_agent = 'certvalidator %s' % __version__
    elif not isinstance(user_agent, str_cls):
        raise TypeError('user_agent must be a unicode string, not %s' %
                        type_name(user_agent))

    cert_id = ocsp.CertId({
        'hash_algorithm':
        algos.DigestAlgorithm({'algorithm': hash_algo}),
        'issuer_name_hash':
        getattr(cert.issuer, hash_algo),
        'issuer_key_hash':
        getattr(issuer.public_key, hash_algo),
        'serial_number':
        cert.serial_number,
    })

    request = ocsp.Request({
        'req_cert': cert_id,
    })
    tbs_request = ocsp.TBSRequest({
        'request_list': ocsp.Requests([request]),
    })

    if nonce:
        nonce_extension = ocsp.TBSRequestExtension({
            'extn_id':
            'nonce',
            'critical':
            False,
            'extn_value':
            core.OctetString(core.OctetString(os.urandom(16)).dump())
        })
        tbs_request['request_extensions'] = ocsp.TBSRequestExtensions(
            [nonce_extension])

    ocsp_request = ocsp.OCSPRequest({
        'tbs_request': tbs_request,
    })

    last_e = None
    for ocsp_url in cert.ocsp_urls:
        try:
            request = Request(ocsp_url)
            request.add_header('Accept', 'application/ocsp-response')
            request.add_header('Content-Type', 'application/ocsp-request')
            request.add_header('User-Agent', user_agent)
            response = urlopen(request, ocsp_request.dump(), timeout)
            ocsp_response = ocsp.OCSPResponse.load(response.read())
            request_nonce = ocsp_request.nonce_value
            if ocsp_response['response_status'].native == 'unauthorized':
                raise errors.OCSPNoMatchesError(
                    'Unable to verify OCSP response since the responder returned unauthorized'
                )
            response_nonce = ocsp_response.nonce_value
            if request_nonce and response_nonce and request_nonce.native != response_nonce.native:
                raise errors.OCSPValidationError(
                    'Unable to verify OCSP response since the request and response nonces do not match'
                )
            return ocsp_response

        except (URLError) as e:
            last_e = e

    raise last_e
예제 #4
0
    def build(self,
              requestor_private_key=None,
              requestor_certificate=None,
              other_certificates=None):
        """
        Validates the request information, constructs the ASN.1 structure and
        then optionally signs it.

        The requestor_private_key, requestor_certificate and other_certificates
        params are all optional and only necessary if the request needs to be
        signed. Signing a request is uncommon for OCSP requests related to web
        TLS connections.

        :param requestor_private_key:
            An asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey
            object for the private key to sign the request with

        :param requestor_certificate:
            An asn1crypto.x509.Certificate or oscrypto.asymmetric.Certificate
            object of the certificate associated with the private key

        :param other_certificates:
            A list of asn1crypto.x509.Certificate or
            oscrypto.asymmetric.Certificate objects that may be useful for the
            OCSP server to verify the request signature. Intermediate
            certificates would be specified here.

        :return:
            An asn1crypto.ocsp.OCSPRequest object of the request
        """
        def _make_extension(name, value):
            return {'extn_id': name, 'critical': False, 'extn_value': value}

        tbs_request_extensions = []
        request_extensions = []
        has_nonce = False

        for name, value in self._tbs_request_extensions.items():
            if name == 'nonce':
                has_nonce = True
            tbs_request_extensions.append(_make_extension(name, value))
        if self._nonce and not has_nonce:
            tbs_request_extensions.append(
                _make_extension('nonce', util.rand_bytes(16)))

        if not tbs_request_extensions:
            tbs_request_extensions = None

        for name, value in self._request_extensions.items():
            request_extensions.append(_make_extension(name, value))

        if not request_extensions:
            request_extensions = None

        tbs_request = ocsp.TBSRequest({
            'request_list': [{
                'req_cert': {
                    'hash_algorithm': {
                        'algorithm': self._key_hash_algo
                    },
                    'issuer_name_hash':
                    getattr(cert.issuer, self._key_hash_algo),
                    'issuer_key_hash':
                    getattr(self._issuer.public_key, self._key_hash_algo),
                    'serial_number':
                    cert.serial_number,
                },
                'single_request_extensions': request_extensions
            } for cert in self._certificates],
            'request_extensions':
            tbs_request_extensions
        })
        signature = None

        if requestor_private_key or requestor_certificate or other_certificates:
            is_oscrypto = isinstance(requestor_private_key,
                                     asymmetric.PrivateKey)
            if not isinstance(requestor_private_key,
                              keys.PrivateKeyInfo) and not is_oscrypto:
                raise TypeError(
                    _pretty_message(
                        '''
                    requestor_private_key must be an instance of
                    asn1crypto.keys.PrivateKeyInfo or
                    oscrypto.asymmetric.PrivateKey, not %s
                    ''', _type_name(requestor_private_key)))

            cert_is_oscrypto = isinstance(requestor_certificate,
                                          asymmetric.Certificate)
            if not isinstance(requestor_certificate,
                              x509.Certificate) and not cert_is_oscrypto:
                raise TypeError(
                    _pretty_message(
                        '''
                    requestor_certificate must be an instance of
                    asn1crypto.x509.Certificate or
                    oscrypto.asymmetric.Certificate, not %s
                    ''', _type_name(requestor_certificate)))

            if other_certificates is not None and not isinstance(
                    other_certificates, list):
                raise TypeError(
                    _pretty_message(
                        '''
                    other_certificates must be a list of
                    asn1crypto.x509.Certificate or
                    oscrypto.asymmetric.Certificate objects, not %s
                    ''', _type_name(other_certificates)))

            if cert_is_oscrypto:
                requestor_certificate = requestor_certificate.asn1

            tbs_request['requestor_name'] = x509.GeneralName(
                name='directory_name', value=requestor_certificate.subject)

            certificates = [requestor_certificate]

            for other_certificate in other_certificates:
                other_cert_is_oscrypto = isinstance(other_certificate,
                                                    asymmetric.Certificate)
                if not isinstance(
                        other_certificate,
                        x509.Certificate) and not other_cert_is_oscrypto:
                    raise TypeError(
                        _pretty_message(
                            '''
                        other_certificate must be an instance of
                        asn1crypto.x509.Certificate or
                        oscrypto.asymmetric.Certificate, not %s
                        ''', _type_name(other_certificate)))
                if other_cert_is_oscrypto:
                    other_certificate = other_certificate.asn1
                certificates.append(other_certificate)

            signature_algo = requestor_private_key.algorithm
            if signature_algo == 'ec':
                signature_algo = 'ecdsa'

            signature_algorithm_id = '%s_%s' % (self._hash_algo,
                                                signature_algo)

            if requestor_private_key.algorithm == 'rsa':
                sign_func = asymmetric.rsa_pkcs1v15_sign
            elif requestor_private_key.algorithm == 'dsa':
                sign_func = asymmetric.dsa_sign
            elif requestor_private_key.algorithm == 'ec':
                sign_func = asymmetric.ecdsa_sign

            if not is_oscrypto:
                requestor_private_key = asymmetric.load_private_key(
                    requestor_private_key)
            signature_bytes = sign_func(requestor_private_key,
                                        tbs_request.dump(), self._hash_algo)

            signature = ocsp.Signature({
                'signature_algorithm': {
                    'algorithm': signature_algorithm_id
                },
                'signature': signature_bytes,
                'certs': certificates
            })

        return ocsp.OCSPRequest({
            'tbs_request': tbs_request,
            'optional_signature': signature
        })
예제 #5
0
    def build(self,
              requestor_private_key=None,
              requestor_certificate=None,
              other_certificates=None):
        def _make_extension(name, value):
            return {'extn_id': name, 'critical': False, 'extn_value': value}

        tbs_request_extensions = []
        request_extensions = []
        has_nonce = False

        for name, value in self._tbs_request_extensions.items():
            if name == 'nonce':
                has_nonce = True
            tbs_request_extensions.append(_make_extension(name, value))
        if self._nonce and not has_nonce:
            tbs_request_extensions.append(
                _make_extension('nonce', util.rand_bytes(16)))

        if not tbs_request_extensions:
            tbs_request_extensions = None

        for name, value in self._request_extensions.items():
            request_extensions.append(_make_extension(name, value))

        if not request_extensions:
            request_extensions = None

        tbs_request = ocsp.TBSRequest({
            'request_list': [{
                'req_cert': {
                    'hash_algorithm': {
                        'algorithm': self._key_hash_algo
                    },
                    'issuer_name_hash':
                    getattr(self._certificate.issuer, self._key_hash_algo),
                    'issuer_key_hash':
                    getattr(self._issuer.public_key, self._key_hash_algo),
                    'serial_number':
                    self._certificate.serial_number,
                },
                'single_request_extensions': request_extensions
            }],
            'request_extensions':
            tbs_request_extensions
        })
        signature = None

        if requestor_private_key or requestor_certificate or other_certificates:
            is_oscrypto = isinstance(requestor_private_key,
                                     asymmetric.PrivateKey)
            if not isinstance(requestor_private_key,
                              keys.PrivateKeyInfo) and not is_oscrypto:
                raise TypeError(
                    _pretty_message(
                        '''
                    requestor_private_key must be an instance of
                    asn1crypto.keys.PrivateKeyInfo or
                    oscrypto.asymmetric.PrivateKey, not %s
                    ''', _type_name(requestor_private_key)))

            cert_is_oscrypto = isinstance(requestor_certificate,
                                          asymmetric.Certificate)
            if not isinstance(requestor_certificate,
                              x509.Certificate) and not cert_is_oscrypto:
                raise TypeError(
                    _pretty_message(
                        '''
                    requestor_certificate must be an instance of
                    asn1crypto.x509.Certificate or
                    oscrypto.asymmetric.Certificate, not %s
                    ''', _type_name(requestor_certificate)))

            if other_certificates is not None and not isinstance(
                    other_certificates, list):
                raise TypeError(
                    _pretty_message(
                        '''
                    other_certificates must be a list of
                    asn1crypto.x509.Certificate or
                    oscrypto.asymmetric.Certificate objects, not %s
                    ''', _type_name(other_certificates)))

            if cert_is_oscrypto:
                requestor_certificate = requestor_certificate.asn1

            tbs_request['requestor_name'] = x509.GeneralName(
                name='directory_name', value=requestor_certificate.subject)

            certificates = [requestor_certificate]

            for other_certificate in other_certificates:
                other_cert_is_oscrypto = isinstance(other_certificate,
                                                    asymmetric.Certificate)
                if not isinstance(
                        other_certificate,
                        x509.Certificate) and not other_cert_is_oscrypto:
                    raise TypeError(
                        _pretty_message(
                            '''
                        other_certificate must be an instance of
                        asn1crypto.x509.Certificate or
                        oscrypto.asymmetric.Certificate, not %s
                        ''', _type_name(other_certificate)))
                if other_cert_is_oscrypto:
                    other_certificate = other_certificate.asn1
                certificates.append(other_certificate)

            signature_algo = requestor_private_key.algorithm
            if signature_algo == 'ec':
                signature_algo = 'ecdsa'

            signature_algorithm_id = '%s_%s' % (self._hash_algo,
                                                signature_algo)

            if requestor_private_key.algorithm == 'rsa':
                sign_func = asymmetric.rsa_pkcs1v15_sign
            elif requestor_private_key.algorithm == 'dsa':
                sign_func = asymmetric.dsa_sign
            elif requestor_private_key.algorithm == 'ec':
                sign_func = asymmetric.ecdsa_sign

            if not is_oscrypto:
                requestor_private_key = asymmetric.load_private_key(
                    requestor_private_key)
            signature_bytes = sign_func(requestor_private_key,
                                        tbs_request.dump(), self._hash_algo)

            signature = ocsp.Signature({
                'signature_algorithm': {
                    'algorithm': signature_algorithm_id
                },
                'signature': signature_bytes,
                'certs': certificates
            })

        return ocsp.OCSPRequest({
            'tbs_request': tbs_request,
            'optional_signature': signature
        })