async def credentials_info(self, request: web.Request): params = await request.json() cert_info_req = params.get('certInfo', False) # TODO implement certInfo # (biggest roadblock: asn1crypto doesn't implement RFC 4514) if cert_info_req: raise web.HTTPNotImplemented() try: cred_id = str(params['credentialID']) except KeyError: raise web.HTTPBadRequest() pki_arch, cert_spec = self._parse_credential_id(cred_id) cert = pki_arch.get_cert(cert_spec.label) subj_pub_key: keys.PublicKeyInfo = cert.public_key enabled = pki_arch.is_subject_key_available(cert_spec.label) key_info = { 'status': "enabled" if enabled else "disabled", 'algo': [ algos.SignedDigestAlgorithmId(algo).dotted for algo in ALGO_SUPPORT.get(subj_pub_key.algorithm, ()) ], 'len': subj_pub_key.bit_size, } if subj_pub_key.algorithm == 'ec': ec_params: keys.ECDomainParameters = \ subj_pub_key['algorithm']['parameters'] if ec_params.name == 'named': key_info['curve'] = ec_params.chosen.dotted certificates_req = params.get('certificates', 'single') if certificates_req not in ('none', 'chain', 'single'): raise web.HTTPBadRequest() response = {'key': key_info} if certificates_req != 'none': certs = [b64_der(cert)] if certificates_req == 'chain': certs.extend( b64_der(pki_arch.get_cert(ca_cert_lbl)) for ca_cert_lbl in pki_arch.get_chain(cert_spec.label)) response['cert'] = {'certificates': certs} response['authMode'] = 'implicit' service_params = self.service_params scal = "2" if service_params.hash_pinning_required else "1" response['SCAL'] = scal response['multisign'] = service_params.multisign return web.json_response(response)
def _process_certificate_info_response(response_data) -> CSCCredentialInfo: try: b64_certs = response_data['cert']['certificates'] except KeyError as e: raise SigningError( "Could not retrieve certificates from response") from e try: certs = [ x509.Certificate.load(base64.b64decode(cert)) for cert in b64_certs ] except ValueError as e: raise SigningError("Could not decode certificates in response") from e try: algo_oids = response_data["key"]["algo"] if not isinstance(algo_oids, list): raise TypeError supported_algos = frozenset( algos.SignedDigestAlgorithmId(oid).native for oid in algo_oids) except (KeyError, ValueError, TypeError) as e: raise SigningError( "Could not retrieve supported signing mechanisms from response" ) from e try: max_batch_size = int(response_data['multisign']) except (KeyError, ValueError) as e: raise SigningError( "Could not retrieve max batch size from response") from e scal_value = response_data.get("SCAL", 1) try: scal_value = int(scal_value) if scal_value not in (1, 2): raise ValueError except ValueError: raise SigningError("SCAL value must be \"1\" or \"2\".") hash_pinning_required = scal_value == 2 return CSCCredentialInfo( # The CSC spec requires the signer's certificate to be first # in the 'certs' array. The order for the others is unspecified, # but that doesn't matter. signing_cert=certs[0], chain=certs[1:], supported_mechanisms=supported_algos, max_batch_size=max_batch_size, hash_pinning_required=hash_pinning_required, response_data=response_data)
def sign_message(data_to_sign, digest_alg, sign_key, use_signed_attributes=True): """Function signs the data and returns the generated ASN.1 :param data_to_sign: A byte string of the data to be signed. :param digest_alg: The digest algorithm to be used for generating the signature. :param sign_key: The key to be used for generating the signature. :param use_signed_attributes: Optional attribute to indicate weather the CMS signature attributes should be included in the signature or not. :return: A CMS ASN.1 byte string of the signed data. """ if use_signed_attributes: digest_func = hashlib.new(digest_alg) digest_func.update(data_to_sign) message_digest = digest_func.digest() class SmimeCapability(core.Sequence): _fields = [('0', core.Any, { 'optional': True }), ('1', core.Any, { 'optional': True }), ('2', core.Any, { 'optional': True }), ('3', core.Any, { 'optional': True }), ('4', core.Any, { 'optional': True })] class SmimeCapabilities(core.Sequence): _fields = [ ('0', SmimeCapability), ('1', SmimeCapability, { 'optional': True }), ('2', SmimeCapability, { 'optional': True }), ('3', SmimeCapability, { 'optional': True }), ('4', SmimeCapability, { 'optional': True }), ('5', SmimeCapability, { 'optional': True }), ] smime_cap = OrderedDict([ ('0', OrderedDict([('0', core.ObjectIdentifier('1.2.840.113549.3.7')) ])), ('1', OrderedDict([('0', core.ObjectIdentifier('1.2.840.113549.3.2')), ('1', core.Integer(128))])), ('2', OrderedDict([('0', core.ObjectIdentifier('1.2.840.113549.3.4')), ('1', core.Integer(128))])), ]) signed_attributes = cms.CMSAttributes([ cms.CMSAttribute({ 'type': cms.CMSAttributeType('content_type'), 'values': cms.SetOfContentType([cms.ContentType('data')]) }), cms.CMSAttribute({ 'type': cms.CMSAttributeType('signing_time'), 'values': cms.SetOfTime( [cms.Time({'utc_time': core.UTCTime(datetime.now())})]) }), cms.CMSAttribute({ 'type': cms.CMSAttributeType('message_digest'), 'values': cms.SetOfOctetString([core.OctetString(message_digest)]) }), cms.CMSAttribute({ 'type': cms.CMSAttributeType('1.2.840.113549.1.9.15'), 'values': cms.SetOfAny([core.Any(SmimeCapabilities(smime_cap))]) }), ]) signature = asymmetric.rsa_pkcs1v15_sign(sign_key[0], signed_attributes.dump(), digest_alg) else: signed_attributes = None signature = asymmetric.rsa_pkcs1v15_sign(sign_key[0], data_to_sign, digest_alg) return cms.ContentInfo({ 'content_type': cms.ContentType('signed_data'), 'content': cms.SignedData({ 'version': cms.CMSVersion('v1'), 'digest_algorithms': cms.DigestAlgorithms([ algos.DigestAlgorithm( {'algorithm': algos.DigestAlgorithmId(digest_alg)}) ]), 'encap_content_info': cms.ContentInfo({'content_type': cms.ContentType('data')}), 'certificates': cms.CertificateSet( [cms.CertificateChoices({'certificate': sign_key[1].asn1})]), 'signer_infos': cms.SignerInfos([ cms.SignerInfo({ 'version': cms.CMSVersion('v1'), 'sid': cms.SignerIdentifier({ 'issuer_and_serial_number': cms.IssuerAndSerialNumber({ 'issuer': sign_key[1].asn1['tbs_certificate']['issuer'], 'serial_number': sign_key[1].asn1['tbs_certificate'] ['serial_number'] }) }), 'digest_algorithm': algos.DigestAlgorithm( {'algorithm': algos.DigestAlgorithmId(digest_alg)}), 'signed_attrs': signed_attributes, 'signature_algorithm': algos.SignedDigestAlgorithm({ 'algorithm': algos.SignedDigestAlgorithmId('rsassa_pkcs1v15') }), 'signature': core.OctetString(signature) }) ]) }) }).dump()
def sign_message( data_to_sign, digest_alg, sign_key, sign_alg="rsassa_pkcs1v15", use_signed_attributes=True, ): """Function signs the data and returns the generated ASN.1 :param data_to_sign: A byte string of the data to be signed. :param digest_alg: The digest algorithm to be used for generating the signature. :param sign_key: The key to be used for generating the signature. :param sign_alg: The algorithm to be used for signing the message. :param use_signed_attributes: Optional attribute to indicate weather the CMS signature attributes should be included in the signature or not. :return: A CMS ASN.1 byte string of the signed data. """ if use_signed_attributes: digest_func = hashlib.new(digest_alg) digest_func.update(data_to_sign) message_digest = digest_func.digest() class SmimeCapability(core.Sequence): _fields = [ ("0", core.Any, { "optional": True }), ("1", core.Any, { "optional": True }), ("2", core.Any, { "optional": True }), ("3", core.Any, { "optional": True }), ("4", core.Any, { "optional": True }), ] class SmimeCapabilities(core.Sequence): _fields = [ ("0", SmimeCapability), ("1", SmimeCapability, { "optional": True }), ("2", SmimeCapability, { "optional": True }), ("3", SmimeCapability, { "optional": True }), ("4", SmimeCapability, { "optional": True }), ("5", SmimeCapability, { "optional": True }), ] smime_cap = OrderedDict([ ( "0", OrderedDict([ ("0", core.ObjectIdentifier("2.16.840.1.101.3.4.1.42")) ]), ), ( "1", OrderedDict([ ("0", core.ObjectIdentifier("2.16.840.1.101.3.4.1.2")) ]), ), ( "2", OrderedDict([("0", core.ObjectIdentifier("1.2.840.113549.3.7")) ]), ), ( "3", OrderedDict([ ("0", core.ObjectIdentifier("1.2.840.113549.3.2")), ("1", core.Integer(128)), ]), ), ( "4", OrderedDict([ ("0", core.ObjectIdentifier("1.2.840.113549.3.4")), ("1", core.Integer(128)), ]), ), ]) signed_attributes = cms.CMSAttributes([ cms.CMSAttribute({ "type": cms.CMSAttributeType("content_type"), "values": cms.SetOfContentType([cms.ContentType("data")]), }), cms.CMSAttribute({ "type": cms.CMSAttributeType("signing_time"), "values": cms.SetOfTime([ cms.Time({ "utc_time": core.UTCTime( datetime.utcnow().replace(tzinfo=timezone.utc)) }) ]), }), cms.CMSAttribute({ "type": cms.CMSAttributeType("message_digest"), "values": cms.SetOfOctetString([core.OctetString(message_digest)]), }), cms.CMSAttribute({ "type": cms.CMSAttributeType("1.2.840.113549.1.9.15"), "values": cms.SetOfAny([core.Any(SmimeCapabilities(smime_cap))]), }), ]) else: signed_attributes = None # Generate the signature data_to_sign = signed_attributes.dump( ) if signed_attributes else data_to_sign if sign_alg == "rsassa_pkcs1v15": signature = asymmetric.rsa_pkcs1v15_sign(sign_key[0], data_to_sign, digest_alg) elif sign_alg == "rsassa_pss": signature = asymmetric.rsa_pss_sign(sign_key[0], data_to_sign, digest_alg) else: raise AS2Exception("Unsupported Signature Algorithm") return cms.ContentInfo({ "content_type": cms.ContentType("signed_data"), "content": cms.SignedData({ "version": cms.CMSVersion("v1"), "digest_algorithms": cms.DigestAlgorithms([ algos.DigestAlgorithm( {"algorithm": algos.DigestAlgorithmId(digest_alg)}) ]), "encap_content_info": cms.ContentInfo({"content_type": cms.ContentType("data")}), "certificates": cms.CertificateSet( [cms.CertificateChoices({"certificate": sign_key[1].asn1})]), "signer_infos": cms.SignerInfos([ cms.SignerInfo({ "version": cms.CMSVersion("v1"), "sid": cms.SignerIdentifier({ "issuer_and_serial_number": cms.IssuerAndSerialNumber({ "issuer": sign_key[1].asn1["tbs_certificate"]["issuer"], "serial_number": sign_key[1].asn1["tbs_certificate"] ["serial_number"], }) }), "digest_algorithm": algos.DigestAlgorithm( {"algorithm": algos.DigestAlgorithmId(digest_alg)}), "signed_attrs": signed_attributes, "signature_algorithm": algos.SignedDigestAlgorithm( {"algorithm": algos.SignedDigestAlgorithmId(sign_alg)}), "signature": core.OctetString(signature), }) ]), }), }).dump()
async def signatures_sign_hash(self, request: web.Request): params = await request.json() try: cred_id = str(params['credentialID']) sad = params['SAD'] except KeyError: raise web.HTTPBadRequest() csc_auth_obj = CSCAuthorization.verify_sad( sad, secret=self.service_params.sad_secret, credential_id=cred_id) pki_arch, cert_spec = self._parse_credential_id(cred_id) try: priv_key_info = pki_arch.key_set.get_private_key( cert_spec.subject_key) except ConfigurationError as e: raise web.HTTPBadRequest() try: hash_list = [base64.b64decode(x) for x in params['hash']] sign_algo_id = algos.SignedDigestAlgorithmId(params['signAlgo']) except (KeyError, ValueError): raise web.HTTPBadRequest() if csc_auth_obj.hashes is not None: if not set(hash_list).issubset(csc_auth_obj.hashes): raise web.HTTPBadRequest() else: if not len(hash_list) <= csc_auth_obj.num_signatures: raise web.HTTPBadRequest() sign_algo_params = params.get('signAlgoParams', None) if sign_algo_params is not None: if sign_algo_id.native != 'rsassa_pss': raise web.HTTPNotImplemented() try: sign_algo_params = algos.RSASSAPSSParams.load( base64.b64decode(sign_algo_params)) except ValueError: raise web.HTTPBadRequest() try: sign_algo = algos.SignedDigestAlgorithm({ 'algorithm': sign_algo_id, 'parameters': sign_algo_params }) except ValueError: raise web.HTTPBadRequest() hash_algo_oid = params.get('hashAlgo', None) try: hash_algo = sign_algo.hash_algo except ValueError: if hash_algo_oid is None: raise web.HTTPBadRequest() hash_algo = algos.DigestAlgorithmId(hash_algo_oid).native def _sign_hash(x): signed = generic_sign_prehashed(priv_key_info, x, sign_algo, digest_algorithm=hash_algo) return base64.b64encode(signed).decode('ascii') result = list(map(_sign_hash, hash_list)) return web.json_response({'signatures': result})