Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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()
Ejemplo n.º 4
0
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()
Ejemplo n.º 5
0
    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})