def _generate_signed_attrs(self, eta_invoice, signing_time): cert = x509.Certificate.load(base64.b64decode(self.certificate)) data = hashlib.sha256(self._serialize_for_signing(eta_invoice).encode()).digest() return cms.CMSAttributes([ cms.CMSAttribute({ 'type': cms.CMSAttributeType('content_type'), 'values': ('digested_data',), }), cms.CMSAttribute({ 'type': cms.CMSAttributeType('message_digest'), 'values': (data,), }), cms.CMSAttribute({ 'type': tsp.CMSAttributeType('signing_certificate_v2'), 'values': ({ 'certs': (tsp.ESSCertIDv2({ 'hash_algorithm': algos.DigestAlgorithm({'algorithm': 'sha256'}), 'cert_hash': hashlib.sha256(cert.dump()).digest() }),) },), }), cms.CMSAttribute({ 'type': cms.CMSAttributeType('signing_time'), 'values': ( cms.Time({'utc_time': core.UTCTime(signing_time.replace(tzinfo=pytz.UTC))}),) }), ])
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 sign(datau, session, cert, cert_value, hashalgo, attrs=True, signed_value=None): if signed_value is None: signed_value = getattr(hashlib, hashalgo)(datau).digest() signed_time = datetime.now() x509 = Certificate.load(cert_value) certificates = [] certificates.append(x509) cert_value_digest = bytes( session.digest(cert_value, Mechanism(LowLevel.CKM_SHA256))) MyLogger().my_logger().info('building signed attributes...') signer = { 'version': 'v1', 'sid': cms.SignerIdentifier({ 'issuer_and_serial_number': cms.IssuerAndSerialNumber({ 'issuer': x509.issuer, 'serial_number': x509.serial_number, }), }), 'digest_algorithm': algos.DigestAlgorithm({'algorithm': hashalgo}), 'signature_algorithm': algos.SignedDigestAlgorithm({'algorithm': 'rsassa_pkcs1v15'}), 'signature': signed_value, } if attrs: signer['signed_attrs'] = [ cms.CMSAttribute({ 'type': cms.CMSAttributeType('content_type'), 'values': ('data', ), }), cms.CMSAttribute({ 'type': cms.CMSAttributeType('message_digest'), 'values': (signed_value, ), }), cms.CMSAttribute({ 'type': cms.CMSAttributeType('signing_time'), 'values': (cms.Time({'utc_time': core.UTCTime(signed_time)}), ) }), cms.CMSAttribute({ 'type': cms.CMSAttributeType('1.2.840.113549.1.9.16.2.47'), 'values': (tsp.SigningCertificateV2({ 'certs': (tsp.ESSCertIDv2({ 'hash_algorithm': algos.DigestAlgorithm({ 'algorithm': hashalgo, 'parameters': None }), 'cert_hash': cert_value_digest, }), ), }), ) }), ] config = { 'version': 'v1', 'digest_algorithms': cms.DigestAlgorithms((algos.DigestAlgorithm({'algorithm': hashalgo}), )), 'encap_content_info': { 'content_type': 'data', }, 'certificates': certificates, # 'crls': [], 'signer_infos': [ signer, ], } datas = cms.ContentInfo({ 'content_type': cms.ContentType('signed_data'), 'content': cms.SignedData(config), }) if attrs: tosign = datas['content']['signer_infos'][0]['signed_attrs'].dump() tosign = b'\x31' + tosign[1:] else: tosign = datau MyLogger().my_logger().info('signed attributes ready') # fetching private key from smart card priv_key = SignatureUtils.fetch_private_key(session, cert) mechanism = Mechanism(LowLevel.CKM_SHA256_RSA_PKCS, None) MyLogger().my_logger().info('signing...') # signing bytes to be signed signature = session.sign(priv_key, tosign, mechanism) datas['content']['signer_infos'][0]['signature'] = bytes(signature) return datas.dump()
def _sign(self, datau, key, signing_cert, trustchain, hashalgo, attrs=True, signed_value=None, hsm=None, pss=False, timestampurl=None, identity=None, s=None): if signed_value is None: signed_value = getattr(hashlib, hashalgo)(datau).digest() signed_time = datetime.datetime.now(tz=util.timezone.utc) esscert = signing_cert.public_bytes(serialization.Encoding.DER) esscert = getattr(hashlib, hashalgo)(esscert).digest() if hsm is not None: keyid, cert = hsm.certificate() cert = cert2asn(cert, False) trustchain = [] else: signing_cert = cert2asn(signing_cert) certificates = [] for c in trustchain: certificates.append(cert2asn(c)) certificates.append(signing_cert) signer = { 'version': 'v1', 'sid': cms.SignerIdentifier({ 'issuer_and_serial_number': cms.IssuerAndSerialNumber({ 'issuer': signing_cert.issuer, 'serial_number': signing_cert.serial_number, }), }), 'digest_algorithm': algos.DigestAlgorithm({'algorithm': hashalgo}), 'signature': signed_value, } signer['signature_algorithm'] = algos.SignedDigestAlgorithm( {'algorithm': 'rsassa_pkcs1v15'}) if attrs: if attrs is True: signer['signed_attrs'] = [ cms.CMSAttribute({ 'type': cms.CMSAttributeType('content_type'), 'values': ('data', ), }), cms.CMSAttribute({ 'type': cms.CMSAttributeType('message_digest'), 'values': (signed_value, ), }), cms.CMSAttribute({ 'type': cms.CMSAttributeType('signing_certificate_v2'), 'values': (tsp.SigningCertificateV2({ 'certs': [ tsp.ESSCertIDv2({'cert_hash': esscert}), ] }), ) }) #cms.CMSAttribute({ #'type': cms.CMSAttributeType('signing_time'), #'values': (cms.Time({'utc_time': core.UTCTime(signed_time)}),) #}), ] else: signer['signed_attrs'] = attrs # TODO: Keep it all in one loop ocsp_revocation = [] ocsp_revocation.append( cms.RevocationInfoChoice({ 'other': cms.OtherRevocationInfoFormat({ 'other_rev_info_format': cms.OtherRevInfoFormatId('ocsp_response'), 'other_rev_info': self._ocsp_response }) })) # TODO: Don't need this because I have a DSS now #for rev in self._revocation_info: #rev = base64.b64decode(rev) #rev = ocsp.OCSPResponse.load(rev) #ocsp_revocation.append( #cms.RevocationInfoChoice({ #'other': cms.OtherRevocationInfoFormat({ #'other_rev_info_format': cms.OtherRevInfoFormatId('ocsp_response'), #'other_rev_info': rev #}) #}) #) config = { 'version': 'v1', 'digest_algorithms': cms.DigestAlgorithms( (algos.DigestAlgorithm({'algorithm': hashalgo}), )), 'encap_content_info': { 'content_type': 'data', }, 'certificates': certificates, 'crls': ocsp_revocation, 'signer_infos': [ signer, ], } datas = cms.ContentInfo({ 'content_type': cms.ContentType('signed_data'), 'content': cms.SignedData(config), }) if attrs: tosign = datas['content']['signer_infos'][0]['signed_attrs'].dump() tosign = b'\x31' + tosign[1:] else: tosign = datau tosign = getattr(hashlib, hashalgo)(tosign).digest() # Fetch the actual signature r = s.get( self._signature_url.format(id=identity, digest=tosign.hex().upper())) if r.status_code != 200: raise APIError('Cannot retrieve the signature: {}\n{}'.format( r.status_code, r.json())) signed_value_signature = r.json()['signature'] signed_value_signature = bytes.fromhex(signed_value_signature) signed_value = getattr(hashlib, hashalgo)(signed_value_signature).digest() datas['content']['signer_infos'][0][ 'signature'] = signed_value_signature # Use globalsigns timestamp # TODO: uncomment next 17 lines to have timestamped signature r = s.get( self._timestamp_url.format(digest=signed_value.hex().upper())) if r.status_code != 200: raise APIError('Cannot retrieve the timestamp: {}\n{}'.format( r.status_code, r.json())) timestamp_token = r.json()['token'] timestamp_token = timestamp_token.encode('ascii') timestamp_token = base64.b64decode(timestamp_token) tsp_dict = cms.ContentInfo.load(timestamp_token) tsp_attrs = [ cms.CMSAttribute({ 'type': cms.CMSAttributeType('signature_time_stamp_token'), 'values': cms.SetOfContentInfo([ cms.ContentInfo({ 'content_type': cms.ContentType('signed_data'), 'content': tsp_dict['content'], }) ]) }) ] datas['content']['signer_infos'][0]['unsigned_attrs'] = tsp_attrs # TODO: OCSP stuff - probably not necessary since we have a DSS #ocsp_seq = pdf.SequenceOfOCSPResponse((self._ocsp_response,)) #ocsp_arc = pdf.RevocationInfoArchival({'ocsp': ocsp_seq}) #revocation_info = pdf.SetOfRevocationInfoArchival() #revocation_info.append(ocsp_arc) #self._ocsp_response #ocsp_attribute = cms.CMSAttribute({ # basic_ocsp_response #'type': cms.CMSAttributeType('adobe_revocation_info_archival'), #'values': pdf.SetOfRevocationInfoArchival([ #pdf.RevocationInfoArchival({ #'ocsp': pdf.SequenceOfOCSPResponse(self._ocsp_response) #})#cert2asn(ocsp_resp.public_bytes(serialization.Encoding.DER), False) #]) #}), #datas['content']['signer_infos'][0]['unsigned_attrs'].append(ocsp_attribute) return datas.dump()