diff --git a/src/citra_qt/game_list_worker.cpp b/src/citra_qt/game_list_worker.cpp
index a20ae4794..de02e79c0 100644
--- a/src/citra_qt/game_list_worker.cpp
+++ b/src/citra_qt/game_list_worker.cpp
@@ -47,12 +47,16 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
         if (!is_dir && HasSupportedFileExtension(physical_name)) {
             std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
             if (!loader)
+            {
                 return true;
+            }
 
             bool executable = false;
-            loader->IsExecutable(executable);
-            if (!executable)
+            auto res  = loader->IsExecutable(executable);
+            if (!executable && res != Loader::ResultStatus::ErrorEncrypted)
+            {
                 return true;
+            }
 
             u64 program_id = 0;
             loader->ReadProgramId(program_id);
diff --git a/src/core/file_sys/ncch_container.cpp b/src/core/file_sys/ncch_container.cpp
index 056f7a901..651b0d307 100644
--- a/src/core/file_sys/ncch_container.cpp
+++ b/src/core/file_sys/ncch_container.cpp
@@ -133,8 +133,37 @@ Loader::ResultStatus NCCHContainer::OpenFile(const std::string& filepath, u32 nc
     return Loader::ResultStatus::Success;
 }
 
+Loader::ResultStatus NCCHContainer::LoadHeader() {
+    if (has_header)
+        return Loader::ResultStatus::Success;
+    if (!file.IsOpen()) {
+
+        return Loader::ResultStatus::Error;
+    }
+
+    // Reset read pointer in case this file has been read before.
+    file.Seek(ncch_offset, SEEK_SET);
+
+    if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header))
+        return Loader::ResultStatus::Error;
+
+    // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
+    if (Loader::MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
+        LOG_DEBUG(Service_FS, "Only loading the first (bootable) NCCH within the NCSD file!");
+        ncch_offset += 0x4000;
+        file.Seek(ncch_offset, SEEK_SET);
+        file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
+    }
+
+    // Verify we are loading the correct file type...
+    if (Loader::MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic)
+        return Loader::ResultStatus::ErrorInvalidFormat;
+
+    has_header = true;
+    return Loader::ResultStatus::Success;
+}
+
 Loader::ResultStatus NCCHContainer::Load() {
-    LOG_INFO(Service_FS, "Loading NCCH from file {}", filepath);
     if (is_loaded)
         return Loader::ResultStatus::Success;
 
@@ -697,7 +726,7 @@ Loader::ResultStatus NCCHContainer::ReadOverrideRomFS(std::shared_ptr<RomFSReade
 }
 
 Loader::ResultStatus NCCHContainer::ReadProgramId(u64_le& program_id) {
-    Loader::ResultStatus result = Load();
+    Loader::ResultStatus result = LoadHeader();
     if (result != Loader::ResultStatus::Success)
         return result;
 
diff --git a/src/core/file_sys/ncch_container.h b/src/core/file_sys/ncch_container.h
index 7eadd9835..fa435cee0 100644
--- a/src/core/file_sys/ncch_container.h
+++ b/src/core/file_sys/ncch_container.h
@@ -210,6 +210,12 @@ public:
 
     Loader::ResultStatus OpenFile(const std::string& filepath, u32 ncch_offset = 0);
 
+    /**
+     * Ensure NCCH header is loaded and ready for reading sections
+     * @return ResultStatus result of function
+     */
+    Loader::ResultStatus LoadHeader();
+
     /**
      * Ensure ExeFS and exheader is loaded and ready for reading sections
      * @return ResultStatus result of function
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 65b9c3e01..79ae93130 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -11,6 +11,7 @@
 #include <fmt/format.h>
 #include "common/file_util.h"
 #include "common/logging/log.h"
+#include "common/common_paths.h"
 #include "common/string_util.h"
 #include "core/core.h"
 #include "core/file_sys/errors.h"
@@ -33,6 +34,16 @@
 #include "core/loader/loader.h"
 #include "core/loader/smdh.h"
 
+namespace {
+bool HasSupportedFileExtension(std::string path) {
+    static const std::array<std::string, 7> extensions = {{".3ds", ".3dsx", ".elf", ".axf",
+    ".cci", ".cxi" ".app"
+    }};
+    const auto file_ext = FileUtil::GetExtensionFromFilename(path);
+    return std::find(extensions.begin(), extensions.end(), file_ext) != extensions.end();
+}
+}
+
 namespace Service::AM {
 
 constexpr u16 PLATFORM_CTR = 0x0004;
@@ -373,6 +384,36 @@ InstallStatus InstallCIA(const std::string& path,
         installFile.Close();
 
         LOG_INFO(Service_AM, "Installed {} successfully.", path);
+
+        const FileUtil::DirectoryEntryCallable callback = [&callback](u64* num_entries_out,
+                                                        const std::string& directory,
+                                                        const std::string& virtual_name) -> bool {
+            const std::string physical_name = directory + DIR_SEP + virtual_name;
+            const bool is_dir = FileUtil::IsDirectory(physical_name);
+            if (!is_dir && HasSupportedFileExtension(physical_name)) {
+                std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
+                if (!loader)
+                {
+                return true;
+                }
+
+                bool executable = false;
+                auto res  = loader->IsExecutable(executable);
+                if (res == Loader::ResultStatus::ErrorEncrypted)
+                {
+                    return false;
+                }
+                return true;
+            } else {
+                return FileUtil::ForeachDirectoryEntry(nullptr, physical_name, callback);
+            }
+
+        };
+        if (!FileUtil::ForeachDirectoryEntry(nullptr, path, callback))
+        {
+            LOG_ERROR(Service_AM, "CIA {} contained encrypted files.", path);
+            return InstallStatus::ErrorEncrypted;
+        }
         return InstallStatus::Success;
     }