def delta_crl_url(self, value): if value is None: self._freshest_crl = None return if self._delta_crl_indicator is not None: raise ValueError( _pretty_message(''' delta_crl_url can not be set if delta_of is set ''')) if not isinstance(value, str_cls): raise TypeError( _pretty_message( ''' delta_crl_url must be a unicode string, not %s ''', _type_name(value))) general_names = x509.GeneralNames([ x509.GeneralName(name='uniform_resource_identifier', value=value) ]) distribution_point_name = x509.DistributionPointName( name='full_name', value=general_names) distribution_point = x509.DistributionPoint( {'distribution_point': distribution_point_name}) self._freshest_crl = x509.CRLDistributionPoints([distribution_point])
def _set_subject_alt(self, name, values): """ Replaces all existing asn1crypto.x509.GeneralName objects of the choice represented by the name parameter with the values :param name: A unicode string of the choice name of the x509.GeneralName object :param values: A list of unicode strings to use as the values for the new x509.GeneralName objects """ if self._subject_alt_name is not None: filtered_general_names = [] for general_name in self._subject_alt_name: if general_name.name != name: filtered_general_names.append(general_name) self._subject_alt_name = x509.GeneralNames(filtered_general_names) else: self._subject_alt_name = x509.GeneralNames() if values is not None: for value in values: new_general_name = x509.GeneralName(name=name, value=value) self._subject_alt_name.append(new_general_name) if len(self._subject_alt_name) == 0: self._subject_alt_name = None
def issuer_certificate_url(self, value): if value is not None and not isinstance(value, str_cls): raise TypeError( _pretty_message( ''' issuer_certificate_url must be a unicode string, not %s ''', _type_name(value))) self._authority_information_access = x509.AuthorityInfoAccessSyntax([{ 'access_method': 'ca_issuers', 'access_location': x509.GeneralName(name='uniform_resource_identifier', value=value) }])
def url(self, value): if not isinstance(value, str_cls): raise TypeError( _pretty_message( ''' url must be a unicode string, not %s ''', _type_name(value))) if self._issuing_distribution_point is None: general_names = x509.GeneralNames([ x509.GeneralName(name='uniform_resource_identifier', value=value) ]) distribution_point_name = x509.DistributionPointName( name='full_name', value=general_names) self._issuing_distribution_point = crl.IssuingDistributionPoint( {'distribution_point': distribution_point_name}) else: distribution_point_name = self._issuing_distribution_point[ 'distribution_point'] general_names = distribution_point_name.chosen general_names[0] = x509.GeneralName( name='uniform_resource_identifier', value=value)
def test_signing_cert_attr_malformed_issuer(): from asn1crypto import x509 cert = TESTING_CA.get_cert(CertLabel('signer1')) bogus_attr = as_signing_certificate_v2(cert) bogus_attr['certs'][0]['issuer_serial']['issuer'][0] = x509.GeneralName( {'dns_name': 'www.example.com'}) output = _tamper_with_signed_attrs('signing_certificate_v2', resign=True, replace_with=bogus_attr) r = PdfFileReader(output) emb = r.embedded_signatures[0] digest = emb.compute_digest() with pytest.raises(SignatureValidationError, match="Signing certificate attribute does not match "): validate_sig_integrity(emb.signer_info, emb.signer_cert, 'data', digest)
def _csr_info(self, subject, public_key, sans): """ Create the csr info portion of the certificate request"s ASN.1 structure :param X509Name subject: subject to add to the certificate request :param asymmetric.PublicKey public_key: public key to use when creating the certificate request"s signature :param sans: collection of dns names to insert into a subjAltName extension for the certificate request :type sans: None or list(str) or tuple(str) or set(str) :return: the certificate request info structure :rtype: csr.CertificationRequestInfo """ x509_subject = x509.Name.build(self._subject_as_dict(subject)) extensions = [(u"basic_constraints", x509.BasicConstraints({"ca": False}), False), (u"key_usage", x509.KeyUsage({"digital_signature", "key_encipherment"}), True), (u"extended_key_usage", x509.ExtKeyUsageSyntax([u"client_auth"]), False)] if sans: names = x509.GeneralNames() for san in sans: names.append( x509.GeneralName("dns_name", _bytes_to_unicode(san))) extensions.append((u"subject_alt_name", names, False)) return csr.CertificationRequestInfo({ "version": u"v1", "subject": x509_subject, "subject_pk_info": public_key.asn1, "attributes": [{ "type": u"extension_request", "values": [[self._create_extension(x) for x in extensions]] }] })
def ocsp_url(self, value): if value is None: self._authority_information_access = None return if not isinstance(value, str_cls): raise TypeError( _pretty_message( ''' ocsp_url must be a unicode string, not %s ''', _type_name(value))) access_description = x509.AccessDescription({ 'access_method': 'ocsp', 'access_location': x509.GeneralName(name='uniform_resource_identifier', value=value) }) self._authority_information_access = x509.AuthorityInfoAccessSyntax( [access_description])
def test_signing_cert_attr_malformed_issuer(): from asn1crypto import x509 cert = TESTING_CA.get_cert(CertLabel('signer1')) bogus_attr = as_signing_certificate_v2(cert) bogus_attr['certs'][0]['issuer_serial']['issuer'][0] = x509.GeneralName( {'dns_name': 'www.example.com'} ) output = _tamper_with_signed_attrs( 'signing_certificate_v2', resign=True, replace_with=bogus_attr ) r = PdfFileReader(output) emb = r.embedded_signatures[0] digest = emb.compute_digest() with pytest.raises( SignatureValidationError, match="Signing certificate attribute does not match ") as exc_info: validate_sig_integrity( emb.signer_info, emb.signer_cert, 'data', digest ) assert exc_info.value.ades_status == AdESStatus.INDETERMINATE assert exc_info.value.ades_subindication \ == AdESIndeterminate.NO_SIGNING_CERTIFICATE_FOUND
def as_signing_certificate_v2(cert: x509.Certificate, hash_algo='sha256') \ -> tsp.SigningCertificateV2: """ Format an ASN.1 ``SigningCertificateV2`` value, where the certificate is identified by the hash algorithm specified. :param cert: An X.509 certificate. :param hash_algo: Hash algorithm to use to digest the certificate. Default is SHA-256. :return: A :class:`tsp.SigningCertificateV2` object referring to the original certificate. """ # see RFC 5035 hash_spec = get_pyca_cryptography_hash(hash_algo) md = hashes.Hash(hash_spec) md.update(cert.dump()) digest_value = md.finalize() return tsp.SigningCertificateV2({ 'certs': [ tsp.ESSCertIDv2({ 'hash_algorithm': { 'algorithm': hash_algo }, 'cert_hash': digest_value, 'issuer_serial': { 'issuer': [x509.GeneralName({'directory_name': cert.issuer})], 'serial_number': cert['tbs_certificate']['serial_number'] } }) ] })
def as_signing_certificate(cert: x509.Certificate) -> tsp.SigningCertificate: """ Format an ASN.1 ``SigningCertificate`` object, where the certificate is identified by its SHA-1 digest. :param cert: An X.509 certificate. :return: A :class:`tsp.SigningCertificate` object referring to the original certificate. """ # see RFC 2634, § 5.4.1 return tsp.SigningCertificate({ 'certs': [ tsp.ESSCertID({ 'cert_hash': hashlib.sha1(cert.dump()).digest(), 'issuer_serial': { 'issuer': [x509.GeneralName({'directory_name': cert.issuer})], 'serial_number': cert['tbs_certificate']['serial_number'] } }) ] })
def _make_crl_distribution_points(self, name, value): """ Constructs an asn1crypto.x509.CRLDistributionPoints object :param name: A unicode string of the attribute name to use in exceptions :param value: Either a unicode string of a URL, or a 2-element tuple of a unicode string of a URL, plus an asn1crypto.x509.Certificate object that will be signing the CRL (for indirect CRLs). :return: None or an asn1crypto.x509.CRLDistributionPoints object """ if value is None: return None is_tuple = isinstance(value, tuple) if not is_tuple and not isinstance(value, str_cls): raise TypeError( _pretty_message( ''' %s must be a unicode string or tuple of (unicode string, asn1crypto.x509.Certificate), not %s ''', name, _type_name(value))) issuer = None if is_tuple: if len(value) != 2: raise ValueError( _pretty_message( ''' %s must be a unicode string or 2-element tuple, not a %s-element tuple ''', name, len(value))) if not isinstance(value[0], str_cls) or not isinstance( value[1], x509.Certificate): raise TypeError( _pretty_message( ''' %s must be a tuple of (unicode string, ans1crypto.x509.Certificate), not (%s, %s) ''', name, _type_name(value[0]), _type_name(value[1]))) url = value[0] issuer = value[1].subject else: url = value general_names = x509.GeneralNames( [x509.GeneralName(name='uniform_resource_identifier', value=url)]) distribution_point_name = x509.DistributionPointName( name='full_name', value=general_names) distribution_point = x509.DistributionPoint( {'distribution_point': distribution_point_name}) if issuer: distribution_point['crl_issuer'] = x509.GeneralNames( [x509.GeneralName(name='directory_name', value=issuer)]) return x509.CRLDistributionPoints([distribution_point])
def request_tsa_response(self, req: tsp.TimeStampReq) -> tsp.TimeStampResp: # We pretend that certReq is always true in the request # TODO generalise my detached signature logic to include cases like this # (see § 5.4 in RFC 5652) # TODO does the RFC status = tsp.PKIStatusInfo({'status': tsp.PKIStatus('granted')}) message_imprint: tsp.MessageImprint = req['message_imprint'] md_algorithm = message_imprint['hash_algorithm']['algorithm'].native digest_algorithm_obj = algos.DigestAlgorithm({ 'algorithm': md_algorithm }) dt = self.fixed_dt or datetime.now(tz=tzlocal.get_localzone()) tst_info = { 'version': 'v1', # See http://oidref.com/1.3.6.1.4.1.4146.2.2 # I don't really care too much, this is a testing device anyway 'policy': tsp.ObjectIdentifier('1.3.6.1.4.1.4146.2.2'), 'message_imprint': message_imprint, # should be sufficiently random (again, this is a testing class) 'serial_number': get_nonce(), 'gen_time': dt, 'tsa': x509.GeneralName( name='directory_name', value=self.tsa_cert.subject ) } try: tst_info['nonce'] = req['nonce'] except KeyError: pass tst_info = tsp.TSTInfo(tst_info) tst_info_data = tst_info.dump() message_digest = getattr(hashlib, md_algorithm)(tst_info_data).digest() signed_attrs = cms.CMSAttributes([ simple_cms_attribute('content_type', 'tst_info'), simple_cms_attribute( 'signing_time', cms.Time({'utc_time': core.UTCTime(dt)}) ), simple_cms_attribute( 'signing_certificate', general.as_signing_certificate(self.tsa_cert) ), simple_cms_attribute('message_digest', message_digest), ]) signature = asymmetric.rsa_pkcs1v15_sign( asymmetric.load_private_key(self.tsa_key), signed_attrs.dump(), md_algorithm ) sig_info = cms.SignerInfo({ 'version': 'v1', 'sid': cms.SignerIdentifier({ 'issuer_and_serial_number': cms.IssuerAndSerialNumber({ 'issuer': self.tsa_cert.issuer, 'serial_number': self.tsa_cert.serial_number, }) }), 'digest_algorithm': digest_algorithm_obj, 'signature_algorithm': algos.SignedDigestAlgorithm( {'algorithm': 'rsassa_pkcs1v15'} ), 'signed_attrs': signed_attrs, 'signature': signature }) certs = set(self.certs_to_embed) certs.add(self.tsa_cert) signed_data = { # must use v3 to get access to the EncapsulatedContentInfo construct 'version': 'v3', 'digest_algorithms': cms.DigestAlgorithms((digest_algorithm_obj,)), 'encap_content_info': cms.EncapsulatedContentInfo({ 'content_type': cms.ContentType('tst_info'), 'content': cms.ParsableOctetString(tst_info_data) }), 'certificates': certs, 'signer_infos': [sig_info] } tst = cms.ContentInfo({ 'content_type': cms.ContentType('signed_data'), 'content': cms.SignedData(signed_data) }) return tsp.TimeStampResp({'status': status, 'time_stamp_token': tst})
def request_tsa_response(self, req: tsp.TimeStampReq) -> tsp.TimeStampResp: # We pretend that certReq is always true in the request status = tsp.PKIStatusInfo({'status': tsp.PKIStatus('granted')}) message_imprint: tsp.MessageImprint = req['message_imprint'] md_algorithm = self.md_algorithm digest_algorithm_obj = algos.DigestAlgorithm({ 'algorithm': md_algorithm }) dt = self.fixed_dt or datetime.now(tz=tzlocal.get_localzone()) tst_info = { 'version': 'v1', 'policy': self.policy, 'message_imprint': message_imprint, 'serial_number': get_nonce(), 'gen_time': dt, 'tsa': x509.GeneralName( name='directory_name', value=self.tsa_cert.subject ) } try: tst_info['nonce'] = req['nonce'] except KeyError: pass tst_info = tsp.TSTInfo(tst_info) tst_info_data = tst_info.dump() message_digest = getattr(hashlib, md_algorithm)(tst_info_data).digest() signing_cert_id = tsp.ESSCertID({ 'cert_hash': hashlib.sha1(self.tsa_cert.dump()).digest() }) signed_attrs = cms.CMSAttributes([ simple_cms_attribute('content_type', 'tst_info'), simple_cms_attribute( 'signing_time', cms.Time({'utc_time': core.UTCTime(dt)}) ), simple_cms_attribute( 'signing_certificate', tsp.SigningCertificate({'certs': [signing_cert_id]}) ), simple_cms_attribute('message_digest', message_digest), ]) signature = generic_sign( self.tsa_key, signed_attrs.dump(), self.signature_algo ) sig_info = cms.SignerInfo({ 'version': 'v1', 'sid': cms.SignerIdentifier({ 'issuer_and_serial_number': cms.IssuerAndSerialNumber({ 'issuer': self.tsa_cert.issuer, 'serial_number': self.tsa_cert.serial_number, }) }), 'digest_algorithm': digest_algorithm_obj, 'signature_algorithm': self.signature_algo, 'signed_attrs': signed_attrs, 'signature': signature }) certs = set(self.certs_to_embed) certs.add(self.tsa_cert) signed_data = { # must use v3 to get access to the EncapsulatedContentInfo construct 'version': 'v3', 'digest_algorithms': cms.DigestAlgorithms((digest_algorithm_obj,)), 'encap_content_info': cms.EncapsulatedContentInfo({ 'content_type': cms.ContentType('tst_info'), 'content': cms.ParsableOctetString(tst_info_data) }), 'certificates': certs, 'signer_infos': [sig_info] } tst = cms.ContentInfo({ 'content_type': cms.ContentType('signed_data'), 'content': cms.SignedData(signed_data) }) return tsp.TimeStampResp({'status': status, 'time_stamp_token': tst})
def _wrap(x): return x509.GeneralName({'uniform_resource_identifier': x})
def build(self, responder_private_key=None, responder_certificate=None): if self._response_status != 'successful': return ocsp.OCSPResponse( {'response_status': self._response_status}) is_oscrypto = isinstance(responder_private_key, asymmetric.PrivateKey) if not isinstance(responder_private_key, keys.PrivateKeyInfo) and not is_oscrypto: raise TypeError( _pretty_message( ''' responder_private_key must be an instance of asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey, not %s ''', _type_name(responder_private_key))) cert_is_oscrypto = isinstance(responder_certificate, asymmetric.Certificate) if not isinstance(responder_certificate, x509.Certificate) and not cert_is_oscrypto: raise TypeError( _pretty_message( ''' responder_certificate must be an instance of asn1crypto.x509.Certificate or oscrypto.asymmetric.Certificate, not %s ''', _type_name(responder_certificate))) if cert_is_oscrypto: responder_certificate = responder_certificate.asn1 if self._certificate is None: raise ValueError( _pretty_message(''' certificate must be set if the response_status is "successful" ''')) if self._certificate_status is None: raise ValueError( _pretty_message(''' certificate_status must be set if the response_status is "successful" ''')) def _make_extension(name, value): return {'extn_id': name, 'critical': False, 'extn_value': value} response_data_extensions = [] single_response_extensions = [] for name, value in self._response_data_extensions.items(): response_data_extensions.append(_make_extension(name, value)) if self._nonce: response_data_extensions.append( _make_extension('nonce', self._nonce)) if not response_data_extensions: response_data_extensions = None for name, value in self._single_response_extensions.items(): single_response_extensions.append(_make_extension(name, value)) if self._certificate_issuer: single_response_extensions.append( _make_extension('certificate_issuer', [ x509.GeneralName(name='directory_name', value=self._certificate_issuer.subject) ])) if not single_response_extensions: single_response_extensions = None responder_key_hash = getattr(responder_certificate.public_key, self._key_hash_algo) if self._certificate_status == 'good': cert_status = ocsp.CertStatus(name='good') elif self._certificate_status == 'unknown': cert_status = ocsp.CertStatus(name='unknown') else: status = self._certificate_status reason = status if status != 'revoked' else 'unspecified' revoked_info = ocsp.RevokedInfo({ 'revocation_time': datetime.now(timezone.utc), 'revocation_reason': crl.CRLReason(0) }) cert_status = ocsp.CertStatus(name='revoked', value=revoked_info) issuer = self._certificate_issuer produced_at = datetime.now(timezone.utc) if self._this_update is None: self._this_update = produced_at if self._next_update is None: self._next_update = self._this_update + timedelta(days=7) response_data = ocsp.ResponseData({ 'responder_id': ocsp.ResponderId(name='by_name', value=self._certificate.subject), 'produced_at': produced_at, 'responses': [{ 'cert_id': { 'hash_algorithm': { 'algorithm': self._key_hash_algo }, 'issuer_name_hash': getattr(self.issuer.subject, self._key_hash_algo), 'issuer_key_hash': getattr(self.issuer.public_key, self._key_hash_algo), 'serial_number': self._certificate.serial_number, }, 'cert_status': cert_status, 'this_update': self._this_update, 'next_update': self._next_update, 'single_extensions': single_response_extensions }], 'response_extensions': response_data_extensions }) signature_algo = responder_private_key.algorithm if signature_algo == 'ec': signature_algo = 'ecdsa' signature_algorithm_id = '%s_%s' % (self._hash_algo, signature_algo) if responder_private_key.algorithm == 'rsa': sign_func = asymmetric.rsa_pkcs1v15_sign elif responder_private_key.algorithm == 'dsa': sign_func = asymmetric.dsa_sign elif responder_private_key.algorithm == 'ec': sign_func = asymmetric.ecdsa_sign if not is_oscrypto: responder_private_key = asymmetric.load_private_key( responder_private_key) signature_bytes = sign_func(responder_private_key, response_data.dump(), self._hash_algo) certs = [responder_certificate] if self._certificate_issuer: certs = [responder_certificate] return ocsp.OCSPResponse({ 'response_status': self._response_status, 'response_bytes': { 'response_type': 'basic_ocsp_response', 'response': { 'tbs_response_data': response_data, 'signature_algorithm': { 'algorithm': signature_algorithm_id }, 'signature': signature_bytes, 'certs': certs } } })
def write_tls_certificate( ca_cert: x509.Certificate, ca_cert_orig: x509.Certificate, signing_key: ecdsa.keys.SigningKey, name: str, subject: x509.Name, subject_alt_names: Sequence[str], ) -> None: private_key, public_key = generate_private_key("rsa:4096") signed_digest_algorithm = x509.SignedDigestAlgorithm( {"algorithm": "sha256_ecdsa"}) certificate = x509.Certificate({ "tbs_certificate": { "version": "v3", "serial_number": random_serial_number(), "signature": signed_digest_algorithm, "issuer": ca_cert_orig.subject, "validity": { "not_before": x509.UTCTime( datetime.datetime(2018, 1, 1, tzinfo=datetime.timezone.utc)), "not_after": x509.UTCTime( datetime.datetime(2021, 1, 1, tzinfo=datetime.timezone.utc)), }, "subject": subject, "subject_public_key_info": public_key, "extensions": [ { "extn_id": "basic_constraints", "critical": True, "extn_value": { "ca": False }, }, { "extn_id": "subject_alt_name", "critical": False, "extn_value": [ x509.GeneralName({"dns_name": dns_name}) for dns_name in subject_alt_names ], }, { "extn_id": "certificate_policies", "critical": False, "extn_value": [ { "policy_identifier": "1.3.6.1.4.1.6449.1.2.1.5.1" }, ], }, ], }, "signature_algorithm": signed_digest_algorithm, }) sign_certificate(signing_key, certificate) with open(name + ".crt", "wb") as f: write_pem(f, certificate, "CERTIFICATE") write_pem(f, ca_cert_orig, "CERTIFICATE") write_pem(f, ca_cert, "CERTIFICATE") with open(name + ".key", "wb") as f: write_pem(f, private_key, "PRIVATE KEY") write_pem(f, certificate, "CERTIFICATE") write_pem(f, ca_cert_orig, "CERTIFICATE") write_pem(f, ca_cert, "CERTIFICATE")
def build(self, issuer_private_key): """ Validates the certificate list information, constructs the ASN.1 structure and then signs it :param issuer_private_key: An asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey object for the private key of the CRL issuer :return: An asn1crypto.crl.CertificateList object of the newly signed CRL """ is_oscrypto = isinstance(issuer_private_key, asymmetric.PrivateKey) if not isinstance(issuer_private_key, keys.PrivateKeyInfo) and not is_oscrypto: raise TypeError( _pretty_message( ''' issuer_private_key must be an instance of asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey, not %s ''', _type_name(issuer_private_key))) if self._this_update is None: self._this_update = datetime.now(timezone.utc) if self._next_update is None: self._next_update = self._this_update + timedelta(days=7) signature_algo = issuer_private_key.algorithm if signature_algo == 'ec': signature_algo = 'ecdsa' signature_algorithm_id = '%s_%s' % (self._hash_algo, signature_algo) def _make_extension(name, value): return { 'extn_id': name, 'critical': self._determine_critical(name), 'extn_value': value } extensions = [] for name in sorted(self._special_extensions): value = getattr(self, '_%s' % name) if value is not None: extensions.append(_make_extension(name, value)) for name in sorted(self._other_extensions.keys()): extensions.append( _make_extension(name, self._other_extensions[name])) # For an indirect CRL we need to set the first if self._certificate_issuer and len(self._revoked_certificates) > 0: self._revoked_certificates[0]['crl_entry_extensions'].append({ 'extn_id': 'certificate_issuer', 'critical': True, 'extn_value': x509.GeneralNames([ x509.GeneralName(name='directory_name', value=self._certificate_issuer.subject) ]) }) tbs_cert_list = crl.TbsCertList({ 'version': 'v3', 'signature': { 'algorithm': signature_algorithm_id }, 'issuer': self._issuer.subject, 'this_update': x509.Time(name='utc_time', value=self._this_update), 'next_update': x509.Time(name='utc_time', value=self._next_update), 'revoked_certificates': crl.RevokedCertificates(self._revoked_certificates), 'crl_extensions': extensions }) if issuer_private_key.algorithm == 'rsa': sign_func = asymmetric.rsa_pkcs1v15_sign elif issuer_private_key.algorithm == 'dsa': sign_func = asymmetric.dsa_sign elif issuer_private_key.algorithm == 'ec': sign_func = asymmetric.ecdsa_sign if not is_oscrypto: issuer_private_key = asymmetric.load_private_key( issuer_private_key) signature = sign_func(issuer_private_key, tbs_cert_list.dump(), self._hash_algo) return crl.CertificateList({ 'tbs_cert_list': tbs_cert_list, 'signature_algorithm': { 'algorithm': signature_algorithm_id }, 'signature': signature })
def request_tsa_response(self, req: tsp.TimeStampReq) \ -> tsp.TimeStampResp: # We pretend that certReq is always true in the request # TODO generalise my detached signature logic to include cases like this # (see § 5.4 in RFC 5652) # TODO does the RFC status = tsp.PKIStatusInfo({'status': tsp.PKIStatus('granted')}) message_imprint: tsp.MessageImprint = req['message_imprint'] md_algorithm = self.override_md if md_algorithm is None: md_algorithm = message_imprint['hash_algorithm'][ 'algorithm'].native digest_algorithm_obj = algos.DigestAlgorithm( {'algorithm': md_algorithm}) dt = self.fixed_dt or datetime.now(tz=tzlocal.get_localzone()) tst_info = { 'version': 'v1', # See http://oidref.com/1.3.6.1.4.1.4146.2.2 # I don't really care too much, this is a testing device anyway 'policy': tsp.ObjectIdentifier('1.3.6.1.4.1.4146.2.2'), 'message_imprint': message_imprint, # should be sufficiently random (again, this is a testing class) 'serial_number': get_nonce(), 'gen_time': dt, 'tsa': x509.GeneralName(name='directory_name', value=self.tsa_cert.subject) } if req['nonce'].native is not None: tst_info['nonce'] = req['nonce'] tst_info = tsp.TSTInfo(tst_info) tst_info_data = tst_info.dump() md_spec = get_pyca_cryptography_hash(md_algorithm) md = hashes.Hash(md_spec) md.update(tst_info_data) message_digest_value = md.finalize() signed_attrs = cms.CMSAttributes([ simple_cms_attribute('content_type', 'tst_info'), simple_cms_attribute('signing_time', cms.Time({'utc_time': core.UTCTime(dt)})), simple_cms_attribute('signing_certificate', general.as_signing_certificate( self.tsa_cert)), simple_cms_attribute('message_digest', message_digest_value), ]) priv_key = serialization.load_der_private_key(self.tsa_key.dump(), password=None) if not isinstance(priv_key, RSAPrivateKey): raise NotImplementedError("Dummy timestamper is RSA-only.") signature = priv_key.sign( signed_attrs.dump(), PKCS1v15(), get_pyca_cryptography_hash(md_algorithm.upper())) sig_info = cms.SignerInfo({ 'version': 'v1', 'sid': cms.SignerIdentifier({ 'issuer_and_serial_number': cms.IssuerAndSerialNumber({ 'issuer': self.tsa_cert.issuer, 'serial_number': self.tsa_cert.serial_number, }) }), 'digest_algorithm': digest_algorithm_obj, 'signature_algorithm': algos.SignedDigestAlgorithm({'algorithm': 'rsassa_pkcs1v15'}), 'signed_attrs': signed_attrs, 'signature': signature }) certs = set(self.certs_to_embed) certs.add(self.tsa_cert) signed_data = { # must use v3 to get access to the EncapsulatedContentInfo construct 'version': 'v3', 'digest_algorithms': cms.DigestAlgorithms((digest_algorithm_obj, )), 'encap_content_info': cms.EncapsulatedContentInfo({ 'content_type': cms.ContentType('tst_info'), 'content': cms.ParsableOctetString(tst_info_data) }), 'certificates': certs, 'signer_infos': [sig_info] } tst = cms.ContentInfo({ 'content_type': cms.ContentType('signed_data'), 'content': cms.SignedData(signed_data) }) return tsp.TimeStampResp({'status': status, 'time_stamp_token': tst})
def build(self, responder_private_key=None, responder_certificate=None): """ Validates the request information, constructs the ASN.1 structure and signs it. The responder_private_key and responder_certificate parameters are onlystr required if the response_status is "successful". :param responder_private_key: An asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey object for the private key to sign the response with :param responder_certificate: An asn1crypto.x509.Certificate or oscrypto.asymmetric.Certificate object of the certificate associated with the private key :return: An asn1crypto.ocsp.OCSPResponse object of the response """ if self._response_status != 'successful': return ocsp.OCSPResponse({ 'response_status': self._response_status }) is_oscrypto = isinstance(responder_private_key, asymmetric.PrivateKey) if not isinstance(responder_private_key, keys.PrivateKeyInfo) and not is_oscrypto: raise TypeError(_pretty_message( ''' responder_private_key must be an instance ofthe c asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey, not %s ''', _type_name(responder_private_key) )) cert_is_oscrypto = isinstance(responder_certificate, asymmetric.Certificate) if not isinstance(responder_certificate, x509.Certificate) and not cert_is_oscrypto: raise TypeError(_pretty_message( ''' responder_certificate must be an instance of asn1crypto.x509.Certificate or oscrypto.asymmetric.Certificate, not %s ''', _type_name(responder_certificate) )) if cert_is_oscrypto: responder_certificate = responder_certificate.asn1 if self._certificate_status_list is None: raise ValueError(_pretty_message( ''' certificate_status_list must be set if the response_status is "successful" ''' )) def _make_extension(name, value): return { 'extn_id': name, 'critical': False, 'extn_value': value } responses = [] for serial, status in self._certificate_status_list: response_data_extensions = [] single_response_extensions = [] for name, value in self._response_data_extensions.items(): response_data_extensions.append(_make_extension(name, value)) if self._nonce: response_data_extensions.append( _make_extension('nonce', self._nonce) ) if not response_data_extensions: response_data_extensions = None for name, value in self._single_response_extensions.items(): single_response_extensions.append(_make_extension(name, value)) if self._certificate_issuer: single_response_extensions.append( _make_extension( 'certificate_issuer', [ x509.GeneralName( name='directory_name', value=self._certificate_issuer.subject ) ] ) ) if not single_response_extensions: single_response_extensions = None responder_key_hash = getattr(responder_certificate.public_key, self._key_hash_algo) if status == 'good': cert_status = ocsp.CertStatus( name='good', value=core.Null() ) elif status == 'unknown': cert_status = ocsp.CertStatus( name='unknown', value=core.Null() ) else: reason = status if status != 'revoked' else 'unspecified' cert_status = ocsp.CertStatus( name='revoked', value={ 'revocation_time': self._revocation_date, 'revocation_reason': reason, } ) issuer = self._certificate_issuer if self._certificate_issuer else responder_certificate produced_at = datetime.now(timezone.utc).replace(microsecond=0) if self._this_update is None: self._this_update = produced_at if self._next_update is None: self._next_update = (self._this_update + timedelta(days=7)).replace(microsecond=0) response = { 'cert_id': { 'hash_algorithm': { 'algorithm': self._key_hash_algo }, 'issuer_name_hash': getattr(issuer.subject, self._key_hash_algo), 'issuer_key_hash': getattr(issuer.public_key, self._key_hash_algo), 'serial_number': serial, }, 'cert_status': cert_status, 'this_update': self._this_update, 'next_update': self._next_update, 'single_extensions': single_response_extensions } responses.append(response) response_data = ocsp.ResponseData({ 'responder_id': ocsp.ResponderId(name='by_key', value=responder_key_hash), 'produced_at': produced_at, 'responses': responses, 'response_extensions': response_data_extensions }) signature_algo = responder_private_key.algorithm if signature_algo == 'ec': signature_algo = 'ecdsa' signature_algorithm_id = '%s_%s' % (self._hash_algo, signature_algo) if responder_private_key.algorithm == 'rsa': sign_func = asymmetric.rsa_pkcs1v15_sign elif responder_private_key.algorithm == 'dsa': sign_func = asymmetric.dsa_sign elif responder_private_key.algorithm == 'ec': sign_func = asymmetric.ecdsa_sign if not is_oscrypto: responder_private_key = asymmetric.load_private_key(responder_private_key) signature_bytes = sign_func(responder_private_key, response_data.dump(), self._hash_algo) certs = None if self._certificate_issuer and getattr(self._certificate_issuer.public_key, self._key_hash_algo) != responder_key_hash: certs = [responder_certificate] return ocsp.OCSPResponse({ 'response_status': self._response_status, 'response_bytes': { 'response_type': 'basic_ocsp_response', 'response': { 'tbs_response_data': response_data, 'signature_algorithm': {'algorithm': signature_algorithm_id}, 'signature': signature_bytes, 'certs': certs, } } })
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, responder_private_key=None, responder_certificate=None): """ Validates the request information, constructs the ASN.1 structure and signs it. The responder_private_key and responder_certificate parameters are only required if the response_status is "successful". :param responder_private_key: An asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey object for the private key to sign the response with :param responder_certificate: An asn1crypto.x509.Certificate or oscrypto.asymmetric.Certificate object of the certificate associated with the private key :return: An asn1crypto.ocsp.OCSPResponse object of the response """ if self._response_status != 'successful': return ocsp.OCSPResponse( {'response_status': self._response_status}) is_oscrypto = isinstance(responder_private_key, asymmetric.PrivateKey) if not isinstance(responder_private_key, keys.PrivateKeyInfo) and not is_oscrypto: raise TypeError( _pretty_message( ''' responder_private_key must be an instance of asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey, not %s ''', _type_name(responder_private_key))) cert_is_oscrypto = isinstance(responder_certificate, asymmetric.Certificate) if not isinstance(responder_certificate, x509.Certificate) and not cert_is_oscrypto: raise TypeError( _pretty_message( ''' responder_certificate must be an instance of asn1crypto.x509.Certificate or oscrypto.asymmetric.Certificate, not %s ''', _type_name(responder_certificate))) if cert_is_oscrypto: responder_certificate = responder_certificate.asn1 for cert in self._certificates: if not cert: raise ValueError( _pretty_message(''' certificates must be set if the response_status is "successful" ''')) for cert_status in self._certificates_status: if not cert_status: raise ValueError( _pretty_message(''' certificates_status for all certificates must be set if the response_status is "successful" ''')) def _make_extension(name, value): return {'extn_id': name, 'critical': False, 'extn_value': value} issuer = self._certificate_issuer if self._certificate_issuer else responder_certificate for x in range(len(self._certificates)): if issuer.subject != self._certificates[x].issuer: raise ValueError( _pretty_message(''' responder_certificate does not appear to be the issuer for the certificate. Perhaps set the .certificate_issuer attribute? ''')) total_issuers = len(self._certificates) response_data_extensions = [] single_response_extensions = [[]] * total_issuers for x in range(len(self._certificates)): for name, value in self._response_data_extensions[x].items(): response_data_extensions.append(_make_extension(name, value)) for name, value in self._single_response_extensions[x].items(): single_response_extensions[x].append( _make_extension(name, value)) # This means single_response_extensions can never be empty single_response_extensions[x].append( _make_extension('certificate_issuer', [ x509.GeneralName(name='directory_name', value=issuer.subject) ])) if self._nonce: response_data_extensions.append( _make_extension('nonce', self._nonce)) if len(response_data_extensions) == 0: response_data_extensions = None responder_key_hash = getattr(responder_certificate.public_key, self._key_hash_algo) certs_status = [] for x in range(len(self._certificates_status)): c_stat = self._certificates_status[x] if c_stat == 'good': cert_status = ocsp.CertStatus(name='good', value=core.Null()) elif c_stat == 'unknown': cert_status = ocsp.CertStatus(name='unknown', value=core.Null()) else: status = c_stat reason = status if status != 'revoked' else 'unspecified' cert_status = ocsp.CertStatus(name='revoked', value={ 'revocation_time': self._revocation_dates[x], 'revocation_reason': reason, }) certs_status.append(cert_status) produced_at = datetime.now(timezone.utc) # Construct the multiple certs response responses = [] for x in range(len(self._certificates)): if self._this_updates[x] is None: self._this_updates[x] = produced_at if self._next_updates[x] is None: self._next_updates[x] = self._this_updates[x] + timedelta( days=7) item = { 'cert_id': { 'hash_algorithm': { 'algorithm': self._key_hash_algo }, 'issuer_name_hash': getattr(self._certificates[x].issuer, self._key_hash_algo), 'issuer_key_hash': getattr(issuer.public_key, self._key_hash_algo), 'serial_number': self._certificates[x].serial_number }, 'cert_status': certs_status[x], 'this_update': self._this_updates[x], 'next_update': self._next_updates[x], 'single_extensions': single_response_extensions[x] } responses.append(item) signature_algo = responder_private_key.algorithm if signature_algo == 'ec': signature_algo = 'ecdsa' signature_algorithm_id = '%s_%s' % (self._hash_algo, signature_algo) if responder_private_key.algorithm == 'rsa': sign_func = asymmetric.rsa_pkcs1v15_sign elif responder_private_key.algorithm == 'dsa': sign_func = asymmetric.dsa_sign elif responder_private_key.algorithm == 'ec': sign_func = asymmetric.ecdsa_sign if not is_oscrypto: responder_private_key = asymmetric.load_private_key( responder_private_key) # Set response_data for use in ocsp-service response_data = ocsp.ResponseData({ 'responder_id': ocsp.ResponderId(name='by_key', value=responder_key_hash), 'produced_at': produced_at, 'responses': responses, 'response_extensions': response_data_extensions }) signature_bytes = sign_func(responder_private_key, response_data.dump(), self._hash_algo) certs = None if self._certificate_issuer: certs = [responder_certificate] return ocsp.OCSPResponse({ 'response_status': self._response_status, 'response_bytes': { 'response_type': 'basic_ocsp_response', 'response': { 'tbs_response_data': response_data, 'signature_algorithm': { 'algorithm': signature_algorithm_id }, 'signature': signature_bytes, 'certs': certs } } })
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 })