Repository: golemfactory/yagna (language: python)

Issue:
Keystore can handle Golem certificates
User should be able to add / delete / list golem certificates alongside x509 ones.

Remember to remove mocked id's in Keystore: https://github.com/golemfactory/yagna/blob/master/utils/manifest-utils/src/policy.rs#L239

Remember to switch golem certificate validation lib to new one 
At the end it should replace this mock crate: https://github.com/golemfactory/yagna/blob/master/utils/manifest-utils/src/golem_certificate.rs




Discussion / hints:

Fix patch:
diff --git a/agent/provider/src/cli/keystore.rs b/agent/provider/src/cli/keystore.rs
index 0b6943e40a..359d2146cd 100644
--- a/agent/provider/src/cli/keystore.rs
+++ b/agent/provider/src/cli/keystore.rs
@@ -1,18 +1,15 @@
-use anyhow::anyhow;
-use std::collections::HashSet;
+use crate::cli::println_conditional;
+use crate::startup_config::ProviderConfig;
 use std::path::PathBuf;
-
 use structopt::StructOpt;
 use strum::VariantNames;
-
+use ya_manifest_utils::keystore::{
+    AddParams, AddResponse, Cert, Keystore, RemoveParams, RemoveResponse,
+};
 use ya_manifest_utils::policy::CertPermissions;
-use ya_manifest_utils::util::{self, CertBasicData, CertBasicDataVisitor};
-use ya_manifest_utils::KeystoreLoadResult;
+use ya_manifest_utils::CompositeKeystore;
 use ya_utils_cli::{CommandOutput, ResponseTable};
 
-use crate::cli::println_conditional;
-use crate::startup_config::ProviderConfig;
-
 /// Manage trusted keys
 ///
 /// Keystore stores X.509 certificates.
@@ -54,6 +51,16 @@ pub struct Add {
     whole_chain: bool,
 }
 
+impl Into<AddParams> for Add {
+    fn into(self) -> AddParams {
+        AddParams {
+            certs: self.certs,
+            permissions: self.permissions,
+            whole_chain: self.whole_chain,
+        }
+    }
+}
+
 #[derive(StructOpt, Clone, Debug)]
 #[structopt(rename_all = "kebab-case")]
 pub struct Remove {
@@ -63,6 +70,14 @@ To find certificate id use `keystore list` command.")]
     ids: Vec<String>,
 }
 
+impl Into<RemoveParams> for Remove {
+    fn into(self) -> RemoveParams {
+        RemoveParams {
+            ids: self.ids.into_iter().collect(),
+        }
+    }
+}
+
 impl KeystoreConfig {
     pub fn run(self, config: ProviderConfig) -> anyhow::Result<()> {
         match self {
@@ -75,74 +90,47 @@ impl KeystoreConfig {
 
 fn list(config: ProviderConfig) -> anyhow::Result<()> {
     let cert_dir = config.cert_dir_path()?;
-    let table = CertTable::new();
-    let table = util::visit_certificates(&cert_dir, table)?;
-    table.print(&config)?;
+    let keystore = CompositeKeystore::load(&cert_dir)?;
+    let certs_data = keystore.list();
+    print_cert_list(&config, certs_data)?;
     Ok(())
 }
 
 fn add(config: ProviderConfig, add: Add) -> anyhow::Result<()> {
     let cert_dir = config.cert_dir_path()?;
-    let keystore_manager = util::KeystoreManager::try_new(&cert_dir)?;
-    let mut permissions_manager = keystore_manager.permissions_manager();
-
-    let KeystoreLoadResult { loaded, skipped } = keystore_manager.load_certs(&add.certs)?;
-
-    permissions_manager.set_many(
-        &loaded.iter().chain(skipped.iter()).cloned().collect(),
-        add.permissions,
-        add.whole_chain,
-    );
+    let mut keystore = CompositeKeystore::load(&cert_dir)?;
+    let AddResponse { added, skipped } = keystore.add(&add.into())?;
 
-    if !loaded.is_empty() {
+    if !added.is_empty() {
         println_conditional(&config, "Added certificates:");
-        let certs_data = util::to_cert_data(&loaded, &permissions_manager)?;
-        print_cert_list(&config, certs_data)?;
+        print_cert_list(&config, added)?;
     }
 
     if !skipped.is_empty() && !config.json {
         println!("Certificates already loaded to keystore:");
-        let certs_data = util::to_cert_data(&skipped, &permissions_manager)?;
-        print_cert_list(&config, certs_data)?;
+        print_cert_list(&config, skipped)?;
     }
-
-    permissions_manager
-        .save(&cert_dir)
-        .map_err(|e| anyhow!("Failed to save permissions file: {e}"))?;
     Ok(())
 }
 
 fn remove(config: ProviderConfig, remove: Remove) -> anyhow::Result<()> {
     let cert_dir = config.cert_dir_path()?;
-    let keystore_manager = util::KeystoreManager::try_new(&cert_dir)?;
-    let mut permissions_manager = keystore_manager.permissions_manager();
-    let ids: HashSet<String> = remove.ids.into_iter().collect();
-    match keystore_manager.remove_certs(&ids)? {
-        util::KeystoreRemoveResult::NothingToRemove => {
-            println_conditional(&config, "No matching certificates to remove.");
-            if config.json {
-                print_cert_list(&config, Vec::new())?;
-            }
+    let mut keystore = CompositeKeystore::load(&cert_dir)?;
+    let RemoveResponse { removed } = keystore.remove(&remove.into())?;
+    if removed.is_empty() {
+        println_conditional(&config, "No matching certificates to remove.");
+        if config.json {
+            print_cert_list(&config, Vec::new())?;
         }
-        util::KeystoreRemoveResult::Removed { removed } => {
-            permissions_manager.set_many(&removed, vec![], true);
-
-            println!("Removed certificates:");
-            let certs_data = util::to_cert_data(&removed, &permissions_manager)?;
-            print_cert_list(&config, certs_data)?;
-        }
-    };
+    } else {
+        println!("Removed certificates:");
+        print_cert_list(&config, removed)?;
+    }
 
-    permissions_manager
-        .save(&cert_dir)
-        .map_err(|e| anyhow!("Failed to save permissions file: {e}"))?;
     Ok(())
 }
 
-fn print_cert_list(
-    config: &ProviderConfig,
-    certs_data: Vec<util::CertBasicData>,
-) -> anyhow::Result<()> {
+fn print_cert_list(config: &ProviderConfig, certs_data: Vec<Cert>) -> anyhow::Result<()> {
     let mut table = CertTable::new();
     for data in certs_data {
         table.add(data);
@@ -174,14 +162,13 @@ impl CertTable {
         Ok(())
     }
 
-    pub fn add(&mut self, data: CertBasicData) {
-        let row = serde_json::json! {[ data.id, data.not_after, data.subject, data.permissions ]};
+    pub fn add(&mut self, cert: Cert) {
+        let row = match cert {
+            Cert::X509(cert) => {
+                serde_json::json! {[ cert.id, cert.not_after, cert.subject, cert.permissions ]}
+            }
+            Cert::Golem { id, cert } => serde_json::json! {[ id, "", "", cert.permissions ]},
+        };
         self.table.values.push(row)
     }
 }
-
-impl CertBasicDataVisitor for CertTable {
-    fn accept(&mut self, data: CertBasicData) {
-        self.add(data)
-    }
-}
diff --git a/agent/provider/src/cli/rule.rs b/agent/provider/src/cli/rule.rs
index 0738e236df..6e20fe439c 100644
--- a/agent/provider/src/cli/rule.rs
+++ b/agent/provider/src/cli/rule.rs
@@ -1,20 +1,18 @@
+use crate::rules::CertRule;
+use crate::{
+    rules::{Mode, RulesManager},
+    startup_config::ProviderConfig,
+};
+use anyhow::Result;
 use std::collections::HashMap;
 use std::path::PathBuf;
-
-use anyhow::{anyhow, Result};
 use structopt::StructOpt;
 use strum::VariantNames;
+use ya_manifest_utils::keystore::{AddParams, AddResponse, Keystore};
 use ya_manifest_utils::policy::CertPermissions;
-use ya_manifest_utils::util::cert_to_id;
-use ya_manifest_utils::{KeystoreLoadResult, KeystoreManager};
+use ya_manifest_utils::CompositeKeystore;
 use ya_utils_cli::{CommandOutput, ResponseTable};
 
-use crate::rules::CertRule;
-use crate::{
-    rules::{Mode, RulesManager},
-    startup_config::ProviderConfig,
-};
-
 #[derive(StructOpt, Clone, Debug)]
 pub enum RuleCommand {
     /// Set Modes for specific Rules
@@ -88,28 +86,18 @@ fn set(set_rule: SetRule, config: ProviderConfig) -> Result<()> {
                 rules.set_partner_mode(cert_id, mode)
             }
             SetOutboundRule::Partner(RuleWithCert::ImportCert { import_cert, mode }) => {
-                let keystore_manager = KeystoreManager::try_new(&rules.cert_dir)?;
-
-                let KeystoreLoadResult { loaded, skipped } =
-                    keystore_manager.load_certs(&vec![import_cert])?;
-
-                //TODO it will be removed after backward compatibility is done
-                rules.keystore.permissions_manager().set_many(
-                    &loaded.iter().chain(skipped.iter()).cloned().collect(),
-                    vec![CertPermissions::All],
-                    true,
-                );
-                rules
-                    .keystore
-                    .permissions_manager()
-                    .save(&rules.cert_dir)
-                    .map_err(|e| anyhow!("Failed to save permissions file: {e}"))?;
+                let mut keystore = CompositeKeystore::load(&rules.cert_dir)?;
+
+                let AddResponse { added, skipped } = keystore.add(&AddParams {
+                    certs: vec![import_cert],
+                    permissions: vec![CertPermissions::All],
+                    whole_chain: true,
+                })?;
 
                 rules.keystore.reload(&rules.cert_dir)?;
 
-                for cert in loaded.into_iter().chain(skipped) {
-                    let cert_id = cert_to_id(&cert)?;
-                    rules.set_partner_mode(cert_id, mode.clone())?;
+                for cert in added.into_iter().chain(skipped) {
+                    rules.set_partner_mode(cert.id(), mode.clone())?;
                 }
 
                 Ok(())
diff --git a/agent/provider/src/market/negotiator/builtin/manifest.rs b/agent/provider/src/market/negotiator/builtin/manifest.rs
index 549d331a3c..b1d6f6f2c3 100644
--- a/agent/provider/src/market/negotiator/builtin/manifest.rs
+++ b/agent/provider/src/market/negotiator/builtin/manifest.rs
@@ -3,9 +3,9 @@ use std::ops::Not;
 use ya_agreement_utils::{Error, OfferDefinition};
 use ya_manifest_utils::policy::{Match, Policy, PolicyConfig};
 use ya_manifest_utils::{
-    decode_manifest, Feature, CAPABILITIES_PROPERTY, DEMAND_MANIFEST_CERT_PERMISSIONS_PROPERTY,
-    DEMAND_MANIFEST_CERT_PROPERTY, DEMAND_MANIFEST_NODE_IDENTITY_PROPERTY,
-    DEMAND_MANIFEST_PROPERTY, DEMAND_MANIFEST_SIG_ALGORITHM_PROPERTY, DEMAND_MANIFEST_SIG_PROPERTY,
+    decode_manifest, Feature, CAPABILITIES_PROPERTY, DEMAND_MANIFEST_CERT_PROPERTY,
+    DEMAND_MANIFEST_NODE_IDENTITY_PROPERTY, DEMAND_MANIFEST_PROPERTY,
+    DEMAND_MANIFEST_SIG_ALGORITHM_PROPERTY, DEMAND_MANIFEST_SIG_PROPERTY,
 };
 
 use crate::market::negotiator::*;
@@ -66,16 +66,11 @@ impl NegotiatorComponent for ManifestSignature {
             .get_property::<String>(DEMAND_MANIFEST_NODE_IDENTITY_PROPERTY)
             .ok();
 
-        let demand_permissions_present = demand
-            .get_property::<String>(DEMAND_MANIFEST_CERT_PERMISSIONS_PROPERTY)
-            .is_ok();
-
         if manifest.is_outbound_requested() {
             match self.rules_manager.check_outbound_rules(
                 manifest,
                 demand.issuer,
                 manifest_sig,
-                demand_permissions_present,
                 node_identity,
             ) {
                 crate::rules::CheckRulesResult::Accept => acceptance(offer),
diff --git a/agent/provider/src/provider_agent.rs b/agent/provider/src/provider_agent.rs
index abc214a53e..ac370386bf 100644
--- a/agent/provider/src/provider_agent.rs
+++ b/agent/provider/src/provider_agent.rs
@@ -65,7 +65,7 @@ impl GlobalsManager {
     }
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone)]
 pub struct AgentNegotiatorsConfig {
     pub rules_manager: RulesManager,
 }
diff --git a/agent/provider/src/rules.rs b/agent/provider/src/rules.rs
index 587874eebf..3aa7286035 100644
--- a/agent/provider/src/rules.rs
+++ b/agent/provider/src/rules.rs
@@ -1,3 +1,7 @@
+use crate::startup_config::FileMonitor;
+use anyhow::{anyhow, Result};
+use itertools::Itertools;
+use serde::{Deserialize, Serialize};
 use std::{
     collections::HashMap,
     convert::TryFrom,
@@ -7,30 +11,24 @@ use std::{
     path::{Path, PathBuf},
     sync::{Arc, RwLock},
 };
-
-use anyhow::{anyhow, Result};
-use itertools::Itertools;
-use serde::{Deserialize, Serialize};
 use structopt::StructOpt;
 use strum::{Display, EnumString, EnumVariantNames};
 use url::Url;
 use ya_client_model::NodeId;
 use ya_manifest_utils::{
     golem_certificate::GolemPermission,
+    keystore::Keystore,
     matching::{
         domain::{DomainPatterns, DomainWhitelistState, DomainsMatcher},
         Matcher,
     },
-    policy::CertPermissions,
-    AppManifest, Keystore,
+    AppManifest, CompositeKeystore,
 };
 
-use crate::startup_config::FileMonitor;
-
-#[derive(Clone, Debug)]
+#[derive(Clone)]
 pub struct RulesManager {
     pub rulestore: Rulestore,
-    pub keystore: Keystore,
+    pub keystore: CompositeKeystore,
     pub cert_dir: PathBuf,
     whitelist: DomainWhitelistState,
     whitelist_file: PathBuf,
@@ -42,7 +40,7 @@ impl RulesManager {
         whitelist_file: &Path,
         cert_dir: &Path,
     ) -> Result<Self> {
-        let keystore = Keystore::load(cert_dir)?;
+        let keystore = CompositeKeystore::load(&cert_dir.into())?;
 
         let patterns = DomainPatterns::load_or_create(whitelist_file)?;
         let whitelist = DomainWhitelistState::try_new(patterns)?;
@@ -65,7 +63,7 @@ impl RulesManager {
     pub fn remove_dangling_rules(&self) -> Result<()> {
         let mut deleted_partner_rules = vec![];
 
-        let keystore_certs = self.keystore.certs_ids()?;
+        let keystore_certs = self.keystore.list_ids();
 
         self.rulestore
             .config
@@ -91,7 +89,7 @@ impl RulesManager {
     }
 
     pub fn set_partner_mode(&self, cert_id: String, mode: Mode) -> Result<()> {
-        let keystore_certs = self.keystore.certs_ids()?;
+        let keystore_certs = self.keystore.list_ids();
 
         if keystore_certs.contains(&cert_id) {
             self.rulestore
@@ -213,7 +211,6 @@ impl RulesManager {
         &self,
         manifest: &AppManifest,
         manifest_sig: Option<ManifestSignatureProps>,
-        demand_permissions_present: bool,
     ) -> Result<()> {
         if let Some(props) = manifest_sig {
             self.keystore
@@ -225,10 +222,6 @@ impl RulesManager {
                 )
                 .map_err(|e| anyhow!("Audited-Payload rule: {e}"))?;
 
-            //TODO Add verification of permission tree when they will be included in x509 (as there will be in both Rules)
-            self.verify_permissions(&props.cert, demand_permissions_present)
-                .map_err(|e| anyhow!("Audited-Payload rule: {e}"))?;
-
             let mode = &self
                 .rulestore
                 .config
@@ -323,7 +316,6 @@ impl RulesManager {
         manifest: AppManifest,
         requestor_id: NodeId,
         manifest_sig: Option<ManifestSignatureProps>,
-        demand_permissions_present: bool,
         node_identity: Option<String>,
     ) -> CheckRulesResult {
         if self.rulestore.is_outbound_disabled() {
@@ -334,7 +326,7 @@ impl RulesManager {
 
         let (accepts, rejects): (Vec<_>, Vec<_>) = vec![
             self.check_everyone_rule(&manifest),
-            self.check_audited_payload_rule(&manifest, manifest_sig, demand_permissions_present),
+            self.check_audited_payload_rule(&manifest, manifest_sig),
             self.check_partner_rule(&manifest, node_identity, requestor_id),
         ]
         .into_iter()
@@ -370,16 +362,6 @@ impl RulesManager {
             false
         }
     }
-
-    fn verify_permissions(&self, cert: &str, demand_permissions_present: bool) -> Result<()> {
-        let mut required = vec![CertPermissions::OutboundManifest];
-
-        if demand_permissions_present {
-            required.push(CertPermissions::UnverifiedPermissionsChain);
-        }
-
-        self.keystore.verify_permissions(cert, required)
-    }
 }
 
 fn verify_golem_permissions(
diff --git a/core/serv/src/main.rs b/core/serv/src/main.rs
index 73663eeaf8..184ba09224 100644
--- a/core/serv/src/main.rs
+++ b/core/serv/src/main.rs
@@ -625,7 +625,7 @@ impl ServiceCommand {
                         graceful: opts.gracefully,
                     })
                     .await?;
-                CommandOutput::object(&result)
+                CommandOutput::object(result)
             }
         }
     }
diff --git a/utils/manifest-utils/src/golem_certificate.rs b/utils/manifest-utils/src/golem_certificate.rs
index 59a95563e0..01aaabc4b4 100644
--- a/utils/manifest-utils/src/golem_certificate.rs
+++ b/utils/manifest-utils/src/golem_certificate.rs
@@ -1,6 +1,8 @@
+use serde::{Deserialize, Serialize};
 use url::Url;
 use ya_client_model::NodeId;
 
+#[derive(Clone, Debug)]
 pub struct GolemCertificate {
     pub node_id: NodeId,
     pub permissions: Vec<GolemPermission>,
@@ -17,7 +19,7 @@ impl GolemCertificate {
     }
 }
 
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 pub struct CertificateId {
     pub public_key: String, // hex
     pub hash: String,       // hex
@@ -46,6 +48,7 @@ pub enum VerificationError {
     UrlParseError(Vec<String>),
 }
 
+#[derive(Clone, Debug, Serialize, Deserialize)]
 pub enum GolemPermission {
     All,
     OutboundUnrestricted,
diff --git a/utils/manifest-utils/src/keystore.rs b/utils/manifest-utils/src/keystore.rs
new file mode 100644
index 0000000000..e317ad4f52
--- /dev/null
+++ b/utils/manifest-utils/src/keystore.rs
@@ -0,0 +1,291 @@
+pub mod golem_keystore;
+pub mod x509_keystore;
+
+use self::{
+    golem_keystore::{GolemKeystore, GolemKeystoreBuilder},
+    x509_keystore::{X509CertData, X509KeystoreBuilder, X509KeystoreManager},
+};
+use crate::{golem_certificate::GolemCertificate, policy::CertPermissions};
+use itertools::Itertools;
+use std::{
+    collections::HashSet,
+    ffi::OsStr,
+    fs,
+    path::{Path, PathBuf},
+};
+
+pub enum Cert {
+    X509(X509CertData),
+    Golem {
+        id: String,
+        cert: super::golem_certificate::GolemCertificate,
+    },
+}
+
+impl Cert {
+    pub fn id(&self) -> String {
+        match self {
+            Cert::Golem { id, cert: _ } => id.into(),
+            Cert::X509(cert) => cert.id.to_string(),
+        }
+    }
+
+    pub fn not_after(&self) -> String {
+        match self {
+            Cert::X509(cert) => cert.not_after.clone(),
+            Cert::Golem { id: _, cert: _ } => "".into(),
+        }
+    }
+
+    pub fn subject(&self) -> String {
+        match self {
+            Cert::X509(cert) => {
+                serde_json::to_string(&cert.subject).expect("Can serialize X509 fields")
+            }
+            Cert::Golem { id: _, cert: _ } => "".into(),
+        }
+    }
+}
+
+trait CommonAddParams {
+    fn certs(&self) -> &Vec<PathBuf>;
+}
+
+pub struct AddParams {
+    pub permissions: Vec<CertPermissions>,
+    pub certs: Vec<PathBuf>,
+    pub whole_chain: bool,
+}
+
+impl AddParams {
+    pub fn new(certs: Vec<PathBuf>) -> Self {
+        let permissions = vec![CertPermissions::All];
+        let whole_chain = true;
+        Self {
+            permissions,
+            certs,
+            whole_chain,
+        }
+    }
+}
+
+impl CommonAddParams for AddParams {
+    fn certs(&self) -> &Vec<PathBuf> {
+        &self.certs
+    }
+}
+
+#[derive(Default)]
+pub struct AddResponse {
+    pub added: Vec<Cert>,
+    pub skipped: Vec<Cert>,
+}
+
+pub trait CommonRemoveParams {
+    fn id(&self) -> &HashSet<String>;
+}
+
+pub struct RemoveParams {
+    pub ids: HashSet<String>,
+}
+
+impl CommonRemoveParams for RemoveParams {
+    fn id(&self) -> &HashSet<String> {
+        &self.ids
+    }
+}
+
+#[derive(Default)]
+pub struct RemoveResponse {
+    pub removed: Vec<Cert>,
+}
+
+pub trait Keystore: KeystoreClone + Send {
+    fn reload(&self, cert_dir: &PathBuf) -> anyhow::Result<()>;
+    fn add(&mut self, add: &AddParams) -> anyhow::Result<AddResponse>;
+    fn remove(&mut self, remove: &RemoveParams) -> anyhow::Result<RemoveResponse>;
+    fn list(&self) -> Vec<Cert>;
+}
+
+trait KeystoreBuilder<K: Keystore> {
+    // file is certificate file, certificate is its content
+    fn try_with(&mut self, file: &PathBuf) -> anyhow::Result<()>;
+    fn build(self) -> anyhow::Result<K>;
+}
+
+pub trait KeystoreClone {
+    fn clone_box(&self) -> Box<dyn Keystore>;
+}
+
+impl<T> KeystoreClone for T
+where
+    T: 'static + Keystore + Clone,
+{
+    fn clone_box(&self) -> Box<dyn Keystore> {
+        Box::new(self.clone())
+    }
+}
+
+impl Clone for Box<dyn Keystore> {
+    fn clone(&self) -> Box<dyn Keystore> {
+        self.clone_box()
+    }
+}
+
+#[derive(Clone)]
+pub struct CompositeKeystore {
+    x509_keystore: X509KeystoreManager,
+    golem_keystore: GolemKeystore,
+}
+
+impl CompositeKeystore {
+    pub fn load(cert_dir: &PathBuf) -> anyhow::Result<Self> {
+        std::fs::create_dir_all(cert_dir)?;
+        let mut x509_builder = X509KeystoreBuilder::new(cert_dir);
+        let mut golem_builder = GolemKeystoreBuilder::new(cert_dir);
+
+        let cert_dir = std::fs::read_dir(cert_dir)?;
+        for dir_entry in cert_dir {
+            let file = dir_entry?;
+            let file = file.path();
+            if let Err(err) = golem_builder
+                .try_with(&file)
+                .or_else(|err| {
+                    log::trace!("File '{} is not a Golem certificate. Trying to parse as Golem certificate. Err: {err}'", file.display());
+                    x509_builder.try_with(&file)
+                }) {
+                    log::debug!("File '{} is not a X509 certificate nor a Golem certificate. Err: {err}'", file.display());
+                }
+        }
+
+        let x509_keystore = x509_builder.build()?;
+        let golem_keystore = golem_builder.build()?;
+
+        Ok(Self {
+            x509_keystore,
+            golem_keystore,
+        })
+    }
+
+    fn keystores(&self) -> Vec<Box<&dyn Keystore>> {
+        vec![
+            Box::new(&self.golem_keystore),
+            Box::new(&self.x509_keystore),
+        ]
+    }
+
+    fn keystores_mut(&mut self) -> Vec<Box<&mut dyn Keystore>> {
+        vec![
+            Box::new(&mut self.golem_keystore),
+            Box::new(&mut self.x509_keystore),
+        ]
+    }
+
+    pub fn list_ids(&self) -> HashSet<String> {
+        self.list()
+            .into_iter()
+            .map(|cert| cert.id())
+            .collect::<HashSet<String>>()
+    }
+
+    pub fn verify_signature(
+        &self,
+        cert: impl AsRef<str>,
+        sig: impl AsRef<str>,
+        sig_alg: impl AsRef<str>,
+        data: impl AsRef<str>,
+    ) -> anyhow::Result<()> {
+        self.x509_keystore
+            .keystore
+            .verify_signature(cert, sig, sig_alg, data)
+    }
+
+    pub fn verify_golem_certificate(&self, cert: &String) -> anyhow::Result<GolemCertificate> {
+        self.golem_keystore.verify_golem_certificate(cert)
+    }
+}
+
+impl Keystore for CompositeKeystore {
+    fn reload(&self, cert_dir: &PathBuf) -> anyhow::Result<()> {
+        for keystore in self.keystores().iter() {
+            keystore.reload(cert_dir)?;
+        }
+        Ok(())
+    }
+
+    fn add(&mut self, add: &AddParams) -> anyhow::Result<AddResponse> {
+        let response = self
+            .keystores_mut()
+            .iter_mut()
+            .map(|keystore| keystore.add(add))
+            .fold_ok(AddResponse::default(), |mut acc, mut res| {
+                acc.added.append(&mut res.added);
+                acc.skipped.append(&mut res.skipped);
+                acc
+            })?;
+        Ok(response)
+    }
+
+    fn remove(&mut self, remove: &RemoveParams) -> anyhow::Result<RemoveResponse> {
+        let response = self
+            .keystores_mut()
+            .iter_mut()
+            .map(|keystore| keystore.remove(remove))
+            .fold_ok(RemoveResponse::default(), |mut acc, mut res| {
+                acc.removed.append(&mut res.removed);
+                acc
+            })?;
+        Ok(response)
+    }
+
+    fn list(&self) -> Vec<Cert> {
+        self.keystores()
+            .iter()
+            .flat_map(|keystore| keystore.list())
+            .collect()
+    }
+}
+
+/// Copies file into `dst_dir`.
+/// Renames file if duplicated: "name.ext" into "name.1.ext" etc.
+fn copy_file(src_file: &PathBuf, dst_dir: &PathBuf) -> anyhow::Result<()> {
+    let file_name = get_file_name(src_file)
+        .ok_or_else(|| anyhow::anyhow!(format!("Cannot get filename of {src_file:?}")))?;
+    let mut new_cert_path = dst_dir.clone();
+    new_cert_path.push(file_name);
+    if new_cert_path.exists() {
+        let file_stem = get_file_stem(&new_cert_path).expect("Has to have stem");
+        let dot_extension = get_file_extension(&new_cert_path)
+            .map(|ex| format!(".{ex}"))
+            .unwrap_or_else(|| String::from(""));
+        for i in 0..u32::MAX {
+            let numbered_filename = format!("{file_stem}.{i}{dot_extension}");
+            new_cert_path = dst_dir.clone();
+            new_cert_path.push(numbered_filename);
+            if !new_cert_path.exists() {
+                break;
+            }
+        }
+        if new_cert_path.exists() {
+            anyhow::bail!("Unable to load certificate");
+        }
+    }
+    fs::copy(src_file, new_cert_path)?;
+    Ok(())
+}
+
+fn get_file_name(path: &Path) -> Option<String> {
+    path.file_name().map(os_str_to_string)
+}
+
+fn os_str_to_string(os_str: &OsStr) -> String {
+    os_str.to_string_lossy().to_string()
+}
+
+fn get_file_extension(path: &Path) -> Option<String> {
+    path.extension().map(os_str_to_string)
+}
+
+fn get_file_stem(path: &Path) -> Option<String> {
+    path.file_stem().map(os_str_to_string)
+}
diff --git a/utils/manifest-utils/src/keystore/golem_keystore.rs b/utils/manifest-utils/src/keystore/golem_keystore.rs
new file mode 100644
index 0000000000..5c4e802997
--- /dev/null
+++ b/utils/manifest-utils/src/keystore/golem_keystore.rs
@@ -0,0 +1,157 @@
+use super::{Cert, Keystore, KeystoreBuilder};
+use crate::{
+    golem_certificate::{self, GolemCertificate},
+    keystore::copy_file,
+    util::str_to_short_hash,
+};
+use anyhow::anyhow;
+use std::{
+    collections::HashMap,
+    fs::File,
+    io::Read,
+    path::{Path, PathBuf},
+    sync::{Arc, RwLock},
+};
+
+#[derive(Debug, Clone)]
+pub struct GolemCertificateEntry {
+    #[allow(dead_code)]
+    path: PathBuf,
+    cert: GolemCertificate,
+}
+
+pub(super) trait GolemCertAddParams {}
+
+pub struct GolemKeystoreBuilder {
+    pub certificates: HashMap<String, GolemCertificateEntry>,
+    pub cert_dir: PathBuf,
+}
+
+impl GolemKeystoreBuilder {
+    pub fn new(cert_dir: impl AsRef<Path>) -> Self {
+        let certificates = Default::default();
+        let cert_dir = PathBuf::from(cert_dir.as_ref());
+        Self {
+            certificates,
+            cert_dir,
+        }
+    }
+}
+
+impl KeystoreBuilder<GolemKeystore> for GolemKeystoreBuilder {
+    fn try_with(&mut self, cert_path: &PathBuf) -> anyhow::Result<()> {
+        let (id, cert) = read_cert(cert_path)?;
+        let file = cert_path.clone();
+        self.certificates
+            .insert(id, GolemCertificateEntry { path: file, cert });
+        Ok(())
+    }
+
+    fn build(self) -> anyhow::Result<GolemKeystore> {
+        let certificates = Arc::new(RwLock::new(self.certificates));
+        let cert_dir = self.cert_dir;
+        Ok(GolemKeystore {
+            certificates,
+            cert_dir,
+        })
+    }
+}
+
+// Return certificate with its id
+fn read_cert(cert_path: &PathBuf) -> anyhow::Result<(String, GolemCertificate)> {
+    let mut cert_file = File::open(cert_path)?;
+    let mut cert_content = String::new();
+    cert_file.read_to_string(&mut cert_content)?;
+    let cert_content = cert_content.trim();
+    let cert = golem_certificate::verify_golem_certificate(cert_content)?;
+    let id = str_to_short_hash(cert_content);
+    Ok((id, cert))
+}
+
+#[derive(Debug, Clone)]
+pub(super) struct GolemKeystore {
+    pub certificates: Arc<RwLock<HashMap<String, GolemCertificateEntry>>>,
+    pub cert_dir: PathBuf,
+}
+
+impl GolemKeystore {
+    pub fn verify_golem_certificate(&self, cert: &str) -> anyhow::Result<GolemCertificate> {
+        golem_certificate::verify_golem_certificate(cert)
+            .map_err(|e| anyhow!("verification of golem certificate failed: {e}"))
+    }
+}
+
+impl Keystore for GolemKeystore {
+    fn reload(&self, cert_dir: &PathBuf) -> anyhow::Result<()> {
+        let mut certificates = HashMap::new();
+        let cert_dir = std::fs::read_dir(cert_dir)?;
+        for dir_entry in cert_dir {
+            let file = dir_entry?;
+            let cert_path = file.path();
+            match read_cert(&cert_path) {
+                Ok((id, cert)) => {
+                    certificates.insert(id, cert);
+                }
+                Err(err) => log::trace!(
+                    "Unable to parse file '{:?}' as Golem cert. Err: {}",
+                    cert_path,
+                    err
+                ),
+            }
+        }
+        Ok(())
+    }
+
+    fn add(&mut self, add: &super::AddParams) -> anyhow::Result<super::AddResponse> {
+        let mut added = Vec::new();
+        let mut skipped = Vec::new();
+        let mut certificates = self.certificates.write().expect("Can read Golem keystore");
+        for path in add.certs.iter() {
+            let mut file = File::open(path)?;
+            let mut content = String::new();
+            file.read_to_string(&mut content)?;
+            let content = content.trim().to_string();
+            let id = str_to_short_hash(&content);
+            match self.verify_golem_certificate(&content) {
+                Ok(cert) => {
+                    if certificates.contains_key(&id) {
+                        skipped.push(Cert::Golem { id, cert });
+                        continue;
+                    }
+                    log::debug!("Adding Golem certificate: {:?}", cert);
+                    copy_file(path, &self.cert_dir)?;
+                    certificates.insert(
+                        id.clone(),
+                        GolemCertificateEntry {
+                            path: path.clone(),
+                            cert: cert.clone(),
+                        },
+                    );
+                    added.push(Cert::Golem { id, cert })
+                }
+                Err(err) => log::error!("Failed to parse Golem certificate. Err: {}", err),
+            }
+        }
+        Ok(super::AddResponse { added, skipped })
+    }
+
+    fn remove(&mut self, _remove: &super::RemoveParams) -> anyhow::Result<super::RemoveResponse> {
+        Ok(Default::default())
+    }
+
+    fn list(&self) -> Vec<super::Cert> {
+        let mut certificates = Vec::new();
+        for (id, cert_entry) in self
+            .certificates
+            .read()
+            .expect("Can read Golem keystore")
+            .iter()
+        {
+            certificates.push(Cert::Golem {
+                id: id.clone(),
+                cert: cert_entry.cert.clone(),
+            });
+        }
+        certificates
+    }
+}
diff --git a/utils/manifest-utils/src/keystore/x509_keystore.rs b/utils/manifest-utils/src/keystore/x509_keystore.rs
new file mode 100644
index 0000000000..fbf7403030
--- /dev/null
+++ b/utils/manifest-utils/src/keystore/x509_keystore.rs
@@ -0,0 +1,682 @@
+use super::{
+    AddParams, AddResponse, Cert, CommonAddParams, Keystore, KeystoreBuilder, RemoveParams,
+    RemoveResponse,
+};
+use crate::{
+    policy::CertPermissions,
+    util::{format_permissions, str_to_short_hash},
+};
+use anyhow::{anyhow, bail};
+use openssl::{
+    hash::MessageDigest,
+    nid::Nid,
+    pkey::{PKey, Public},
+    sign::Verifier,
+    x509::{
+        store::{X509Store, X509StoreBuilder},
+        X509ObjectRef, X509Ref, X509StoreContext, X509VerifyResult, X509,
+    },
+};
+use std::{
+    collections::{BTreeMap, HashMap, HashSet},
+    convert::TryFrom,
+    fs::{self, DirEntry, File},
+    io::Read,
+    path::{Path, PathBuf},
+    sync::{Arc, RwLock},
+};
+
+pub(super) const PERMISSIONS_FILE: &str = "cert-permissions.json";
+pub(super) trait X509AddParams {}
+
+pub struct X509CertData {
+    pub id: String,
+    pub not_after: String,
+    pub subject: BTreeMap<String, String>,
+    pub permissions: String,
+}
+
+impl X509CertData {
+    pub fn create(cert: &X509Ref, permissions: &PermissionsManager) -> anyhow::Result<Self> {
+        let mut data = X509CertData::try_from(cert)?;
+        let permissions = permissions.get(cert);
+        data.permissions = format_permissions(&permissions);
+        Ok(data)
+    }
+}
+
+pub(super) struct AddX509Response {
+    pub loaded: Vec<X509>,
+    pub skipped: Vec<X509>,
+}
+
+pub struct X509KeystoreBuilder {
+    builder: X509StoreBuilder,
+    cert_dir: PathBuf,
+}
+
+impl X509KeystoreBuilder {
+    pub fn new(cert_dir: impl AsRef<Path>) -> Self {
+        let builder = X509StoreBuilder::new().expect("OpenSSL works");
+        let cert_dir = PathBuf::from(cert_dir.as_ref());
+        Self { builder, cert_dir }
+    }
+}
+
+impl KeystoreBuilder<X509KeystoreManager> for X509KeystoreBuilder {
+    fn try_with(&mut self, file: &PathBuf) -> anyhow::Result<()> {
+        for cert in parse_cert_file(file)? {
+            self.builder.add_cert(cert)?
+        }
+        Ok(())
+    }
+
+    fn build(self) -> anyhow::Result<X509KeystoreManager> {
+        let permissions = PermissionsManager::load(&self.cert_dir).map_err(|e| {
+            anyhow!(
+                "Failed to load permissions file: {}, {e}",
+                self.cert_dir.display()
+            )
+        })?;
+        let keystore = self.builder.build();
+        let inner = Arc::new(RwLock::new(CertStore::new(keystore, permissions)));
+        let keystore = X509Keystore { store: inner };
+        let ids = keystore.certs_ids()?;
+        Ok(X509KeystoreManager {
+            keystore,
+            ids,
+            cert_dir: self.cert_dir,
+        })
+    }
+}
+
+#[derive(Clone)]
+pub(super) struct X509KeystoreManager {
+    pub(super) keystore: X509Keystore,
+    ids: HashSet<String>,
+    cert_dir: PathBuf,
+}
+
+impl X509KeystoreManager {
+    /// Copies certificates from given file to `cert-dir` and returns newly added certificates.
+    /// Certificates already existing in `cert-dir` are skipped.
+    fn add_certs<ADD: CommonAddParams>(
+        &self,
+        add: &ADD,
+        permissions_manager: &mut PermissionsManager,
+    ) -> anyhow::Result<AddX509Response> {
+        let mut added = HashMap::new();
+        let mut skipped = HashMap::new();
+
+        for cert_path in add.certs() {
+            let mut new_certs = Vec::new();
+            let file_certs = parse_cert_file(cert_path)?;
+            if file_certs.is_empty() {
+                continue;
+            }
+            let file_certs_len = file_certs.len();
+            for file_cert in file_certs {
+                let id = cert_to_id(&file_cert)?;
+                if !self.ids.contains(&id) && !added.contains_key(&id) {
+                    new_certs.push(file_cert.clone());
+                    added.insert(id, file_cert);
+                } else {
+                    skipped.insert(id, file_cert);
+                }
+            }
+            if file_certs_len == new_certs.len() {
+                super::copy_file(cert_path, &self.cert_dir)?;
+            } else {
+                self.load_as_certificate_files(cert_path, new_certs)?;
+            }
+        }
+
+        // TODO delete when working on X509 rules/permissions
+        permissions_manager.set_many(
+            &added.values().chain(skipped.values()).cloned().collect(),
+            &Vec::new(),
+            true,
+        );
+
+        Ok(AddX509Response {
+            loaded: added.into_values().collect(),
+            skipped: skipped.into_values().collect(),
+        })
+    }
+
+    /// Loads certificates as individual files to `cert-dir`
+    fn load_as_certificate_files(&self, cert_path: &Path, certs: Vec<X509>) -> anyhow::Result<()> {
+        let file_stem = super::get_file_stem(cert_path)
+            .ok_or_else(|| anyhow::anyhow!("Cannot get file name stem."))?;
+        let dot_extension = super::get_file_extension(cert_path)
+            .map(|ex| format!(".{ex}"))
+            .unwrap_or_else(|| String::from(""));
+        for cert in certs.into_iter() {
+            let id = cert_to_id(&cert)?;
+            let mut new_cert_path = self.cert_dir.clone();
+            new_cert_path.push(format!("{file_stem}.{id}{dot_extension}"));
+            let cert = cert.to_pem()?;
+            fs::write(new_cert_path, cert)?;
+        }
+        Ok(())
+    }
+
+    pub fn permissions_manager(&self) -> PermissionsManager {
+        self.keystore.permissions_manager()
+    }
+}
+
+impl Keystore for X509KeystoreManager {
+    fn reload(&self, cert_dir: &PathBuf) -> anyhow::Result<()> {
+        self.keystore.reload(cert_dir)
+    }
+
+    fn add(&mut self, add: &AddParams) -> anyhow::Result<AddResponse> {
+        let mut permissions_manager = self.keystore.permissions_manager();
+        let AddX509Response { loaded, skipped } = self.add_certs(add, &mut permissions_manager)?;
+
+        permissions_manager
+            .save(&self.cert_dir)
+            .map_err(|e| anyhow!("Failed to save permissions file: {e}"))?;
+        let added = loaded
+            .into_iter()
+            .map(|cert| X509CertData::create(&cert, &permissions_manager))
+            .collect::<anyhow::Result<Vec<X509CertData>>>()?
+            .into_iter()
+            .map(Cert::X509)
+            .collect();
+        let skipped = skipped
+            .into_iter()
+            .map(|cert| X509CertData::create(&cert, &permissions_manager))
+            .collect::<anyhow::Result<Vec<X509CertData>>>()?
+            .into_iter()
+            .map(Cert::X509)
+            .collect();
+        Ok(AddResponse { added, skipped })
+    }
+
+    fn remove(&mut self, remove: &RemoveParams) -> anyhow::Result<RemoveResponse> {
+        let ids = &remove.ids;
+        if ids.difference(&self.ids).eq(ids) {
+            return Ok(RemoveResponse::default());
+        }
+        let mut removed = HashMap::new();
+
+        let cert_dir_entries: Vec<Result<DirEntry, std::io::Error>> =
+            std::fs::read_dir(self.cert_dir.clone())?.collect();
+        for dir_entry in cert_dir_entries {
+            let cert_file = dir_entry?;
+            let cert_file = cert_file.path();
+            let certs = parse_cert_file(&cert_file)?;
+            if certs.is_empty() {
+                // No certificates in parsed file.
+                continue;
+            }
+            let mut ids_cert = certs.into_iter().fold(HashMap::new(), |mut certs, cert| {
+                if let Ok(id) = cert_to_id(&cert) {
+                    certs.insert(id, cert);
+                }
+                certs
+            });
+            let mut split_and_skip = false;
+            for id in ids.iter() {
+                if let Some(cert) = ids_cert.remove(id) {
+                    removed.insert(id, cert);
+                    split_and_skip = true;
+                }
+            }
+            if split_and_skip {
+                let file_stem =
+                    super::get_file_stem(&cert_file).expect("Cannot get file name stem");
+                let dot_extension = super::get_file_extension(&cert_file)
+                    .map_or_else(|| String::from(""), |ex| format!(".{ex}"));
+                for (id, cert) in ids_cert {
+                    let cert = cert.to_pem()?;
+                    let mut file_path = self.cert_dir.clone();
+                    let filename = format!("{file_stem}.{id}{dot_extension}");
+                    file_path.push(filename);
+                    fs::write(file_path, cert)?;
+                }
+                fs::remove_file(cert_file)?;
+            }
+        }
+
+        let mut permissions_manager = self.permissions_manager();
+        let removed_certs: Vec<X509> = removed.clone().into_values().collect();
+        permissions_manager.set_many(&removed_certs, &vec![], true);
+        permissions_manager
+            .save(&self.cert_dir)
+            .map_err(|e| anyhow!("Failed to save permissions file: {e}"))?;
+
+        let removed: Vec<Cert> = removed
+            .into_values()
+            .map(|cert| X509CertData::create(&cert, &permissions_manager))
+            .collect::<anyhow::Result<Vec<X509CertData>>>()?
+            .into_iter()
+            .map(Cert::X509)
+            .collect();
+        Ok(RemoveResponse { removed })
+    }
+
+    fn list(&self) -> Vec<Cert> {
+        self.keystore.list().into_iter().map(Cert::X509).collect()
+    }
+}
+
+struct CertStore {
+    store: X509Store,
+    permissions: PermissionsManager,
+}
+
+#[derive(Clone)]
+pub struct X509Keystore {
+    store: Arc<RwLock<CertStore>>,
+}
+
+impl Default for X509Keystore {
+    fn default() -> Self {
+        let store = X509StoreBuilder::new().expect("SSL works").build();
+        let store = CertStore::new(store, Default::default());
+        Self {
+            store: Arc::new(RwLock::new(store)),
+        }
+    }
+}
+
+impl CertStore {
+    pub fn new(store: X509Store, permissions: PermissionsManager) -> CertStore {
+        CertStore { store, permissions }
+    }
+}
+
+impl X509Keystore {
+    /// Reads DER or PEM certificates (or PEM certificate stacks) from `cert-dir` and creates new `X509Store`.
+    pub fn load(cert_dir: impl AsRef<Path>) -> anyhow::Result<Self> {
+        let permissions = PermissionsManager::load(&cert_dir).map_err(|e| {
+            anyhow!(
+                "Failed to load permissions file: {}, {e}",
+                cert_dir.as_ref().display()
+            )
+        })?;
+
+        let mut store = X509StoreBuilder::new()?;
+        let cert_dir = std::fs::read_dir(cert_dir)?;
+        for dir_entry in cert_dir {
+            let cert = dir_entry?.path();
+            if let Err(e) = Self::load_file(&mut store, &cert) {
+                log::debug!(
+                    "Skipping '{}' while loading a X509Keystore. Error: {e}",
+                    cert.display()
+                );
+            }
+        }
+        let store = CertStore::new(store.build(), permissions);
+        let store = Arc::new(RwLock::new(store));
+        Ok(Self { store })
+    }
+
+    pub fn reload(&self, cert_dir: impl AsRef<Path>) -> anyhow::Result<()> {
+        let keystore = X509Keystore::load(&cert_dir)?;
+        self.replace(keystore);
+        Ok(())
+    }
+
+    fn replace(&self, other: X509Keystore) {
+        let store = {
+            let mut inner = other.store.write().unwrap();
+            std::mem::replace(
+                &mut (*inner),
+                CertStore {
+                    store: X509StoreBuilder::new().unwrap().build(),
+                    permissions: Default::default(),
+                },
+            )
+        };
+        let mut inner = self.store.write().unwrap();
+        *inner = store;
+    }
+
+    fn list(&self) -> Vec<X509CertData> {
+        let inner = self.store.read().unwrap();
+        inner
+            .store
+            .objects()
+            .iter()
+            .flat_map(X509ObjectRef::x509)
+            .map(X509CertData::try_from)
+            .flat_map(|cert| match cert {
+                Ok(cert) => Some(cert),
+                Err(err) => {
+                    log::debug!("Failed to read X509 cert. Err: {}", err);
+                    None
+                }
+            })
+            .collect()
+    }
+
+    /// Decodes byte64 `sig`, verifies `cert`and reads its pub key,
+    /// prepares digest using `sig_alg`, verifies `data` using `sig` and pub key.
+    pub fn verify_signature(
+        &self,
+        cert: impl AsRef<str>,
+        sig: impl AsRef<str>,
+        sig_alg: impl AsRef<str>,
+        data: impl AsRef<str>,
+    ) -> anyhow::Result<()> {
+        let sig = crate::decode_data(sig)?;
+
+        let pkey = self.verify_cert(cert)?;
+
+        let msg_digest = MessageDigest::from_name(sig_alg.as_ref())
+            .ok_or_else(|| anyhow::anyhow!("Unknown signature algorithm: {}", sig_alg.as_ref()))?;
+        let mut verifier = Verifier::new(msg_digest, pkey.as_ref())?;
+        if !(verifier.verify_oneshot(&sig, data.as_ref().as_bytes())?) {
+            return Err(anyhow::anyhow!("Invalid signature"));
+        }
+        Ok(())
+    }
+
+    pub fn certs_ids(&self) -> anyhow::Result<HashSet<String>> {
+        let inner = self.store.read().unwrap();
+        let mut ids = HashSet::new();
+        for cert in inner.store.objects() {
+            if let Some(cert) = cert.x509() {
+                let id = cert_to_id(cert)?;
+                ids.insert(id);
+            }
+        }
+        Ok(ids)
+    }
+
+    fn load_file(store: &mut X509StoreBuilder, cert: &PathBuf) -> anyhow::Result<()> {
+        for cert in parse_cert_file(cert)? {
+            store.add_cert(cert)?
+        }
+        Ok(())
+    }
+
+    fn verify_cert<S: AsRef<str>>(&self, cert: S) -> anyhow::Result<PKey<Public>> {
+        let cert_chain = Self::decode_cert_chain(cert)?;
+        let store = self
+            .store
+            .read()
+            .map_err(|err| anyhow::anyhow!("Err: {}", err.to_string()))?;
+        let cert = match cert_chain.last().map(Clone::clone) {
+            Some(cert) => cert,
+            None => bail!("Unable to verify certificate. No certificate."),
+        };
+        let mut cert_stack = openssl::stack::Stack::new()?;
+        for cert in cert_chain {
+            cert_stack.push(cert).unwrap();
+        }
+        let mut ctx = X509StoreContext::new()?;
+        if !(ctx.init(&store.store, &cert, &cert_stack, |ctx| ctx.verify_cert())?) {
+            bail!("Invalid certificate");
+        }
+        Ok(cert.public_key()?)
+    }
+
+    pub fn verify_permissions<S: AsRef<str>>(
+        &self,
+        cert: S,
+        required: Vec<CertPermissions>,
+    ) -> anyhow::Result<()> {
+        if required.contains(&CertPermissions::All) {
+            bail!("`All` permissions shouldn't be required.")
+        }
+
+        if required.is_empty() {
+            return Ok(());
+        }
+
+        let cert_chain = Self::decode_cert_chain(cert)?;
+        // Demands do not contain certificates permissions
+        // so only first certificate in chain signer permissions are verified.
+        let cert = match cert_chain.first() {
+            Some(cert) => cert,
+            None => bail!("Unable to verify certificate permissions. No certificate."),
+        };
+        let issuer = self.find_issuer(cert)?;
+
+        self.has_permissions(&issuer, &required)
+    }
+
+    fn get_permissions(&self, cert: &X509Ref) -> anyhow::Result<Vec<CertPermissions>> {
+        let store = self
+            .store
+            .read()
+            .map_err(|err| anyhow::anyhow!("RwLock error: {}", err.to_string()))?;
+        Ok(store.permissions.get(cert))
+    }
+
+    fn has_permissions(
+        &self,
+        cert: &X509Ref,
+        required: &Vec<CertPermissions>,
+    ) -> anyhow::Result<()> {
+        let cert_permissions = self.get_permissions(cert)?;
+
+        if cert_permissions.contains(&CertPermissions::All)
+            && (!required.contains(&CertPermissions::UnverifiedPermissionsChain)
+                || (cert_permissions.contains(&CertPermissions::UnverifiedPermissionsChain)
+                    && required.contains(&CertPermissions::UnverifiedPermissionsChain)))
+        {
+            return Ok(());
+        }
+
+        if required
+            .iter()
+            .all(|permission| cert_permissions.contains(permission))
+        {
+            return Ok(());
+        }
+
+        bail!(
+            "Not sufficient permissions. Required: `{}`, but has only: `{}`",
+            format_permissions(required),
+            format_permissions(&cert_permissions)
+        )
+    }
+
+    fn find_issuer(&self, cert: &X509) -> anyhow::Result<X509> {
+        let store = self
+            .store
+            .read()
+            .map_err(|err| anyhow::anyhow!("RwLock error: {}", err.to_string()))?;
+        store
+            .store
+            .objects()
+            .iter()
+            .filter_map(|cert| cert.x509())
+            .map(|cert| cert.to_owned())
+            .find(|trusted| trusted.issued(cert) == X509VerifyResult::OK)
+            .ok_or_else(|| anyhow!("Issuer certificate not found in X509Keystore"))
+    }
+
+    fn decode_cert_chain<S: AsRef<str>>(cert: S) -> anyhow::Result<Vec<X509>> {
+        let cert = crate::decode_data(cert)?;
+        Ok(match X509::from_der(&cert) {
+            Ok(cert) => vec![cert],
+            Err(_) => X509::stack_from_pem(&cert)?,
+        })
+    }
+
+    pub fn permissions_manager(&self) -> PermissionsManager {
+        self.store.read().unwrap().permissions.clone()
+    }
+}
+
+fn parse_cert_file(cert: &PathBuf) -> anyhow::Result<Vec<X509>> {
+    let extension = super::get_file_extension(cert);
+    let mut cert = File::open(cert)?;
+    let mut cert_buffer = Vec::new();
+    cert.read_to_end(&mut cert_buffer)?;
+    match extension {
+        Some(ref der) if der.to_lowercase() == "der" => Ok(vec![X509::from_der(&cert_buffer)?]),
+        Some(ref pem) if pem.to_lowercase() == "pem" => Ok(X509::stack_from_pem(&cert_buffer)?),
+        _ => {
+            // Certificates can have various other extensions like .cer .crt .key (and .key can be both DER and PEM)
+            // Initial parsing dictated by `extension` is done because it would improper to parse .pem as a DER
+            Ok(X509::stack_from_pem(&cert_buffer)
+                .or_else(|_| X509::from_der(&cert_buffer).map(|cert| vec![cert]))?)
+        }
+    }
+}
+
+pub fn cert_to_id(cert: &X509Ref) -> anyhow::Result<String> {
+    let txt = cert.to_text()?;
+    Ok(str_to_short_hash(txt))
+}
+
+impl TryFrom<&X509Ref> for X509CertData {
+    type Error = anyhow::Error;
+
+    fn try_from(cert: &X509Ref) -> Result<Self, Self::Error> {
+        let id = cert_to_id(cert)?;
+        let not_after = cert.not_after().to_string();
+        let mut subject = BTreeMap::new();
+        add_cert_subject_entries(&mut subject, cert, Nid::COMMONNAME, "CN");
+        add_cert_subject_entries(&mut subject, cert, Nid::PKCS9_EMAILADDRESS, "E");
+        add_cert_subject_entries(&mut subject, cert, Nid::ORGANIZATIONNAME, "O");
+        add_cert_subject_entries(&mut subject, cert, Nid::ORGANIZATIONALUNITNAME, "OU");
+        add_cert_subject_entries(&mut subject, cert, Nid::COUNTRYNAME, "C");
+        add_cert_subject_entries(&mut subject, cert, Nid::STATEORPROVINCENAME, "ST");
+
+        Ok(X509CertData {
+            id,
+            not_after,
+            subject,
+            permissions: "".to_string(),
+        })
+    }
+}
+
+/// Adds entries with given `nid` to given `subject` String.
+fn add_cert_subject_entries(
+    subject: &mut BTreeMap<String, String>,
+    cert: &X509Ref,
+    nid: Nid,
+    entry_short_name: &str,
+) {
+    if let Some(entries) = cert_subject_entries(cert, nid) {
+        subject.insert(entry_short_name.to_string(), entries);
+    }
+}
+
+/// Reads subject entries and returns them as comma separated `String`.
+fn cert_subject_entries(cert: &X509Ref, nid: Nid) -> Option<String> {
+    let entries =
+        cert.subject_name()
+            .entries_by_nid(nid)
+            .fold(String::from(""), |mut names, name| {
+                if !names.is_empty() {
+                    names.push_str(", ");
+                }
+                let name = String::from_utf8_lossy(name.data().as_slice());
+                names.push_str(&name);
+                names
+            });
+    if entries.is_empty() {
+        return None;
+    }
+    Some(entries)
+}
+
+#[derive(Clone, Default)]
+pub struct PermissionsManager {
+    permissions: HashMap<String, Vec<CertPermissions>>,
+}
+
+impl PermissionsManager {
+    pub fn load(cert_dir: impl AsRef<Path>) -> anyhow::Result<PermissionsManager> {
+        let path = cert_dir.as_ref().join(PERMISSIONS_FILE);
+        let content = match std::fs::read_to_string(path) {
+            Ok(content) if !content.is_empty() => content,
+            _ => return Ok(Default::default()),
+        };
+        let permissions = serde_json::from_str(&content)?;
+        Ok(PermissionsManager { permissions })
+    }
+
+    pub fn set(&mut self, cert: &str, mut permissions: Vec<CertPermissions>) {
+        if permissions.contains(&CertPermissions::All) {
+            let supports_unverified_permissions =
+                permissions.contains(&CertPermissions::UnverifiedPermissionsChain);
+            permissions.clear();
+            permissions.push(CertPermissions::All);
+            if supports_unverified_permissions {
+                permissions.push(CertPermissions::UnverifiedPermissionsChain);
+            }
+        }
+        self.permissions.insert(cert.to_string(), permissions);
+    }
+
+    pub fn set_x509(
+        &mut self,
+        cert: &X509,
+        permissions: Vec<CertPermissions>,
+    ) -> anyhow::Result<()> {
+        let id = cert_to_id(cert)?;
+        self.set(&id, permissions);
+        Ok(())
+    }
+
+    pub fn set_many(
+        &mut self,
+        // With slice I would need add `openssl` dependency directly to ya-rovider.
+        #[allow(clippy::ptr_arg)] certs: &Vec<X509>,
+        permissions: &Vec<CertPermissions>,
+        whole_chain: bool,
+    ) {
+        let certs = match whole_chain {
+            false => Self::leaf_certs(certs),
+            true => certs.clone(),
+        };
+
+        for cert in certs {
+            if let Err(e) = self.set_x509(&cert, permissions.clone()) {
+                log::error!(
+                    "Failed to set permissions for certificate {:?}. {e}",
+                    cert.subject_name()
+                );
+            }
+        }
+    }
+
+    /// If we don't have this certificate registered, it means it has no permissions,
+    /// so empty vector is returned.
+    pub fn get(&self, cert: &X509Ref) -> Vec<CertPermissions> {
+        let id = match cert_to_id(cert) {
+            Ok(id) => id,
+            Err(_) => return vec![],
+        };
+        self.permissions.get(&id).cloned().unwrap_or_default()
+    }
+
+    pub fn save(&self, path: &Path) -> anyhow::Result<()> {
+        let mut file = File::create(path.join(PERMISSIONS_FILE))?;
+        Ok(serde_json::to_writer_pretty(&mut file, &self.permissions)?)
+    }
+
+    fn leaf_certs(certs: &[X509]) -> Vec<X509> {
+        if certs.len() == 1 {
+            // when there is 1 cert it is a leaf cert
+            return certs.to_vec();
+        }
+        certs
+            .iter()
+            .cloned()
+            .filter(|cert| {
+                !certs
+                    .iter()
+                    .any(|cert2| cert.issued(cert2) == X509VerifyResult::OK)
+            })
+            .collect()
+    }
+}
+
+impl std::fmt::Debug for X509Keystore {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "Keystore")
+    }
+}
diff --git a/utils/manifest-utils/src/lib.rs b/utils/manifest-utils/src/lib.rs
index 13a02aad53..56fb650b34 100644
--- a/utils/manifest-utils/src/lib.rs
+++ b/utils/manifest-utils/src/lib.rs
@@ -1,11 +1,12 @@
 pub mod golem_certificate;
+pub mod keystore;
 pub mod manifest;
 pub mod matching;
 pub mod policy;
 pub mod util;
 
 pub use manifest::*;
-pub use policy::{Keystore, Policy, PolicyConfig};
-pub use util::{
-    decode_data, DecodingError, KeystoreLoadResult, KeystoreManager, KeystoreRemoveResult,
-};
+// pub use keystore::
+pub use keystore::CompositeKeystore;
+pub use policy::{Policy, PolicyConfig};
+pub use util::{decode_data, DecodingError};
diff --git a/utils/manifest-utils/src/manifest.rs b/utils/manifest-utils/src/manifest.rs
index f1ed98fe10..82726431ae 100644
--- a/utils/manifest-utils/src/manifest.rs
+++ b/utils/manifest-utils/src/manifest.rs
@@ -22,8 +22,6 @@ pub const DEMAND_MANIFEST_PROPERTY: &str = "golem.srv.comp.payload";
 pub const DEMAND_MANIFEST_SIG_PROPERTY: &str = "golem.srv.comp.payload.sig";
 pub const DEMAND_MANIFEST_SIG_ALGORITHM_PROPERTY: &str = "golem.srv.comp.payload.sig.algorithm";
 pub const DEMAND_MANIFEST_CERT_PROPERTY: &str = "golem.srv.comp.payload.cert";
-pub const DEMAND_MANIFEST_CERT_PERMISSIONS_PROPERTY: &str =
-    "golem.srv.comp.payload.cert.permissions";
 pub const DEMAND_MANIFEST_NODE_IDENTITY_PROPERTY: &str = "golem.node.identity";
 
 pub const AGREEMENT_MANIFEST_PROPERTY: &str = "demand.properties.golem.srv.comp.payload";
@@ -287,9 +285,11 @@ impl<'de> Deserialize<'de> for Command {
 #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
 #[derive(Clone, Copy, Debug, Serialize, Deserialize, EnumString, AsRefStr)]
 #[serde(rename_all = "camelCase")]
+#[derive(Default)]
 pub enum ArgMatch {
     /// Byte-to-byte argument equality (default).
     #[strum(ascii_case_insensitive)]
+    #[default]
     Strict,
     /// Treat argument as regular expression.
     /// Syntax: Perl-compatible regular expressions (UTF-8 Unicode mode),
@@ -298,12 +298,6 @@ pub enum ArgMatch {
     Regex,
 }
 
-impl Default for ArgMatch {
-    fn default() -> Self {
-        ArgMatch::Strict
-    }
-}
-
 /// # Net
 /// Applies constraints to networking.
 /// Currently, outgoing requests to the public Internet network are covered.
diff --git a/utils/manifest-utils/src/matching/domain.rs b/utils/manifest-utils/src/matching/domain.rs
index 4f07422305..0f48d4122a 100644
--- a/utils/manifest-utils/src/matching/domain.rs
+++ b/utils/manifest-utils/src/matching/domain.rs
@@ -61,7 +61,7 @@ impl DomainPatterns {
             if let Some(parent) = path.parent() {
                 std::fs::create_dir_all(parent)?;
             }
-            std::fs::File::create(&path)?;
+            std::fs::File::create(path)?;
             let patterns = Self::default();
             patterns.save(path)?;
             Ok(patterns)
@@ -113,7 +113,7 @@ impl TryFrom<&DomainPatterns> for DomainsMatcher {
         }
         if !regex_patterns.is_empty() {
             let regex_patterns = regex_patterns.into_iter().collect::<Vec<String>>();
-            let regex_patterns = RegexSetBuilder::new(&regex_patterns)
+            let regex_patterns = RegexSetBuilder::new(regex_patterns)
                 .case_insensitive(true)
                 .ignore_whitespace(true)
                 .build()?;
diff --git a/utils/manifest-utils/src/policy.rs b/utils/manifest-utils/src/policy.rs
index 6fbb317445..c4fa987e40 100644
--- a/utils/manifest-utils/src/policy.rs
+++ b/utils/manifest-utils/src/policy.rs
@@ -1,27 +1,14 @@
-use anyhow::{anyhow, bail};
 use std::collections::{HashMap, HashSet};
 use std::fmt::Debug;
-use std::fs::File;
 use std::hash::Hash;
 use std::ops::Not;
-use std::path::{Path, PathBuf};
+
 use std::str::FromStr;
-use std::sync::{Arc, RwLock};
 
-use openssl::hash::MessageDigest;
-use openssl::pkey::{PKey, Public};
-use openssl::sign::Verifier;
-use openssl::x509::store::{X509Store, X509StoreBuilder};
-use openssl::x509::{X509ObjectRef, X509Ref, X509StoreContext, X509VerifyResult, X509};
 use serde::{Deserialize, Serialize};
 use structopt::StructOpt;
 use strum::{Display, EnumIter, EnumString, EnumVariantNames, IntoEnumIterator, VariantNames};
 
-use crate::golem_certificate::{verify_golem_certificate, GolemCertificate};
-use crate::util::{cert_to_id, format_permissions, CertBasicDataVisitor, X509Visitor};
-
-pub(crate) const PERMISSIONS_FILE: &str = "cert-permissions.json";
-
 /// Policy configuration
 #[derive(StructOpt, Clone, Debug, Default)]
 pub struct PolicyConfig {
@@ -122,354 +109,6 @@ pub enum CertPermissions {
     UnverifiedPermissionsChain,
 }
 
-#[derive(Clone, Default)]
-pub struct PermissionsManager {
-    permissions: HashMap<String, Vec<CertPermissions>>,
-}
-
-pub struct CertStore {
-    store: X509Store,
-    permissions: PermissionsManager,
-}
-
-#[derive(Clone)]
-pub struct Keystore {
-    inner: Arc<RwLock<CertStore>>,
-}
-
-impl Default for Keystore {
-    fn default() -> Self {
-        let store = X509StoreBuilder::new().expect("SSL works").build();
-        Self {
-            inner: CertStore::new(store, Default::default()),
-        }
-    }
-}
-
-impl CertStore {
-    pub fn new(store: X509Store, permissions: PermissionsManager) -> Arc<RwLock<CertStore>> {
-        Arc::new(RwLock::new(CertStore { store, permissions }))
-    }
-}
-
-impl Keystore {
-    /// Reads DER or PEM certificates (or PEM certificate stacks) from `cert-dir` and creates new `X509Store`.
-    pub fn load(cert_dir: impl AsRef<Path>) -> anyhow::Result<Self> {
-        std::fs::create_dir_all(&cert_dir)?;
-        let permissions = PermissionsManager::load(&cert_dir).map_err(|e| {
-            anyhow!(
-                "Failed to load permissions file: {}, {e}",
-                cert_dir.as_ref().display()
-            )
-        })?;
-
-        let mut store = X509StoreBuilder::new()?;
-        let cert_dir = std::fs::read_dir(cert_dir)?;
-        for dir_entry in cert_dir {
-            let cert = dir_entry?.path();
-            if let Err(e) = Self::load_file(&mut store, &cert) {
-                log::debug!(
-                    "Skipping '{}' while loading a keystore. Error: {e}",
-                    cert.display()
-                );
-            }
-        }
-        let store = CertStore::new(store.build(), permissions);
-        Ok(Keystore { inner: store })
-    }
-
-    pub fn reload(&self, cert_dir: impl AsRef<Path>) -> anyhow::Result<()> {
-        let keystore = Keystore::load(&cert_dir)?;
-        self.replace(keystore);
-        Ok(())
-    }
-
-    fn replace(&self, other: Keystore) {
-        let store = {
-            let mut inner = other.inner.write().unwrap();
-            std::mem::replace(
-                &mut (*inner),
-                CertStore {
-                    store: X509StoreBuilder::new().unwrap().build(),
-                    permissions: Default::default(),
-                },
-            )
-        };
-        let mut inner = self.inner.write().unwrap();
-        *inner = store;
-    }
-
-    /// Decodes byte64 `sig`, verifies `cert`and reads its pub key,
-    /// prepares digest using `sig_alg`, verifies `data` using `sig` and pub key.
-    pub fn verify_signature(
-        &self,
-        cert: impl AsRef<str>,
-        sig: impl AsRef<str>,
-        sig_alg: impl AsRef<str>,
-        data: impl AsRef<str>,
-    ) -> anyhow::Result<()> {
-        let sig = crate::decode_data(sig)?;
-
-        let pkey = self.verify_cert(cert)?;
-
-        let msg_digest = MessageDigest::from_name(sig_alg.as_ref())
-            .ok_or_else(|| anyhow::anyhow!("Unknown signature algorithm: {}", sig_alg.as_ref()))?;
-        let mut verifier = Verifier::new(msg_digest, pkey.as_ref())?;
-        if !(verifier.verify_oneshot(&sig, data.as_ref().as_bytes())?) {
-            return Err(anyhow::anyhow!("Invalid signature"));
-        }
-        Ok(())
-    }
-
-    pub fn verify_golem_certificate(&self, cert: &str) -> anyhow::Result<GolemCertificate> {
-        verify_golem_certificate(cert)
-            .map_err(|e| anyhow!("verification of golem certificate failed: {e}"))
-    }
-
-    pub fn certs_ids(&self) -> anyhow::Result<HashSet<String>> {
-        let inner = self.inner.read().unwrap();
-        let mut ids = HashSet::new();
-        for cert in inner.store.objects() {
-            if let Some(cert) = cert.x509() {
-                let id = crate::util::cert_to_id(cert)?;
-                ids.insert(id);
-            }
-        }
-
-        //TODO it will be deleted when keystore will handle golem certs properly
-        ids.insert("all".into());
-        ids.insert("outbound".into());
-        ids.insert("expired".into());
-        ids.insert("invalid-signature".into());
-        ids.insert("outbound-urls".into());
-        ids.insert("no-permissions".into());
-
-        Ok(ids)
-    }
-
-    pub(crate) fn visit_certs<T: CertBasicDataVisitor>(
-        &self,
-        visitor: &mut X509Visitor<T>,
-    ) -> anyhow::Result<()> {
-        let inner = self.inner.read().unwrap();
-        for cert in inner.store.objects().iter().flat_map(X509ObjectRef::x509) {
-            visitor.accept(cert, &inner.permissions)?;
-        }
-        Ok(())
-    }
-
-    fn load_file(store: &mut X509StoreBuilder, cert: &PathBuf) -> anyhow::Result<()> {
-        for cert in crate::util::parse_cert_file(cert)? {
-            store.add_cert(cert)?
-        }
-        Ok(())
-    }
-
-    fn verify_cert<S: AsRef<str>>(&self, cert: S) -> anyhow::Result<PKey<Public>> {
-        let cert_chain = Self::decode_cert_chain(cert)?;
-        let store = self
-            .inner
-            .read()
-            .map_err(|err| anyhow::anyhow!("Err: {}", err.to_string()))?;
-        let cert = match cert_chain.last().map(Clone::clone) {
-            Some(cert) => cert,
-            None => bail!("Unable to verify certificate. No certificate."),
-        };
-        let mut cert_stack = openssl::stack::Stack::new()?;
-        for cert in cert_chain {
-            cert_stack.push(cert).unwrap();
-        }
-        let mut ctx = X509StoreContext::new()?;
-        if !(ctx.init(&store.store, &cert, &cert_stack, |ctx| ctx.verify_cert())?) {
-            bail!("Invalid certificate");
-        }
-        Ok(cert.public_key()?)
-    }
-
-    pub fn verify_permissions<S: AsRef<str>>(
-        &self,
-        cert: S,
-        required: Vec<CertPermissions>,
-    ) -> anyhow::Result<()> {
-        if required.contains(&CertPermissions::All) {
-            bail!("`All` permissions shouldn't be required.")
-        }
-
-        if required.is_empty() {
-            return Ok(());
-        }
-
-        let cert_chain = Self::decode_cert_chain(cert)?;
-        // Demands do not contain certificates permissions
-        // so only first certificate in chain signer permissions are verified.
-        let cert = match cert_chain.first() {
-            Some(cert) => cert,
-            None => bail!("Unable to verify certificate permissions. No certificate."),
-        };
-        let issuer = self.find_issuer(cert)?;
-
-        self.has_permissions(&issuer, &required)
-    }
-
-    fn get_permissions(&self, cert: &X509Ref) -> anyhow::Result<Vec<CertPermissions>> {
-        let store = self
-            .inner
-            .read()
-            .map_err(|err| anyhow::anyhow!("RwLock error: {}", err.to_string()))?;
-        Ok(store.permissions.get(cert))
-    }
-
-    fn has_permissions(
-        &self,
-        cert: &X509Ref,
-        required: &Vec<CertPermissions>,
-    ) -> anyhow::Result<()> {
-        let cert_permissions = self.get_permissions(cert)?;
-
-        if cert_permissions.contains(&CertPermissions::All)
-            && (!required.contains(&CertPermissions::UnverifiedPermissionsChain)
-                || (cert_permissions.contains(&CertPermissions::UnverifiedPermissionsChain)
-                    && required.contains(&CertPermissions::UnverifiedPermissionsChain)))
-        {
-            return Ok(());
-        }
-
-        if required
-            .iter()
-            .all(|permission| cert_permissions.contains(permission))
-        {
-            return Ok(());
-        }
-
-        bail!(
-            "Not sufficient permissions. Required: `{}`, but has only: `{}`",
-            format_permissions(required),
-            format_permissions(&cert_permissions)
-        )
-    }
-
-    fn find_issuer(&self, cert: &X509) -> anyhow::Result<X509> {
-        let store = self
-            .inner
-            .read()
-            .map_err(|err| anyhow::anyhow!("RwLock error: {}", err.to_string()))?;
-        store
-            .store
-            .objects()
-            .iter()
-            .filter_map(|cert| cert.x509())
-            .map(|cert| cert.to_owned())
-            .find(|trusted| trusted.issued(cert) == X509VerifyResult::OK)
-            .ok_or_else(|| anyhow!("Issuer certificate not found in keystore"))
-    }
-
-    fn decode_cert_chain<S: AsRef<str>>(cert: S) -> anyhow::Result<Vec<X509>> {
-        let cert = crate::decode_data(cert)?;
-        Ok(match X509::from_der(&cert) {
-            Ok(cert) => vec![cert],
-            Err(_) => X509::stack_from_pem(&cert)?,
-        })
-    }
-
-    pub fn permissions_manager(&self) -> PermissionsManager {
-        self.inner.read().unwrap().permissions.clone()
-    }
-}
-
-impl PermissionsManager {
-    pub fn load(cert_dir: impl AsRef<Path>) -> anyhow::Result<PermissionsManager> {
-        let path = cert_dir.as_ref().join(PERMISSIONS_FILE);
-        let content = match std::fs::read_to_string(path) {
-            Ok(content) if !content.is_empty() => content,
-            _ => return Ok(Default::default()),
-        };
-        let permissions = serde_json::from_str(&content)?;
-        Ok(PermissionsManager { permissions })
-    }
-
-    pub fn set(&mut self, cert: &str, mut permissions: Vec<CertPermissions>) {
-        if permissions.contains(&CertPermissions::All) {
-            let supports_unverified_permissions =
-                permissions.contains(&CertPermissions::UnverifiedPermissionsChain);
-            permissions.clear();
-            permissions.push(CertPermissions::All);
-            if supports_unverified_permissions {
-                permissions.push(CertPermissions::UnverifiedPermissionsChain);
-            }
-        }
-        self.permissions.insert(cert.to_string(), permissions);
-    }
-
-    pub fn set_x509(
-        &mut self,
-        cert: &X509,
-        permissions: Vec<CertPermissions>,
-    ) -> anyhow::Result<()> {
-        let id = cert_to_id(cert)?;
-        self.set(&id, permissions);
-        Ok(())
-    }
-
-    pub fn set_many(
-        &mut self,
-        // With slice I would need add `openssl` dependency directly to ya-rovider.
-        #[allow(clippy::ptr_arg)] certs: &Vec<X509>,
-        permissions: Vec<CertPermissions>,
-        whole_chain: bool,
-    ) {
-        let certs = match whole_chain {
-            false => Self::leaf_certs(certs),
-            true => certs.clone(),
-        };
-
-        for cert in certs {
-            if let Err(e) = self.set_x509(&cert, permissions.clone()) {
-                log::error!(
-                    "Failed to set permissions for certificate {:?}. {e}",
-                    cert.subject_name()
-                );
-            }
-        }
-    }
-
-    /// If we don't have this certificate registered, it means it has no permissions,
-    /// so empty vector is returned.
-    pub fn get(&self, cert: &X509Ref) -> Vec<CertPermissions> {
-        let id = match cert_to_id(cert) {
-            Ok(id) => id,
-            Err(_) => return vec![],
-        };
-        self.permissions.get(&id).cloned().unwrap_or_default()
-    }
-
-    pub fn save(&self, path: &Path) -> anyhow::Result<()> {
-        let mut file = File::create(&path.join(PERMISSIONS_FILE))?;
-        Ok(serde_json::to_writer_pretty(&mut file, &self.permissions)?)
-    }
-
-    fn leaf_certs(certs: &[X509]) -> Vec<X509> {
-        if certs.len() == 1 {
-            // when there is 1 cert it is a leaf cert
-            return certs.to_vec();
-        }
-        certs
-            .iter()
-            .cloned()
-            .filter(|cert| {
-                !certs
-                    .iter()
-                    .any(|cert2| cert.issued(cert2) == X509VerifyResult::OK)
-            })
-            .collect()
-    }
-}
-
-impl std::fmt::Debug for Keystore {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "Keystore")
-    }
-}
-
 fn parse_property_match(input: &str) -> anyhow::Result<(String, Match)> {
     let mut split = input.splitn(2, '=');
     let property = split
diff --git a/utils/manifest-utils/src/util.rs b/utils/manifest-utils/src/util.rs
index 3243e6950b..1862dacbb7 100644
--- a/utils/manifest-utils/src/util.rs
+++ b/utils/manifest-utils/src/util.rs
@@ -1,17 +1,6 @@
+use crate::policy::CertPermissions;
 use itertools::Itertools;
-use std::collections::{BTreeMap, HashMap, HashSet};
-use std::convert::TryFrom;
-use std::ffi::OsStr;
-use std::fs::{self, DirEntry, File};
-use std::path::{Path, PathBuf};
-
 use md5::{Digest, Md5};
-use openssl::nid::Nid;
-use openssl::x509::{X509Ref, X509};
-use std::io::prelude::*;
-
-use crate::policy::{CertPermissions, PermissionsManager};
-use crate::Keystore;
 
 /// Tries do decode base64. On failure tries to unescape snailquotes.
 pub fn decode_data<S: AsRef<str>>(input: S) -> Result<Vec<u8>, DecodingError> {
@@ -30,136 +19,6 @@ pub enum DecodingError {
     BlobJsonString(#[from] snailquote::UnescapeError),
 }
 
-pub fn parse_cert_file(cert: &PathBuf) -> anyhow::Result<Vec<X509>> {
-    let extension = get_file_extension(cert);
-    let mut cert = File::open(cert)?;
-    let mut cert_buffer = Vec::new();
-    cert.read_to_end(&mut cert_buffer)?;
-    match extension {
-        Some(ref der) if der.to_lowercase() == "der" => Ok(vec![X509::from_der(&cert_buffer)?]),
-        Some(ref pem) if pem.to_lowercase() == "pem" => Ok(X509::stack_from_pem(&cert_buffer)?),
-        _ => {
-            // Certificates can have various other extensions like .cer .crt .key (and .key can be both DER and PEM)
-            // Initial parsing dictated by `extension` is done because it would improper to parse .pem as a DER
-            Ok(X509::stack_from_pem(&cert_buffer)
-                .or_else(|_| X509::from_der(&cert_buffer).map(|cert| vec![cert]))?)
-        }
-    }
-}
-
-fn get_file_extension(path: &Path) -> Option<String> {
-    path.extension().map(os_str_to_string)
-}
-
-fn get_file_name(path: &Path) -> Option<String> {
-    path.file_name().map(os_str_to_string)
-}
-
-fn get_file_stem(path: &Path) -> Option<String> {
-    path.file_stem().map(os_str_to_string)
-}
-
-fn os_str_to_string(os_str: &OsStr) -> String {
-    os_str.to_string_lossy().to_string()
-}
-
-pub fn to_cert_data(
-    certs: &Vec<X509>,
-    permissions: &PermissionsManager,
-) -> anyhow::Result<Vec<CertBasicData>> {
-    let mut certs_data = Vec::new();
-    for cert in certs {
-        let data = CertBasicData::create(cert.as_ref(), permissions)?;
-        certs_data.push(data);
-    }
-    Ok(certs_data)
-}
-
-/// Adds entries with given `nid` to given `subject` String.
-fn add_cert_subject_entries(
-    subject: &mut BTreeMap<String, String>,
-    cert: &X509Ref,
-    nid: Nid,
-    entry_short_name: &str,
-) {
-    if let Some(entries) = cert_subject_entries(cert, nid) {
-        subject.insert(entry_short_name.to_string(), entries);
-    }
-}
-
-/// Reads subject entries and returns them as comma separated `String`.
-fn cert_subject_entries(cert: &X509Ref, nid: Nid) -> Option<String> {
-    let entries =
-        cert.subject_name()
-            .entries_by_nid(nid)
-            .fold(String::from(""), |mut names, name| {
-                if !names.is_empty() {
-                    names.push_str(", ");
-                }
-                let name = String::from_utf8_lossy(name.data().as_slice());
-                names.push_str(&name);
-                names
-            });
-    if entries.is_empty() {
-        return None;
-    }
-    Some(entries)
-}
-
-pub fn cert_to_id(cert: &X509Ref) -> anyhow::Result<String> {
-    let txt = cert.to_text()?;
-    Ok(str_to_short_hash(&txt))
-}
-
-pub fn visit_certificates<T: CertBasicDataVisitor>(
-    cert_dir: &PathBuf,
-    visitor: T,
-) -> anyhow::Result<T> {
-    let keystore = Keystore::load(cert_dir)?;
-    let mut visitor = X509Visitor { visitor };
-    keystore.visit_certs(&mut visitor)?;
-    Ok(visitor.visitor)
-}
-
-pub struct CertBasicData {
-    pub id: String,
-    pub not_after: String,
-    pub subject: BTreeMap<String, String>,
-    pub permissions: String,
-}
-
-impl TryFrom<&X509Ref> for CertBasicData {
-    type Error = anyhow::Error;
-
-    fn try_from(cert: &X509Ref) -> Result<Self, Self::Error> {
-        let id = cert_to_id(cert)?;
-        let not_after = cert.not_after().to_string();
-        let mut subject = BTreeMap::new();
-        add_cert_subject_entries(&mut subject, cert, Nid::COMMONNAME, "CN");
-        add_cert_subject_entries(&mut subject, cert, Nid::PKCS9_EMAILADDRESS, "E");
-        add_cert_subject_entries(&mut subject, cert, Nid::ORGANIZATIONNAME, "O");
-        add_cert_subject_entries(&mut subject, cert, Nid::ORGANIZATIONALUNITNAME, "OU");
-        add_cert_subject_entries(&mut subject, cert, Nid::COUNTRYNAME, "C");
-        add_cert_subject_entries(&mut subject, cert, Nid::STATEORPROVINCENAME, "ST");
-
-        Ok(CertBasicData {
-            id,
-            not_after,
-            subject,
-            permissions: "".to_string(),
-        })
-    }
-}
-
-impl CertBasicData {
-    pub fn create(cert: &X509Ref, permissions: &PermissionsManager) -> anyhow::Result<Self> {
-        let mut data = CertBasicData::try_from(cert)?;
-        let permissions = permissions.get(cert);
-        data.permissions = format_permissions(&permissions);
-        Ok(data)
-    }
-}
-
 pub fn format_permissions(permissions: &Vec<CertPermissions>) -> String {
     if permissions.is_empty() {
         "none".to_string()
@@ -168,186 +27,6 @@ pub fn format_permissions(permissions: &Vec<CertPermissions>) -> String {
     }
 }
 
-pub trait CertBasicDataVisitor {
-    fn accept(&mut self, cert_data: CertBasicData);
-}
-
-pub(crate) struct X509Visitor<T: CertBasicDataVisitor> {
-    visitor: T,
-}
-
-impl<T: CertBasicDataVisitor> X509Visitor<T> {
-    pub(crate) fn accept(
-        &mut self,
-        cert: &X509Ref,
-        permissions: &PermissionsManager,
-    ) -> anyhow::Result<()> {
-        let cert_data = CertBasicData::create(cert, permissions)?;
-        self.visitor.accept(cert_data);
-        Ok(())
-    }
-}
-
-pub struct KeystoreManager {
-    keystore: Keystore,
-    ids: HashSet<String>,
-    cert_dir: PathBuf,
-}
-
-impl KeystoreManager {
-    pub fn try_new(cert_dir: &PathBuf) -> anyhow::Result<Self> {
-        let keystore = Keystore::load(cert_dir)?;
-        let ids = keystore.certs_ids()?;
-        let cert_dir = cert_dir.clone();
-        Ok(Self {
-            ids,
-            cert_dir,
-            keystore,
-        })
-    }
-
-    pub fn permissions_manager(&self) -> PermissionsManager {
-        self.keystore.permissions_manager()
-    }
-
-    /// Copies certificates from given file to `cert-dir` and returns newly added certificates.
-    /// Certificates already existing in `cert-dir` are skipped.
-    pub fn load_certs(self, cert_paths: &Vec<PathBuf>) -> anyhow::Result<KeystoreLoadResult> {
-        let mut loaded = HashMap::new();
-        let mut skipped = HashMap::new();
-
-        for cert_path in cert_paths {
-            let mut new_certs = Vec::new();
-            let file_certs = parse_cert_file(cert_path)?;
-            if file_certs.is_empty() {
-                continue;
-            }
-            let file_certs_len = file_certs.len();
-            for file_cert in file_certs {
-                let id = cert_to_id(&file_cert)?;
-                if !self.ids.contains(&id) && !loaded.contains_key(&id) {
-                    new_certs.push(file_cert.clone());
-                    loaded.insert(id, file_cert);
-                } else {
-                    skipped.insert(id, file_cert);
-                }
-            }
-            if file_certs_len == new_certs.len() {
-                self.load_as_keychain_file(cert_path)?;
-            } else {
-                self.load_as_certificate_files(cert_path, new_certs)?;
-            }
-        }
-
-        Ok(KeystoreLoadResult {
-            loaded: loaded.into_values().collect(),
-            skipped: skipped.into_values().collect(),
-        })
-    }
-
-    pub fn remove_certs(self, ids: &HashSet<String>) -> anyhow::Result<KeystoreRemoveResult> {
-        if ids.difference(&self.ids).eq(ids) {
-            return Ok(KeystoreRemoveResult::NothingToRemove);
-        }
-        let mut removed = HashMap::new();
-
-        let cert_dir_entries: Vec<Result<DirEntry, std::io::Error>> =
-            std::fs::read_dir(self.cert_dir.clone())?.collect();
-        for dir_entry in cert_dir_entries {
-            let cert_file = dir_entry?;
-            let cert_file = cert_file.path();
-            let certs = parse_cert_file(&cert_file)?;
-            if certs.is_empty() {
-                // No certificates in parsed file.
-                continue;
-            }
-            let mut ids_cert = certs.into_iter().fold(HashMap::new(), |mut certs, cert| {
-                if let Ok(id) = cert_to_id(&cert) {
-                    certs.insert(id, cert);
-                }
-                certs
-            });
-            let mut split_and_skip = false;
-            for id in ids.iter() {
-                if let Some(cert) = ids_cert.remove(id) {
-                    removed.insert(id, cert);
-                    split_and_skip = true;
-                }
-            }
-            if split_and_skip {
-                let file_stem = get_file_stem(&cert_file).expect("Cannot get file name stem");
-                let dot_extension = get_file_extension(&cert_file)
-                    .map_or_else(|| String::from(""), |ex| format!(".{ex}"));
-                for (id, cert) in ids_cert {
-                    let cert = cert.to_pem()?;
-                    let mut file_path = self.cert_dir.clone();
-                    let filename = format!("{file_stem}.{id}{dot_extension}");
-                    file_path.push(filename);
-                    fs::write(file_path, cert)?;
-                }
-                fs::remove_file(cert_file)?;
-            }
-        }
-
-        let removed: Vec<X509> = removed.into_values().collect();
-        Ok(KeystoreRemoveResult::Removed { removed })
-    }
-
-    /// Loads keychain file to `cert-dir`
-    fn load_as_keychain_file(&self, cert_path: &PathBuf) -> anyhow::Result<()> {
-        let file_name = get_file_name(cert_path)
-            .ok_or_else(|| anyhow::anyhow!(format!("Cannot get filename of {cert_path:?}")))?;
-        let mut new_cert_path = self.cert_dir.clone();
-        new_cert_path.push(file_name);
-        if new_cert_path.exists() {
-            let file_stem = get_file_stem(&new_cert_path).expect("Has to have stem");
-            let dot_extension = get_file_extension(&new_cert_path)
-                .map(|ex| format!(".{ex}"))
-                .unwrap_or_else(|| String::from(""));
-            for i in 0..u32::MAX {
-                let numbered_filename = format!("{file_stem}.{i}{dot_extension}");
-                new_cert_path = self.cert_dir.clone();
-                new_cert_path.push(numbered_filename);
-                if !new_cert_path.exists() {
-                    break;
-                }
-            }
-            if new_cert_path.exists() {
-                anyhow::bail!("Unable to load certificate");
-            }
-        }
-        fs::copy(cert_path, new_cert_path)?;
-        Ok(())
-    }
-
-    /// Loads certificates as individual files to `cert-dir`
-    fn load_as_certificate_files(&self, cert_path: &Path, certs: Vec<X509>) -> anyhow::Result<()> {
-        let file_stem = get_file_stem(cert_path)
-            .ok_or_else(|| anyhow::anyhow!("Cannot get file name stem."))?;
-        let dot_extension = get_file_extension(cert_path)
-            .map(|ex| format!(".{ex}"))
-            .unwrap_or_else(|| String::from(""));
-        for cert in certs.into_iter() {
-            let id = cert_to_id(&cert)?;
-            let mut new_cert_path = self.cert_dir.clone();
-            new_cert_path.push(format!("{file_stem}.{id}{dot_extension}"));
-            let cert = cert.to_pem()?;
-            fs::write(new_cert_path, cert)?;
-        }
-        Ok(())
-    }
-}
-
-pub struct KeystoreLoadResult {
-    pub loaded: Vec<X509>,
-    pub skipped: Vec<X509>,
-}
-
-pub enum KeystoreRemoveResult {
-    NothingToRemove,
-    Removed { removed: Vec<X509> },
-}
-
 /// Calculates Md5 of `txt` and returns first 8 characters.
 pub fn str_to_short_hash(txt: impl AsRef<[u8]>) -> String {
     let digest = Md5::digest(txt);


Test patch:
diff --git a/agent/provider/tests/keystore_cli.rs b/agent/provider/tests/keystore_cli.rs
index 5413cb561d..312a61ebd8 100644
--- a/agent/provider/tests/keystore_cli.rs
+++ b/agent/provider/tests/keystore_cli.rs
@@ -146,6 +146,7 @@ fn test_keystore_list_cmd_creates_cert_dir_in_dir_set_by_arg() {
     "Add multiple certificates and check permissions"
 )]
 #[serial]
+#[ignore] //TODO delete it together with X509 permissions
 fn test_keystore_add_certificate_permissions(
     certificates: Vec<&str>,
     permissions: Vec<&str>,
@@ -218,8 +219,13 @@ fn test_keystore_add_certificate_second_time() {
 
     let result = list_certificates_command(&cert_dir).unwrap();
 
+    assert!(result.contains_key("4e0df976"));
+    assert!(result.contains_key("c128af8c"));
+    //TODO update it when rules permissions are figured out
+    /*
     assert_eq!(check_permissions(&result, "4e0df976"), "none");
     assert_eq!(check_permissions(&result, "c128af8c"), "none");
+    */
 }
 
 #[serial]
@@ -254,8 +260,13 @@ fn test_keystore_remove_certificate_check_permissions() {
 
     let result = list_certificates_command(&cert_dir).unwrap();
 
+    assert!(result.contains_key("4e0df976"));
+    assert!(result.contains_key("c128af8c"));
+    //TODO update it when rules/permissions are figured out
+    /*
     assert_eq!(check_permissions(&result, "4e0df976"), "outbound-manifest");
     assert_eq!(check_permissions(&result, "c128af8c"), "outbound-manifest");
+     */
 }
 
 fn list_certificates_command(
diff --git a/agent/provider/tests/manifest_negotiator.rs b/agent/provider/tests/manifest_negotiator.rs
index b99b9358cb..d1b1f8069c 100644
--- a/agent/provider/tests/manifest_negotiator.rs
+++ b/agent/provider/tests/manifest_negotiator.rs
@@ -331,6 +331,7 @@ fn manifest_negotiator_test_with_valid_payload_signature(
     "Rejected because requestor node id doesn't match with node_json id"
 )]
 #[serial]
+#[ignore = "Update them when permissions/rules are figured out"]
 fn manifest_negotiator_test_with_node_identity(
     partner_rule: &str,
     urls: &str,
@@ -470,6 +471,7 @@ fn manifest_negotiator_test_with_invalid_payload_signature(
     "Manifest rejected, even when permissions are set to `All` because `unverified-permissions-chain` permission is also required when Demand comes with its permissions"
 )]
 #[serial]
+#[ignore = "update when permissions/rules are figured out"]
 fn test_manifest_negotiator_certs_permissions(
     signature: Signature,
     cert_permissions_b64: Option<&str>,
diff --git a/utils/manifest-utils/test-utils/src/lib.rs b/utils/manifest-utils/test-utils/src/lib.rs
index 29ecfdb46c..33d775ce1b 100644
--- a/utils/manifest-utils/test-utils/src/lib.rs
+++ b/utils/manifest-utils/test-utils/src/lib.rs
@@ -1,19 +1,15 @@
-use std::fs::{self, File};
-use std::str;
-use std::sync::Once;
-use std::{collections::HashSet, path::PathBuf};
-
 use openssl::hash::MessageDigest;
 use openssl::pkey::PKey;
 use openssl::rsa::Rsa;
 use openssl::sign::Signer;
+use std::fs::{self, File};
+use std::str;
+use std::sync::Once;
+use std::{collections::HashSet, path::PathBuf};
 use tar::Archive;
-
+use ya_manifest_utils::keystore::{AddParams, AddResponse, Keystore, RemoveParams, RemoveResponse};
 use ya_manifest_utils::policy::CertPermissions;
-use ya_manifest_utils::{
-    util::{self, CertBasicData, CertBasicDataVisitor},
-    KeystoreLoadResult, KeystoreRemoveResult,
-};
+use ya_manifest_utils::CompositeKeystore;
 
 static INIT: Once = Once::new();
 
@@ -22,9 +18,9 @@ pub fn load_certificates_from_dir(
     resource_cert_dir: &PathBuf,
     test_cert_dir: &PathBuf,
     certfile_names: &[&str],
-    certs_permissions: &Vec<CertPermissions>,
-) -> KeystoreLoadResult {
-    let cert_paths: Vec<PathBuf> = certfile_names
+    cert_permissions: &Vec<CertPermissions>,
+) -> AddResponse {
+    let certs: Vec<PathBuf> = certfile_names
         .iter()
         .map(|certfile_name| {
             let mut cert_path = resource_cert_dir.clone();
@@ -32,58 +28,26 @@ pub fn load_certificates_from_dir(
             cert_path
         })
         .collect();
-    let keystore_manager =
-        util::KeystoreManager::try_new(test_cert_dir).expect("Can create keystore manager");
-    let mut permissions = keystore_manager.permissions_manager();
-
-    let certs = keystore_manager
-        .load_certs(&cert_paths)
-        .expect("Can load certificates");
-
-    permissions.set_many(&certs.loaded, certs_permissions.clone(), false);
-    permissions.set_many(&certs.skipped, certs_permissions.clone(), false);
-    permissions
-        .save(test_cert_dir)
-        .expect("Should be able to save permissions file.");
-
+    let mut keystore = CompositeKeystore::load(test_cert_dir).expect("Can create keystore manager");
+
+    let add_params = AddParams {
+        certs,
+        permissions: cert_permissions.clone(),
+        whole_chain: false,
+    };
+    let certs = keystore.add(&add_params).expect("Can load certificates");
     certs
 }
 
-pub fn remove_certificates(test_cert_dir: &PathBuf, cert_ids: &[&str]) -> KeystoreRemoveResult {
-    let keystore_manager =
-        util::KeystoreManager::try_new(test_cert_dir).expect("Can create keystore manager");
-    keystore_manager
-        .remove_certs(&slice_to_set(cert_ids))
+pub fn remove_certificates(test_cert_dir: &PathBuf, cert_ids: &[&str]) -> RemoveResponse {
+    let mut keystore = CompositeKeystore::load(test_cert_dir).expect("Can create keystore manager");
+    keystore
+        .remove(&RemoveParams {
+            ids: slice_to_set(cert_ids),
+        })
         .expect("Can load certificates")
 }
 
-#[derive(Default)]
-pub struct TestCertDataVisitor {
-    expected: HashSet<String>,
-    actual: HashSet<String>,
-}
-
-impl TestCertDataVisitor {
-    #[allow(dead_code)]
-    pub fn new(expected: &[&str]) -> Self {
-        Self {
-            expected: expected.iter().map(|s| s.to_string()).collect(),
-            ..Default::default()
-        }
-    }
-
-    #[allow(dead_code)]
-    pub fn test(&self) {
-        assert_eq!(self.expected, self.actual)
-    }
-}
-
-impl CertBasicDataVisitor for TestCertDataVisitor {
-    fn accept(&mut self, cert_data: CertBasicData) {
-        self.actual.insert(cert_data.id);
-    }
-}
-
 pub struct TestResources {
     pub temp_dir: &'static str,
 }
diff --git a/utils/manifest-utils/tests/certificate_expiration.rs b/utils/manifest-utils/tests/certificate_expiration.rs
index 85212edb9a..a9453a8d0e 100644
--- a/utils/manifest-utils/tests/certificate_expiration.rs
+++ b/utils/manifest-utils/tests/certificate_expiration.rs
@@ -1,6 +1,5 @@
 use utils::*;
-
-use ya_manifest_utils::Keystore;
+use ya_manifest_utils::keystore::x509_keystore::X509Keystore;
 
 #[test]
 fn accept_not_expired_certificate() {
@@ -22,7 +21,7 @@ fn accept_not_expired_certificate() {
         &test_cert_dir.path().join("self_signed.pem"),
     );
 
-    let sut = Keystore::load(&test_cert_dir).unwrap();
+    let sut = X509Keystore::load(&test_cert_dir).unwrap();
 
     let (req, csr_key_pair) = create_csr().unwrap();
     let signed_cert = sign_csr(req, csr_key_pair.clone(), &self_signed_cert, ca_key_pair).unwrap();
@@ -61,7 +60,7 @@ fn not_accept_expired_certificate() {
         &test_cert_dir.path().join("self_signed.pem"),
     );
 
-    let sut = Keystore::load(&test_cert_dir).unwrap();
+    let sut = X509Keystore::load(&test_cert_dir).unwrap();
 
     let (req, csr_key_pair) = create_csr().unwrap();
     let signed_cert = sign_csr(req, csr_key_pair.clone(), &self_signed_cert, ca_key_pair).unwrap();
@@ -100,7 +99,7 @@ fn not_accept_not_ready_certificate() {
         &test_cert_dir.path().join("self_signed.pem"),
     );
 
-    let sut = Keystore::load(&test_cert_dir).unwrap();
+    let sut = X509Keystore::load(&test_cert_dir).unwrap();
 
     let (req, csr_key_pair) = create_csr().unwrap();
     let signed_cert = sign_csr(req, csr_key_pair.clone(), &self_signed_cert, ca_key_pair).unwrap();
@@ -149,7 +148,7 @@ mod utils {
     }
 
     pub fn write_cert_to_file(cert: &X509Ref, file_path: &Path) {
-        let mut file = File::create(&file_path).unwrap();
+        let mut file = File::create(file_path).unwrap();
         file.write_all(&cert.to_pem().unwrap()).unwrap();
     }
 
diff --git a/utils/manifest-utils/tests/certificate_store.rs b/utils/manifest-utils/tests/certificate_store.rs
index f4e3c06ced..badfb14e74 100644
--- a/utils/manifest-utils/tests/certificate_store.rs
+++ b/utils/manifest-utils/tests/certificate_store.rs
@@ -1,13 +1,10 @@
 #[macro_use]
 extern crate serial_test;
 
-use std::fs;
-
+use std::{collections::HashSet, fs};
 use test_case::test_case;
-
 use ya_manifest_test_utils::*;
-use ya_manifest_utils::policy::CertPermissions;
-use ya_manifest_utils::util::visit_certificates;
+use ya_manifest_utils::{keystore::Keystore, policy::CertPermissions, CompositeKeystore};
 
 static TEST_RESOURCES: TestResources = TestResources {
     temp_dir: env!("CARGO_TARGET_TMPDIR"),
@@ -79,11 +76,19 @@ fn certificate_store_test(
         &vec![CertPermissions::All],
     );
     remove_certificates(&test_cert_dir, ids_to_remove);
-    let mut visitor = TestCertDataVisitor::new(expected_ids);
     // When
-    visitor = visit_certificates(&test_cert_dir, visitor).expect("Can visit loaded certificates");
+    let keystore = CompositeKeystore::load(&test_cert_dir).expect("Can laod keystore");
+    let loaded_ids = keystore
+        .list()
+        .into_iter()
+        .map(|c| c.id())
+        .collect::<HashSet<String>>();
     // Then
-    visitor.test();
+    let expected_ids = expected_ids
+        .iter()
+        .map(|id| id.to_string())
+        .collect::<HashSet<String>>();
+    assert_eq!(expected_ids, loaded_ids);
     let certs = TEST_RESOURCES.loaded_cert_files();
     assert_eq!(certs, slice_to_set(expected_files));
 }
@@ -113,11 +118,17 @@ fn certificate_name_collision_test() {
         &[colliding_name, &format!("copy/{colliding_name}")],
         &vec![CertPermissions::All],
     );
-    let mut visitor = TestCertDataVisitor::new(&["4e0df976", "0e136cb3"]);
+
+    let expected_ids: HashSet<String> = HashSet::from(["4e0df976".into(), "0e136cb3".into()]);
     // When
-    visitor = visit_certificates(&test_cert_dir, visitor).expect("Can visit loaded certificates");
+    let keystore = CompositeKeystore::load(&test_cert_dir).expect("Can laod keystore");
+    let loaded_ids = keystore
+        .list()
+        .into_iter()
+        .map(|c| c.id())
+        .collect::<HashSet<String>>();
     // Then
-    visitor.test();
+    assert_eq!(expected_ids, loaded_ids);
     let certs = TEST_RESOURCES.loaded_cert_files();
     assert_eq!(
         certs,
diff --git a/utils/manifest-utils/tests/certificate_validation.rs b/utils/manifest-utils/tests/certificate_validation.rs
index 71675df9ae..9cdb5fe187 100644
--- a/utils/manifest-utils/tests/certificate_validation.rs
+++ b/utils/manifest-utils/tests/certificate_validation.rs
@@ -4,8 +4,7 @@ extern crate serial_test;
 use std::{fs, path::PathBuf};
 
 use ya_manifest_test_utils::*;
-use ya_manifest_utils::policy::CertPermissions;
-use ya_manifest_utils::Keystore;
+use ya_manifest_utils::{keystore::x509_keystore::X509Keystore, policy::CertPermissions};
 
 static TEST_RESOURCES: TestResources = TestResources {
     temp_dir: env!("CARGO_TARGET_TMPDIR"),
@@ -26,7 +25,7 @@ fn valid_certificate_test() {
     let request = prepare_request(resource_cert_dir);
 
     // Then
-    let keystore = Keystore::load(&test_cert_dir).expect("Can load certificates");
+    let keystore = X509Keystore::load(&test_cert_dir).expect("Can load certificates");
     keystore
         .verify_signature(request.cert, request.sig, request.sig_alg, request.data)
         .expect("Signature and cert can be validated")
@@ -47,7 +46,7 @@ fn invalid_certificate_test() {
     let request = prepare_request(resource_cert_dir);
 
     // Then
-    let keystore = Keystore::load(&test_cert_dir).expect("Can load certificates");
+    let keystore = X509Keystore::load(&test_cert_dir).expect("Can load certificates");
     let result =
         keystore.verify_signature(request.cert, request.sig, request.sig_alg, request.data);
     assert!(