Ejemplo n.º 1
0
 def __init__(self, config=None):
     if config is None:
         config = get_config_from_file("config.ini")
     self.config = config
     # Seed the trust stores
     Sync(self.config).seed()
     self.cert_processor = CertProcessor(config)
Ejemplo n.º 2
0
    def setUp(self):
        self.USER_GNUPGHOME = tempfile.TemporaryDirectory()
        self.ADMIN_GNUPGHOME = tempfile.TemporaryDirectory()
        self.AUTHORITY_FOLDER = tempfile.TemporaryDirectory()
        self.FQDN = "my.test.server"
        self.fqdn_patch = patch.dict("os.environ", {"FQDN": self.FQDN})
        self.fqdn_patch.start()
        config = ConfigParser()
        config.read_string(
            """
            [ca]
            key = {authority_folder}/RootCA.key
            cert = {authority_folder}/RootCA.pem
            issuer = My Company Name
            alternate_name = *.myname.com

            [gnupg]
            user={user_gnupghome}
            admin={admin_gnupghome}

            [storage]
            engine=sqlite3

            [storage.sqlite3]
            db_path=:memory:
            """.format(
                user_gnupghome=self.USER_GNUPGHOME.name,
                admin_gnupghome=self.ADMIN_GNUPGHOME.name,
                authority_folder=self.AUTHORITY_FOLDER.name,
            )
        )
        self.common_name = "user@host"
        self.key = generate_key()
        self.engine = storage.SQLiteStorageEngine(config)
        cur = self.engine.conn.cursor()
        cur.execute("DROP TABLE IF EXISTS certs")
        self.engine.conn.commit()
        self.engine.init_db()
        self.cert_processor = CertProcessor(config)
        self.user_gpg = gnupg.GPG(gnupghome=self.USER_GNUPGHOME.name)
        self.admin_gpg = gnupg.GPG(gnupghome=self.ADMIN_GNUPGHOME.name)
        self.users = [
            User("*****@*****.**", gen_passwd(), generate_key(), gpg=self.user_gpg)
        ]
        for user in self.users:
            self.user_gpg.import_keys(self.user_gpg.export_keys(user.fingerprint))
Ejemplo n.º 3
0
    def setUp(self):
        self.USER_GNUPGHOME = tempfile.TemporaryDirectory()
        self.ADMIN_GNUPGHOME = tempfile.TemporaryDirectory()
        self.NEW_USER_GNUPGHOME = tempfile.TemporaryDirectory()
        self.NEW_ADMIN_GNUPGHOME = tempfile.TemporaryDirectory()
        self.SEED_DIR = tempfile.TemporaryDirectory()
        self.config = ConfigParser()
        self.config.read_string("""
            [mtls]
            min_lifetime=60
            max_lifetime=0
            seed_dir={seed_dir}

            [ca]
            key = secrets/certs/authority/RootCA.key
            cert = secrets/certs/authority/RootCA.pem
            issuer = My Company Name
            alternate_name = *.myname.com

            [gnupg]
            user={user_gnupghome}
            admin={admin_gnupghome}

            [storage]
            engine=sqlite3

            [storage.sqlite3]
            db_path=:memory:
            """.format(
            user_gnupghome=self.USER_GNUPGHOME.name,
            admin_gnupghome=self.ADMIN_GNUPGHOME.name,
            seed_dir=self.SEED_DIR.name,
        ))
        self.common_name = "user@host"
        self.key = generate_key()
        self.engine = storage.SQLiteStorageEngine(self.config)
        cur = self.engine.conn.cursor()
        cur.execute("DROP TABLE IF EXISTS certs")
        self.engine.conn.commit()
        self.engine.init_db()
        self.cert_processor = CertProcessor(self.config)
        self.user_gpg = gnupg.GPG(gnupghome=self.USER_GNUPGHOME.name)
        self.admin_gpg = gnupg.GPG(gnupghome=self.ADMIN_GNUPGHOME.name)
        self.new_user_gpg = gnupg.GPG(gnupghome=self.NEW_USER_GNUPGHOME.name)
        self.new_admin_gpg = gnupg.GPG(gnupghome=self.NEW_ADMIN_GNUPGHOME.name)
        self.new_users = [
            User("user@host",
                 gen_passwd(),
                 generate_key(),
                 gpg=self.new_user_gpg)
        ]
        self.new_admins = [
            User("admin@host",
                 gen_passwd(),
                 generate_key(),
                 gpg=self.new_admin_gpg)
        ]
Ejemplo n.º 4
0
    def setUp(self):
        self.USER_GNUPGHOME = tempfile.TemporaryDirectory()
        self.ADMIN_GNUPGHOME = tempfile.TemporaryDirectory()
        config = ConfigParser()
        config.read_string(
            """
            [ca]
            key = secrets/certs/authority/RootCA.key
            cert = secrets/certs/authority/RootCA.pem
            issuer = My Company Name
            alternate_name = *.myname.com

            [gnupg]
            user={user_gnupghome}
            admin={admin_gnupghome}

            [storage]
            engine=postgres

            [storage.postgres]
            database = mtls
            user = postgres
            password = postgres
            host = localhost
            """.format(
                user_gnupghome=self.USER_GNUPGHOME.name,
                admin_gnupghome=self.ADMIN_GNUPGHOME.name,
            )
        )
        self.common_name = "user@host"
        self.key = generate_key()
        self.engine = storage.PostgresqlStorageEngine(config)
        cur = self.engine.conn.cursor()
        cur.execute("DROP TABLE IF EXISTS certs")
        self.engine.conn.commit()
        self.engine.init_db()

        self.cert_processor = CertProcessor(config)
        self.user_gpg = gnupg.GPG(gnupghome=self.USER_GNUPGHOME.name)
        self.admin_gpg = gnupg.GPG(gnupghome=self.ADMIN_GNUPGHOME.name)
        self.users = [
            User("user@host", gen_passwd(), generate_key(), gpg=self.user_gpg),
            User("user2@host", gen_passwd(), generate_key(), gpg=self.user_gpg),
            User("user3@host", gen_passwd(), generate_key(), gpg=self.user_gpg),
        ]
        self.invalid_users = [
            User("user4@host", gen_passwd(), generate_key(), gpg=self.user_gpg)
        ]
        self.admin_users = [
            User("admin@host", gen_passwd(), generate_key(), gpg=self.admin_gpg)
        ]
        for user in self.users:
            self.user_gpg.import_keys(self.user_gpg.export_keys(user.fingerprint))
        for user in self.admin_users:
            self.admin_gpg.import_keys(self.admin_gpg.export_keys(user.fingerprint))
Ejemplo n.º 5
0
    def setUp(self):
        dir_path = os.path.dirname(os.path.realpath(__file__))
        self.USER_GNUPGHOME = tempfile.TemporaryDirectory(prefix=dir_path + "/secrets/")
        self.ADMIN_GNUPGHOME = tempfile.TemporaryDirectory(prefix=dir_path + "/secrets/")
        relative_user = "******" + self.USER_GNUPGHOME.name.split(dir_path)[1]
        relative_admin = "." + self.ADMIN_GNUPGHOME.name.split(dir_path)[1]
        config = ConfigParser()
        config.read_string(
            """
            [ca]
            key = secrets/certs/authority/RootCA.key
            cert = secrets/certs/authority/RootCA.pem
            issuer = My Company Name
            alternate_name = *.myname.com

            [gnupg]
            user={user_gnupghome}
            admin={admin_gnupghome}

            [storage]
            engine=sqlite3

            [storage.sqlite3]
            db_path=:memory:
            """.format(
                user_gnupghome=relative_user, admin_gnupghome=relative_admin
            )
        )
        self.common_name = "user@host"
        self.key = generate_key()
        self.engine = storage.SQLiteStorageEngine(config)
        cur = self.engine.conn.cursor()
        cur.execute("DROP TABLE IF EXISTS certs")
        self.engine.conn.commit()
        self.engine.init_db()
        self.cert_processor = CertProcessor(config)
        self.user_gpg = gnupg.GPG(gnupghome=self.USER_GNUPGHOME.name)
        self.admin_gpg = gnupg.GPG(gnupghome=self.ADMIN_GNUPGHOME.name)
        self.users = [
            User("user@host", gen_passwd(), generate_key(), gpg=self.user_gpg),
            User("user2@host", gen_passwd(), generate_key(), gpg=self.user_gpg),
            User("user3@host", gen_passwd(), generate_key(), gpg=self.user_gpg),
        ]
        self.invalid_users = [
            User("user4@host", gen_passwd(), generate_key(), gpg=self.user_gpg)
        ]
        self.admin_users = [
            User("admin@host", gen_passwd(), generate_key(), gpg=self.admin_gpg)
        ]
        for user in self.users:
            self.user_gpg.import_keys(self.user_gpg.export_keys(user.fingerprint))
        for user in self.admin_users:
            self.admin_gpg.import_keys(self.admin_gpg.export_keys(user.fingerprint))
Ejemplo n.º 6
0
from cert_processor import CertProcessorKeyNotFoundError
from cert_processor import CertProcessorInvalidSignatureError

__author__ = 'Danny Grove <*****@*****.**>'
VERSION = 'version 0.1'

app = Flask(__name__)
config = ConfigParser()
config_path = os.path.abspath(
    os.path.join(
        os.path.dirname(__file__),
        'config.ini'
    )
)
config.read(config_path)
cert_processor = CertProcessor(config)


def error_response(msg, status_code=501):
    return json.dumps({
        'error': True,
        'msg': msg
    }), status_code


@app.route('/', methods=['POST'])
def create_cert():
    body = request.get_json()
    lifetime = int(body['lifetime'])
    if lifetime < 1:
        error_response('lifetime must be greater than 1 hour')
Ejemplo n.º 7
0
 def __init__(self, config=None):
     if config is None:
         config = get_config_from_file("config.ini")
     self.config = config
     self.cert_processor = CertProcessor(config)
     self.seed()
Ejemplo n.º 8
0
class Handler:
    def __init__(self, config=None):
        if config is None:
            config = get_config_from_file("config.ini")
        self.config = config
        self.cert_processor = CertProcessor(config)
        self.seed()

    def seed(self):
        """Seeds the User and Admin trust databases."""
        logger.info("Seeding PGP Trust Databases")
        seed_base_dir = self.config.get("mtls",
                                        "seed_dir",
                                        fallback="/tmp/seeds")
        if os.path.isdir(seed_base_dir):
            for trust in ["user", "admin"]:
                seed_dir = os.path.join(seed_base_dir, trust)
                if os.path.isdir(seed_dir):
                    logger.info("Seeding {} Trust Store".format(trust))
                    for f in os.listdir(seed_dir):
                        f_path = os.path.join(seed_dir, f)
                        if os.path.isfile(f_path):
                            fingerprint = f.split(".")[0]
                            with open(f_path, "r") as gpg_data:
                                gpg_data = str(gpg_data.read())
                                if trust == "admin":
                                    self.import_and_trust(
                                        gpg_data,
                                        self.cert_processor.admin_gpg)
                                # If we add an admin, they're also a user,
                                # so we can just pull the fingerprint once
                                # and use that for logging. It will only show
                                # it's being 'added' to the admin store, but
                                # that's fine since that assumption is already
                                # made
                                fingerprint = self.import_and_trust(
                                    gpg_data, self.cert_processor.user_gpg)
                                logger.info("Added {fp} to {t} Store".format(
                                    fp=fingerprint, t=trust))

    def import_and_trust(self, key_data, gpg):
        """Imports a key into a given keyring and trust database as well as
        properly trusting it for use.

        Args:
            key_data (str): The key data in ACSII or binary format.
            gpg (gnupg.GPG): The gpg instance.

        Returns:
            str: The fingerprint of the newly imported and trusted key.
        """
        import_data = gpg.import_keys(key_data)
        fingerprint = import_data.fingerprints[0]
        gpg.trust_keys([fingerprint], "TRUST_ULTIMATE")
        return fingerprint

    def create_cert(self, body):
        """Create a certificate."""
        lifetime = int(body["lifetime"])
        min_lifetime = int(self.config.get("mtls", "min_lifetime",
                                           fallback=60))
        max_lifetime = int(self.config.get("mtls", "max_lifetime", fallback=0))
        if lifetime < min_lifetime:
            logger.info(
                "User requested lifetime less than minimum. {} < {}".format(
                    lifetime, min_lifetime))
            error_response("lifetime must be greater than {} seconds".format(
                min_lifetime))
        if max_lifetime != 0:
            if lifetime > max_lifetime:
                logger.info(
                    "User requested lifetime greater than maximum. {} < {}".
                    format(lifetime, max_lifetime))
                error_response("lifetime must be less than {} seconds".format(
                    max_lifetime))
        csr_str = body["csr"]
        csr = self.cert_processor.get_csr(csr_str)
        if csr is None:
            return error_response("Could not load CSR")
        try:
            csr_public_bytes = csr.public_bytes(serialization.Encoding.PEM)
            sig_path = write_sig_to_file(body["signature"])
            fingerprint = self.cert_processor.verify(csr_public_bytes,
                                                     sig_path)
            os.remove(sig_path)
        except CertProcessorUntrustedSignatureError as e:
            logger.info("Unauthorized: {}".format(e))
            return error_response("Unauthorized", 403)
        except CertProcessorInvalidSignatureError:
            logger.info("Invalid signature in CSR.")
            return error_response("Invalid signature", 401)
        if csr is None:
            logger.info("Invalid CSR.")
            return error_response("Invalid CSR")
        cert = None
        try:
            cert = self.cert_processor.generate_cert(csr, lifetime,
                                                     fingerprint)
            return json.dumps({"cert": cert.decode("UTF-8")}), 200
        except CertProcessorKeyNotFoundError:
            logger.critical("Key missing. Service not properly initialized")
            return error_response("Internal Error")
        except CertProcessorMismatchedPublicKeyError:
            logger.error("CSR Public Key does not match found certificate.")
            return error_response("Internal Error")
        except CertProcessorNotAdminUserError:
            logger.error(
                "User {} is not an admin and attempted ".format(fingerprint) +
                "to generate a certificate they are not allowed to generate.")
            return error_response("Invalid Request", 403)
        except CertProcessorNoPGPKeyFoundError:
            logger.info("PGP Key not found.")
            return error_response("Unauthorized", 401)

    def revoke_cert(self, body):
        """
        A user should be able to revoke their own certificate. An admin should
        be able to revoke the certificate of any user.

        Args:
            body: A dictionary from the JSON input.

        Returns:
            (json, int): a tuple of the json response and http status code.
        """
        is_admin = False
        fingerprint = None
        sig_path = write_sig_to_file(body["signature"])
        try:
            fingerprint = self.cert_processor.admin_verify(
                json.dumps(body["query"]).encode("UTF-8"), sig_path)
            is_admin = True
            logger.info(
                "Admin {adminfp} revoking certificate with query {query}".
                format(adminfp=fingerprint, query=json.dumps(body["query"])))
            os.remove(sig_path)
        except (CertProcessorInvalidSignatureError,
                CertProcessorUntrustedSignatureError):
            try:
                fingerprint = self.cert_processor.verify(
                    json.dumps(body["query"]).encode("UTF-8"), sig_path)
                logger.info(
                    "User {userfp} revoking certificate with query {query}".
                    format(userfp=fingerprint,
                           query=json.dumps(body["query"])))
                os.remove(sig_path)
            except (
                    CertProcessorInvalidSignatureError,
                    CertProcessorUntrustedSignatureError,
            ):
                os.remove(sig_path)
                return error_response("Unauthorized", 403)

        certs = self.cert_processor.storage.get_cert(**body["query"])
        if certs is None:
            return error_response("No Cert to revoke")
        for cert in certs:
            cert = x509.load_pem_x509_certificate(str(cert).encode("UTF-8"),
                                                  backend=default_backend())
            self.cert_processor.revoke_cert(cert.serial_number)
        return json.dumps({"msg": "success"}), 200

    def add_user(self, body, is_admin=False):
        """Add a user or admin."""
        fingerprint = None
        sig_path = write_sig_to_file(body["signature"])
        try:
            fingerprint = self.cert_processor.admin_verify(
                body["fingerprint"].encode("UTF-8"), sig_path)
        except (CertProcessorInvalidSignatureError,
                CertProcessorUntrustedSignatureError):
            os.remove(sig_path)
            logger.error(
                "Invalid signature on adding fingerprint: {fp}".format(
                    fp=body["fingerprint"]))
            return error_response("Unauthorized", 403)
        # Remove signature file
        os.remove(sig_path)

        fingerprint = body["fingerprint"]

        try:
            if is_admin:
                has_user = self.has_user(self.cert_processor.admin_gpg,
                                         fingerprint)
                if not has_user:
                    logger.info(
                        "Admin {adminfp} adding admin user {userfp}".format(
                            adminfp=fingerprint, userfp=body["fingerprint"]))
                    # Add a user to the admin trust store
                    self.add_and_trust_user(self.cert_processor.admin_gpg,
                                            fingerprint)

            has_user = self.has_user(self.cert_processor.user_gpg, fingerprint)

            if not has_user:
                # Add the user to the user trust store
                logger.info(
                    "Admin {adminfp} adding admin user {userfp}".format(
                        adminfp=fingerprint, userfp=body["fingerprint"]))
                self.add_and_trust_user(self.cert_processor.user_gpg,
                                        fingerprint)
            return json.dumps({"msg": "success"}), 201
        except GPGKeyNotFoundException:
            return (
                json.dumps(
                    {"msg": "Key not found on keyserver. Could not import"}),
                422,
            )

    def has_user(self, gpg, fingerprint):
        keys = gpg.list_keys(keys=fingerprint)
        if len(keys) is 0:
            return False
        return True

    def add_and_trust_user(self, gpg, fingerprint):
        result = self.cert_processor.user_gpg.recv_keys(
            self.config.get("gnupg",
                            "keyserver",
                            fallback="keyserver.ubuntu.com"),
            fingerprint,
        )
        if result.count is None or result.count == 0:
            raise GPGKeyNotFoundException()
        self.cert_processor.user_gpg.trust_keys([fingerprint],
                                                "TRUST_ULTIMATE")

    def remove_user(self, body, is_admin=False):
        """Remove a user or admin."""
        fingerprint = None
        sig_path = write_sig_to_file(body["signature"])
        try:
            fingerprint = self.cert_processor.admin_verify(
                body["fingerprint"].encode("UTF-8"), sig_path)
            logger.info("Admin {adminfp} adding user {userfp}".format(
                adminfp=fingerprint, userfp=body["fingerprint"]))
        except (CertProcessorInvalidSignatureError,
                CertProcessorUntrustedSignatureError):
            os.remove(sig_path)
            logger.error(
                "Invalid signature on adding fingerprint: {fp}".format(
                    fp=body["fingerprint"]))
            return error_response("Unauthorized", 403)
        # Remove signature file
        os.remove(sig_path)

        if is_admin:
            # Add a user to the admin trust store
            self.cert_processor.admin_gpg.delete_keys(body["fingerprint"])

        # Add the user to the user trust store
        self.cert_processor.user_gpg.delete_keys(body["fingerprint"])
        return json.dumps({"msg": "success"}), 201
Ejemplo n.º 9
0
class TestCertProcessorCRLDistributionPath(TestCertProcessorBase):
    def setUp(self):
        self.USER_GNUPGHOME = tempfile.TemporaryDirectory()
        self.ADMIN_GNUPGHOME = tempfile.TemporaryDirectory()
        self.AUTHORITY_FOLDER = tempfile.TemporaryDirectory()
        self.FQDN = "my.test.server"
        self.fqdn_patch = patch.dict("os.environ", {"FQDN": self.FQDN})
        self.fqdn_patch.start()
        config = ConfigParser()
        config.read_string(
            """
            [ca]
            key = {authority_folder}/RootCA.key
            cert = {authority_folder}/RootCA.pem
            issuer = My Company Name
            alternate_name = *.myname.com

            [gnupg]
            user={user_gnupghome}
            admin={admin_gnupghome}

            [storage]
            engine=sqlite3

            [storage.sqlite3]
            db_path=:memory:
            """.format(
                user_gnupghome=self.USER_GNUPGHOME.name,
                admin_gnupghome=self.ADMIN_GNUPGHOME.name,
                authority_folder=self.AUTHORITY_FOLDER.name,
            )
        )
        self.common_name = "user@host"
        self.key = generate_key()
        self.engine = storage.SQLiteStorageEngine(config)
        cur = self.engine.conn.cursor()
        cur.execute("DROP TABLE IF EXISTS certs")
        self.engine.conn.commit()
        self.engine.init_db()
        self.cert_processor = CertProcessor(config)
        self.user_gpg = gnupg.GPG(gnupghome=self.USER_GNUPGHOME.name)
        self.admin_gpg = gnupg.GPG(gnupghome=self.ADMIN_GNUPGHOME.name)
        self.users = [
            User("*****@*****.**", gen_passwd(), generate_key(), gpg=self.user_gpg)
        ]
        for user in self.users:
            self.user_gpg.import_keys(self.user_gpg.export_keys(user.fingerprint))

    def tearDown(self):
        self.USER_GNUPGHOME.cleanup()
        self.ADMIN_GNUPGHOME.cleanup()
        self.AUTHORITY_FOLDER.cleanup()
        self.fqdn_patch.stop()

    def test_crl_distribution_path(self):
        user = self.users[0]
        csr = user.gen_csr(email=user.email)
        sig = self.user_gpg.sign(
            csr.public_bytes(serialization.Encoding.PEM),
            keyid=user.fingerprint,
            detach=True,
            clearsign=True,
            passphrase=user.password,
        )
        bcert = self.cert_processor.generate_cert(csr, 60, user.fingerprint)
        cert = x509.load_pem_x509_certificate(bcert, backend=default_backend())
        self.assertIsInstance(cert, openssl.x509._Certificate)
        has_crl_extension = False
        for extension in cert.extensions:
            if isinstance(extension.value, x509.CRLDistributionPoints):
                has_crl_extension = True
                for distributionPoint in extension.value:
                    uris = distributionPoint.full_name
                    for uri in uris:
                        crl_path = "http://{FQDN}/crl".format(FQDN=self.FQDN)
                        self.assertEqual(uri.value, crl_path)
        self.assertTrue(has_crl_extension)
Ejemplo n.º 10
0
 def test_missing_storage(self):
     with self.assertRaises(storage.StorageEngineMissing):
         self.cert_processor = CertProcessor(self.config)
Ejemplo n.º 11
0
class Handler:
    def __init__(self, config=None):
        if config is None:
            config = get_config_from_file("config.ini")
        self.config = config
        # Seed the trust stores
        Sync(self.config).seed()
        self.cert_processor = CertProcessor(config)

    def create_cert(self, body):
        """Create a certificate."""
        lifetime = int(body["lifetime"])
        min_lifetime = int(self.config.get("mtls", "min_lifetime",
                                           fallback=60))
        max_lifetime = int(self.config.get("mtls", "max_lifetime", fallback=0))
        if lifetime < min_lifetime:
            logger.info(
                "User requested lifetime less than minimum. {} < {}".format(
                    lifetime, min_lifetime))
            return error_response(
                "lifetime must be greater than {} seconds".format(
                    min_lifetime))
        if max_lifetime != 0:
            if lifetime > max_lifetime:
                logger.info(
                    "User requested lifetime greater than maximum. {} < {}".
                    format(lifetime, max_lifetime))
                return error_response(
                    "lifetime must be less than {} seconds".format(
                        max_lifetime))
        csr_str = body["csr"]
        csr = self.cert_processor.get_csr(csr_str)
        if csr is None:
            return error_response("Could not load CSR")
        try:
            logger.info("create_cert: get csr_public_bytes")
            csr_public_bytes = csr.public_bytes(serialization.Encoding.PEM)
            logger.info("create_cert: write to temp sig file")
            sig_path = write_sig_to_file(body["signature"])
            logger.info("create_cert: get fingerprint")
            fingerprint = self.cert_processor.verify(csr_public_bytes,
                                                     sig_path)
            logger.info("create_cert: remove sig file")
            os.remove(sig_path)
        except CertProcessorUntrustedSignatureError as e:
            logger.info("Unauthorized: {}".format(e))
            return error_response("Unauthorized", 403)
        except CertProcessorInvalidSignatureError:
            logger.info("Invalid signature in CSR.")
            return error_response("Invalid signature", 401)
        except Exception as e:
            logger.critical("Unknown Error: {}".format(e))
            return error_response("Internal Server Error", 500)
        if csr is None:
            logger.info("Invalid CSR.")
            return error_response("Invalid CSR")
        cert = None
        try:
            logger.info(
                f"create_cert: generating certificate for: {fingerprint}")
            cert = self.cert_processor.generate_cert(csr, lifetime,
                                                     fingerprint)
            logger.info(
                f"create_cert: sending certificate to client for: {fingerprint}"
            )
            return json.dumps({"cert": cert.decode("UTF-8")}), 200
        except CertProcessorKeyNotFoundError:
            logger.critical("Key missing. Service not properly initialized")
            return error_response("Internal Error")
        except CertProcessorMismatchedPublicKeyError:
            logger.error("CSR Public Key does not match found certificate.")
            return error_response("Internal Error")
        except CertProcessorNotAdminUserError:
            logger.error(
                "User {} is not an admin and attempted ".format(fingerprint) +
                "to generate a certificate they are not allowed to generate.")
            return error_response("Invalid Request", 403)
        except CertProcessorNoPGPKeyFoundError:
            logger.info("PGP Key not found.")
            return error_response("Unauthorized", 401)
        except Exception as e:
            logger.critical(f"Unhandled Exception: {e}")
            return error_response("Internal Server Error", 500)

    def revoke_cert(self, body):
        """
        A user should be able to revoke their own certificate. An admin should
        be able to revoke the certificate of any user.

        Args:
            body: A dictionary from the JSON input.

        Returns:
            (json, int): a tuple of the json response and http status code.
        """
        is_admin = False
        fingerprint = None
        sig_path = write_sig_to_file(body["signature"])
        try:
            fingerprint = self.cert_processor.admin_verify(
                json.dumps(body["query"]).encode("UTF-8"), sig_path)
            is_admin = True
            logger.info(
                "Admin {adminfp} revoking certificate with query {query}".
                format(adminfp=fingerprint, query=json.dumps(body["query"])))
            os.remove(sig_path)
        except (CertProcessorInvalidSignatureError,
                CertProcessorUntrustedSignatureError):
            try:
                fingerprint = self.cert_processor.verify(
                    json.dumps(body["query"]).encode("UTF-8"), sig_path)
                logger.info(
                    "User {userfp} revoking certificate with query {query}".
                    format(userfp=fingerprint,
                           query=json.dumps(body["query"])))
                os.remove(sig_path)
            except (
                    CertProcessorInvalidSignatureError,
                    CertProcessorUntrustedSignatureError,
            ):
                os.remove(sig_path)
                return error_response("Unauthorized", 403)

        certs = self.cert_processor.storage.get_cert(**body["query"])
        if certs is None:
            return error_response("No Cert to revoke")
        for cert in certs:
            cert = x509.load_pem_x509_certificate(str(cert).encode("UTF-8"),
                                                  backend=default_backend())
            self.cert_processor.revoke_cert(cert.serial_number)
        return json.dumps({"msg": "success"}), 200

    def add_user(self, body, is_admin=False):
        """Add a user or admin."""
        fingerprint = None
        sig_path = write_sig_to_file(body["signature"])
        try:
            fingerprint = self.cert_processor.admin_verify(
                body["fingerprint"].encode("UTF-8"), sig_path)
        except (CertProcessorInvalidSignatureError,
                CertProcessorUntrustedSignatureError):
            os.remove(sig_path)
            logger.error(
                "Invalid signature on adding fingerprint: {fp}".format(
                    fp=body["fingerprint"]))
            return error_response("Unauthorized", 403)
        # Remove signature file
        os.remove(sig_path)

        fingerprint = body["fingerprint"]

        try:
            if is_admin:
                has_user = self.has_user(self.cert_processor.admin_gpg,
                                         fingerprint)
                if not has_user:
                    logger.info(
                        "Admin {adminfp} adding admin user {userfp}".format(
                            adminfp=fingerprint, userfp=body["fingerprint"]))
                    # Add a user to the admin trust store
                    self.add_and_trust_user(self.cert_processor.admin_gpg,
                                            fingerprint)

            has_user = self.has_user(self.cert_processor.user_gpg, fingerprint)

            if not has_user:
                # Add the user to the user trust store
                logger.info(
                    "Admin {adminfp} adding admin user {userfp}".format(
                        adminfp=fingerprint, userfp=body["fingerprint"]))
                self.add_and_trust_user(self.cert_processor.user_gpg,
                                        fingerprint)
            return json.dumps({"msg": "success"}), 201
        except GPGKeyNotFoundException:
            return (
                json.dumps(
                    {"msg": "Key not found on keyserver. Could not import"}),
                422,
            )

    def has_user(self, gpg, fingerprint):
        keys = gpg.list_keys(keys=fingerprint)
        if len(keys) == 0:
            return False
        return True

    def add_and_trust_user(self, gpg, fingerprint):
        result = self.cert_processor.user_gpg.recv_keys(
            self.config.get("gnupg",
                            "keyserver",
                            fallback="keyserver.ubuntu.com"),
            fingerprint,
        )
        if result.count is None or result.count == 0:
            raise GPGKeyNotFoundException()
        self.cert_processor.user_gpg.trust_keys([fingerprint],
                                                "TRUST_ULTIMATE")

    def remove_user(self, body, is_admin=False):
        """Remove a user or admin."""
        fingerprint = None
        sig_path = write_sig_to_file(body["signature"])
        try:
            fingerprint = self.cert_processor.admin_verify(
                body["fingerprint"].encode("UTF-8"), sig_path)
            logger.info("Admin {adminfp} adding user {userfp}".format(
                adminfp=fingerprint, userfp=body["fingerprint"]))
        except (CertProcessorInvalidSignatureError,
                CertProcessorUntrustedSignatureError):
            os.remove(sig_path)
            logger.error(
                "Invalid signature on adding fingerprint: {fp}".format(
                    fp=body["fingerprint"]))
            return error_response("Unauthorized", 403)
        # Remove signature file
        os.remove(sig_path)

        if is_admin:
            # Add a user to the admin trust store
            self.cert_processor.admin_gpg.delete_keys(body["fingerprint"])

        # Add the user to the user trust store
        self.cert_processor.user_gpg.delete_keys(body["fingerprint"])
        return json.dumps({"msg": "success"}), 201
Ejemplo n.º 12
0
    def setUp(self):
        self.USER_GNUPGHOME = tempfile.TemporaryDirectory()
        self.ADMIN_GNUPGHOME = tempfile.TemporaryDirectory()
        self.INVALID_GNUPGHOME = tempfile.TemporaryDirectory()
        self.NEW_USER_GNUPGHOME = tempfile.TemporaryDirectory()
        self.config = ConfigParser()
        self.config.read_string("""
            [mtls]
            min_lifetime=60
            max_lifetime=0

            [ca]
            key = secrets/certs/authority/RootCA.key
            cert = secrets/certs/authority/RootCA.pem
            issuer = My Company Name
            alternate_name = *.myname.com

            [gnupg]
            user={user_gnupghome}
            admin={admin_gnupghome}

            [storage]
            engine=sqlite3

            [storage.sqlite3]
            db_path=:memory:
            """.format(
            user_gnupghome=self.USER_GNUPGHOME.name,
            admin_gnupghome=self.ADMIN_GNUPGHOME.name,
        ))
        self.common_name = "user@host"
        self.key = generate_key()
        self.engine = storage.SQLiteStorageEngine(self.config)
        cur = self.engine.conn.cursor()
        cur.execute("DROP TABLE IF EXISTS certs")
        self.engine.conn.commit()
        self.engine.init_db()
        self.cert_processor = CertProcessor(self.config)
        self.handler = Handler(self.config)
        self.user_gpg = gnupg.GPG(gnupghome=self.USER_GNUPGHOME.name)
        self.admin_gpg = gnupg.GPG(gnupghome=self.ADMIN_GNUPGHOME.name)
        self.invalid_gpg = gnupg.GPG(gnupghome=self.INVALID_GNUPGHOME.name)
        self.new_user_gpg = gnupg.GPG(gnupghome=self.NEW_USER_GNUPGHOME.name)
        self.users = [
            User("user@host", gen_passwd(), generate_key(), gpg=self.user_gpg),
            User("user2@host", gen_passwd(), generate_key(),
                 gpg=self.user_gpg),
            User("user3@host", gen_passwd(), generate_key(),
                 gpg=self.user_gpg),
        ]
        self.invalid_users = [
            User("user4@host",
                 gen_passwd(),
                 generate_key(),
                 gpg=self.invalid_gpg)
        ]
        self.admin_users = [
            User("admin@host",
                 gen_passwd(),
                 generate_key(),
                 gpg=self.admin_gpg)
        ]
        self.new_users = [
            User("newuser@host",
                 gen_passwd(),
                 generate_key(),
                 gpg=self.new_user_gpg),
            User("newuser2@host",
                 gen_passwd(),
                 generate_key(),
                 gpg=self.new_user_gpg),
        ]
        for user in self.users:
            self.user_gpg.import_keys(
                self.user_gpg.export_keys(user.fingerprint))
            self.user_gpg.trust_keys([user.fingerprint], "TRUST_ULTIMATE")
        for user in self.admin_users:
            # Import to admin keychain
            self.admin_gpg.import_keys(
                self.admin_gpg.export_keys(user.fingerprint))
            self.admin_gpg.trust_keys([user.fingerprint], "TRUST_ULTIMATE")
            # Import to user keychain
            self.user_gpg.import_keys(
                self.admin_gpg.export_keys(user.fingerprint))
            self.user_gpg.trust_keys([user.fingerprint], "TRUST_ULTIMATE")
        for user in self.invalid_users:
            self.invalid_gpg.import_keys(
                self.invalid_gpg.export_keys(user.fingerprint))
            self.invalid_gpg.trust_keys([user.fingerprint], "TRUST_ULTIMATE")
        for user in self.new_users:
            self.new_user_gpg.import_keys(
                self.new_user_gpg.export_keys(user.fingerprint))
            self.new_user_gpg.trust_keys([user.fingerprint], "TRUST_ULTIMATE")
Ejemplo n.º 13
0
class TestHandler(unittest.TestCase):
    def setUp(self):
        self.USER_GNUPGHOME = tempfile.TemporaryDirectory()
        self.ADMIN_GNUPGHOME = tempfile.TemporaryDirectory()
        self.INVALID_GNUPGHOME = tempfile.TemporaryDirectory()
        self.NEW_USER_GNUPGHOME = tempfile.TemporaryDirectory()
        self.config = ConfigParser()
        self.config.read_string("""
            [mtls]
            min_lifetime=60
            max_lifetime=0

            [ca]
            key = secrets/certs/authority/RootCA.key
            cert = secrets/certs/authority/RootCA.pem
            issuer = My Company Name
            alternate_name = *.myname.com

            [gnupg]
            user={user_gnupghome}
            admin={admin_gnupghome}

            [storage]
            engine=sqlite3

            [storage.sqlite3]
            db_path=:memory:
            """.format(
            user_gnupghome=self.USER_GNUPGHOME.name,
            admin_gnupghome=self.ADMIN_GNUPGHOME.name,
        ))
        self.common_name = "user@host"
        self.key = generate_key()
        self.engine = storage.SQLiteStorageEngine(self.config)
        cur = self.engine.conn.cursor()
        cur.execute("DROP TABLE IF EXISTS certs")
        self.engine.conn.commit()
        self.engine.init_db()
        self.cert_processor = CertProcessor(self.config)
        self.handler = Handler(self.config)
        self.user_gpg = gnupg.GPG(gnupghome=self.USER_GNUPGHOME.name)
        self.admin_gpg = gnupg.GPG(gnupghome=self.ADMIN_GNUPGHOME.name)
        self.invalid_gpg = gnupg.GPG(gnupghome=self.INVALID_GNUPGHOME.name)
        self.new_user_gpg = gnupg.GPG(gnupghome=self.NEW_USER_GNUPGHOME.name)
        self.users = [
            User("user@host", gen_passwd(), generate_key(), gpg=self.user_gpg),
            User("user2@host", gen_passwd(), generate_key(),
                 gpg=self.user_gpg),
            User("user3@host", gen_passwd(), generate_key(),
                 gpg=self.user_gpg),
        ]
        self.invalid_users = [
            User("user4@host",
                 gen_passwd(),
                 generate_key(),
                 gpg=self.invalid_gpg)
        ]
        self.admin_users = [
            User("admin@host",
                 gen_passwd(),
                 generate_key(),
                 gpg=self.admin_gpg)
        ]
        self.new_users = [
            User("newuser@host",
                 gen_passwd(),
                 generate_key(),
                 gpg=self.new_user_gpg),
            User("newuser2@host",
                 gen_passwd(),
                 generate_key(),
                 gpg=self.new_user_gpg),
        ]
        for user in self.users:
            self.user_gpg.import_keys(
                self.user_gpg.export_keys(user.fingerprint))
            self.user_gpg.trust_keys([user.fingerprint], "TRUST_ULTIMATE")
        for user in self.admin_users:
            # Import to admin keychain
            self.admin_gpg.import_keys(
                self.admin_gpg.export_keys(user.fingerprint))
            self.admin_gpg.trust_keys([user.fingerprint], "TRUST_ULTIMATE")
            # Import to user keychain
            self.user_gpg.import_keys(
                self.admin_gpg.export_keys(user.fingerprint))
            self.user_gpg.trust_keys([user.fingerprint], "TRUST_ULTIMATE")
        for user in self.invalid_users:
            self.invalid_gpg.import_keys(
                self.invalid_gpg.export_keys(user.fingerprint))
            self.invalid_gpg.trust_keys([user.fingerprint], "TRUST_ULTIMATE")
        for user in self.new_users:
            self.new_user_gpg.import_keys(
                self.new_user_gpg.export_keys(user.fingerprint))
            self.new_user_gpg.trust_keys([user.fingerprint], "TRUST_ULTIMATE")

    def tearDown(self):
        self.USER_GNUPGHOME.cleanup()
        self.ADMIN_GNUPGHOME.cleanup()
        self.INVALID_GNUPGHOME.cleanup()
        self.NEW_USER_GNUPGHOME.cleanup()

    def test_user_revoke_cert_serial_number(self):
        user = self.users[0]
        csr = user.gen_csr()
        bcert = self.cert_processor.generate_cert(csr, 60, user.fingerprint)
        cert = x509.load_pem_x509_certificate(bcert, backend=default_backend())
        body = {"query": {"serial_number": str(cert.serial_number)}}
        data = json.dumps(body["query"]).encode("UTF-8")
        sig = self.user_gpg.sign(
            data,
            keyid=user.fingerprint,
            detach=True,
            clearsign=True,
            passphrase=user.password,
        )
        body["signature"] = str(sig)
        response = json.loads(self.handler.revoke_cert(body)[0])
        self.assertTrue(response["msg"] == "success")

    def test_admin_revoke_cert_serial_number(self):
        admin = self.admin_users[0]
        user = self.users[0]
        user_csr = user.gen_csr()
        user_bcert = self.cert_processor.generate_cert(user_csr, 60,
                                                       user.fingerprint)
        user_cert = x509.load_pem_x509_certificate(user_bcert,
                                                   backend=default_backend())
        body = {"query": {"serial_number": str(user_cert.serial_number)}}
        data = json.dumps(body["query"]).encode("UTF-8")
        sig = self.admin_gpg.sign(
            data,
            keyid=admin.fingerprint,
            detach=True,
            clearsign=True,
            passphrase=admin.password,
        )
        body["signature"] = str(sig)
        response = json.loads(self.handler.revoke_cert(body)[0])
        self.assertTrue(response["msg"] == "success")

    def test_invalid_revoke_cert_serial_number(self):
        valid_user = self.users[0]
        user = self.invalid_users[0]
        csr = valid_user.gen_csr()
        bcert = self.cert_processor.generate_cert(csr, 60,
                                                  valid_user.fingerprint)
        cert = x509.load_pem_x509_certificate(bcert, backend=default_backend())
        body = {"query": {"serial_number": str(cert.serial_number)}}
        data = json.dumps(body["query"]).encode("UTF-8")
        sig = self.invalid_gpg.sign(
            data,
            keyid=user.fingerprint,
            detach=True,
            clearsign=True,
            passphrase=user.password,
        )
        body["signature"] = str(sig)
        response = json.loads(self.handler.revoke_cert(body)[0])
        self.assertEqual(response["error"], True, msg=response)

    def test_create_cert(self):
        for user in self.users:
            csr = user.gen_csr()
            sig = self.user_gpg.sign(
                csr.public_bytes(serialization.Encoding.PEM),
                keyid=user.fingerprint,
                detach=True,
                clearsign=True,
                passphrase=user.password,
            )
            payload = {
                "csr":
                csr.public_bytes(serialization.Encoding.PEM).decode("utf-8"),
                "signature": str(sig),
                "lifetime": 60,
                "type": "CERTIFICATE",
            }
            response = json.loads(self.handler.create_cert(payload)[0])
            self.assertIn("-----BEGIN CERTIFICATE-----", response["cert"])
            cert = x509.load_pem_x509_certificate(
                response["cert"].encode("UTF-8"), backend=default_backend())
            self.assertIsInstance(cert, openssl.x509._Certificate)

    def test_create_cert_for_other_user_as_user(self):
        user = self.users[0]
        csr = user.gen_csr("Some other random user",
                           "*****@*****.**")
        sig = self.user_gpg.sign(
            csr.public_bytes(serialization.Encoding.PEM),
            keyid=user.fingerprint,
            detach=True,
            clearsign=True,
            passphrase=user.password,
        )
        payload = {
            "csr":
            csr.public_bytes(serialization.Encoding.PEM).decode("utf-8"),
            "signature": str(sig),
            "lifetime": 60,
            "type": "CERTIFICATE",
        }
        response = json.loads(self.handler.create_cert(payload)[0])
        self.assertEqual(response["error"], True, msg=response)

    def test_create_cert_for_other_user_as_admin(self):
        user = self.admin_users[0]
        csr = user.gen_csr("Some other random user",
                           "*****@*****.**")
        sig = self.admin_gpg.sign(
            csr.public_bytes(serialization.Encoding.PEM),
            keyid=user.fingerprint,
            detach=True,
            clearsign=True,
            passphrase=user.password,
        )
        payload = {
            "csr":
            csr.public_bytes(serialization.Encoding.PEM).decode("utf-8"),
            "signature": str(sig),
            "lifetime": 60,
            "type": "CERTIFICATE",
        }
        response = json.loads(self.handler.create_cert(payload)[0])
        self.assertIn("-----BEGIN CERTIFICATE-----",
                      response.get("cert", ""),
                      msg=response)
        cert = x509.load_pem_x509_certificate(response["cert"].encode("UTF-8"),
                                              backend=default_backend())
        email = cert.subject.get_attributes_for_oid(NameOID.EMAIL_ADDRESS)[0]
        email = email.value
        self.assertEqual(email, "*****@*****.**", msg=response)

    def test_invalid_user_create_cert(self):
        user = self.invalid_users[0]
        csr = user.gen_csr()
        sig = self.invalid_gpg.sign(
            csr.public_bytes(serialization.Encoding.PEM),
            keyid=user.fingerprint,
            detach=True,
            clearsign=True,
            passphrase=user.password,
        )
        payload = {
            "csr":
            csr.public_bytes(serialization.Encoding.PEM).decode("utf-8"),
            "signature": str(sig),
            "lifetime": 60,
            "type": "CERTIFICATE",
        }
        response = json.loads(self.handler.create_cert(payload)[0])
        self.assertEqual(response["error"], True)

    def test_add_user_valid_admin(self):
        admin = self.admin_users[0]
        new_user = self.new_users[0]
        sig = self.admin_gpg.sign(
            new_user.fingerprint.encode("UTF-8"),
            keyid=admin.fingerprint,
            clearsign=True,
            detach=True,
            passphrase=admin.password,
        )
        payload = {
            "fingerprint": new_user.fingerprint,
            "signature": str(sig),
            "type": "USER",
        }
        response = json.loads(self.handler.add_user(payload)[0])
        self.assertEqual(response["msg"], "success")

    def test_add_user_invalid_admin(self):
        user = self.users[0]
        new_user = self.new_users[0]
        sig = self.user_gpg.sign(
            new_user.fingerprint.encode("UTF-8"),
            keyid=user.fingerprint,
            clearsign=True,
            detach=True,
            passphrase=user.password,
        )
        payload = {
            "fingerprint": new_user.fingerprint,
            "signature": str(sig),
            "type": "USER",
        }
        response = json.loads(self.handler.add_user(payload)[0])
        self.assertEqual(response["error"], True)

    def test_add_admin_valid_admin(self):
        admin = self.admin_users[0]
        new_user = self.new_users[0]
        sig = self.admin_gpg.sign(
            "B10116B8193F2DBD",
            keyid=admin.fingerprint,
            clearsign=True,
            detach=True,
            passphrase=admin.password,
        )
        payload = {
            "fingerprint": "B10116B8193F2DBD",
            "signature": str(sig),
            "type": "USER",
        }
        response = self.handler.add_user(payload, is_admin=True)
        response_json = json.loads(response[0])
        self.assertEqual(response_json["msg"], "success")

    def test_add_admin_twice_valid_admin(self):
        admin = self.admin_users[0]
        new_user = self.new_users[0]
        sig = self.admin_gpg.sign(
            "B10116B8193F2DBD",
            keyid=admin.fingerprint,
            clearsign=True,
            detach=True,
            passphrase=admin.password,
        )
        payload = {
            "fingerprint": "B10116B8193F2DBD",
            "signature": str(sig),
            "type": "USER",
        }
        response = self.handler.add_user(payload, is_admin=True)
        response_json = json.loads(response[0])
        self.assertEqual(response_json["msg"], "success")
        response = self.handler.add_user(payload, is_admin=True)
        response_json = json.loads(response[0])
        self.assertEqual(response_json["msg"], "success")

    def test_add_admin_add_key_not_on_keyserver(self):
        admin = self.admin_users[0]
        new_user = self.invalid_users[0]
        sig = self.admin_gpg.sign(
            new_user.fingerprint.encode("UTF-8"),
            keyid=admin.fingerprint,
            clearsign=True,
            detach=True,
            passphrase=admin.password,
        )
        payload = {
            "fingerprint": new_user.fingerprint,
            "signature": str(sig),
            "type": "USER",
        }
        response = self.handler.add_user(payload, is_admin=True)
        self.assertEqual(response[1], 422)

    def test_add_admin_invalid_admin(self):
        admin = self.users[0]
        new_user = self.new_users[0]
        sig = self.admin_gpg.sign(
            new_user.fingerprint.encode("UTF-8"),
            keyid=admin.fingerprint,
            clearsign=True,
            detach=True,
            passphrase=admin.password,
        )
        payload = {
            "fingerprint": new_user.fingerprint,
            "signature": str(sig),
            "type": "USER",
        }
        response = json.loads(self.handler.add_user(payload, is_admin=True)[0])
        self.assertEqual(response["error"], True)

    def test_remove_user_valid_admin(self):
        admin = self.admin_users[0]
        new_user = self.new_users[0]
        sig = self.admin_gpg.sign(
            new_user.fingerprint.encode("UTF-8"),
            keyid=admin.fingerprint,
            clearsign=True,
            detach=True,
            passphrase=admin.password,
        )
        payload = {
            "fingerprint": new_user.fingerprint,
            "signature": str(sig),
            "type": "USER",
        }
        response = json.loads(self.handler.add_user(payload)[0])
        self.assertEqual(response["msg"], "success")
        response = json.loads(self.handler.remove_user(payload)[0])
        self.assertEqual(response["msg"], "success")

    def test_remove_user_invalid_admin(self):
        admin = self.users[0]
        new_user = self.new_users[0]
        sig = self.admin_gpg.sign(
            new_user.fingerprint.encode("UTF-8"),
            keyid=admin.fingerprint,
            clearsign=True,
            detach=True,
            passphrase=admin.password,
        )
        payload = {
            "fingerprint": new_user.fingerprint,
            "signature": str(sig),
            "type": "USER",
        }
        response = json.loads(self.handler.add_user(payload, is_admin=True)[0])
        self.assertEqual(response["error"], True)

    def test_remove_admin_valid_admin(self):
        admin = self.admin_users[0]
        sig = self.admin_gpg.sign(
            "C92FE5A3FBD58DD3EC5AA26BB10116B8193F2DBD".encode("UTF-8"),
            keyid=admin.fingerprint,
            clearsign=True,
            detach=True,
            passphrase=admin.password,
        )
        payload = {
            "fingerprint": "C92FE5A3FBD58DD3EC5AA26BB10116B8193F2DBD",
            "signature": str(sig),
            "type": "ADMIN",
        }
        response = json.loads(self.handler.add_user(payload)[0])
        self.assertEqual(response["msg"], "success")
        response = json.loads(self.handler.remove_user(payload)[0])
        self.assertEqual(response["msg"], "success")

    def test_remove_admin_invalid_admin(self):
        user = self.users[0]
        new_user = self.new_users[0]
        sig = self.user_gpg.sign(
            new_user.fingerprint.encode("UTF-8"),
            keyid=user.fingerprint,
            clearsign=True,
            detach=True,
            passphrase=user.password,
        )
        payload = {
            "fingerprint": new_user.fingerprint,
            "signature": str(sig),
            "type": "ADMIN",
        }
        response = json.loads(self.handler.add_user(payload, is_admin=True)[0])
        self.assertEqual(response["error"], True)