Example #1
0
 def test_rsa_pss_verify(self):
     with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f:
         original_data = f.read()
     with open(os.path.join(fixtures_dir, 'rsa_pss_signature'), 'rb') as f:
         signature = f.read()
     public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt'))
     asymmetric.rsa_pss_verify(public, signature, original_data, 'sha1')
Example #2
0
 def test_rsa_pss_verify(self):
     with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f:
         original_data = f.read()
     with open(os.path.join(fixtures_dir, 'rsa_pss_signature'), 'rb') as f:
         signature = f.read()
     public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt'))
     asymmetric.rsa_pss_verify(public, signature, original_data, 'sha1')
Example #3
0
 def test_rsa_pss_verify_fail(self):
     with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f:
         original_data = f.read()
     with open(os.path.join(fixtures_dir, 'rsa_pss_signature'), 'rb') as f:
         signature = f.read()
     public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt'))
     with self.assertRaises(errors.SignatureError):
         asymmetric.rsa_pss_verify(public, signature, original_data + b'1', 'sha1')
Example #4
0
 def test_rsa_pss_verify_fail(self):
     with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f:
         original_data = f.read()
     with open(os.path.join(fixtures_dir, 'rsa_pss_signature'), 'rb') as f:
         signature = f.read()
     public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt'))
     with self.assertRaises(errors.SignatureError):
         asymmetric.rsa_pss_verify(public, signature, original_data + b'1', 'sha1')
Example #5
0
    def test_rsa_pss_sha256_sign(self):
        original_data = b'This is data to sign'
        private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key'))
        public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt'))

        signature = asymmetric.rsa_pss_sign(private, original_data, 'sha256')
        self.assertIsInstance(signature, byte_cls)

        asymmetric.rsa_pss_verify(public, signature, original_data, 'sha256')
Example #6
0
    def test_rsa_pss_sha256_sign(self):
        original_data = b'This is data to sign'
        private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key'))
        public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt'))

        signature = asymmetric.rsa_pss_sign(private, original_data, 'sha256')
        self.assertIsInstance(signature, byte_cls)

        asymmetric.rsa_pss_verify(public, signature, original_data, 'sha256')
Example #7
0
def verify_message(data_to_verify, signature, verify_cert):
    """Function parses an ASN.1 encrypted message and extracts/decrypts 
            the original message.

    :param data_to_verify: 
        A byte string of the data to be verified against the signature. 

    :param signature: A CMS ASN.1 byte string containing the signature.
    
    :param verify_cert: The certificate to be used for verifying the signature.

    :return: The digest algorithm that was used in the signature.    
    """

    cms_content = cms.ContentInfo.load(signature)
    digest_alg = None
    if cms_content['content_type'].native == 'signed_data':

        for signer in cms_content['content']['signer_infos']:

            digest_alg = signer['digest_algorithm']['algorithm'].native
            if digest_alg not in DIGEST_ALGORITHMS:
                raise Exception('Unsupported Digest Algorithm')

            sig_alg = signer['signature_algorithm']['algorithm'].native
            sig = signer['signature'].native
            signed_data = data_to_verify

            if signer['signed_attrs']:
                attr_dict = {}
                for attr in signer['signed_attrs']:
                    try:
                        attr_dict[attr.native['type']] = attr.native['values']
                    except (ValueError, KeyError):
                        continue

                message_digest = bytes()
                for d in attr_dict['message_digest']:
                    message_digest += d

                digest_func = hashlib.new(digest_alg)
                digest_func.update(data_to_verify)
                calc_message_digest = digest_func.digest()
                if message_digest != calc_message_digest:
                    raise IntegrityError(
                        'Failed to verify message signature: Message Digest does not match.'
                    )

                signed_data = signer['signed_attrs'].untag().dump()

            try:
                if sig_alg == 'rsassa_pkcs1v15':
                    asymmetric.rsa_pkcs1v15_verify(verify_cert, sig,
                                                   signed_data, digest_alg)
                elif sig_alg == 'rsassa_pss':
                    asymmetric.rsa_pss_verify(verify_cert, sig, signed_data,
                                              digest_alg)
                else:
                    raise AS2Exception('Unsupported Signature Algorithm')
            except Exception as e:
                raise IntegrityError(
                    'Failed to verify message signature: {}'.format(e))
    else:
        raise IntegrityError('Signed data not found in ASN.1 ')

    return digest_alg
Example #8
0
    def on_post(self, req, resp):
        """
        Validate and parse certificate signing request, the RESTful way
        """
        reasons = []
        body = req.stream.read(req.content_length).encode("ascii")

        header, _, der_bytes = pem.unarmor(body)
        csr = CertificationRequest.load(der_bytes)
        common_name = csr["certification_request_info"]["subject"].native["common_name"]

        """
        Handle domain computer automatic enrollment
        """
        machine = req.context.get("machine")
        if machine:
            if config.MACHINE_ENROLLMENT_ALLOWED:
                if common_name != machine:
                    raise falcon.HTTPBadRequest(
                        "Bad request",
                        "Common name %s differs from Kerberos credential %s!" % (common_name, machine))

                # Automatic enroll with Kerberos machine cerdentials
                resp.set_header("Content-Type", "application/x-pem-file")
                cert, resp.body = authority._sign(csr, body, overwrite=True)
                logger.info(u"Automatically enrolled Kerberos authenticated machine %s from %s",
                    machine, req.context.get("remote_addr"))
                return
            else:
                reasons.append("Machine enrollment not allowed")

        """
        Attempt to renew certificate using currently valid key pair
        """
        try:
            path, buf, cert = authority.get_signed(common_name)
        except EnvironmentError:
            pass # No currently valid certificate for this common name
        else:
            cert_pk = cert["tbs_certificate"]["subject_public_key_info"].native
            csr_pk = csr["certification_request_info"]["subject_pk_info"].native

            if cert_pk == csr_pk: # Same public key, assume renewal
                expires = cert["tbs_certificate"]["validity"]["not_after"].native.replace(tzinfo=None)
                renewal_header = req.get_header("X-Renewal-Signature")

                if not renewal_header:
                    # No header supplied, redirect to signed API call
                    resp.status = falcon.HTTP_SEE_OTHER
                    resp.location = os.path.join(os.path.dirname(req.relative_uri), "signed", common_name)
                    return

                try:
                    renewal_signature = b64decode(renewal_header)
                except TypeError, ValueError:
                    logger.error(u"Renewal failed, bad signature supplied for %s", common_name)
                    reasons.append("Renewal failed, bad signature supplied")
                else:
                    try:
                        asymmetric.rsa_pss_verify(
                            asymmetric.load_certificate(cert),
                            renewal_signature, buf + body, "sha512")
                    except SignatureError:
                        logger.error(u"Renewal failed, invalid signature supplied for %s", common_name)
                        reasons.append("Renewal failed, invalid signature supplied")
                    else:
                        # At this point renewal signature was valid but we need to perform some extra checks
                        if datetime.utcnow() > expires:
                            logger.error(u"Renewal failed, current certificate for %s has expired", common_name)
                            reasons.append("Renewal failed, current certificate expired")
                        elif not config.CERTIFICATE_RENEWAL_ALLOWED:
                            logger.error(u"Renewal requested for %s, but not allowed by authority settings", common_name)
                            reasons.append("Renewal requested, but not allowed by authority settings")
                        else:
                            resp.set_header("Content-Type", "application/x-x509-user-cert")
                            _, resp.body = authority._sign(csr, body, overwrite=True)
                            logger.info(u"Renewed certificate for %s", common_name)
                            return
Example #9
0
    def on_post(self, req, resp):
        """
        Validate and parse certificate signing request, the RESTful way
        """
        reasons = []
        body = req.stream.read(req.content_length)

        try:
            header, _, der_bytes = pem.unarmor(body)
            csr = CertificationRequest.load(der_bytes)
        except ValueError:
            raise falcon.HTTPBadRequest(
                "Bad request", "Malformed certificate signing request")

        common_name = csr["certification_request_info"]["subject"].native[
            "common_name"]
        """
        Handle domain computer automatic enrollment
        """
        machine = req.context.get("machine")
        if machine:
            if config.MACHINE_ENROLLMENT_ALLOWED:
                if common_name != machine:
                    raise falcon.HTTPBadRequest(
                        "Bad request",
                        "Common name %s differs from Kerberos credential %s!" %
                        (common_name, machine))

                # Automatic enroll with Kerberos machine cerdentials
                resp.set_header("Content-Type", "application/x-pem-file")
                cert, resp.body = self.authority._sign(csr,
                                                       body,
                                                       overwrite=True)
                logger.info(
                    "Automatically enrolled Kerberos authenticated machine %s from %s",
                    machine, req.context.get("remote_addr"))
                return
            else:
                reasons.append("Machine enrollment not allowed")
        """
        Attempt to renew certificate using currently valid key pair
        """
        try:
            path, buf, cert, signed, expires = self.authority.get_signed(
                common_name)
        except EnvironmentError:
            pass  # No currently valid certificate for this common name
        else:
            cert_pk = cert["tbs_certificate"]["subject_public_key_info"].native
            csr_pk = csr["certification_request_info"][
                "subject_pk_info"].native

            if cert_pk == csr_pk:  # Same public key, assume renewal
                expires = cert["tbs_certificate"]["validity"][
                    "not_after"].native.replace(tzinfo=None)
                renewal_header = req.get_header("X-Renewal-Signature")

                if not renewal_header:
                    # No header supplied, redirect to signed API call
                    resp.status = falcon.HTTP_SEE_OTHER
                    resp.location = os.path.join(
                        os.path.dirname(req.relative_uri), "signed",
                        common_name)
                    return

                try:
                    renewal_signature = b64decode(renewal_header)
                except (TypeError, ValueError):
                    logger.error(
                        "Renewal failed, bad signature supplied for %s",
                        common_name)
                    reasons.append("Renewal failed, bad signature supplied")
                else:
                    try:
                        asymmetric.rsa_pss_verify(
                            asymmetric.load_certificate(cert),
                            renewal_signature, buf + body, "sha512")
                    except SignatureError:
                        logger.error(
                            "Renewal failed, invalid signature supplied for %s",
                            common_name)
                        reasons.append(
                            "Renewal failed, invalid signature supplied")
                    else:
                        # At this point renewal signature was valid but we need to perform some extra checks
                        if datetime.utcnow() > expires:
                            logger.error(
                                "Renewal failed, current certificate for %s has expired",
                                common_name)
                            reasons.append(
                                "Renewal failed, current certificate expired")
                        elif not config.CERTIFICATE_RENEWAL_ALLOWED:
                            logger.error(
                                "Renewal requested for %s, but not allowed by authority settings",
                                common_name)
                            reasons.append(
                                "Renewal requested, but not allowed by authority settings"
                            )
                        else:
                            resp.set_header("Content-Type",
                                            "application/x-x509-user-cert")
                            _, resp.body = self.authority._sign(csr,
                                                                body,
                                                                overwrite=True)
                            logger.info("Renewed certificate for %s",
                                        common_name)
                            return
        """
        Process automatic signing if the IP address is whitelisted,
        autosigning was requested and certificate can be automatically signed
        """
        if req.get_param_as_bool("autosign"):
            if not self.authority.server_flags(common_name):
                for subnet in config.AUTOSIGN_SUBNETS:
                    if req.context.get("remote_addr") in subnet:
                        try:
                            resp.set_header("Content-Type",
                                            "application/x-pem-file")
                            _, resp.body = self.authority._sign(csr, body)
                            logger.info("Autosigned %s as %s is whitelisted",
                                        common_name,
                                        req.context.get("remote_addr"))
                            return
                        except EnvironmentError:
                            logger.info(
                                "Autosign for %s from %s failed, signed certificate already exists",
                                common_name, req.context.get("remote_addr"))
                            reasons.append(
                                "Autosign failed, signed certificate already exists"
                            )
                        break
                else:
                    reasons.append(
                        "Autosign failed, IP address not whitelisted")
            else:
                reasons.append(
                    "Autosign failed, only client certificates allowed to be signed automatically"
                )

        # Attempt to save the request otherwise
        try:
            request_path, _, _ = self.authority.store_request(
                body, address=str(req.context.get("remote_addr")))
        except errors.RequestExists:
            reasons.append("Same request already uploaded exists")
            # We should still redirect client to long poll URL below
        except errors.DuplicateCommonNameError:
            # TODO: Certificate renewal
            logger.warning(
                "Rejected signing request with overlapping common name from %s",
                req.context.get("remote_addr"))
            raise falcon.HTTPConflict(
                "CSR with such CN already exists",
                "Will not overwrite existing certificate signing request, explicitly delete CSR and try again"
            )
        else:
            push.publish("request-submitted", common_name)

        # Wait the certificate to be signed if waiting is requested
        logger.info("Stored signing request %s from %s", common_name,
                    req.context.get("remote_addr"))
        if req.get_param("wait"):
            # Redirect to nginx pub/sub
            url = config.LONG_POLL_SUBSCRIBE % hashlib.sha256(body).hexdigest()
            click.echo("Redirecting to: %s" % url)
            resp.status = falcon.HTTP_SEE_OTHER
            resp.set_header("Location", url)
            logger.debug("Redirecting signing request from %s to %s",
                         req.context.get("remote_addr"), url)
        else:
            # Request was accepted, but not processed
            resp.status = falcon.HTTP_202
            resp.body = ". ".join(reasons)
            if req.client_accepts("application/json"):
                resp.body = json.dumps(
                    {
                        "title": "Accepted",
                        "description": resp.body
                    },
                    cls=MyEncoder)