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
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')
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
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 })
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 })