Exemplo n.º 1
0
    def delete(self, context, key_id):
        """Represents deleting the key."""

        # Confirm context is provided, if not raise forbidden
        if not context:
            msg = _("User is not authorized to use key manager.")
            raise exception.Forbidden(msg)

        if not key_id:
            raise exception.KeyManagerError('key identifier not provided')

        headers = {'X-Vault-Token': self._root_token_id}
        try:
            resource_url = self._get_url() + 'v1/secret/' + key_id
            resp = self._session.delete(resource_url,
                                        verify=self._verify_server,
                                        headers=headers)
        except requests.exceptions.Timeout as ex:
            raise exception.KeyManagerError(six.text_type(ex))
        except requests.exceptions.ConnectionError as ex:
            raise exception.KeyManagerError(six.text_type(ex))
        except Exception as ex:
            raise exception.KeyManagerError(six.text_type(ex))

        if resp.status_code in _EXCEPTIONS_BY_CODE:
            raise exception.KeyManagerError(resp.reason)
        if resp.status_code == requests.codes['forbidden']:
            raise exception.Forbidden()
        if resp.status_code == requests.codes['not_found']:
            raise exception.ManagedObjectNotFoundError(uuid=key_id)
Exemplo n.º 2
0
    def get(self, context, key_id, metadata_only=False):
        """Retrieves the key identified by the specified id."""

        # Confirm context is provided, if not raise forbidden
        if not context:
            msg = _("User is not authorized to use key manager.")
            raise exception.Forbidden(msg)

        if not key_id:
            raise exception.KeyManagerError('key identifier not provided')

        headers = {'X-Vault-Token': self._root_token_id}
        try:
            resource_url = self._get_url() + 'v1/secret/' + key_id
            resp = self._session.get(resource_url,
                                     verify=self._verify_server,
                                     headers=headers)
        except requests.exceptions.Timeout as ex:
            raise exception.KeyManagerError(six.text_type(ex))
        except requests.exceptions.ConnectionError as ex:
            raise exception.KeyManagerError(six.text_type(ex))
        except Exception as ex:
            raise exception.KeyManagerError(six.text_type(ex))

        if resp.status_code in _EXCEPTIONS_BY_CODE:
            raise exception.KeyManagerError(resp.reason)
        if resp.status_code == requests.codes['forbidden']:
            raise exception.Forbidden()
        if resp.status_code == requests.codes['not_found']:
            raise exception.ManagedObjectNotFoundError(uuid=key_id)

        record = resp.json()['data']
        key = None if metadata_only else binascii.unhexlify(record['value'])

        clazz = None
        for type_clazz, type_name in self._secret_type_dict.items():
            if type_name == record['type']:
                clazz = type_clazz

        if clazz is None:
            raise exception.KeyManagerError("Unknown type : %r" %
                                            record['type'])

        if hasattr(clazz, 'algorithm') and hasattr(clazz, 'bit_length'):
            return clazz(record['algorithm'], record['bit_length'], key,
                         record['name'], record['created'], key_id)
        else:
            return clazz(key, record['name'], record['created'], key_id)
Exemplo n.º 3
0
    def create_key_pair(self,
                        context,
                        algorithm,
                        length,
                        expiration=None,
                        name=None):
        """Creates an asymmetric key pair.

        This implementation returns UUIDs for the created keys in the order:
            (private, public)
        Forbidden is raised if the context is None.
        """
        if context is None:
            raise exception.Forbidden()

        if algorithm.lower() != 'rsa':
            msg = 'Invalid algorithm: {}, only RSA supported'.format(algorithm)
            raise ValueError(msg)

        valid_lengths = [2048, 3072, 4096]

        if length not in valid_lengths:
            msg = 'Invalid bit length: {}, only {} supported'.format(
                length, valid_lengths)
            raise ValueError(msg)

        private_key, public_key = self._generate_public_and_private_key(
            length, name)

        private_key_uuid = self.store(context, private_key)
        public_key_uuid = self.store(context, public_key)

        return private_key_uuid, public_key_uuid
Exemplo n.º 4
0
    def list(self, context, object_type=None, metadata_only=False):
        """Lists the managed objects given the criteria."""

        # Confirm context is provided, if not raise forbidden
        if not context:
            msg = _("User is not authorized to use key manager.")
            raise exception.Forbidden(msg)

        if object_type and object_type not in self._secret_type_dict:
            msg = _("Invalid secret type: %s") % object_type
            raise exception.KeyManagerError(reason=msg)

        resp = self._do_http_request(self._session.get,
                                     self._get_resource_url())

        if resp.status_code == requests.codes['not_found']:
            keys = []
        else:
            keys = resp.json()['data']['keys']

        objects = []
        for obj_id in keys:
            try:
                obj = self.get(context, obj_id, metadata_only=metadata_only)
                if object_type is None or isinstance(obj, object_type):
                    objects.append(obj)
            except exception.ManagedObjectNotFoundError as e:
                LOG.warning(
                    _("Error occurred while retrieving object "
                      "metadata, not adding it to the list: %s"), e)
                pass
        return objects
Exemplo n.º 5
0
 def test_generate_symmetric_key_raises_exception(self):
     key_spec = ss.KeySpec(ss.KeyAlgorithm.AES, 128)
     self.plugin.key_manager.create_key.side_effect = exception.Forbidden()
     self.assertRaises(
         ss.SecretGeneralException,
         self.plugin.generate_symmetric_key,
         key_spec
     )
Exemplo n.º 6
0
 def test_delete_secret_throws_exception(self):
     secret_metadata = {css.CastellanSecretStore.KEY_ID: key_ref1}
     self.plugin.key_manager.delete.side_effect = exception.Forbidden()
     self.assertRaises(
         ss.SecretGeneralException,
         self.plugin.delete_secret,
         secret_metadata
     )
Exemplo n.º 7
0
    def list(self, context, object_type=None, metadata_only=False):
        """Lists the managed objects given the criteria."""

        # Confirm context is provided, if not raise forbidden
        if not context:
            msg = _("User is not authorized to use key manager.")
            raise exception.Forbidden(msg)

        if object_type and object_type not in self._secret_type_dict:
            msg = _("Invalid secret type: %s") % object_type
            raise exception.KeyManagerError(reason=msg)

        headers = {'X-Vault-Token': self._root_token_id}
        try:
            resource_url = self._get_url() + 'v1/secret/?list=true'
            resp = self._session.get(resource_url,
                                     verify=self._verify_server,
                                     headers=headers)
            keys = resp.json()['data']['keys']
        except requests.exceptions.Timeout as ex:
            raise exception.KeyManagerError(six.text_type(ex))
        except requests.exceptions.ConnectionError as ex:
            raise exception.KeyManagerError(six.text_type(ex))
        except Exception as ex:
            raise exception.KeyManagerError(six.text_type(ex))

        if resp.status_code in _EXCEPTIONS_BY_CODE:
            raise exception.KeyManagerError(resp.reason)
        if resp.status_code == requests.codes['forbidden']:
            raise exception.Forbidden()
        if resp.status_code == requests.codes['not_found']:
            keys = []

        objects = []
        for obj_id in keys:
            try:
                obj = self.get(context, obj_id, metadata_only=metadata_only)
                if object_type is None or isinstance(obj, object_type):
                    objects.append(obj)
            except exception.ManagedObjectNotFoundError as e:
                LOG.warning(
                    _("Error occurred while retrieving object "
                      "metadata, not adding it to the list: %s"), e)
                pass
        return objects
Exemplo n.º 8
0
 def test_get_secret_throws_exception(self):
     secret_metadata = self.plugin._meta_dict(key_ref1, 256, 'AES')
     self.plugin.key_manager.get.side_effect = exception.Forbidden()
     self.assertRaises(
         ss.SecretGeneralException,
         self.plugin.get_secret,
         ss.SecretType.SYMMETRIC,
         secret_metadata
     )
Exemplo n.º 9
0
    def store(self, context, managed_object, **kwargs):
        """Stores (i.e., registers) a key with the key manager."""
        if context is None:
            raise exception.Forbidden()

        key_id = self._generate_key_id()
        self.keys[key_id] = managed_object

        return key_id
Exemplo n.º 10
0
    def delete(self, context, managed_object_id, **kwargs):
        """Deletes the object identified by the specified id.

        A Forbidden exception is raised if the context is None and a
        KeyError is raised if the UUID is invalid.
        """
        if context is None:
            raise exception.Forbidden()

        del self.keys[managed_object_id]
Exemplo n.º 11
0
            def delete(self, context, managed_object_id):
                if managed_object_id == self.fixed_key_id:
                    LOG.debug("Not deleting key associated with"
                              " fixed_key key ID")

                    if context is None:
                        raise exception.Forbidden()
                else:
                    super(MigrationKeyManager,
                          self).delete(context, managed_object_id)
Exemplo n.º 12
0
    def store(self, context, key_value, **kwargs):
        """Stores (i.e., registers) a key with the key manager."""

        # Confirm context is provided, if not raise forbidden
        if not context:
            msg = _("User is not authorized to use key manager.")
            raise exception.Forbidden(msg)

        key_id = uuid.uuid4().hex
        return self._store_key_value(key_id, key_value)
Exemplo n.º 13
0
 def test_generate_asymmetric_throws_exception(self):
     key_spec = ss.KeySpec(ss.KeyAlgorithm.RSA, 2048)
     self.plugin.key_manager.create_key_pair.side_effect = (
         exception.Forbidden()
     )
     self.assertRaises(
         ss.SecretGeneralException,
         self.plugin.generate_asymmetric_key,
         key_spec
     )
Exemplo n.º 14
0
    def test_store_secret_raises_exception(self):
        payload = 'encrypt me!!'
        key_spec = mock.MagicMock()
        content_type = mock.MagicMock()
        transport_key = None
        secret_dto = ss.SecretDTO(ss.SecretType.SYMMETRIC, payload, key_spec,
                                  content_type, transport_key)

        self.plugin.key_manager.store.side_effect = exception.Forbidden()
        self.assertRaises(ss.SecretGeneralException, self.plugin.store_secret,
                          secret_dto)
Exemplo n.º 15
0
    def get(self, context, managed_object_id, **kwargs):
        """Retrieves the key identified by the specified id.

        This implementation returns the key that is associated with the
        specified UUID. A Forbidden exception is raised if the specified
        context is None; a KeyError is raised if the UUID is invalid.
        """
        if context is None:
            raise exception.Forbidden()

        return self.keys[managed_object_id]
Exemplo n.º 16
0
    def create_key(self, context, **kwargs):
        """Creates a symmetric key.

        This implementation returns a UUID for the created key. The algorithm
        for the key will always be AES. A Forbidden exception is raised if the
        specified context is None.
        """
        if context is None:
            raise exception.Forbidden()

        key = self._generate_key(**kwargs)
        return self.store(context, key)
Exemplo n.º 17
0
    def create_key(self, context, algorithm, length, name=None, **kwargs):
        """Creates a symmetric key."""

        # Confirm context is provided, if not raise forbidden
        if not context:
            msg = _("User is not authorized to use key manager.")
            raise exception.Forbidden(msg)

        key_id = uuid.uuid4().hex
        key_value = os.urandom(length or 32)
        key = sym_key.SymmetricKey(algorithm, length or 32, key_value, key_id,
                                   name or int(time.time()))
        return self._store_key_value(key_id, key)
Exemplo n.º 18
0
    def list(self, context, object_type=None, metadata_only=False):
        """Retrieves a list of managed objects that match the criteria.

        A Forbidden exception is raised if the context is None.
        If no search criteria is given, all objects are returned.
        """
        if context is None:
            raise exception.Forbidden()

        objects = []
        for obj_id in self.keys:
            obj = self.get(context, obj_id, metadata_only=metadata_only)
            if type(obj) == object_type or object_type is None:
                objects.append(obj)
        return objects
Exemplo n.º 19
0
    def delete(self, context, key_id):
        """Represents deleting the key."""

        # Confirm context is provided, if not raise forbidden
        if not context:
            msg = _("User is not authorized to use key manager.")
            raise exception.Forbidden(msg)

        if not key_id:
            raise exception.KeyManagerError('key identifier not provided')

        resp = self._do_http_request(self._session.delete,
                                     self._get_resource_url(key_id))

        if resp.status_code == requests.codes['not_found']:
            raise exception.ManagedObjectNotFoundError(uuid=key_id)
Exemplo n.º 20
0
            def get(self, context, managed_object_id):
                if managed_object_id == self.fixed_key_id:
                    LOG.debug("Processing request for secret associated"
                              " with fixed_key key ID")

                    if context is None:
                        raise exception.Forbidden()

                    key_bytes = bytes(binascii.unhexlify(self.fixed_key))
                    secret = symmetric_key.SymmetricKey(
                        'AES',
                        len(key_bytes) * 8, key_bytes)
                else:
                    secret = super(MigrationKeyManager,
                                   self).get(context, managed_object_id)
                return secret
Exemplo n.º 21
0
 def _get_keystone_auth(self, context):
     if context.__class__.__name__ == 'KeystonePassword':
         return identity.Password(
             auth_url=context.auth_url,
             username=context.username,
             password=context.password,
             user_id=context.user_id,
             user_domain_id=context.user_domain_id,
             user_domain_name=context.user_domain_name,
             trust_id=context.trust_id,
             domain_id=context.domain_id,
             domain_name=context.domain_name,
             project_id=context.project_id,
             project_name=context.project_name,
             project_domain_id=context.project_domain_id,
             project_domain_name=context.project_domain_name,
             reauthenticate=context.reauthenticate)
     elif context.__class__.__name__ == 'KeystoneToken':
         return identity.Token(
             auth_url=context.auth_url,
             token=context.token,
             trust_id=context.trust_id,
             domain_id=context.domain_id,
             domain_name=context.domain_name,
             project_id=context.project_id,
             project_name=context.project_name,
             project_domain_id=context.project_domain_id,
             project_domain_name=context.project_domain_name,
             reauthenticate=context.reauthenticate)
     # this will be kept for oslo.context compatibility until
     # projects begin to use utils.credential_factory
     elif context.__class__.__name__ == 'RequestContext':
         if getattr(context, 'get_auth_plugin', None):
             return context.get_auth_plugin()
         else:
             return identity.Token(
                 auth_url=self.conf.barbican.auth_endpoint,
                 token=context.auth_token,
                 project_id=context.project_id,
                 project_name=context.project_name,
                 project_domain_id=context.project_domain_id,
                 project_domain_name=context.project_domain_name)
     else:
         msg = _("context must be of type KeystonePassword, "
                 "KeystoneToken, or RequestContext.")
         LOG.error(msg)
         raise exception.Forbidden(reason=msg)
Exemplo n.º 22
0
    def get(self, context, managed_object_id, metadata_only=False, **kwargs):
        """Retrieves the key identified by the specified id.

        This implementation returns the key that is associated with the
        specified UUID. A Forbidden exception is raised if the specified
        context is None; a KeyError is raised if the UUID is invalid.
        """
        if context is None:
            raise exception.Forbidden()

        obj = self.keys[managed_object_id]
        if metadata_only:
            if hasattr(obj, "_key"):
                obj._key = None
            if hasattr(obj, "_data"):
                obj._data = None
            if hasattr(obj, "_passphrase"):
                obj._passphrase = None
        return obj
Exemplo n.º 23
0
    def _do_http_request(self, method, resource, json=None):
        verify = self._verify_server
        headers = self._build_auth_headers()

        try:
            resp = method(resource, headers=headers, json=json, verify=verify)
        except requests.exceptions.Timeout as ex:
            raise exception.KeyManagerError(six.text_type(ex))
        except requests.exceptions.ConnectionError as ex:
            raise exception.KeyManagerError(six.text_type(ex))
        except Exception as ex:
            raise exception.KeyManagerError(six.text_type(ex))

        if resp.status_code in _EXCEPTIONS_BY_CODE:
            raise exception.KeyManagerError(resp.reason)
        if resp.status_code == requests.codes['forbidden']:
            raise exception.Forbidden()

        return resp
    def _get_barbican_client(self, context):
        """Creates a client to connect to the Barbican service.

        :param context: the user context for authentication
        :return: a Barbican Client object
        :raises Forbidden: if the context is None
        :raises KeyManagerError: if context is missing tenant or tenant is
                                 None or error occurs while creating client
        """

        # Confirm context is provided, if not raise forbidden
        if not context:
            msg = u._("User is not authorized to use key manager.")
            LOG.error(msg)
            raise exception.Forbidden(msg)

        if not hasattr(context, 'tenant') or context.tenant is None:
            msg = u._("Unable to create Barbican Client without tenant "
                      "attribute in context object.")
            LOG.error(msg)
            raise exception.KeyManagerError(reason=msg)

        if self._barbican_client and self._current_context == context:
            return self._barbican_client

        try:
            self._current_context = context
            auth = self._get_keystone_auth(context)
            sess = session.Session(auth=auth)

            self._barbican_endpoint = self._get_barbican_endpoint(auth, sess)
            self._barbican_client = barbican_client.Client(
                session=sess, endpoint=self._barbican_endpoint)

        except Exception as e:
            LOG.error(u._LE("Error creating Barbican client: %s"), e)
            raise exception.KeyManagerError(reason=e)

        self._base_url = self._create_base_url(auth, sess,
                                               self._barbican_endpoint)

        return self._barbican_client
Exemplo n.º 25
0
    def _get_keystone_auth(self, context):
        auth_url = self.conf.barbican.auth_endpoint

        if context.__class__.__name__ is 'KeystonePassword':
            return identity.v3.Password(
                auth_url=auth_url,
                username=context.username,
                password=context.password,
                user_id=context.user_id,
                user_domain_id=context.user_domain_id,
                user_domain_name=context.user_domain_name,
                trust_id=context.trust_id,
                domain_id=context.domain_id,
                domain_name=context.domain_name,
                project_id=context.project_id,
                project_name=context.project_name,
                project_domain_id=context.project_domain_id,
                project_domain_name=context.project_domain_name,
                reauthenticate=context.reauthenticate)
        elif context.__class__.__name__ is 'KeystoneToken':
            return identity.v3.Token(
                auth_url=auth_url,
                token=context.token,
                trust_id=context.trust_id,
                domain_id=context.domain_id,
                domain_name=context.domain_name,
                project_id=context.project_id,
                project_name=context.project_name,
                project_domain_id=context.project_domain_id,
                project_domain_name=context.project_domain_name,
                reauthenticate=context.reauthenticate)
        # this will be kept for oslo.context compatibility until
        # projects begin to use utils.credential_factory
        elif context.__class__.__name__ is 'RequestContext':
            return identity.v3.Token(auth_url=auth_url,
                                     token=context.auth_token,
                                     project_id=context.tenant)
        else:
            msg = "context must be of type KeystonePassword, KeystoneToken, "
            "or RequestContext."
            LOG.error(msg)
            raise exception.Forbidden(reason=msg)
Exemplo n.º 26
0
    def _store_key_value(self, key_id, value):

        type_value = self._secret_type_dict.get(type(value))
        if type_value is None:
            raise exception.KeyManagerError("Unknown type for value : %r" %
                                            value)

        headers = {'X-Vault-Token': self._root_token_id}
        try:
            resource_url = self._get_url() + 'v1/secret/' + key_id
            record = {
                'type':
                type_value,
                'value':
                binascii.hexlify(value.get_encoded()).decode('utf-8'),
                'algorithm':
                (value.algorithm if hasattr(value, 'algorithm') else None),
                'bit_length':
                (value.bit_length if hasattr(value, 'bit_length') else None),
                'name':
                value.name,
                'created':
                value.created
            }
            resp = self._session.post(resource_url,
                                      verify=self._verify_server,
                                      json=record,
                                      headers=headers)
        except requests.exceptions.Timeout as ex:
            raise exception.KeyManagerError(six.text_type(ex))
        except requests.exceptions.ConnectionError as ex:
            raise exception.KeyManagerError(six.text_type(ex))
        except Exception as ex:
            raise exception.KeyManagerError(six.text_type(ex))

        if resp.status_code in _EXCEPTIONS_BY_CODE:
            raise exception.KeyManagerError(resp.reason)
        if resp.status_code == requests.codes['forbidden']:
            raise exception.Forbidden()

        return key_id
Exemplo n.º 27
0
    def _get_barbican_client(self, context):
        """Creates a client to connect to the Barbican service.

        :param context: the user context for authentication
        :return: a Barbican Client object
        :raises Forbidden: if the context is None
        :raises KeyManagerError: if context is missing tenant or tenant is
                                 None or error occurs while creating client
        """

        # Confirm context is provided, if not raise forbidden
        if not context:
            msg = _("User is not authorized to use key manager.")
            LOG.error(msg)
            raise exception.Forbidden(msg)

        if self._barbican_client and self._current_context == context:
            return self._barbican_client

        try:
            auth = self._get_keystone_auth(context)
            sess = session.Session(auth=auth,
                                   verify=self.conf.barbican.verify_ssl)

            self._barbican_endpoint = self._get_barbican_endpoint(auth, sess)
            self._barbican_client = barbican_client_import.Client(
                session=sess,
                endpoint=self._barbican_endpoint)
            self._current_context = context

        # TODO(pbourke): more fine grained exception handling - we are eating
        # tracebacks here
        except Exception as e:
            LOG.error("Error creating Barbican client: %s", e)
            raise exception.KeyManagerError(reason=e)

        self._base_url = self._create_base_url(auth,
                                               sess,
                                               self._barbican_endpoint)

        return self._barbican_client
Exemplo n.º 28
0
    def _build_auth_headers(self):
        if self._root_token_id:
            return {'X-Vault-Token': self._root_token_id}

        if self._approle_token_id:
            return {'X-Vault-Token': self._approle_token_id}

        if self._approle_role_id:
            params = {
                'role_id': self._approle_role_id
            }
            if self._approle_secret_id:
                params['secret_id'] = self._approle_secret_id
            approle_login_url = '{}v1/auth/approle/login'.format(
                self._get_url()
            )
            token_issue_utc = timeutils.utcnow()
            try:
                resp = self._session.post(url=approle_login_url,
                                          json=params,
                                          verify=self._verify_server)
            except requests.exceptions.Timeout as ex:
                raise exception.KeyManagerError(str(ex))
            except requests.exceptions.ConnectionError as ex:
                raise exception.KeyManagerError(str(ex))
            except Exception as ex:
                raise exception.KeyManagerError(str(ex))

            if resp.status_code in _EXCEPTIONS_BY_CODE:
                raise exception.KeyManagerError(resp.reason)
            if resp.status_code == requests.codes['forbidden']:
                raise exception.Forbidden()

            resp = resp.json()
            self._cached_approle_token_id = resp['auth']['client_token']
            self._approle_token_issue = token_issue_utc
            self._approle_token_ttl = resp['auth']['lease_duration']
            return {'X-Vault-Token': self._approle_token_id}

        return {}
Exemplo n.º 29
0
    def create_key_pair(self,
                        context,
                        algorithm,
                        length,
                        expiration=None,
                        name=None):
        """Creates an asymmetric key pair."""

        # Confirm context is provided, if not raise forbidden
        if not context:
            msg = _("User is not authorized to use key manager.")
            raise exception.Forbidden(msg)

        if algorithm.lower() != 'rsa':
            raise NotImplementedError(
                "VaultKeyManager only implements rsa keys")

        priv_key = rsa.generate_private_key(public_exponent=65537,
                                            key_size=length,
                                            backend=default_backend())

        private_key = pri_key.PrivateKey(
            'RSA', length,
            priv_key.private_bytes(Encoding.PEM, PrivateFormat.PKCS8,
                                   NoEncryption()))

        private_key_id = uuid.uuid4().hex
        private_id = self._store_key_value(private_key_id, private_key)

        # pub_key = priv_key.public_key()
        public_key = pub_key.PublicKey(
            'RSA', length,
            priv_key.public_key().public_bytes(
                Encoding.PEM, PublicFormat.SubjectPublicKeyInfo))

        public_key_id = uuid.uuid4().hex
        public_id = self._store_key_value(public_key_id, public_key)

        return private_id, public_id
Exemplo n.º 30
0
    def get(self, context, key_id, metadata_only=False):
        """Retrieves the key identified by the specified id."""

        # Confirm context is provided, if not raise forbidden
        if not context:
            msg = _("User is not authorized to use key manager.")
            raise exception.Forbidden(msg)

        if not key_id:
            raise exception.KeyManagerError('key identifier not provided')

        resp = self._do_http_request(self._session.get,
                                     self._get_resource_url(key_id))

        if resp.status_code == requests.codes['not_found']:
            raise exception.ManagedObjectNotFoundError(uuid=key_id)

        record = resp.json()['data']
        if self._get_api_version() != '1':
            record = record['data']

        key = None if metadata_only else binascii.unhexlify(record['value'])

        clazz = None
        for type_clazz, type_name in self._secret_type_dict.items():
            if type_name == record['type']:
                clazz = type_clazz

        if clazz is None:
            raise exception.KeyManagerError("Unknown type : %r" %
                                            record['type'])

        if hasattr(clazz, 'algorithm') and hasattr(clazz, 'bit_length'):
            return clazz(record['algorithm'], record['bit_length'], key,
                         record['name'], record['created'], key_id)
        else:
            return clazz(key, record['name'], record['created'], key_id)