diff --git a/.lycheeignore b/.lycheeignore
index 26f8b06f527a14cb0f54ddd012ffa986fad0aebd..75cbd1325b4cdc47db91dd3b78d4df018fca3dde 100644
--- a/.lycheeignore
+++ b/.lycheeignore
@@ -3,3 +3,7 @@
 # an URL fragment that's used in format! macro
 # see: https://github.com/lycheeverse/lychee/issues/1492
 https://raw.githubusercontent.com/Nitrokey/nethsm-sdk-py/main/tests/%7B%7D
+
+# URLs that only become available after release of a component
+https://signstar.archlinux.page/rustdoc/signstar_common/
+https://docs.rs/signstar_common/latest/signstar_common/
diff --git a/Cargo.lock b/Cargo.lock
index fa094f4d97c2347f0641f80d00c8cfd368a6575c..d9cdcaef775aa0b025c6eab01f31406b06c0313a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3133,6 +3133,14 @@ dependencies = [
  "rand_core",
 ]
 
+[[package]]
+name = "signstar-common"
+version = "0.1.0"
+dependencies = [
+ "nethsm",
+ "thiserror 2.0.11",
+]
+
 [[package]]
 name = "signstar-configure-build"
 version = "0.1.2"
diff --git a/Cargo.toml b/Cargo.toml
index 54407d2a65690cf4cdba9a5135730c73a6c73411..6b970a364d09697fa3fdafbde79925457794a9fb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,6 +7,7 @@ members = [
   "nethsm-config",
   "nethsm-tests",
   "signstar-configure-build",
+  "signstar-common",
   "signstar-request-signature",
 ]
 
@@ -31,6 +32,7 @@ serde = { version = "1.0.215", features = ["derive"] }
 # Then adjust the tests in signstar-request-signature/src/lib.rs to additionally test
 # inputs that reference the "old_sha2".
 sha2 = "0.11.0-pre.4"
+signstar-common = { path = "signstar-common", version = "0.1.0" }
 signstar-request-signature = { path = "signstar-request-signature", version = "0.1.0" }
 strum = { version = "0.27.0", features = ["derive"] }
 testdir = "0.9.3"
diff --git a/signstar-common/Cargo.toml b/signstar-common/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..c07c559c0e42f1a7ce8baa561deffb5f98bdb8e5
--- /dev/null
+++ b/signstar-common/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+authors.workspace = true
+description = "Common components for Signstar libraries and command-line interfaces"
+edition.workspace = true
+homepage.workspace = true
+keywords = ["user", "signstar", "nethsm"]
+license.workspace = true
+name = "signstar-common"
+repository.workspace = true
+version = "0.1.0"
+
+[dependencies]
+nethsm.workspace = true
+thiserror.workspace = true
diff --git a/signstar-common/README.md b/signstar-common/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..9f04895b81e171a5d96eed5c187c2b2978f180d5
--- /dev/null
+++ b/signstar-common/README.md
@@ -0,0 +1,22 @@
+# Signstar common
+
+Shared components and data types for Signstar tools and libraries
+
+## Documentation
+
+- <https://signstar.archlinux.page/rustdoc/signstar_common/> for development version of the crate
+- <https://docs.rs/signstar_common/latest/signstar_common/> for released versions of the crate
+
+## Contributing
+
+Please refer to the [contributing guidelines] to learn how to contribute to this project.
+
+## License
+
+This project may be used under the terms of the [Apache-2.0] or [MIT] license.
+
+Changes to this project - unless stated otherwise - automatically fall under the terms of both of the aforementioned licenses.
+
+[Apache-2.0]: https://www.apache.org/licenses/LICENSE-2.0
+[MIT]: https://opensource.org/licenses/MIT
+[contributing guidelines]: ../CONTRIBUTING.md
diff --git a/signstar-common/src/admin_credentials.rs b/signstar-common/src/admin_credentials.rs
new file mode 100644
index 0000000000000000000000000000000000000000..eedc834e77bf5d494c801f53d8364631f6946cc2
--- /dev/null
+++ b/signstar-common/src/admin_credentials.rs
@@ -0,0 +1,113 @@
+//! Data and functions for using administrative credentials on a Signstar host.
+//!
+//! # Examples
+//!
+//! ```
+//! use signstar_common::admin_credentials::get_credentials_dir;
+//!
+//! // Get the directory path in which administrative credentials reside.
+//! println!("{:?}", get_credentials_dir());
+//! ```
+
+use std::{
+    fs::{Permissions, create_dir_all, set_permissions},
+    os::unix::fs::{PermissionsExt, chown},
+    path::PathBuf,
+};
+
+use crate::common::{CREDENTIALS_DIR_MODE, get_data_home};
+
+/// File name of plaintext administrative credentials.
+const PLAINTEXT_CREDENTIALS_FILE: &str = "admin-credentials.toml";
+
+/// File name of systemd-creds encrypted administrative credentials.
+const SYSTEMD_CREDS_CREDENTIALS_FILE: &str = "admin-credentials.creds";
+
+/// The directory for administrative credentials (encrypted and unencrypted).
+const CREDENTIALS_DIR: &str = "creds/";
+
+/// An error that may occur when handling credentials.
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+    /// Applying permissions to a file failed.
+    #[error("Unable to apply permissions {permissions} to {path}:\n{source}")]
+    ApplyPermissions {
+        permissions: u32,
+        path: PathBuf,
+        source: std::io::Error,
+    },
+
+    /// No plaintext administrative credentials file can be found
+    #[error("Unable to create directory {dir}:\n{source}")]
+    CreateDirectory {
+        dir: &'static str,
+        source: std::io::Error,
+    },
+
+    /// The ownership of a directory can not be set.
+    #[error("Ownership of directory {dir} can not be changed to user {system_user}: {source}")]
+    DirChangeOwner {
+        dir: PathBuf,
+        system_user: String,
+        source: std::io::Error,
+    },
+}
+
+/// Returns the path of the directory in which administrative credentials reside.
+pub fn get_credentials_dir() -> PathBuf {
+    get_data_home().join(PathBuf::from(CREDENTIALS_DIR))
+}
+
+/// Returns the file path for plaintext administrative credentials.
+pub fn get_plaintext_credentials_file() -> PathBuf {
+    get_credentials_dir().join(PathBuf::from(PLAINTEXT_CREDENTIALS_FILE))
+}
+
+/// Returns the file path for systemd-creds encrypted administrative credentials.
+pub fn get_systemd_creds_credentials_file() -> PathBuf {
+    get_credentials_dir().join(PathBuf::from(SYSTEMD_CREDS_CREDENTIALS_FILE))
+}
+
+/// Creates the directory for administrative credentials.
+///
+/// # Errors
+///
+/// Returns an error if the directory or one of its parents can not be created.
+/// Refer to [`create_dir_all`] for further information on failure scenarios.
+pub fn create_credentials_dir() -> Result<(), Error> {
+    let credentials_dir = get_credentials_dir();
+    create_dir_all(credentials_dir.as_path()).map_err(|source| Error::CreateDirectory {
+        dir: CREDENTIALS_DIR,
+        source,
+    })?;
+
+    // Set the permissions of the credentials directory to `CREDENTIALS_DIR_MODE`.
+    set_permissions(
+        credentials_dir.as_path(),
+        Permissions::from_mode(CREDENTIALS_DIR_MODE),
+    )
+    .map_err(|source| Error::ApplyPermissions {
+        permissions: CREDENTIALS_DIR_MODE,
+        path: credentials_dir.clone(),
+        source,
+    })?;
+
+    // Recursively chown all directories to root, until `DATA_HOME` is
+    // reached.
+    let data_home = get_data_home();
+    let mut chown_dir = credentials_dir.clone();
+    while chown_dir != data_home {
+        chown(&chown_dir, Some(0), Some(0)).map_err(|source| Error::DirChangeOwner {
+            dir: chown_dir.to_path_buf(),
+            system_user: "root".to_string(),
+            source,
+        })?;
+        if let Some(parent) = &chown_dir.parent() {
+            chown_dir = parent.to_path_buf()
+        } else {
+            break;
+        }
+    }
+
+    Ok(())
+}
diff --git a/signstar-common/src/common.rs b/signstar-common/src/common.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8dd86eb9b2e64180346145e114c36497127b356e
--- /dev/null
+++ b/signstar-common/src/common.rs
@@ -0,0 +1,26 @@
+//! Common data for all Signstar functionalities.
+//!
+//! # Examples
+//!
+//! ```
+//! use signstar_common::common::get_data_home;
+//!
+//! // Get the directory below which all Signstar related data is stored.
+//! println!("{:?}", get_data_home());
+//! ```
+
+use std::path::PathBuf;
+
+/// The directory below which o store all Signstar related data.
+const DATA_HOME: &str = "/var/lib/signstar/";
+
+/// The file mode of directories containing credentials.
+pub const CREDENTIALS_DIR_MODE: u32 = 0o100700;
+
+/// The file mode of secret files.
+pub const SECRET_FILE_MODE: u32 = 0o100600;
+
+/// Get the directory below which all Signstar related data is stored.
+pub fn get_data_home() -> PathBuf {
+    PathBuf::from(DATA_HOME)
+}
diff --git a/signstar-common/src/config.rs b/signstar-common/src/config.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5cba4ce1913a78b03d864ed2c1b0129f989aacce
--- /dev/null
+++ b/signstar-common/src/config.rs
@@ -0,0 +1,205 @@
+//! Default locations for Signstar configuration files.
+//!
+//! # Examples
+//!
+//! ```
+//! use signstar_common::config::{
+//!     get_config_file_or_default,
+//!     get_config_file_paths,
+//!     get_config_file,
+//!     get_default_config_dir_path,
+//!     get_default_config_file_path,
+//!     get_etc_override_config_file_path,
+//!     get_etc_override_dir_path,
+//!     get_run_override_config_file_path,
+//!     get_run_override_dir_path,
+//!     get_usr_local_override_config_file_path,
+//!     get_usr_local_override_dir_path,
+//! };
+//!
+//! // Get directory paths for Signstar configuration files.
+//! println!("{:?}", get_etc_override_dir_path());
+//! println!("{:?}", get_run_override_dir_path());
+//! println!("{:?}", get_usr_local_override_dir_path());
+//! println!("{:?}", get_default_config_dir_path());
+//!
+//! // Get file paths for Signstar configuration files.
+//! println!("{:?}", get_etc_override_config_file_path());
+//! println!("{:?}", get_run_override_config_file_path());
+//! println!("{:?}", get_usr_local_override_config_file_path());
+//! println!("{:?}", get_default_config_file_path());
+//!
+//! // Get the first config file found, according to directory precedence.
+//! println!("{:?}", get_config_file());
+//!
+//! // Get the first config file found, according to directory precedence, or the default if none are found.
+//! println!("{:?}", get_config_file_or_default());
+//!
+//! // Get all configuration file paths, sorted by directory precedence.
+//! println!("{:?}", get_config_file_paths());
+//! ```
+
+use std::{fs::create_dir_all, path::PathBuf};
+
+/// The default config directory below "/usr" for Signstar hosts.
+const DEFAULT_CONFIG_DIR: &str = "/usr/share/signstar/";
+
+/// The override config directory below "/etc" for Signstar hosts.
+const ETC_OVERRIDE_CONFIG_DIR: &str = "/etc/signstar/";
+
+/// The override config directory below "/run" for Signstar hosts.
+const RUN_OVERRIDE_CONFIG_DIR: &str = "/run/signstar/";
+
+/// The override config directory below "/usr/local" for Signstar hosts.
+const USR_LOCAL_OVERRIDE_CONFIG_DIR: &str = "/usr/local/share/signstar/";
+
+/// The filename of a Signstar configuration file.
+const CONFIG_FILE: &str = "config.toml";
+
+/// An error that may occur when handling configuration directories or files.
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+    /// A directory can not be created.
+    #[error("Unable to create directory {dir}:\n{source}")]
+    CreateDirectory { dir: String, source: std::io::Error },
+}
+
+/// Returns the first Signstar configuration file available, or [`None`] if none found.
+///
+/// Considers files named `config.toml` in the following directories in descending priority:
+/// - `/etc/signstar`
+/// - `/run/signstar`
+/// - `/usr/local/share/signstar`
+/// - `/usr/share/signstar`
+///
+/// The first existing file is returned.
+/// If no file is found [`None`] is returned.
+pub fn get_config_file() -> Option<PathBuf> {
+    [
+        get_etc_override_config_file_path(),
+        get_run_override_config_file_path(),
+        get_usr_local_override_config_file_path(),
+        get_default_config_file_path(),
+    ]
+    .into_iter()
+    .find(|file| file.is_file())
+}
+
+/// Returns the first Signstar configuration file available, or the default if none found.
+///
+/// Considers files named `config.toml` in the following directories in descending priority:
+/// - `/etc/signstar`
+/// - `/run/signstar`
+/// - `/usr/local/share/signstar`
+/// - `/usr/share/signstar`
+///
+/// The first existing file is returned.
+/// If no file is found, the default location `/usr/share/signstar/config.toml` is returned.
+pub fn get_config_file_or_default() -> PathBuf {
+    let Some(config) = get_config_file() else {
+        return get_default_config_file_path();
+    };
+    config
+}
+
+/// Returns a list of all configuration file locations, sorted by precedence.
+pub fn get_config_file_paths() -> Vec<PathBuf> {
+    vec![
+        get_etc_override_config_file_path(),
+        get_run_override_config_file_path(),
+        get_usr_local_override_config_file_path(),
+        get_default_config_file_path(),
+    ]
+}
+
+/// Returns the file path of the configuration file override below /etc.
+pub fn get_etc_override_config_file_path() -> PathBuf {
+    PathBuf::from([ETC_OVERRIDE_CONFIG_DIR, CONFIG_FILE].concat())
+}
+
+/// Returns the directory path of the configuration override directory below /etc.
+pub fn get_etc_override_dir_path() -> PathBuf {
+    PathBuf::from(ETC_OVERRIDE_CONFIG_DIR)
+}
+
+/// Returns the file path of the configuration file override below /run.
+pub fn get_run_override_config_file_path() -> PathBuf {
+    PathBuf::from([RUN_OVERRIDE_CONFIG_DIR, CONFIG_FILE].concat())
+}
+
+/// Returns the directory path of the configuration override directory below /run.
+pub fn get_run_override_dir_path() -> PathBuf {
+    PathBuf::from(RUN_OVERRIDE_CONFIG_DIR)
+}
+
+/// Returns the file path of the configuration file override below /usr/local.
+pub fn get_usr_local_override_config_file_path() -> PathBuf {
+    PathBuf::from([USR_LOCAL_OVERRIDE_CONFIG_DIR, CONFIG_FILE].concat())
+}
+
+/// Returns the directory path of the configuration override directory below /usr/local.
+pub fn get_usr_local_override_dir_path() -> PathBuf {
+    PathBuf::from(USR_LOCAL_OVERRIDE_CONFIG_DIR)
+}
+
+/// Returns the file path of the default configuration file /usr.
+pub fn get_default_config_file_path() -> PathBuf {
+    PathBuf::from([DEFAULT_CONFIG_DIR, CONFIG_FILE].concat())
+}
+
+/// Returns the directory path of the default configuration directory below /usr.
+pub fn get_default_config_dir_path() -> PathBuf {
+    PathBuf::from(DEFAULT_CONFIG_DIR)
+}
+
+/// Creates the default configuration directory below /usr.
+///
+/// # Errors
+///
+/// Returns an error if the directory or one of its parents can not be created.
+/// Refer to [`create_dir_all`] for further information on failure scenarios.
+pub fn create_default_config_dir() -> Result<(), Error> {
+    create_dir_all(get_default_config_dir_path()).map_err(|source| Error::CreateDirectory {
+        dir: DEFAULT_CONFIG_DIR.to_string(),
+        source,
+    })
+}
+
+/// Creates the configuration override dir below /etc.
+///
+/// # Errors
+///
+/// Returns an error if the directory or one of its parents can not be created.
+/// Refer to [`create_dir_all`] for further information on failure scenarios.
+pub fn create_etc_override_config_dir() -> Result<(), Error> {
+    create_dir_all(get_etc_override_dir_path()).map_err(|source| Error::CreateDirectory {
+        dir: ETC_OVERRIDE_CONFIG_DIR.to_string(),
+        source,
+    })
+}
+
+/// Creates the configuration override dir below /run.
+///
+/// # Errors
+///
+/// Returns an error if the directory or one of its parents can not be created.
+/// Refer to [`create_dir_all`] for further information on failure scenarios.
+pub fn create_run_override_config_dir() -> Result<(), Error> {
+    create_dir_all(get_run_override_dir_path()).map_err(|source| Error::CreateDirectory {
+        dir: RUN_OVERRIDE_CONFIG_DIR.to_string(),
+        source,
+    })
+}
+
+/// Creates the configuration override dir below /usr/local.
+///
+/// # Errors
+///
+/// Returns an error if the directory or one of its parents can not be created.
+/// Refer to [`create_dir_all`] for further information on failure scenarios.
+pub fn create_usr_local_override_config_dir() -> Result<(), Error> {
+    create_dir_all(get_usr_local_override_dir_path()).map_err(|source| Error::CreateDirectory {
+        dir: USR_LOCAL_OVERRIDE_CONFIG_DIR.to_string(),
+        source,
+    })
+}
diff --git a/signstar-common/src/lib.rs b/signstar-common/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0f446ff0cd68b48c5bc7b20a9767b89852f7079c
--- /dev/null
+++ b/signstar-common/src/lib.rs
@@ -0,0 +1,8 @@
+//! Common components and data for Signstar crates.
+
+pub mod admin_credentials;
+pub mod common;
+pub mod config;
+pub mod nethsm;
+pub mod ssh;
+pub mod system_user;
diff --git a/signstar-common/src/nethsm.rs b/signstar-common/src/nethsm.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a746d85c36094c00d2eb13e7b9f177657ae33ee4
--- /dev/null
+++ b/signstar-common/src/nethsm.rs
@@ -0,0 +1,26 @@
+//! Defaults for NetHSM backends.
+
+use std::net::Ipv4Addr;
+
+use nethsm::LogLevel;
+
+/// The default admin user name of newly provisioned NetHSM.
+pub const USER_DEFAULT_ADMIN: &str = "admin";
+
+/// The default IP address of an unprovisioned NetHSM.
+pub const NETWORK_DEFAULT_IP_ADDRESS: Ipv4Addr = Ipv4Addr::new(192, 168, 1, 1);
+
+/// The default netmask of an unprovisioned NetHSM.
+pub const NETWORK_DEFAULT_NETMASK: Ipv4Addr = Ipv4Addr::new(255, 255, 255, 0);
+
+/// The default gateway of an unprovisioned NetHSM.
+pub const NETWORK_DEFAULT_GATEWAY: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
+
+/// The default logging IP address of an unprovisioned NetHSM.
+pub const LOGGING_DEFAULT_IP_ADDRESS: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
+
+/// The default logging port of an unprovisioned NetHSM.
+pub const LOGGING_DEFAULT_PORT: u32 = 514;
+
+/// The default logging level of an unprovisioned NetHSM.
+pub const LOGGING_DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Info;
diff --git a/signstar-common/src/ssh.rs b/signstar-common/src/ssh.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9ca2d3fe13135c08af245888a97cf7834f2ccaad
--- /dev/null
+++ b/signstar-common/src/ssh.rs
@@ -0,0 +1,31 @@
+//! Defaults for SSH.
+//!
+//! # Examples
+//!
+//! ```
+//! use signstar_common::ssh::{get_ssh_authorized_key_base_dir, get_sshd_config_dropin_dir};
+//!
+//! // Get directory path for SSH authorized_keys files for Signstar users.
+//! println!("{:?}", get_ssh_authorized_key_base_dir());
+//!
+//! // Get directory path for sshd_config drop-in files.
+//! println!("{:?}", get_sshd_config_dropin_dir());
+//! ```
+
+use std::path::PathBuf;
+
+/// The base directory below which SSH authorized_keys files for users are located.
+const SSH_AUTHORIZED_KEY_BASE_DIR: &str = "/etc/ssh/";
+
+/// The directory below which sshd_config drop-in files are located.
+const SSHD_CONFIG_DROPIN_DIR: &str = "/etc/ssh/sshd_config.d/";
+
+/// Returns the directory path below which SSH authorized_keys files for Signstar users are located.
+pub fn get_ssh_authorized_key_base_dir() -> PathBuf {
+    PathBuf::from(SSH_AUTHORIZED_KEY_BASE_DIR)
+}
+
+/// Returns the directory path below which SSH authorized_keys files for Signstar users are located.
+pub fn get_sshd_config_dropin_dir() -> PathBuf {
+    PathBuf::from(SSHD_CONFIG_DROPIN_DIR)
+}
diff --git a/signstar-common/src/system_user.rs b/signstar-common/src/system_user.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b0406e10a74e106bf10df473cac78e3d88164ff9
--- /dev/null
+++ b/signstar-common/src/system_user.rs
@@ -0,0 +1,62 @@
+//! Defaults for system users.
+//!
+//! ```
+//! use signstar_common::system_user::{get_home_base_dir_path, get_relative_user_secrets_dir};
+//!
+//! // Get the base directory below which Signstar system user homes are located.
+//! println!("{:?}", get_home_base_dir_path());
+//!
+//! // Get the relative directory below which Signstar secrets are located per system user.
+//! println!("{:?}", get_relative_user_secrets_dir());
+//! ```
+
+use std::path::PathBuf;
+
+use crate::common::get_data_home;
+
+/// The relative base directory below which system user homes are located.
+///
+/// This directory resides relative to the data home on the system.
+const HOME_BASE_DIR: &str = "home/";
+
+/// The directory name below which credentials files are stored.
+///
+/// The directory is evaluated relative to a user's home.
+const USER_SECRETS_DIR: &str = ".local/state/signstar/secrets/";
+
+/// The file extension of plaintext credential files.
+const PLAINTEXT_SECRETS_EXTENSION: &str = "txt";
+
+/// The file extension of systemd-creds encrypted credential files.
+const SYSTEMD_CREDS_SECRETS_EXTENSION: &str = "creds";
+
+/// Returns the base directory below which Signstar system user homes are located.
+pub fn get_home_base_dir_path() -> PathBuf {
+    get_data_home().join(PathBuf::from(HOME_BASE_DIR))
+}
+
+/// Returns the relative directory below which Signstar secrets are located per system user.
+pub fn get_relative_user_secrets_dir() -> PathBuf {
+    PathBuf::from(USER_SECRETS_DIR)
+}
+
+/// Returns the path to the secrets directory for a specific system user.
+pub fn get_user_secrets_dir(system_user: &str) -> PathBuf {
+    get_home_base_dir_path()
+        .join(PathBuf::from(system_user))
+        .join(get_relative_user_secrets_dir())
+}
+
+/// Returns the path to a plaintext secrets file for a system user and backend user.
+pub fn get_plaintext_secret_file(system_user: &str, backend_user: &str) -> PathBuf {
+    get_user_secrets_dir(system_user).join(PathBuf::from(
+        [backend_user, ".", PLAINTEXT_SECRETS_EXTENSION].concat(),
+    ))
+}
+
+/// Returns the path to a systemd-creds encrypted secrets file for a system user and backend user.
+pub fn get_systemd_creds_secret_file(system_user: &str, backend_user: &str) -> PathBuf {
+    get_user_secrets_dir(system_user).join(PathBuf::from(
+        [backend_user, ".", SYSTEMD_CREDS_SECRETS_EXTENSION].concat(),
+    ))
+}