diff --git a/tool/pom.xml b/tool/pom.xml
index 9c13a58c6eb..7e5a76674d6 100644
--- a/tool/pom.xml
+++ b/tool/pom.xml
@@ -142,6 +142,9 @@
true
${test.level}
+
+ Guk]i%Aua4-wB
+
true
true
false
diff --git a/tool/src/test/java/org/wildfly/security/tool/FileSystemEncryptRealmCommandTest.java b/tool/src/test/java/org/wildfly/security/tool/FileSystemEncryptRealmCommandTest.java
index c273cdffdfa..6f698df14ff 100644
--- a/tool/src/test/java/org/wildfly/security/tool/FileSystemEncryptRealmCommandTest.java
+++ b/tool/src/test/java/org/wildfly/security/tool/FileSystemEncryptRealmCommandTest.java
@@ -26,7 +26,9 @@
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
+
import javax.crypto.SecretKey;
+
import org.apache.commons.cli.MissingArgumentException;
import org.junit.Test;
import org.wildfly.security.auth.principal.NamePrincipal;
@@ -66,6 +68,16 @@ private void runCommand(String inputLocation, String outputLocation, String file
executeCommandAndCheckStatus(requiredArgs, expectedStatus);
}
+ private void runCommand(String inputLocation, String outputLocation, String fileSystemRealmName, String keyStoreLocation,
+ String keyPairAlias, String keyStorePassword, int levels, boolean create, int expectedStatus) {
+ String[] requiredArgs;
+ requiredArgs = new String[]{"--input-location", inputLocation, "--output-location", outputLocation, "--realm-name", fileSystemRealmName,
+ "--keystore", keyStoreLocation, "--key-pair", keyPairAlias, "--password", keyStorePassword,
+ "--levels", String.valueOf(levels), "--create", String.valueOf(create),
+ "--credential-store", CREDENTIAL_STORE_PATH};
+ executeCommandAndCheckStatus(requiredArgs, expectedStatus);
+ }
+
private void runCommand(String inputLocation, String outputLocation, String fileSystemRealmName, String credentialStore, String secretKey, String encoded, boolean create, int expectedStatus) {
String[] requiredArgs;
requiredArgs = new String[]{"--input-location", inputLocation, "--output-location", outputLocation, "--realm-name", fileSystemRealmName, "--credential-store", credentialStore, "--secret-key", secretKey, "--encoded", encoded, "--create", String.valueOf(create)};
@@ -165,6 +177,17 @@ public void testSingleUserWithRoles() throws Exception {
}
}
+ @Test
+ public void testSingleUserWithRolesAndIntegrity() throws Exception {
+ String inputLocation = RELATIVE_BASE_DIR + "fs-unencrypted-realms/single-user-with-roles-and-integrity";
+ String outputLocation = RELATIVE_BASE_DIR + "fs-encrypted-realms";
+ String fileSystemRealmName = "single-user-with-roles-and-integrity";
+ String keyStoreLocation = RELATIVE_BASE_DIR + "mykeystore.pfx";
+ String keyPairAlias = "integrity-key";
+ String keyStorePassword = "Guk]i%Aua4-wB";
+ runCommand(inputLocation, outputLocation, fileSystemRealmName, keyStoreLocation, keyPairAlias, keyStorePassword, 2, true, 0);
+ }
+
@Test
public void testSingleUserWithRolesAndKey() throws Exception {
String inputLocation = RELATIVE_BASE_DIR + "fs-unencrypted-realms/single-user-with-key/";
@@ -210,4 +233,4 @@ private boolean fileExists(String path) {
File tempFile = new File(path);
return tempFile.exists();
}
-}
+}
\ No newline at end of file
diff --git a/tool/src/test/java/org/wildfly/security/tool/FileSystemRealmIntegrityCommandTest.java b/tool/src/test/java/org/wildfly/security/tool/FileSystemRealmIntegrityCommandTest.java
new file mode 100644
index 00000000000..65cbbd5c752
--- /dev/null
+++ b/tool/src/test/java/org/wildfly/security/tool/FileSystemRealmIntegrityCommandTest.java
@@ -0,0 +1,771 @@
+/*
+ * Copyright 2023 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wildfly.security.tool;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.wildfly.security.tool.Command.GENERAL_CONFIGURATION_ERROR;
+import static org.wildfly.security.tool.Command.GENERAL_CONFIGURATION_WARNING;
+import static org.wildfly.security.tool.ElytronTool.ElytronToolExitStatus_OK;
+import static org.wildfly.security.tool.FileSystemRealmIntegrityCommand.FILE_SYSTEM_REALM_INTEGRITY_COMMAND;
+import static org.wildfly.security.tool.Params.BULK_CONVERT_PARAM;
+import static org.wildfly.security.tool.Params.CREDENTIAL_STORE_LOCATION_PARAM;
+import static org.wildfly.security.tool.Params.DEFAULT_KEY_PAIR_ALIAS;
+import static org.wildfly.security.tool.Params.ENCODED_PARAM;
+import static org.wildfly.security.tool.Params.FILE_SEPARATOR;
+import static org.wildfly.security.tool.Params.HASH_CHARSET_PARAM;
+import static org.wildfly.security.tool.Params.HASH_ENCODING_PARAM;
+import static org.wildfly.security.tool.Params.INPUT_LOCATION_PARAM;
+import static org.wildfly.security.tool.Params.KEYSTORE_PARAM;
+import static org.wildfly.security.tool.Params.KEYSTORE_TYPE_PARAM;
+import static org.wildfly.security.tool.Params.KEY_PAIR_ALIAS_PARAM;
+import static org.wildfly.security.tool.Params.LEVELS_PARAM;
+import static org.wildfly.security.tool.Params.OUTPUT_LOCATION_PARAM;
+import static org.wildfly.security.tool.Params.PASSWORD_ENV_PARAM;
+import static org.wildfly.security.tool.Params.PASSWORD_PARAM;
+import static org.wildfly.security.tool.Params.REALM_NAME_PARAM;
+import static org.wildfly.security.tool.Params.SECRET_KEY_ALIAS_PARAM;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.junit.Test;
+
+/** @author Cameron Rodriguez */
+public class FileSystemRealmIntegrityCommandTest extends AbstractCommandTest {
+
+ private static final String RELATIVE_BASE_DIR = "./target/test-classes/filesystem-integrity/";
+ private static final String RELATIVE_UNSIGNED_DIR = RELATIVE_BASE_DIR + "fs-unsigned-realms/";
+ private static final String RELATIVE_SIGNED_DIR = RELATIVE_BASE_DIR + "fs-signed-realms/";
+
+ /* KeyStores (expires around June 2031) & credential stores used:
+ *
+ * > fsKeyStore.pfx - PKCS#12 keystore. Two aliases: integrity-key (RSA-4096 key algo, SHA384withRSA signing algo) and
+ * integrity-cert (imported certificate of integrity-key)
+ * > fsKeyStoreEC.jceks - JCEKS keystore. One alias: curveKeyPair (256-bit EC [secp256r1] key algo, SHA256withECDSA signing algo)
+ * > fsKeyStoreEmpty.jks - JKS keystore. No aliases.
+ * > fsCredStore.cs - SecretKey credential store. Two SecretKey aliases: secKey and key
+ */
+ private static final Path FS_KEYSTORE_PATH = Path.of(RELATIVE_BASE_DIR, "fsKeyStore.pfx");
+ private static final Path FS_REALM_SIGNED_PATH = Path.of(RELATIVE_SIGNED_DIR);
+ private static final String KEYSTORE_PASSWORD = "Guk]i%Aua4-wB";
+
+ @Override
+ protected String getCommandType() {
+ return FILE_SYSTEM_REALM_INTEGRITY_COMMAND;
+ }
+
+ @Test
+ public void testHelp() {
+ String[] args = new String[]{"--help"};
+ executeCommandAndCheckStatus(args);
+ }
+
+ /** Also tests non-default key pair alias */
+ @Test
+ public void testSingleUserRealmWithJCEKS() throws IOException {
+ String realmName = "fsRealmSingle";
+ Path inputLocation = Path.of(RELATIVE_UNSIGNED_DIR, realmName);
+ Path keyStore = Path.of(RELATIVE_BASE_DIR, "fsKeyStoreEC.jceks");
+ String keyStoreType = "JCEKS";
+ String keyPairAlias = "curveKeyPair";
+ String[] args = {
+ "--" + INPUT_LOCATION_PARAM, inputLocation.toString(),
+ "--" + OUTPUT_LOCATION_PARAM, FS_REALM_SIGNED_PATH.toString(),
+ "--" + REALM_NAME_PARAM, realmName,
+ "--" + KEYSTORE_PARAM, keyStore.toString(),
+ "--" + KEYSTORE_TYPE_PARAM, keyStoreType,
+ "--" + KEY_PAIR_ALIAS_PARAM, keyPairAlias,
+ "--" + PASSWORD_PARAM, KEYSTORE_PASSWORD
+ };
+
+ runCommand(inputLocation, args, ElytronToolExitStatus_OK);
+ assertTrue("Could not find identity `bob` within single user realm: " + FS_REALM_SIGNED_PATH.resolve(realmName),
+ FS_REALM_SIGNED_PATH.resolve(Path.of(realmName, "b", "o")).toFile().exists());
+
+ ScriptParameters params = new ScriptParameters(realmName)
+ .setRealmPath(FS_REALM_SIGNED_PATH.resolve(realmName))
+ .setKeyStorePath(keyStore)
+ .setKeyStorePassword(KEYSTORE_PASSWORD)
+ .setKeyPairAlias(keyPairAlias)
+ .setKeyStoreType(keyStoreType);
+ validateScript(params, FS_REALM_SIGNED_PATH.resolve(realmName + ".cli"));
+ }
+
+ @Test
+ public void testMultiUserRealmWithSummary() throws IOException {
+ String realmName = "fsRealmMultiUser";
+ Path inputLocation = Path.of(RELATIVE_UNSIGNED_DIR, "fsRealm");
+ String[] args = {
+ "--" + INPUT_LOCATION_PARAM, inputLocation.toString(),
+ "--" + OUTPUT_LOCATION_PARAM, FS_REALM_SIGNED_PATH.toString(),
+ "--" + REALM_NAME_PARAM, realmName,
+ "--" + KEYSTORE_PARAM, FS_KEYSTORE_PATH.toString(),
+ "--" + KEY_PAIR_ALIAS_PARAM, DEFAULT_KEY_PAIR_ALIAS,
+ "--" + PASSWORD_PARAM, KEYSTORE_PASSWORD,
+ "--summary"
+ };
+
+ String output = runCommand(inputLocation, args, ElytronToolExitStatus_OK);
+ validateMultiUserIdentitiesPresent(realmName);
+
+ // Summary validation
+ assertTrue("Could not find header for summary of filesystem-realm-integrity command",
+ output.contains("Summary for execution of Elytron Tool command filesystem-realm-integrity"));
+ assertTrue("Could not find summary string for CLI configuration",
+ output.contains("Options were specified via CLI"));
+ assertTrue("Could not find summary string for configuring CLI script",
+ output.contains(String.format("Configured script for WildFly named %s.cli at %s.", realmName, FS_REALM_SIGNED_PATH.normalize().toAbsolutePath())));
+
+ ScriptParameters params = new ScriptParameters(realmName)
+ .setRealmPath(FS_REALM_SIGNED_PATH)
+ .setKeyStorePath(FS_KEYSTORE_PATH)
+ .setKeyPairAlias(DEFAULT_KEY_PAIR_ALIAS)
+ .setKeyStorePassword(KEYSTORE_PASSWORD);
+ validateScript(params, FS_REALM_SIGNED_PATH.resolve(realmName + ".cli"));
+ }
+
+ /** Also tests upgrading from {@code urn:elytron:identity.1.1} and non-default secret key alias */
+ @Test
+ public void testEncryptedRealmWithFourLevels() throws IOException {
+ String realmName = "fsRealmEncrypted";
+ Path inputLocation = Path.of(RELATIVE_UNSIGNED_DIR, realmName);
+ Path credStorePath = Path.of(RELATIVE_BASE_DIR, "fsCredStore.cs");
+ String secretKey = "secKey";
+ String levels = "4";
+ String[] args = {
+ "--" + INPUT_LOCATION_PARAM, inputLocation.toString(),
+ "--" + OUTPUT_LOCATION_PARAM, FS_REALM_SIGNED_PATH.toString(),
+ "--" + REALM_NAME_PARAM, realmName,
+ "--" + KEYSTORE_PARAM, FS_KEYSTORE_PATH.toString(),
+ "--" + PASSWORD_PARAM, KEYSTORE_PASSWORD,
+ "--" + CREDENTIAL_STORE_LOCATION_PARAM, credStorePath.toString(),
+ "--" + SECRET_KEY_ALIAS_PARAM, secretKey,
+ "--" + LEVELS_PARAM, levels
+ };
+
+ runCommand(inputLocation, args, ElytronToolExitStatus_OK);
+ validateMultiUserIdentitiesPresent(realmName,
+ FS_REALM_SIGNED_PATH.resolve(Path.of(realmName, "M", "F", "W", "G", "MFWGSY3F.xml")),
+ FS_REALM_SIGNED_PATH.resolve(Path.of(realmName, "M", "J", "X", "W", "MJXWE.xml")),
+ FS_REALM_SIGNED_PATH.resolve(Path.of(realmName, "M", "N", "Q", "W", "MNQW2ZLSN5XA.xml")));
+
+ ScriptParameters params = new ScriptParameters(realmName)
+ .setRealmPath(FS_REALM_SIGNED_PATH.resolve(realmName))
+ .setKeyStorePath(FS_KEYSTORE_PATH)
+ .setKeyStorePassword(KEYSTORE_PASSWORD)
+ .setCredentialStorePath(credStorePath)
+ .setSecretKeyAlias(secretKey)
+ .setLevels(levels);
+ validateScript(params, FS_REALM_SIGNED_PATH.resolve(realmName + ".cli"));
+ }
+
+ @Test
+ public void testRealmWithNameEncodedAndPasswordEnv() throws IOException {
+ String realmName = "fsRealmNameEncoded";
+ Path inputLocation = Path.of(RELATIVE_UNSIGNED_DIR, realmName);
+ String passwordEnvVar = "FS_INTEGRITY_PASSWORD_TEST_VAR";
+ String encoded = "false";
+ String[] args = {
+ "--" + INPUT_LOCATION_PARAM, inputLocation.toString(),
+ "--" + OUTPUT_LOCATION_PARAM, FS_REALM_SIGNED_PATH.toString(),
+ "--" + REALM_NAME_PARAM, realmName,
+ "--" + KEYSTORE_PARAM, FS_KEYSTORE_PATH.toString(),
+ "--" + PASSWORD_ENV_PARAM, passwordEnvVar,
+ "--" + ENCODED_PARAM, encoded
+ };
+
+ runCommand(inputLocation, args, ElytronToolExitStatus_OK);
+ validateMultiUserIdentitiesPresent(realmName);
+
+ ScriptParameters params = new ScriptParameters(realmName)
+ .setRealmPath(FS_REALM_SIGNED_PATH.resolve(realmName))
+ .setKeyStorePath(FS_KEYSTORE_PATH)
+ .setKeyStorePassword(KEYSTORE_PASSWORD);
+ validateScript(params, FS_REALM_SIGNED_PATH.resolve(realmName + ".cli"));
+ }
+
+ @Test
+ public void testRealmWithHashEncoding() throws IOException {
+ String realmName = "fsRealmHashEncoding";
+ Path inputLocation = Path.of(RELATIVE_UNSIGNED_DIR, realmName);
+ String hashEncoding = "hex";
+ String[] args = {
+ "--" + INPUT_LOCATION_PARAM, inputLocation.toString(),
+ "--" + OUTPUT_LOCATION_PARAM, FS_REALM_SIGNED_PATH.toString(),
+ "--" + REALM_NAME_PARAM, realmName,
+ "--" + KEYSTORE_PARAM, FS_KEYSTORE_PATH.toString(),
+ "--" + KEY_PAIR_ALIAS_PARAM, DEFAULT_KEY_PAIR_ALIAS,
+ "--" + PASSWORD_PARAM, KEYSTORE_PASSWORD,
+ "--" + HASH_ENCODING_PARAM, hashEncoding
+ };
+
+ runCommand(inputLocation, args, ElytronToolExitStatus_OK);
+ validateMultiUserIdentitiesPresent(realmName);
+
+ ScriptParameters params = new ScriptParameters(realmName)
+ .setRealmPath(FS_REALM_SIGNED_PATH.resolve(realmName))
+ .setKeyStorePath(FS_KEYSTORE_PATH)
+ .setKeyStorePassword(KEYSTORE_PASSWORD);
+ validateScript(params, FS_REALM_SIGNED_PATH.resolve(realmName + ".cli"));
+ }
+
+ @Test
+ public void testRealmWithHashCharset() throws IOException {
+ String realmName = "fsRealmCharset";
+ Path inputLocation = Path.of(RELATIVE_UNSIGNED_DIR, realmName);
+ String hashCharset = "ISO-8859-1";
+ String[] args = {
+ "--" + INPUT_LOCATION_PARAM, inputLocation.toString(),
+ "--" + OUTPUT_LOCATION_PARAM, FS_REALM_SIGNED_PATH.toString(),
+ "--" + REALM_NAME_PARAM, realmName,
+ "--" + KEYSTORE_PARAM, FS_KEYSTORE_PATH.toString(),
+ "--" + PASSWORD_PARAM, KEYSTORE_PASSWORD,
+ "--" + HASH_CHARSET_PARAM, hashCharset
+ };
+
+ runCommand(inputLocation, args, ElytronToolExitStatus_OK);
+ validateMultiUserIdentitiesPresent(realmName);
+
+ ScriptParameters params = new ScriptParameters(realmName)
+ .setRealmPath(FS_REALM_SIGNED_PATH.resolve(realmName))
+ .setKeyStorePath(FS_KEYSTORE_PATH)
+ .setKeyStorePassword(KEYSTORE_PASSWORD)
+ .setHashCharset(hashCharset);
+ validateScript(params, FS_REALM_SIGNED_PATH.resolve(realmName + ".cli"));
+ }
+
+ @Test
+ public void testRealmUpgradeInPlace() throws IOException {
+ String realmName = "fsRealmUpgradeInPlace";
+ Path location = Path.of(RELATIVE_BASE_DIR, realmName);
+ String[] args = {
+ "--" + INPUT_LOCATION_PARAM, location.toString(),
+ "--" + REALM_NAME_PARAM, realmName,
+ "--" + KEYSTORE_PARAM, FS_KEYSTORE_PATH.toString(),
+ "--" + PASSWORD_PARAM, KEYSTORE_PASSWORD,
+ };
+
+ String output = runCommand(location, args, ElytronToolExitStatus_OK);
+ assertTrue("Expected to find notice about in-place upgrade",
+ Pattern.compile("In-place upgrade for descriptor block \\d+: filesystem realm backed up at "
+ + Pattern.quote(location.normalize().toAbsolutePath() + "-backup"))
+ .matcher(output).find());
+
+ ScriptParameters params = new ScriptParameters(realmName)
+ .setRealmPath(location)
+ .setKeyStorePath(FS_KEYSTORE_PATH)
+ .setKeyStorePassword(KEYSTORE_PASSWORD);
+ validateScript(params, FS_REALM_SIGNED_PATH.resolve(realmName + ".cli"));
+ }
+
+ /**
+ * There isn't really a good way of checking for this, without reimplmenting the full verification
+ * functionality of {@link org.wildfly.security.auth.realm.FileSystemSecurityRealm FileSystemSecurityRealm}. For
+ * now, this test is expected to pass without issue.
+ */
+ @Test
+ public void testIntegrityAlreadyEnabled() throws IOException {
+ String realmName = "fsRealmIntegrityEnabled";
+ Path inputLocation = Path.of(RELATIVE_UNSIGNED_DIR, realmName);
+ String[] args = {
+ "--" + INPUT_LOCATION_PARAM, inputLocation.toString(),
+ "--" + OUTPUT_LOCATION_PARAM, FS_REALM_SIGNED_PATH.toString(),
+ "--" + REALM_NAME_PARAM, realmName,
+ "--" + KEYSTORE_PARAM, FS_KEYSTORE_PATH.toString(),
+ "--" + PASSWORD_PARAM, KEYSTORE_PASSWORD,
+ };
+
+ runCommand(inputLocation, args, ElytronToolExitStatus_OK);
+ validateMultiUserIdentitiesPresent(realmName);
+
+ ScriptParameters params = new ScriptParameters(realmName)
+ .setRealmPath(FS_REALM_SIGNED_PATH.resolve(realmName))
+ .setKeyStorePath(FS_KEYSTORE_PATH)
+ .setKeyStorePassword(KEYSTORE_PASSWORD);
+ validateScript(params, FS_REALM_SIGNED_PATH.resolve(realmName + ".cli"));
+ }
+
+ @Test
+ public void testBulkUpgradeAndRealmEnumeration() {
+ // Also run with a summary
+ String[] args = {
+ "--" + BULK_CONVERT_PARAM, Path.of("./target/test-classes/bulk-integrity-conversion-desc").toString(),
+ "--summary"
+ };
+
+ // Empty realm will not be converted
+ String output = executeCommandAndCheckStatusAndGetOutput(args, ElytronToolExitStatus_OK);
+ assertTrue("Expected to find info about parsing descriptor file",
+ output.contains("Options were specified via descriptor file:"));
+
+ // Check for output realms. Unnamed realms will be enumerated as needed
+ File[] signedRealmDirs = FS_REALM_SIGNED_PATH.toFile().listFiles();
+
+ assertTrue("Could not find upgraded realm fsRealmUpgradeInPlaceBulk",
+ Path.of(RELATIVE_BASE_DIR, "fsRealmUpgradeInPlaceBulk", "a", "l", "alice-MFWGSY3F.xml").toFile().exists());
+ assertTrue("Could not find creation of realm fsRealmEncryptedBulk",
+ Path.of(RELATIVE_SIGNED_DIR, "fsRealmEncryptedBulk", "M","J", "X", "W", "MJXWE.xml" ).toFile().exists());
+ assertTrue("No signed filesystem realms could be found in " + FS_REALM_SIGNED_PATH.normalize().toAbsolutePath(),
+ signedRealmDirs != null && signedRealmDirs.length > 0);
+
+ /* Path.startsWith() would attempt (and fail) to resolve the prefix as a real path; casting to a String avoids this problem
+ * Looking for 2 directories + 2 CLI scripts */
+ String enumeratedRealmPrefix = FS_REALM_SIGNED_PATH.normalize().toAbsolutePath()
+ + FILE_SEPARATOR + "filesystem-realm-with-integrity-";
+
+ assertEquals("Could not find creation of two enumerated filesystem realms", 4,
+ Arrays.stream(signedRealmDirs)
+ .filter(elem -> elem.toPath().normalize().toAbsolutePath().toString().startsWith(enumeratedRealmPrefix))
+ .count());
+ }
+
+
+
+ @Test
+ public void testMissingInputRealm() {
+ String realmName = "fsRealmNonExistent";
+ Path inputLocation = Path.of(RELATIVE_UNSIGNED_DIR, realmName);
+ String[] args = {
+ "--" + INPUT_LOCATION_PARAM, inputLocation.toString(),
+ "--" + OUTPUT_LOCATION_PARAM, FS_REALM_SIGNED_PATH.toString(),
+ "--" + REALM_NAME_PARAM, realmName,
+ "--" + KEYSTORE_PARAM, FS_KEYSTORE_PATH.toString(),
+ "--" + PASSWORD_PARAM, KEYSTORE_PASSWORD,
+ };
+
+ String output = "";
+ try {
+ output = executeCommandAndCheckStatusAndGetOutput(args, GENERAL_CONFIGURATION_ERROR);
+ } catch (RuntimeException e) {
+ output = e.getMessage();
+ } finally {
+ assertTrue("Expected to find error that input location does not exist",
+ output.contains(ElytronToolMessages.msg.inputLocationDoesNotExist().getMessage()));
+ }
+ }
+
+ @Test
+ public void testEmptyRealm() {
+ String realmName = "fsRealmEmpty";
+ Path inputLocation = Path.of(RELATIVE_UNSIGNED_DIR, realmName);
+ String[] args = {
+ "--" + INPUT_LOCATION_PARAM, inputLocation.toString(),
+ "--" + OUTPUT_LOCATION_PARAM, FS_REALM_SIGNED_PATH.toString(),
+ "--" + REALM_NAME_PARAM, realmName,
+ "--" + KEYSTORE_PARAM, FS_KEYSTORE_PATH.toString(),
+ "--" + KEY_PAIR_ALIAS_PARAM, DEFAULT_KEY_PAIR_ALIAS,
+ "--" + PASSWORD_PARAM, KEYSTORE_PASSWORD
+ };
+
+ String output = "";
+ try {
+ output = executeCommandAndCheckStatusAndGetOutput(args, GENERAL_CONFIGURATION_WARNING);
+ } catch (RuntimeException e) {
+ output = e.getMessage();
+ } finally {
+ assertTrue("Expected to find warning message that realm was empty",
+ output.contains("due to no identities present in filesystem realm"));
+ }
+ }
+
+ @Test
+ public void testNotAKeyStore() {
+ Path inputLocation = Path.of(RELATIVE_UNSIGNED_DIR, "fsRealm");
+
+ String notKSRealmName = "fsRealmNotAKeyStore";
+ Path notKeyStore = Path.of(RELATIVE_BASE_DIR, "fsCredStore.cs");
+ String[] notKSArgs = {
+ "--" + INPUT_LOCATION_PARAM, inputLocation.toString(),
+ "--" + OUTPUT_LOCATION_PARAM, FS_REALM_SIGNED_PATH.toString(),
+ "--" + REALM_NAME_PARAM, notKSRealmName,
+ "--" + KEYSTORE_PARAM, notKeyStore.toString(),
+ "--" + PASSWORD_PARAM, KEYSTORE_PASSWORD
+ };
+
+ String output = "";
+ try {
+ output = executeCommandAndCheckStatusAndGetOutput(notKSArgs, GENERAL_CONFIGURATION_WARNING);
+ } catch (RuntimeException e) {
+ output = e.getMessage();
+ } finally {
+ assertTrue("Expected to find warning message that KeyStore format was invalid",
+ output.contains("due to failure to load KeyStore"));
+ }
+ }
+
+ @Test
+ public void testEmptyKeyStore() {
+ Path inputLocation = Path.of(RELATIVE_UNSIGNED_DIR, "fsRealm");
+
+ String emptyKSRealmName = "fsRealmEmptyKeyStore";
+ Path emptyKeyStore = Path.of(RELATIVE_BASE_DIR, "fsKeyStoreEmpty.jks");
+ String[] emptyKSArgs = {
+ "--" + INPUT_LOCATION_PARAM, inputLocation.toString(),
+ "--" + OUTPUT_LOCATION_PARAM, FS_REALM_SIGNED_PATH.toString(),
+ "--" + REALM_NAME_PARAM, emptyKSRealmName,
+ "--" + KEYSTORE_PARAM, emptyKeyStore.toString(),
+ "--" + PASSWORD_PARAM, KEYSTORE_PASSWORD
+ };
+
+ String output = "";
+ try {
+ output = executeCommandAndCheckStatusAndGetOutput(emptyKSArgs, GENERAL_CONFIGURATION_WARNING);
+ } catch (RuntimeException e) {
+ output = e.getMessage();
+ } finally {
+ assertTrue("Expected to find warning message that private or public key was missing",
+ output.contains("due to missing private key"));
+ }
+ }
+
+ @Test
+ public void testNotAKeyPair() {
+ Path inputLocation = Path.of(RELATIVE_UNSIGNED_DIR, "fsRealm");
+
+ String certRealmName = "fsRealmSecretKey";
+ String certAlias = "integrity-cert";
+ String[] certArgs = {
+ "--" + INPUT_LOCATION_PARAM, inputLocation.toString(),
+ "--" + OUTPUT_LOCATION_PARAM, FS_REALM_SIGNED_PATH.toString(),
+ "--" + REALM_NAME_PARAM, certRealmName,
+ "--" + KEYSTORE_PARAM, FS_KEYSTORE_PATH.toString(),
+ "--" + KEY_PAIR_ALIAS_PARAM, certAlias,
+ "--" + PASSWORD_PARAM, KEYSTORE_PASSWORD
+ };
+
+ String output = "";
+ try {
+ output = executeCommandAndCheckStatusAndGetOutput(certArgs, GENERAL_CONFIGURATION_WARNING);
+ } catch (RuntimeException e) {
+ output = e.getMessage();
+ } finally {
+ assertTrue("Expected to find warning message that private key was missing",
+ output.contains("due to missing private key"));
+ }
+ }
+
+ @Test
+ public void testMissingKeyStore() {
+ String realmName = "fsRealmMissingKeyStore";
+ Path inputLocation = Path.of(RELATIVE_UNSIGNED_DIR, "fsRealm");
+ Path keyStore = Path.of(RELATIVE_BASE_DIR, "nonExistentKeyStore.jks");
+ String[] emptyKSArgs = {
+ "--" + INPUT_LOCATION_PARAM, inputLocation.toString(),
+ "--" + OUTPUT_LOCATION_PARAM, FS_REALM_SIGNED_PATH.toString(),
+ "--" + REALM_NAME_PARAM, realmName,
+ "--" + KEYSTORE_PARAM, keyStore.toString(),
+ "--" + PASSWORD_PARAM, KEYSTORE_PASSWORD
+ };
+
+ String output = "";
+ try {
+ output = executeCommandAndCheckStatusAndGetOutput(emptyKSArgs, GENERAL_CONFIGURATION_ERROR);
+ } catch (RuntimeException e) {
+ output = e.getMessage();
+ } finally {
+ assertTrue("Expected to find warning message that key pair was not loaded",
+ output.contains(ElytronToolMessages.msg.keyStoreDoesNotExist().getMessage()));
+ }
+ }
+
+ @Test
+ public void testInvalidIdentityVersion() {
+ // Schema version is invalid
+ String invalidRealmName = "fsRealmInvalidIdentityVersion";
+ Path invalidInputLocation = Path.of(RELATIVE_UNSIGNED_DIR, invalidRealmName);
+ String[] invalidArgs = {
+ "--" + INPUT_LOCATION_PARAM, invalidInputLocation.toString(),
+ "--" + OUTPUT_LOCATION_PARAM, FS_REALM_SIGNED_PATH.toString(),
+ "--" + REALM_NAME_PARAM, invalidRealmName,
+ "--" + KEYSTORE_PARAM, FS_KEYSTORE_PATH.toString(),
+ "--" + PASSWORD_PARAM, KEYSTORE_PASSWORD,
+ };
+
+ try {
+ executeCommandAndCheckStatusAndGetOutput(invalidArgs, GENERAL_CONFIGURATION_ERROR);
+ } catch (RuntimeException ignored) {}
+ }
+
+ @Test
+ public void testMissingIdentityVersion() {
+ // Schema version attribute is missing
+ String missingRealmName = "fsRealmMissingIdentityVersion";
+ Path missingInputLocation = Path.of(RELATIVE_UNSIGNED_DIR, missingRealmName);
+ String[] missingArgs = {
+ "--" + INPUT_LOCATION_PARAM, missingInputLocation.toString(),
+ "--" + OUTPUT_LOCATION_PARAM, FS_REALM_SIGNED_PATH.toString(),
+ "--" + REALM_NAME_PARAM, missingRealmName,
+ "--" + KEYSTORE_PARAM, FS_KEYSTORE_PATH.toString(),
+ "--" + PASSWORD_PARAM, KEYSTORE_PASSWORD,
+ };
+ try {
+ executeCommandAndCheckStatusAndGetOutput(missingArgs, GENERAL_CONFIGURATION_ERROR);
+ } catch (RuntimeException ignored) {}
+ }
+
+ @Test
+ public void testInvalidBulkUpgrade() {
+ String[] args = {
+ "--" + BULK_CONVERT_PARAM, Path.of("./target/test-classes/bulk-integrity-conversion-desc-INVALID").toString(),
+ "--summary"
+ };
+
+ String output = executeCommandAndCheckStatusAndGetOutput(args, GENERAL_CONFIGURATION_WARNING);
+ assertTrue("Expected to find info about parsing descriptor file",
+ output.contains("Options were specified via descriptor file:"));
+
+ assertTrue("Could not find warning for missing required input-location",
+ output.contains("due to missing input realm location."));
+ assertTrue("Could not find warning for skipping block due to \"missing required parameter\"",
+ output.contains("due to missing required parameter."));
+ assertTrue("Could not find warning for missing required password",
+ output.contains("due to missing KeyStore password."));
+ }
+
+ private String runCommand(Path inputLocation, String[] args, int expectedStatus) {
+ String output = executeCommandAndCheckStatusAndGetOutput(args, expectedStatus);
+ assertTrue("Could not find creation of realm " + inputLocation,
+ output.contains(ElytronToolMessages.msg.fileSystemRealmIntegrityCreatingRealm(inputLocation.normalize().toAbsolutePath().toString())));
+
+ return output;
+ }
+
+ /** Uses default relative paths for identities, as present in {@code resources/fs-unsigned-realms/fsRealm/} */
+ private void validateMultiUserIdentitiesPresent(String realmName) {
+ validateMultiUserIdentitiesPresent(realmName,
+ FS_REALM_SIGNED_PATH.resolve(Path.of(realmName, "a", "l", "alice-MFWGSY3F.xml")),
+ FS_REALM_SIGNED_PATH.resolve(Path.of(realmName, "b", "o", "bob-MJXWE.xml")),
+ FS_REALM_SIGNED_PATH.resolve(Path.of(realmName, "c", "a", "cameron-MNQW2ZLSN5XA.xml")));
+ }
+
+ /**
+ * Assert that all converted identity files are present.
+ *
+ * @param realmName name of the output filesystem realm
+ * @param alicePath full path to the identity "alice"
+ * @param bobPath full path to the identity "bob"
+ * @param cameronPath full path to the identity "cameron"
+ * */
+ private void validateMultiUserIdentitiesPresent(String realmName, Path alicePath, Path bobPath, Path cameronPath) {
+ assertTrue("Could not find identity `alice` within multi-user realm: " + FS_REALM_SIGNED_PATH.resolve(realmName),
+ alicePath.toFile().exists());
+ assertTrue("Could not find identity `bob` within multi-user realm: " + FS_REALM_SIGNED_PATH.resolve(realmName),
+ bobPath.toFile().exists());
+ assertTrue("Could not find identity `cameron` within multi-user realm: " + FS_REALM_SIGNED_PATH.resolve(realmName),
+ cameronPath.toFile().exists());
+ }
+
+ /**
+ * Validate that all expected parameters are present in the CLI script file.
+ *
+ * @param params parameters to check within the CLI script
+ * @param scriptPath full path to the CLI script file.
+ * @throws IOException if an exception occurs while reading the script
+ */
+ private void validateScript(ScriptParameters params, Path scriptPath) throws IOException {
+ if (scriptPath.toFile().exists()) {
+ List script = Files.readAllLines(scriptPath);
+
+ // Identify line adding filesystem realm
+ String realmLine = null;
+ String realmName = params.getRealmName();
+ for (String line : script) {
+ if (line.contains(realmName)) {
+ realmLine = line;
+ }
+ }
+ assertNotNull(String.format("Filesystem realm %s was not present in the CLI script", realmName),
+ realmLine);
+
+ // Identify lines adding keystore (mykeystore#) and optional credential store (mycredstore#)
+ Matcher keyStoreNameMatcher = Pattern.compile("^.*key-store=(mykeystore\\d+).*$").matcher(realmLine);
+ assertTrue(keyStoreNameMatcher.matches());
+
+ String keyStoreLine = null;
+ String keyStoreName = keyStoreNameMatcher.group(1);
+ String credStoreLine = null;
+ String credStoreName = null;
+
+ Matcher credStoreNameMatcher = Pattern.compile("^.*credential-store=(mycredstore\\d+).*$").matcher(realmLine);
+ if (credStoreNameMatcher.matches()) {
+ credStoreName = credStoreNameMatcher.group(1);
+ }
+
+ for (String line : script) {
+ if (line.startsWith("/subsystem=elytron/key-store=")) {
+ keyStoreLine = line;
+ } else if (line.startsWith("/subsystem=elytron/secret-key-credential-store=")) {
+ credStoreLine = line;
+ }
+ }
+
+ assertNotNull(String.format("KeyStore %s for filesystem realm %s was not present in the CLI script", keyStoreName, realmName),
+ keyStoreLine);
+
+ if (credStoreLine != null) {
+ assertTrue(String.format("Configuration for credential store was found in CLI script for realm %s, but was not specified in parameters", realmName),
+ params.credentialStoreProvided());
+ } else {
+ assertFalse(String.format("Credential store %s for filesystem realm %s was not present in the CLI script", credStoreName, realmName),
+ params.credentialStoreProvided());
+ }
+
+ for (ImmutablePair param : params.getScriptParameters()) {
+ switch (param.getKey()) {
+ case FILESYSTEM_REALM:
+ assertTrue(String.format("Parameter %s could not be found in configuration for filesystem realm %s.\n"
+ + "Command found: %s", param.getValue(), realmName, realmLine),
+ realmLine.contains(param.getValue())
+ );
+ break;
+ case KEY_STORE:
+ assertTrue(String.format("Parameter %s could not be found in configuration for keystore %s.\n"
+ + "Command found: %s", param.getValue(), keyStoreName, keyStoreLine),
+ keyStoreLine.contains(param.getValue())
+ );
+ break;
+ case CREDENTIAL_STORE:
+ assertTrue(String.format("Parameter %s could not be found in configuration for credential store %s.\n"
+ + "Command found: %s", param.getValue(), credStoreName, credStoreLine),
+ credStoreLine.contains(param.getValue())
+ );
+ break;
+ default:
+ throw new IllegalArgumentException(String.format("Unknown resource %s provided for parameter %s",
+ param.getKey(), param.getValue()));
+ }
+ }
+ }
+ }
+
+ /** Set the parameters to check for in the CLI script. */
+ static class ScriptParameters {
+ // Ordered based on position in script file
+ public enum RESOURCES {KEY_STORE, CREDENTIAL_STORE, FILESYSTEM_REALM}
+
+ private final String realmName;
+ private String realmPath;
+ private String keyPairAlias;
+ private String secretKeyAlias;
+ private String levels;
+ private String hashCharset;
+
+ private String keyStorePath;
+ private String keyStorePassword;
+ private String keyStoreType;
+ private String credentialStorePath;
+
+ public ScriptParameters(String realmName) {
+ this.realmName = realmName;
+ this.realmPath = null;
+ this.keyPairAlias = null;
+ this.secretKeyAlias = null;
+ this.levels = null;
+ this.hashCharset = null;
+ this.keyStorePath = null;
+ this.keyStorePassword = null;
+ this.keyStoreType = null;
+ this.credentialStorePath = null;
+ }
+
+ ScriptParameters(ScriptParameters parameters) {
+ this.realmName = parameters.realmName;
+ this.realmPath = parameters.realmPath;
+ this.keyPairAlias = parameters.keyPairAlias;
+ this.secretKeyAlias = parameters.secretKeyAlias;
+ this.levels = parameters.levels;
+ this.hashCharset = parameters.hashCharset;
+ this.keyStorePath = parameters.keyStorePath;
+ this.keyStorePassword = parameters.keyStorePassword;
+ this.keyStoreType = parameters.keyStoreType;
+ this.credentialStorePath = parameters.credentialStorePath;
+ }
+
+ /** @return An {@link ArrayList} matching Elytron resources to formatted CLI script parameters. */
+ public ArrayList> getScriptParameters() {
+ ArrayList> scriptParams = new ArrayList<>();
+
+ if (realmName != null) scriptParams.add(new ImmutablePair<>(RESOURCES.FILESYSTEM_REALM, "filesystem-realm="+realmName));
+ if (realmPath != null) scriptParams.add(new ImmutablePair<>(RESOURCES.FILESYSTEM_REALM, "path="+realmPath));
+ if (keyPairAlias != null) scriptParams.add(new ImmutablePair<>(RESOURCES.FILESYSTEM_REALM, "key-store-alias="+keyPairAlias));
+ if (secretKeyAlias != null) scriptParams.add(new ImmutablePair<>(RESOURCES.FILESYSTEM_REALM, "secret-key="+secretKeyAlias));
+ if (levels != null) scriptParams.add(new ImmutablePair<>(RESOURCES.FILESYSTEM_REALM, "levels="+levels));
+ if (hashCharset != null) scriptParams.add(new ImmutablePair<>(RESOURCES.FILESYSTEM_REALM, "hash-charset="+hashCharset));
+ if (keyStorePath != null) scriptParams.add(new ImmutablePair<>(RESOURCES.KEY_STORE, "path="+keyStorePath));
+ if (keyStorePassword != null) scriptParams.add(new ImmutablePair<>(RESOURCES.KEY_STORE, "credential-reference={clear-text=\""+keyStorePassword+"\"}"));
+ if (keyStoreType != null) scriptParams.add(new ImmutablePair<>(RESOURCES.KEY_STORE, "type="+keyStoreType));
+ if (credentialStorePath != null) scriptParams.add(new ImmutablePair<>(RESOURCES.CREDENTIAL_STORE, "path="+credentialStorePath));
+
+ return scriptParams;
+ }
+
+ public String getRealmName() {
+ return this.realmName;
+ }
+
+ public boolean credentialStoreProvided() {
+ return this.credentialStorePath != null;
+ }
+
+ public ScriptParameters setRealmPath(Path realmPath) {
+ this.realmPath = realmPath.normalize().toAbsolutePath().toString();
+ return new ScriptParameters(this);
+ }
+ public ScriptParameters setKeyStorePassword(String keyStorePassword) {
+ this.keyStorePassword = keyStorePassword;
+ return new ScriptParameters(this);
+ }
+ public ScriptParameters setSecretKeyAlias(String secretKeyAlias) {
+ this.secretKeyAlias = secretKeyAlias;
+ return new ScriptParameters(this);
+ }
+ public ScriptParameters setLevels(String levels) {
+ this.levels = levels;
+ return new ScriptParameters(this);
+ }
+ public ScriptParameters setHashCharset(String hashCharset) {
+ this.hashCharset = hashCharset;
+ return new ScriptParameters(this);
+ }
+ public ScriptParameters setKeyStorePath(Path keyStorePath) {
+ this.keyStorePath = keyStorePath.normalize().toAbsolutePath().toString();
+ return new ScriptParameters(this);
+ }
+ public ScriptParameters setKeyStoreType(String keyStoreType) {
+ this.keyStoreType = keyStoreType;
+ return new ScriptParameters(this);
+ }
+ public ScriptParameters setKeyPairAlias(String keyPairAlias) {
+ this.keyPairAlias = keyPairAlias;
+ return new ScriptParameters(this);
+ }
+ public ScriptParameters setCredentialStorePath(Path credentialStorePath) {
+ this.credentialStorePath = credentialStorePath.normalize().toAbsolutePath().toString();
+ return new ScriptParameters(this);
+ }
+ }
+}
diff --git a/tool/src/test/resources/bulk-encryption-conversion-desc-without-names b/tool/src/test/resources/bulk-encryption-conversion-desc-without-names
index 734f6ec42b7..d28bded00a9 100644
--- a/tool/src/test/resources/bulk-encryption-conversion-desc-without-names
+++ b/tool/src/test/resources/bulk-encryption-conversion-desc-without-names
@@ -1,22 +1,22 @@
input-location:target/test-classes/filesystem-encrypt/fs-unencrypted-realms/multiple-credential-types
output-location:target/test-classes/filesystem-encrypt/fs-encrypted-realms
-credential-store:mycredstore.cs
+credential-store:target/test-classes/filesystem-encrypt/mycredstore.cs
create:true
levels:1
input-location:target/test-classes/filesystem-encrypt/fs-unencrypted-realms/level-4
output-location:target/test-classes/filesystem-encrypt/fs-encrypted-realms
-credential-store:mycredstore.cs
+credential-store:target/test-classes/filesystem-encrypt/mycredstore.cs
create:true
levels:4
-input-location:target/test-classes/filesystem-encrypt/fs-unencrypted-realms/hashcharset
+input-location:target/test-classes/filesystem-encrypt/fs-unencrypted-realms/fsRealmCharset
output-location:target/test-classes/filesystem-encrypt/fs-encrypted-realms
-credential-store:mycredstore.cs
+credential-store:target/test-classes/filesystem-encrypt/mycredstore.cs
create:true
input-location:target/test-classes/filesystem-encrypt/fs-unencrypted-realms/hashencoding
output-location:target/test-classes/filesystem-encrypt/fs-encrypted-realms
hash-encoding:hex
-credential-store:mycredstore.cs
+credential-store:target/test-classes/filesystem-encrypt/mycredstore.cs
create:true
diff --git a/tool/src/test/resources/bulk-integrity-conversion-desc b/tool/src/test/resources/bulk-integrity-conversion-desc
new file mode 100644
index 00000000000..bd5c8b4eba8
--- /dev/null
+++ b/tool/src/test/resources/bulk-integrity-conversion-desc
@@ -0,0 +1,28 @@
+input-location:target/test-classes/filesystem-integrity/fsRealmUpgradeInPlaceBulk
+keystore:target/test-classes/filesystem-integrity/fsKeyStore.pfx
+password:Guk]i%Aua4-wB
+
+input-location:target/test-classes/filesystem-integrity/fs-unsigned-realms/fsRealmEncrypted
+output-location:target/test-classes/filesystem-integrity/fs-signed-realms
+realm-name:fsRealmEncryptedBulk
+keystore:target/test-classes/filesystem-integrity/fsKeyStore.pfx
+type:pkcs12
+password:Guk]i%Aua4-wB
+key-pair:integrity-key
+credential-store:target/test-classes/filesystem-integrity/fsCredStore.cs
+secret-key:secKey
+levels:4
+hash-encoding:BASE64
+hash-charset:UTF-8
+encoded:true
+
+input-location:target/test-classes/filesystem-integrity/fs-unsigned-realms/fsRealmSingle
+output-location:target/test-classes/filesystem-integrity/fs-signed-realms
+keystore:target/test-classes/filesystem-integrity/fsKeyStore.pfx
+password-env:FS_INTEGRITY_PASSWORD_TEST_VAR
+
+input-location:target/test-classes/filesystem-integrity/fs-unsigned-realms/fsRealmHashEncoding
+output-location:target/test-classes/filesystem-integrity/fs-signed-realms
+keystore:target/test-classes/filesystem-integrity/fsKeyStore.pfx
+password:Guk]i%Aua4-wB
+hash-encoding:hex
diff --git a/tool/src/test/resources/bulk-integrity-conversion-desc-INVALID b/tool/src/test/resources/bulk-integrity-conversion-desc-INVALID
new file mode 100644
index 00000000000..3b5ed59468e
--- /dev/null
+++ b/tool/src/test/resources/bulk-integrity-conversion-desc-INVALID
@@ -0,0 +1,7 @@
+output-location:target/test-classes/filesystem-integrity/fs-signed-realms
+keystore:target/test-classes/filesystem-integrity/fsKeyStore.pfx
+password:Guk]i%Aua4-wB
+
+input-location:target/test-classes/filesystem-integrity/fs-unsigned-realms/fsRealmEmpty
+output-location:target/test-classes/filesystem-integrity/fs-signed-realms
+keystore:target/test-classes/filesystem-integrity/fsKeyStore.pfx
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-encrypt/fs-unencrypted-realms/single-user-with-roles-and-integrity/c/a/cameron-MNQW2ZLSN5XA.xml b/tool/src/test/resources/filesystem-encrypt/fs-unencrypted-realms/single-user-with-roles-and-integrity/c/a/cameron-MNQW2ZLSN5XA.xml
new file mode 100644
index 00000000000..f502e38f407
--- /dev/null
+++ b/tool/src/test/resources/filesystem-encrypt/fs-unencrypted-realms/single-user-with-roles-and-integrity/c/a/cameron-MNQW2ZLSN5XA.xml
@@ -0,0 +1,27 @@
+
+
+
+ A3YRu0iebR9tX8reCXbzm6xiKqWEAVnrKLClSLI20Cln
+
+
+
+
+
+
+ Od9LJmJdsDXCArHRe4J/2O8/NqZ8v9r5jRlm6RPwF9Q=OT+Ld9SyQyWWbtPXxfP8z3cj5icRLEMf4m9jUg1tVcIsAHfCEJVlpmEL9gpnKseG1RsKXJUVrsKe
+4qctW5vPbRKkiomhSjgPmWIZU3ufxO8CPJhTJGkYkG7vcoc7saHUOrvKwUFvcCQDsKEDoa+zZV7a
+VRvhP4hxnKzY+Moj0jYSSqscwW/+2yU5cmC/KY6L7IM8y/IxuPsGJvRUbMpExo9jXiPKTbNbITyu
+z66Hah1TQM2jxg7fvri0WuZ5hodkXtbLoXLRi4aGn1GU/pX4A4ZbJbB2GEmFiOpBgrjZAEAv9h8d
+9O3aP8cd5xbOvW9PZQB2pMuYQbwbBzPcEypl4Wlsllc3O8QXPucB9KHL024PHzIHlFuVVoY3eF0D
+RIptK7UHt6DrQHnDoeuQ9mFO9mzSXTnbpBm/mZfk5f029YPm9eK5Bv6Inh8YmGkF9lZ6iwGQRt6H
+jsAjIfUTB6A7nyONocwYHkCfh4zMrUc9PbunIEQQTVN7zN0Ar51tLjgzR08811o5H8Ac0YfDqY0I
+9JwPd8LDd7JAaP3GtKfuTypGa8uamP/4kdhAfYWINRbuPuvtj8IMlUPM8yR/zN6xMEowrBGLbyqZ
+khugOFlTXEuKVndn3IIWwPgJklH1Q14aend9zLEQoGUZCToLQYRWjiDgCbl/kwRDpB4BqgpvBco=sQJCJE08Q3X5f3v0VAmvoBb4eIymE32jilIdaHgiWD2NJgZIEt38CVfNqneyPTng8vs0EHgBC+L5
+uMt4/ig7ZOZN0Yy8uUosg8ByKqyStbCOLgH/jH884+vS8cd5D7w0TBy6F5GiuHXQ/8KjY3rGJjhv
+1gEIrXdWSCvW1dKdvgrBP/vb8j2bxvqGTwiVjEwZC38z2IEYQi9cWL3vBSfASaeM8dTFqYKdUEcq
+eZliZSrzsTtZwG4EeBfjHl16hwB3WnOebqoUuE8dojZTfO11rPojZCf/xGk12sE+3c5IfGJ95qq9
+TDXXQyrcOJpL8jGeTWRv4kPdgL6SqUz1Kp0uloGvGtPd18Qxrp+vf16573PYdW78uYv+/fg0hdkb
+thDMA/YCI01gnaph0r2iNXseO9aA24ySU9jOiTYYcQVEvRQlDp9Ha7j7HoVHeAvyoUDbsuLigLTt
+qXeLq1HfT4LwQUhhcGeQsPibNkiQV0X/HvUffA+dq2umscZy6TKy4e7wmQo2NPrRAZ4k+aS9ymGl
+VrwLYYygJtZY4KVgipPZzF2vOiPK37zhngcA74+ehUI5lA0LcTRppW1f9mM+ygUYTJp93MBLJGFN
+obMbdgsVjiPTviurxFO6Ip+dVcKlIYxPgHZ5Wcpap47qsmDjJI9QP0VLJ95GJHufSejTfHFuGAU=AQAB
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-encrypt/mykeystore.pfx b/tool/src/test/resources/filesystem-encrypt/mykeystore.pfx
new file mode 100644
index 00000000000..7cd56a98b3c
Binary files /dev/null and b/tool/src/test/resources/filesystem-encrypt/mykeystore.pfx differ
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealm/a/l/alice-MFWGSY3F.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealm/a/l/alice-MFWGSY3F.xml
new file mode 100644
index 00000000000..3e94e6776f9
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealm/a/l/alice-MFWGSY3F.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealm/b/o/bob-MJXWE.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealm/b/o/bob-MJXWE.xml
new file mode 100644
index 00000000000..3ab4c390194
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealm/b/o/bob-MJXWE.xml
@@ -0,0 +1,6 @@
+
+
+
+ AWJvYlBhc3N3b3JkMSE=
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealm/c/a/cameron-MNQW2ZLSN5XA.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealm/c/a/cameron-MNQW2ZLSN5XA.xml
new file mode 100644
index 00000000000..7beb401e84a
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealm/c/a/cameron-MNQW2ZLSN5XA.xml
@@ -0,0 +1,11 @@
+
+
+
+ A3YRu0iebR9tX8reCXbzm6xiKqWEAVnrKLClSLI20Cln
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmCharset/a/l/alice-MFWGSY3F.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmCharset/a/l/alice-MFWGSY3F.xml
new file mode 100644
index 00000000000..3e94e6776f9
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmCharset/a/l/alice-MFWGSY3F.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmCharset/b/o/bob-MJXWE.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmCharset/b/o/bob-MJXWE.xml
new file mode 100644
index 00000000000..b81f77d7ad1
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmCharset/b/o/bob-MJXWE.xml
@@ -0,0 +1,6 @@
+
+
+
+ ASdwYXNzd29yZEh5dsOkw6TDpCc=
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmCharset/c/a/cameron-MNQW2ZLSN5XA.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmCharset/c/a/cameron-MNQW2ZLSN5XA.xml
new file mode 100644
index 00000000000..7beb401e84a
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmCharset/c/a/cameron-MNQW2ZLSN5XA.xml
@@ -0,0 +1,11 @@
+
+
+
+ A3YRu0iebR9tX8reCXbzm6xiKqWEAVnrKLClSLI20Cln
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmEmpty/b/o/_git.placeholder b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmEmpty/b/o/_git.placeholder
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmEncrypted/M/F/W/G/MFWGSY3F.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmEncrypted/M/F/W/G/MFWGSY3F.xml
new file mode 100644
index 00000000000..c4830930e51
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmEncrypted/M/F/W/G/MFWGSY3F.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmEncrypted/M/J/X/W/MJXWE.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmEncrypted/M/J/X/W/MJXWE.xml
new file mode 100644
index 00000000000..3fd611dedf7
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmEncrypted/M/J/X/W/MJXWE.xml
@@ -0,0 +1,6 @@
+
+
+
+ RUxZAUMQea5tSojKFao5y+4qjBO3+nRdE6370ceCwT5v42VvyIc=
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmEncrypted/M/N/Q/W/MNQW2ZLSN5XA.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmEncrypted/M/N/Q/W/MNQW2ZLSN5XA.xml
new file mode 100644
index 00000000000..3921dabe8dc
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmEncrypted/M/N/Q/W/MNQW2ZLSN5XA.xml
@@ -0,0 +1,11 @@
+
+
+
+ RUxZAUMQmN2tER7sx6WCUfx5BZ+liMHRsRAWcomJsM5uQheDmOy2pIBFvxT6sFy3NCLaUPRE71sRRJyIA+jrtiabP+wMbw==
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmHashEncoding/a/l/alice-MFWGSY3F.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmHashEncoding/a/l/alice-MFWGSY3F.xml
new file mode 100644
index 00000000000..3e94e6776f9
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmHashEncoding/a/l/alice-MFWGSY3F.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmHashEncoding/b/o/bob-MJXWE.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmHashEncoding/b/o/bob-MJXWE.xml
new file mode 100644
index 00000000000..d6cf02fd084
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmHashEncoding/b/o/bob-MJXWE.xml
@@ -0,0 +1,6 @@
+
+
+
+ 01626f6250617373776f72643121
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmHashEncoding/c/a/cameron-MNQW2ZLSN5XA.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmHashEncoding/c/a/cameron-MNQW2ZLSN5XA.xml
new file mode 100644
index 00000000000..c1ec8b20822
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmHashEncoding/c/a/cameron-MNQW2ZLSN5XA.xml
@@ -0,0 +1,11 @@
+
+
+
+ 037611bb489e6d1f6d5fcade0976f39bac622aa5840159eb28b0a548b236d02967
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmIntegrityEnabled/a/l/alice-MFWGSY3F.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmIntegrityEnabled/a/l/alice-MFWGSY3F.xml
new file mode 100644
index 00000000000..1583b76f63f
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmIntegrityEnabled/a/l/alice-MFWGSY3F.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+ +veHZAO+xv2Z7hm1+1/oROvYQWqIq8HhZ7TJzdF6OhU=goJthSm1THNRu0Sym5MlkPxM5L/WJLdw6HvqlNKLaRe0V7oMVBU3fqxkQf6yBHLnXL7Hh9w0du+m
+pJyQQMc0YwysLasw9JyGujGhiLjr7vyPPR+PvGJDpm0RY0GYpKHoH5C2RQL4cJ3HeyNyRfI1rben
+di54RSrcMzWYObVA0RJd9EwacCqWuvGv1GZMPYLP/vWJfjHP2cEClwvAELtrwpxB84OouomZoVdE
+zv3tyKVSld2P6lmbn39yj+Hz8eFabM/GncAr0DTW98EpP+1TRWvvRlVAQ/aLWAAdifg9ZsAia4aB
+vTp1SDS7HQyfDnlBt66T5piHSTZDzM4wPlYPRnNnfFhIPYk4d9AKOgaFjPY1ppxd+wYaRSHblE4l
+svia0HUR/kWjCF8CFucCLO0unQh3vLLkBpO4uzQC3b1Cqxd/vUy4pQc0WCRzHATqJDI61+DDCMUN
+YcSgnszplhQn3C3BQFZ0elC93xi5b8Up3O9Jr7AZChahI+1prtp0WnZqQiom4NiqEZ22+CFUzUEu
+oeVEs2cYu6C3DB6SGJlNsL4JOHEvfUVZLoVqu7CPd6KYgIpEoTdNhY3n/ApghtKmkJpo1NxZLGfp
+sKA24oy3PwZRoXg8KcnZbc4yeZ4DsFVTSCGhlH8gvxqQfCd2pM4Y7fC9Eus1rlmNMzv+Lza9ovs=sQJCJE08Q3X5f3v0VAmvoBb4eIymE32jilIdaHgiWD2NJgZIEt38CVfNqneyPTng8vs0EHgBC+L5
+uMt4/ig7ZOZN0Yy8uUosg8ByKqyStbCOLgH/jH884+vS8cd5D7w0TBy6F5GiuHXQ/8KjY3rGJjhv
+1gEIrXdWSCvW1dKdvgrBP/vb8j2bxvqGTwiVjEwZC38z2IEYQi9cWL3vBSfASaeM8dTFqYKdUEcq
+eZliZSrzsTtZwG4EeBfjHl16hwB3WnOebqoUuE8dojZTfO11rPojZCf/xGk12sE+3c5IfGJ95qq9
+TDXXQyrcOJpL8jGeTWRv4kPdgL6SqUz1Kp0uloGvGtPd18Qxrp+vf16573PYdW78uYv+/fg0hdkb
+thDMA/YCI01gnaph0r2iNXseO9aA24ySU9jOiTYYcQVEvRQlDp9Ha7j7HoVHeAvyoUDbsuLigLTt
+qXeLq1HfT4LwQUhhcGeQsPibNkiQV0X/HvUffA+dq2umscZy6TKy4e7wmQo2NPrRAZ4k+aS9ymGl
+VrwLYYygJtZY4KVgipPZzF2vOiPK37zhngcA74+ehUI5lA0LcTRppW1f9mM+ygUYTJp93MBLJGFN
+obMbdgsVjiPTviurxFO6Ip+dVcKlIYxPgHZ5Wcpap47qsmDjJI9QP0VLJ95GJHufSejTfHFuGAU=AQAB
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmIntegrityEnabled/b/o/bob-MJXWE.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmIntegrityEnabled/b/o/bob-MJXWE.xml
new file mode 100644
index 00000000000..8f66fd23c17
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmIntegrityEnabled/b/o/bob-MJXWE.xml
@@ -0,0 +1,22 @@
+
+
+
+ AWJvYlBhc3N3b3JkMSE=
+
+ CCYTLliNvlcsrIrzBaV/mVsTr/wj/2b2aQnz36IP0aI=MlxRpJuJd8Dbd+Lg+Y5QH+rTIg4WDjVH+GFWqpq+PN4Z5QKUl8QJdjcB5GYBiV2fV2MqobDrqYqg
+mY//THUeyoRe9denp6qY/SO8Ho4TVimuu/dAH1+omJTPUmKn+V5K+Krnpb4vgPmdtrmFmgfmUHC+
+P50/VFWyJrXG74jAR49n1rRMN62GT62Yn5bvzczKmw8NLIf/SyYO0CNnNWZG7Pv0YTrQi9QDHbfu
++lg8ek5sJGPod7Vwr5vtE6eW0M7yYAdf/3Wz21OEVOfueo5/0pJ4CpUTkQYWSMPDmSg1xnzzrhFh
+UXZhsIwxtsZhJygDFtIQ477BqP2PQdnbAmxQbTa7aRZjALzZQE+5FQlOeBYQmQGNoGVfnzuMl3O9
+9isCJS4jEVvT5FhNq/r5JVJfOSAdsZgOEbuhJATGIYuzWV4nfNasBWYAw2wTZszEqj5vL0JHf2uZ
+fa8cKh3qSQQJ9DwVN6mtPLgMUG09RwYbAHXXYhi4S1StAoJ7fYH/Tw+jcAQ7V0swICOlxtNYPNX5
+3V6jnTIynGdv/pt2Pyxt+HqLSAIgpcKml2rNZHslgDkHxjdBrvVydVLli/rE0jrjy/1Sizoe5RSd
+NkrIseBJFJ+TzvcOI8C+azsp+hLyu/keXF5SWYq1dHL2TiRt5onn68imjr8Fos2jKd+TJFTINFI=sQJCJE08Q3X5f3v0VAmvoBb4eIymE32jilIdaHgiWD2NJgZIEt38CVfNqneyPTng8vs0EHgBC+L5
+uMt4/ig7ZOZN0Yy8uUosg8ByKqyStbCOLgH/jH884+vS8cd5D7w0TBy6F5GiuHXQ/8KjY3rGJjhv
+1gEIrXdWSCvW1dKdvgrBP/vb8j2bxvqGTwiVjEwZC38z2IEYQi9cWL3vBSfASaeM8dTFqYKdUEcq
+eZliZSrzsTtZwG4EeBfjHl16hwB3WnOebqoUuE8dojZTfO11rPojZCf/xGk12sE+3c5IfGJ95qq9
+TDXXQyrcOJpL8jGeTWRv4kPdgL6SqUz1Kp0uloGvGtPd18Qxrp+vf16573PYdW78uYv+/fg0hdkb
+thDMA/YCI01gnaph0r2iNXseO9aA24ySU9jOiTYYcQVEvRQlDp9Ha7j7HoVHeAvyoUDbsuLigLTt
+qXeLq1HfT4LwQUhhcGeQsPibNkiQV0X/HvUffA+dq2umscZy6TKy4e7wmQo2NPrRAZ4k+aS9ymGl
+VrwLYYygJtZY4KVgipPZzF2vOiPK37zhngcA74+ehUI5lA0LcTRppW1f9mM+ygUYTJp93MBLJGFN
+obMbdgsVjiPTviurxFO6Ip+dVcKlIYxPgHZ5Wcpap47qsmDjJI9QP0VLJ95GJHufSejTfHFuGAU=AQAB
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmIntegrityEnabled/c/a/cameron-MNQW2ZLSN5XA.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmIntegrityEnabled/c/a/cameron-MNQW2ZLSN5XA.xml
new file mode 100644
index 00000000000..f502e38f407
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmIntegrityEnabled/c/a/cameron-MNQW2ZLSN5XA.xml
@@ -0,0 +1,27 @@
+
+
+
+ A3YRu0iebR9tX8reCXbzm6xiKqWEAVnrKLClSLI20Cln
+
+
+
+
+
+
+ Od9LJmJdsDXCArHRe4J/2O8/NqZ8v9r5jRlm6RPwF9Q=OT+Ld9SyQyWWbtPXxfP8z3cj5icRLEMf4m9jUg1tVcIsAHfCEJVlpmEL9gpnKseG1RsKXJUVrsKe
+4qctW5vPbRKkiomhSjgPmWIZU3ufxO8CPJhTJGkYkG7vcoc7saHUOrvKwUFvcCQDsKEDoa+zZV7a
+VRvhP4hxnKzY+Moj0jYSSqscwW/+2yU5cmC/KY6L7IM8y/IxuPsGJvRUbMpExo9jXiPKTbNbITyu
+z66Hah1TQM2jxg7fvri0WuZ5hodkXtbLoXLRi4aGn1GU/pX4A4ZbJbB2GEmFiOpBgrjZAEAv9h8d
+9O3aP8cd5xbOvW9PZQB2pMuYQbwbBzPcEypl4Wlsllc3O8QXPucB9KHL024PHzIHlFuVVoY3eF0D
+RIptK7UHt6DrQHnDoeuQ9mFO9mzSXTnbpBm/mZfk5f029YPm9eK5Bv6Inh8YmGkF9lZ6iwGQRt6H
+jsAjIfUTB6A7nyONocwYHkCfh4zMrUc9PbunIEQQTVN7zN0Ar51tLjgzR08811o5H8Ac0YfDqY0I
+9JwPd8LDd7JAaP3GtKfuTypGa8uamP/4kdhAfYWINRbuPuvtj8IMlUPM8yR/zN6xMEowrBGLbyqZ
+khugOFlTXEuKVndn3IIWwPgJklH1Q14aend9zLEQoGUZCToLQYRWjiDgCbl/kwRDpB4BqgpvBco=sQJCJE08Q3X5f3v0VAmvoBb4eIymE32jilIdaHgiWD2NJgZIEt38CVfNqneyPTng8vs0EHgBC+L5
+uMt4/ig7ZOZN0Yy8uUosg8ByKqyStbCOLgH/jH884+vS8cd5D7w0TBy6F5GiuHXQ/8KjY3rGJjhv
+1gEIrXdWSCvW1dKdvgrBP/vb8j2bxvqGTwiVjEwZC38z2IEYQi9cWL3vBSfASaeM8dTFqYKdUEcq
+eZliZSrzsTtZwG4EeBfjHl16hwB3WnOebqoUuE8dojZTfO11rPojZCf/xGk12sE+3c5IfGJ95qq9
+TDXXQyrcOJpL8jGeTWRv4kPdgL6SqUz1Kp0uloGvGtPd18Qxrp+vf16573PYdW78uYv+/fg0hdkb
+thDMA/YCI01gnaph0r2iNXseO9aA24ySU9jOiTYYcQVEvRQlDp9Ha7j7HoVHeAvyoUDbsuLigLTt
+qXeLq1HfT4LwQUhhcGeQsPibNkiQV0X/HvUffA+dq2umscZy6TKy4e7wmQo2NPrRAZ4k+aS9ymGl
+VrwLYYygJtZY4KVgipPZzF2vOiPK37zhngcA74+ehUI5lA0LcTRppW1f9mM+ygUYTJp93MBLJGFN
+obMbdgsVjiPTviurxFO6Ip+dVcKlIYxPgHZ5Wcpap47qsmDjJI9QP0VLJ95GJHufSejTfHFuGAU=AQAB
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmInvalidIdentityVersion/a/l/alice-MFWGSY3F.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmInvalidIdentityVersion/a/l/alice-MFWGSY3F.xml
new file mode 100644
index 00000000000..56ed134dba7
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmInvalidIdentityVersion/a/l/alice-MFWGSY3F.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmInvalidIdentityVersion/b/o/bob-MJXWE.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmInvalidIdentityVersion/b/o/bob-MJXWE.xml
new file mode 100644
index 00000000000..233e051a1fb
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmInvalidIdentityVersion/b/o/bob-MJXWE.xml
@@ -0,0 +1,6 @@
+
+
+
+ AWJvYlBhc3N3b3JkMSE=
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmInvalidIdentityVersion/c/a/cameron-MNQW2ZLSN5XA.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmInvalidIdentityVersion/c/a/cameron-MNQW2ZLSN5XA.xml
new file mode 100644
index 00000000000..fd971ea8d65
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmInvalidIdentityVersion/c/a/cameron-MNQW2ZLSN5XA.xml
@@ -0,0 +1,11 @@
+
+
+
+ A3YRu0iebR9tX8reCXbzm6xiKqWEAVnrKLClSLI20Cln
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmMissingIdentityVersion/a/l/alice-MFWGSY3F.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmMissingIdentityVersion/a/l/alice-MFWGSY3F.xml
new file mode 100644
index 00000000000..7e960228ca4
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmMissingIdentityVersion/a/l/alice-MFWGSY3F.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmMissingIdentityVersion/b/o/bob-MJXWE.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmMissingIdentityVersion/b/o/bob-MJXWE.xml
new file mode 100644
index 00000000000..30e8e39a4eb
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmMissingIdentityVersion/b/o/bob-MJXWE.xml
@@ -0,0 +1,6 @@
+
+
+
+ AWJvYlBhc3N3b3JkMSE=
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmMissingIdentityVersion/c/a/cameron-MNQW2ZLSN5XA.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmMissingIdentityVersion/c/a/cameron-MNQW2ZLSN5XA.xml
new file mode 100644
index 00000000000..893da0afa6d
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmMissingIdentityVersion/c/a/cameron-MNQW2ZLSN5XA.xml
@@ -0,0 +1,11 @@
+
+
+
+ A3YRu0iebR9tX8reCXbzm6xiKqWEAVnrKLClSLI20Cln
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmNameEncoded/a/l/alice.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmNameEncoded/a/l/alice.xml
new file mode 100644
index 00000000000..3e94e6776f9
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmNameEncoded/a/l/alice.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmNameEncoded/b/o/bob.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmNameEncoded/b/o/bob.xml
new file mode 100644
index 00000000000..3ab4c390194
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmNameEncoded/b/o/bob.xml
@@ -0,0 +1,6 @@
+
+
+
+ AWJvYlBhc3N3b3JkMSE=
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmNameEncoded/c/a/cameron.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmNameEncoded/c/a/cameron.xml
new file mode 100644
index 00000000000..7beb401e84a
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmNameEncoded/c/a/cameron.xml
@@ -0,0 +1,11 @@
+
+
+
+ A3YRu0iebR9tX8reCXbzm6xiKqWEAVnrKLClSLI20Cln
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmSingle/b/o/bob-MJXWE.xml b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmSingle/b/o/bob-MJXWE.xml
new file mode 100644
index 00000000000..3ab4c390194
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fs-unsigned-realms/fsRealmSingle/b/o/bob-MJXWE.xml
@@ -0,0 +1,6 @@
+
+
+
+ AWJvYlBhc3N3b3JkMSE=
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fsCredStore.cs b/tool/src/test/resources/filesystem-integrity/fsCredStore.cs
new file mode 100644
index 00000000000..4740902262f
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fsCredStore.cs
@@ -0,0 +1,2 @@
+# Properties Credential Store (Do Not Modify)
+seckey=RUxZAUsEIZKOYq2I2hyBGESh1/TWE6j8ccxsHkG/oYwz/kqKSQ==
diff --git a/tool/src/test/resources/filesystem-integrity/fsKeyStore.pfx b/tool/src/test/resources/filesystem-integrity/fsKeyStore.pfx
new file mode 100644
index 00000000000..7cd56a98b3c
Binary files /dev/null and b/tool/src/test/resources/filesystem-integrity/fsKeyStore.pfx differ
diff --git a/tool/src/test/resources/filesystem-integrity/fsKeyStoreEC.jceks b/tool/src/test/resources/filesystem-integrity/fsKeyStoreEC.jceks
new file mode 100644
index 00000000000..112a898d01b
Binary files /dev/null and b/tool/src/test/resources/filesystem-integrity/fsKeyStoreEC.jceks differ
diff --git a/tool/src/test/resources/filesystem-integrity/fsKeyStoreEmpty.jks b/tool/src/test/resources/filesystem-integrity/fsKeyStoreEmpty.jks
new file mode 100644
index 00000000000..084510fdc92
Binary files /dev/null and b/tool/src/test/resources/filesystem-integrity/fsKeyStoreEmpty.jks differ
diff --git a/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlace/a/l/alice-MFWGSY3F.xml b/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlace/a/l/alice-MFWGSY3F.xml
new file mode 100644
index 00000000000..3e94e6776f9
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlace/a/l/alice-MFWGSY3F.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlace/b/o/bob-MJXWE.xml b/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlace/b/o/bob-MJXWE.xml
new file mode 100644
index 00000000000..3ab4c390194
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlace/b/o/bob-MJXWE.xml
@@ -0,0 +1,6 @@
+
+
+
+ AWJvYlBhc3N3b3JkMSE=
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlace/c/a/cameron-MNQW2ZLSN5XA.xml b/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlace/c/a/cameron-MNQW2ZLSN5XA.xml
new file mode 100644
index 00000000000..7beb401e84a
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlace/c/a/cameron-MNQW2ZLSN5XA.xml
@@ -0,0 +1,11 @@
+
+
+
+ A3YRu0iebR9tX8reCXbzm6xiKqWEAVnrKLClSLI20Cln
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlaceBulk/a/l/alice-MFWGSY3F.xml b/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlaceBulk/a/l/alice-MFWGSY3F.xml
new file mode 100644
index 00000000000..3e94e6776f9
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlaceBulk/a/l/alice-MFWGSY3F.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlaceBulk/b/o/bob-MJXWE.xml b/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlaceBulk/b/o/bob-MJXWE.xml
new file mode 100644
index 00000000000..3ab4c390194
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlaceBulk/b/o/bob-MJXWE.xml
@@ -0,0 +1,6 @@
+
+
+
+ AWJvYlBhc3N3b3JkMSE=
+
+
\ No newline at end of file
diff --git a/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlaceBulk/c/a/cameron-MNQW2ZLSN5XA.xml b/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlaceBulk/c/a/cameron-MNQW2ZLSN5XA.xml
new file mode 100644
index 00000000000..7beb401e84a
--- /dev/null
+++ b/tool/src/test/resources/filesystem-integrity/fsRealmUpgradeInPlaceBulk/c/a/cameron-MNQW2ZLSN5XA.xml
@@ -0,0 +1,11 @@
+
+
+
+ A3YRu0iebR9tX8reCXbzm6xiKqWEAVnrKLClSLI20Cln
+
+
+
+
+
+
+
\ No newline at end of file