diff --git a/core/ajax/update.php b/core/ajax/update.php
index 7da9b71b7510e8e9759c7ab0e6b993442c8437e5..a5c1f79e3eaaa332f223ce6f91ea4ddcc6b47b44 100644
--- a/core/ajax/update.php
+++ b/core/ajax/update.php
@@ -47,6 +47,7 @@ if (OC::checkUpgrade(false)) {
 	$updater = new \OC\Updater(
 			\OC::$server->getHTTPHelper(),
 			$config,
+			\OC::$server->getIntegrityCodeChecker(),
 			$logger
 	);
 	$incompatibleApps = [];
@@ -108,6 +109,12 @@ if (OC::checkUpgrade(false)) {
 	$updater->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use($eventSource, $l) {
 		$eventSource->send('success', (string)$l->t('Reset log level to  "%s"', [ $logLevelName ]));
 	});
+	$updater->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use($eventSource, $l) {
+		$eventSource->send('success', (string)$l->t('Starting code integrity check'));
+	});
+	$updater->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use($eventSource, $l) {
+		$eventSource->send('success', (string)$l->t('Finished code integrity check'));
+	});
 
 	try {
 		$updater->upgrade();
diff --git a/core/command/integrity/signapp.php b/core/command/integrity/signapp.php
new file mode 100644
index 0000000000000000000000000000000000000000..83a7972068f254ace769d44a7bb0feae02bdb5a5
--- /dev/null
+++ b/core/command/integrity/signapp.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Integrity;
+
+use OC\IntegrityCheck\Checker;
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use phpseclib\Crypt\RSA;
+use phpseclib\File\X509;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Class SignApp
+ *
+ * @package OC\Core\Command\Integrity
+ */
+class SignApp extends Command {
+	/** @var Checker */
+	private $checker;
+	/** @var FileAccessHelper */
+	private $fileAccessHelper;
+
+	/**
+	 * @param Checker $checker
+	 * @param FileAccessHelper $fileAccessHelper
+	 */
+	public function __construct(Checker $checker,
+								FileAccessHelper $fileAccessHelper) {
+		parent::__construct(null);
+		$this->checker = $checker;
+		$this->fileAccessHelper = $fileAccessHelper;
+	}
+
+	protected function configure() {
+		$this
+			->setName('integrity:sign-app')
+			->setDescription('Sign app using a private key.')
+			->addOption('appId', null, InputOption::VALUE_REQUIRED, 'Application to sign')
+			->addOption('privateKey', null, InputOption::VALUE_REQUIRED, 'Path to private key to use for signing')
+			->addOption('certificate', null, InputOption::VALUE_REQUIRED, 'Path to certificate to use for signing');
+	}
+
+	/**
+	 * {@inheritdoc }
+	 */
+	protected function execute(InputInterface $input, OutputInterface $output) {
+		$appId = $input->getOption('appId');
+		$privateKeyPath = $input->getOption('privateKey');
+		$keyBundlePath = $input->getOption('certificate');
+		if(is_null($appId) || is_null($privateKeyPath) || is_null($keyBundlePath)) {
+			$output->writeln('--appId, --privateKey and --certificate are required.');
+			return null;
+		}
+
+		$privateKey = $this->fileAccessHelper->file_get_contents($privateKeyPath);
+		$keyBundle = $this->fileAccessHelper->file_get_contents($keyBundlePath);
+
+		if($privateKey === false) {
+			$output->writeln(sprintf('Private key "%s" does not exists.', $privateKeyPath));
+			return null;
+		}
+
+		if($keyBundle === false) {
+			$output->writeln(sprintf('Certificate "%s" does not exists.', $keyBundlePath));
+			return null;
+		}
+
+		$rsa = new RSA();
+		$rsa->loadKey($privateKey);
+		$x509 = new X509();
+		$x509->loadX509($keyBundle);
+		$x509->setPrivateKey($rsa);
+		$this->checker->writeAppSignature($appId, $x509, $rsa);
+
+		$output->writeln('Successfully signed "'.$appId.'"');
+	}
+}
diff --git a/core/command/integrity/signcore.php b/core/command/integrity/signcore.php
new file mode 100644
index 0000000000000000000000000000000000000000..4d097ad4f967a6804d1bbdcd917c5114c48f03a4
--- /dev/null
+++ b/core/command/integrity/signcore.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Integrity;
+
+use OC\IntegrityCheck\Checker;
+use OC\IntegrityCheck\Helpers\EnvironmentHelper;
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use phpseclib\Crypt\RSA;
+use phpseclib\File\X509;
+use Symfony\Component\Console\Command\Command;
+use OCP\IConfig;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Class SignCore
+ *
+ * @package OC\Core\Command\Integrity
+ */
+class SignCore extends Command {
+	/** @var Checker */
+	private $checker;
+	/** @var FileAccessHelper */
+	private $fileAccessHelper;
+
+	/**
+	 * @param Checker $checker
+	 * @param FileAccessHelper $fileAccessHelper
+	 */
+	public function __construct(Checker $checker,
+								FileAccessHelper $fileAccessHelper) {
+		parent::__construct(null);
+		$this->checker = $checker;
+		$this->fileAccessHelper = $fileAccessHelper;
+	}
+
+	protected function configure() {
+		$this
+			->setName('integrity:sign-core')
+			->setDescription('Sign core using a private key.')
+			->addOption('privateKey', null, InputOption::VALUE_REQUIRED, 'Path to private key to use for signing')
+			->addOption('certificate', null, InputOption::VALUE_REQUIRED, 'Path to certificate to use for signing');
+	}
+
+	/**
+	 * {@inheritdoc }
+	 */
+	protected function execute(InputInterface $input, OutputInterface $output) {
+		$privateKeyPath = $input->getOption('privateKey');
+		$keyBundlePath = $input->getOption('certificate');
+		if(is_null($privateKeyPath) || is_null($keyBundlePath)) {
+			$output->writeln('--privateKey and --certificate are required.');
+			return null;
+		}
+
+		$privateKey = $this->fileAccessHelper->file_get_contents($privateKeyPath);
+		$keyBundle = $this->fileAccessHelper->file_get_contents($keyBundlePath);
+
+		if($privateKey === false) {
+			$output->writeln(sprintf('Private key "%s" does not exists.', $privateKeyPath));
+			return null;
+		}
+
+		if($keyBundle === false) {
+			$output->writeln(sprintf('Certificate "%s" does not exists.', $keyBundlePath));
+			return null;
+		}
+
+		$rsa = new RSA();
+		$rsa->loadKey($privateKey);
+		$x509 = new X509();
+		$x509->loadX509($keyBundle);
+		$x509->setPrivateKey($rsa);
+		$this->checker->writeCoreSignature($x509, $rsa);
+
+		$output->writeln('Successfully signed "core"');
+	}
+}
diff --git a/core/command/upgrade.php b/core/command/upgrade.php
index d1a7c09c30c349cb787c6065281df7d9d396a57f..9031e284f85f7eee23cc60e843cfb967f83794c5 100644
--- a/core/command/upgrade.php
+++ b/core/command/upgrade.php
@@ -53,6 +53,7 @@ class Upgrade extends Command {
 
 	/**
 	 * @param IConfig $config
+	 * @param ILogger $logger
 	 */
 	public function __construct(IConfig $config, ILogger $logger) {
 		parent::__construct();
@@ -122,9 +123,12 @@ class Upgrade extends Command {
 			}
 
 			$self = $this;
-			$updater = new Updater(\OC::$server->getHTTPHelper(),
-				$this->config,
-				$this->logger);
+			$updater = new Updater(
+					\OC::$server->getHTTPHelper(),
+					$this->config,
+					\OC::$server->getIntegrityCodeChecker(),
+					$this->logger
+			);
 
 			$updater->setSimulateStepEnabled($simulateStepEnabled);
 			$updater->setUpdateStepEnabled($updateStepEnabled);
diff --git a/core/js/integritycheck-failed-notification.js b/core/js/integritycheck-failed-notification.js
new file mode 100644
index 0000000000000000000000000000000000000000..5bc758d54cb39a49f55205242650226c3dfbe7c0
--- /dev/null
+++ b/core/js/integritycheck-failed-notification.js
@@ -0,0 +1,38 @@
+/**
+ * @author Lukas Reschke
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * This gets only loaded if the integrity check has failed and then shows a notification
+ */
+$(document).ready(function(){
+	var text = t(
+			'core',
+			'<a href="{docUrl}">There were problems with the code integrity check. More information…</a>',
+			{
+				docUrl: OC.generateUrl('/settings/admin#security-warning')
+			}
+	);
+
+	OC.Notification.showHtml(
+		text,
+		{
+			isHTML: true
+		}
+	);
+});
+
diff --git a/core/js/setupchecks.js b/core/js/setupchecks.js
index 6e2058d54fc63ae8335ecdaad83c18d4ba6acc6e..5ac1945da735e1bf11e4434ebaff43088703d602 100644
--- a/core/js/setupchecks.js
+++ b/core/js/setupchecks.js
@@ -104,6 +104,20 @@
 							type: OC.SetupChecks.MESSAGE_TYPE_WARNING
 						});
 					}
+					if(!data.hasPassedCodeIntegrityCheck) {
+						messages.push({
+							msg: t(
+									'core',
+									'Some files have not passed the integrity check. Further information on how to resolve this issue can be found in our <a href="{docLink}">documentation</a>. (<a href="{codeIntegrityDownloadEndpoint}">List of invalid files…</a> / <a href="{rescanEndpoint}">Rescan…</a>)',
+									{
+										docLink: data.codeIntegrityCheckerDocumentation,
+										codeIntegrityDownloadEndpoint: OC.generateUrl('/settings/integrity/failed'),
+										rescanEndpoint: OC.generateUrl('/settings/integrity/rescan?requesttoken={requesttoken}', {'requesttoken': OC.requestToken})
+									}
+							),
+							type: OC.SetupChecks.MESSAGE_TYPE_ERROR
+						});
+					}
 				} else {
 					messages.push({
 						msg: t('core', 'Error occurred while checking server setup'),
diff --git a/core/js/tests/specs/setupchecksSpec.js b/core/js/tests/specs/setupchecksSpec.js
index 8dd2214621af27c3ebfa25cf905232d1d148a6ad..4bad893cf37888a94e40c92e108781015a703e39 100644
--- a/core/js/tests/specs/setupchecksSpec.js
+++ b/core/js/tests/specs/setupchecksSpec.js
@@ -75,6 +75,7 @@ describe('OC.SetupChecks tests', function() {
 					memcacheDocs: 'https://doc.owncloud.org/server/go.php?to=admin-performance',
 					forwardedForHeadersWorking: true,
 					isCorrectMemcachedPHPModuleInstalled: true,
+					hasPassedCodeIntegrityCheck: true,
 				})
 			);
 
@@ -109,6 +110,7 @@ describe('OC.SetupChecks tests', function() {
 					memcacheDocs: 'https://doc.owncloud.org/server/go.php?to=admin-performance',
 					forwardedForHeadersWorking: true,
 					isCorrectMemcachedPHPModuleInstalled: true,
+					hasPassedCodeIntegrityCheck: true,
 				})
 			);
 
@@ -145,6 +147,7 @@ describe('OC.SetupChecks tests', function() {
 					isMemcacheConfigured: true,
 					forwardedForHeadersWorking: true,
 					isCorrectMemcachedPHPModuleInstalled: true,
+					hasPassedCodeIntegrityCheck: true,
 				})
 			);
 
@@ -178,6 +181,7 @@ describe('OC.SetupChecks tests', function() {
 					isMemcacheConfigured: true,
 					forwardedForHeadersWorking: true,
 					isCorrectMemcachedPHPModuleInstalled: true,
+					hasPassedCodeIntegrityCheck: true,
 				})
 			);
 
@@ -206,6 +210,7 @@ describe('OC.SetupChecks tests', function() {
 					isMemcacheConfigured: true,
 					forwardedForHeadersWorking: true,
 					isCorrectMemcachedPHPModuleInstalled: false,
+					hasPassedCodeIntegrityCheck: true,
 				})
 			);
 
@@ -234,6 +239,7 @@ describe('OC.SetupChecks tests', function() {
 					forwardedForHeadersWorking: false,
 					reverseProxyDocs: 'https://docs.owncloud.org/foo/bar.html',
 					isCorrectMemcachedPHPModuleInstalled: true,
+					hasPassedCodeIntegrityCheck: true,
 				})
 			);
 
@@ -283,6 +289,7 @@ describe('OC.SetupChecks tests', function() {
 					forwardedForHeadersWorking: true,
 					phpSupported: {eol: true, version: '5.4.0'},
 					isCorrectMemcachedPHPModuleInstalled: true,
+					hasPassedCodeIntegrityCheck: true,
 				})
 			);
 
diff --git a/core/register_command.php b/core/register_command.php
index 4044d2d200cad56707087d0fe049edf25a7550fa..16dda55878ece8dae33dca18de655204e83053d6 100644
--- a/core/register_command.php
+++ b/core/register_command.php
@@ -32,6 +32,14 @@ $application->add(new OC\Core\Command\Check(\OC::$server->getConfig()));
 $infoParser = new \OC\App\InfoParser(\OC::$server->getHTTPHelper(), \OC::$server->getURLGenerator());
 $application->add(new OC\Core\Command\App\CheckCode($infoParser));
 $application->add(new OC\Core\Command\L10n\CreateJs());
+$application->add(new \OC\Core\Command\Integrity\SignApp(
+		\OC::$server->getIntegrityCodeChecker(),
+		new \OC\IntegrityCheck\Helpers\FileAccessHelper()
+));
+$application->add(new \OC\Core\Command\Integrity\SignCore(
+		\OC::$server->getIntegrityCodeChecker(),
+		new \OC\IntegrityCheck\Helpers\FileAccessHelper()
+));
 
 if (\OC::$server->getConfig()->getSystemValue('installed', false)) {
 	$application->add(new OC\Core\Command\App\Disable());
diff --git a/lib/private/integritycheck/checker.php b/lib/private/integritycheck/checker.php
new file mode 100644
index 0000000000000000000000000000000000000000..edfe6b082e7db73446a5e971d6cd1e06835429ac
--- /dev/null
+++ b/lib/private/integritycheck/checker.php
@@ -0,0 +1,449 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\IntegrityCheck;
+
+use OC\IntegrityCheck\Exceptions\InvalidSignatureException;
+use OC\IntegrityCheck\Helpers\AppLocator;
+use OC\IntegrityCheck\Helpers\EnvironmentHelper;
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use OC\Integritycheck\Iterator\ExcludeFileByNameFilterIterator;
+use OC\IntegrityCheck\Iterator\ExcludeFoldersByPathFilterIterator;
+use OCP\App\IAppManager;
+use OCP\ICache;
+use OCP\ICacheFactory;
+use OCP\IConfig;
+use phpseclib\Crypt\RSA;
+use phpseclib\File\X509;
+
+/**
+ * Class Checker handles the code signing using X.509 and RSA. ownCloud ships with
+ * a public root certificate certificate that allows to issue new certificates that
+ * will be trusted for signing code. The CN will be used to verify that a certificate
+ * given to a third-party developer may not be used for other applications. For
+ * example the author of the application "calendar" would only receive a certificate
+ * only valid for this application.
+ *
+ * @package OC\IntegrityCheck
+ */
+class Checker {
+	const CACHE_KEY = 'oc.integritycheck.checker';
+	/** @var EnvironmentHelper */
+	private $environmentHelper;
+	/** @var AppLocator */
+	private $appLocator;
+	/** @var FileAccessHelper */
+	private $fileAccessHelper;
+	/** @var IConfig */
+	private $config;
+	/** @var ICache */
+	private $cache;
+	/** @var IAppManager */
+	private $appManager;
+
+	/**
+	 * @param EnvironmentHelper $environmentHelper
+	 * @param FileAccessHelper $fileAccessHelper
+	 * @param AppLocator $appLocator
+	 * @param IConfig $config
+	 * @param ICacheFactory $cacheFactory
+	 * @param IAppManager $appManager
+	 */
+	public function __construct(EnvironmentHelper $environmentHelper,
+								FileAccessHelper $fileAccessHelper,
+								AppLocator $appLocator,
+								IConfig $config = null,
+								ICacheFactory $cacheFactory,
+								IAppManager $appManager = null) {
+		$this->environmentHelper = $environmentHelper;
+		$this->fileAccessHelper = $fileAccessHelper;
+		$this->appLocator = $appLocator;
+		$this->config = $config;
+		$this->cache = $cacheFactory->create(self::CACHE_KEY);
+		$this->appManager = $appManager;
+	}
+
+	/**
+	 * Enumerates all files belonging to the folder. Sensible defaults are excluded.
+	 *
+	 * @param string $folderToIterate
+	 * @return \RecursiveIteratorIterator
+	 */
+	private function getFolderIterator($folderToIterate) {
+		$dirItr = new \RecursiveDirectoryIterator(
+			$folderToIterate,
+			\RecursiveDirectoryIterator::SKIP_DOTS
+		);
+		$excludeGenericFilesIterator = new ExcludeFileByNameFilterIterator($dirItr);
+		$excludeFoldersIterator = new ExcludeFoldersByPathFilterIterator($excludeGenericFilesIterator);
+
+		return new \RecursiveIteratorIterator(
+			$excludeFoldersIterator,
+			\RecursiveIteratorIterator::SELF_FIRST
+		);
+	}
+
+	/**
+	 * Returns an array of ['filename' => 'SHA512-hash-of-file'] for all files found
+	 * in the iterator.
+	 *
+	 * @param \RecursiveIteratorIterator $iterator
+	 * @param string $path
+	 * @return array Array of hashes.
+	 */
+	private function generateHashes(\RecursiveIteratorIterator $iterator,
+									$path) {
+		$hashes = [];
+
+		$baseDirectoryLength = strlen($path);
+		foreach($iterator as $filename => $data) {
+			/** @var \DirectoryIterator $data */
+			if($data->isDir()) {
+				continue;
+			}
+
+			$relativeFileName = substr($filename, $baseDirectoryLength);
+
+			// Exclude signature.json files in the appinfo and root folder
+			if($relativeFileName === '/appinfo/signature.json') {
+				continue;
+			}
+			// Exclude signature.json files in the appinfo and core folder
+			if($relativeFileName === '/core/signature.json') {
+				continue;
+			}
+
+			$hashes[$relativeFileName] = hash_file('sha512', $filename);
+		}
+		return $hashes;
+	}
+
+	/**
+	 * Creates the signature data
+	 *
+	 * @param array $hashes
+	 * @param X509 $certificate
+	 * @param RSA $privateKey
+	 * @return string
+	 */
+	private function createSignatureData(array $hashes,
+										 X509 $certificate,
+										 RSA $privateKey) {
+		ksort($hashes);
+
+		$privateKey->setSignatureMode(RSA::SIGNATURE_PSS);
+		$privateKey->setMGFHash('sha512');
+		$signature = $privateKey->sign(json_encode($hashes));
+
+		return [
+				'hashes' => $hashes,
+				'signature' => base64_encode($signature),
+				'certificate' => $certificate->saveX509($certificate->currentCert),
+			];
+	}
+
+	/**
+	 * Write the signature of the specified app
+	 *
+	 * @param string $appId
+	 * @param X509 $certificate
+	 * @param RSA $privateKey
+	 * @throws \Exception
+	 */
+	public function writeAppSignature($appId,
+									  X509 $certificate,
+									  RSA $privateKey) {
+		$path = $this->appLocator->getAppPath($appId);
+		$iterator = $this->getFolderIterator($path);
+		$hashes = $this->generateHashes($iterator, $path);
+		$signature = $this->createSignatureData($hashes, $certificate, $privateKey);
+		$this->fileAccessHelper->file_put_contents(
+				$path . '/appinfo/signature.json',
+				json_encode($signature, JSON_PRETTY_PRINT)
+		);
+	}
+
+	/**
+	 * Write the signature of core
+	 *
+	 * @param X509 $certificate
+	 * @param RSA $rsa
+	 */
+	public function writeCoreSignature(X509 $certificate,
+									   RSA $rsa) {
+		$iterator = $this->getFolderIterator($this->environmentHelper->getServerRoot());
+		$hashes = $this->generateHashes($iterator, $this->environmentHelper->getServerRoot());
+		$signatureData = $this->createSignatureData($hashes, $certificate, $rsa);
+		$this->fileAccessHelper->file_put_contents(
+				$this->environmentHelper->getServerRoot() . '/core/signature.json',
+				json_encode($signatureData, JSON_PRETTY_PRINT)
+		);
+	}
+
+	/**
+	 * Verifies the signature for the specified path.
+	 *
+	 * @param string $signaturePath
+	 * @param string $basePath
+	 * @param string $certificateCN
+	 * @return array
+	 * @throws InvalidSignatureException
+	 * @throws \Exception
+	 */
+	private function verify($signaturePath, $basePath, $certificateCN) {
+		$signatureData = json_decode($this->fileAccessHelper->file_get_contents($signaturePath), true);
+		if(!is_array($signatureData)) {
+			throw new InvalidSignatureException('Signature data not found.');
+		}
+
+		$expectedHashes = $signatureData['hashes'];
+		ksort($expectedHashes);
+		$signature = base64_decode($signatureData['signature']);
+		$certificate = $signatureData['certificate'];
+
+		// Check if certificate is signed by ownCloud Root Authority
+		$x509 = new \phpseclib\File\X509();
+		$rootCertificatePublicKey = $this->fileAccessHelper->file_get_contents($this->environmentHelper->getServerRoot().'/resources/codesigning/root.crt');
+		$x509->loadCA($rootCertificatePublicKey);
+		$x509->loadX509($certificate);
+		if(!$x509->validateSignature()) {
+			throw new InvalidSignatureException('Certificate is not valid.');
+		}
+		// Verify if certificate has proper CN. "core" CN is always trusted.
+		if($x509->getDN(X509::DN_OPENSSL)['CN'] !== $certificateCN && $x509->getDN(X509::DN_OPENSSL)['CN'] !== 'core') {
+			throw new InvalidSignatureException(
+					sprintf('Certificate is not valid for required scope. (Requested: %s, current: %s)', $certificateCN, $x509->getDN(true))
+			);
+		}
+
+		// Check if the signature of the files is valid
+		$rsa = new \phpseclib\Crypt\RSA();
+		$rsa->loadKey($x509->currentCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']);
+		$rsa->setSignatureMode(RSA::SIGNATURE_PSS);
+		$rsa->setMGFHash('sha512');
+		if(!$rsa->verify(json_encode($expectedHashes), $signature)) {
+			throw new InvalidSignatureException('Signature could not get verified.');
+		}
+
+		// Compare the list of files which are not identical
+		$currentInstanceHashes = $this->generateHashes($this->getFolderIterator($basePath), $basePath);
+		$differencesA = array_diff($expectedHashes, $currentInstanceHashes);
+		$differencesB = array_diff($currentInstanceHashes, $expectedHashes);
+		$differences = array_unique(array_merge($differencesA, $differencesB));
+		$differenceArray = [];
+		foreach($differences as $filename => $hash) {
+			// Check if file should not exist in the new signature table
+			if(!array_key_exists($filename, $expectedHashes)) {
+				$differenceArray['EXTRA_FILE'][$filename]['expected'] = '';
+				$differenceArray['EXTRA_FILE'][$filename]['current'] = $hash;
+				continue;
+			}
+
+			// Check if file is missing
+			if(!array_key_exists($filename, $currentInstanceHashes)) {
+				$differenceArray['FILE_MISSING'][$filename]['expected'] = $expectedHashes[$filename];
+				$differenceArray['FILE_MISSING'][$filename]['current'] = '';
+				continue;
+			}
+
+			// Check if hash does mismatch
+			if($expectedHashes[$filename] !== $currentInstanceHashes[$filename]) {
+				$differenceArray['INVALID_HASH'][$filename]['expected'] = $expectedHashes[$filename];
+				$differenceArray['INVALID_HASH'][$filename]['current'] = $currentInstanceHashes[$filename];
+				continue;
+			}
+
+			// Should never happen.
+			throw new \Exception('Invalid behaviour in file hash comparison experienced. Please report this error to the developers.');
+		}
+
+		return $differenceArray;
+	}
+
+	/**
+	 * Whether the code integrity check has passed successful or not
+	 *
+	 * @return bool
+	 */
+	public function hasPassedCheck() {
+		$results = $this->getResults();
+		if(empty($results)) {
+			return true;
+		}
+
+		return false;
+	}
+
+	/**
+	 * @return array
+	 */
+	public function getResults() {
+		$cachedResults = $this->cache->get(self::CACHE_KEY);
+		if(!is_null($cachedResults)) {
+			return json_decode($cachedResults, true);
+		}
+
+		return json_decode($this->config->getAppValue('core', self::CACHE_KEY, '{}'), true);
+	}
+
+	/**
+	 * Stores the results in the app config as well as cache
+	 *
+	 * @param string $scope
+	 * @param array $result
+	 */
+	private function storeResults($scope, array $result) {
+		$resultArray = $this->getResults();
+		unset($resultArray[$scope]);
+		if(!empty($result)) {
+			$resultArray[$scope] = $result;
+		}
+		$this->config->setAppValue('core', self::CACHE_KEY, json_encode($resultArray));
+		$this->cache->set(self::CACHE_KEY, json_encode($resultArray));
+	}
+
+
+	/**
+	 * Verify the signature of $appId. Returns an array with the following content:
+	 * [
+	 * 	'FILE_MISSING' =>
+	 * 	[
+	 * 		'filename' => [
+	 * 			'expected' => 'expectedSHA512',
+	 * 			'current' => 'currentSHA512',
+	 * 		],
+	 * 	],
+	 * 	'EXTRA_FILE' =>
+	 * 	[
+	 * 		'filename' => [
+	 * 			'expected' => 'expectedSHA512',
+	 * 			'current' => 'currentSHA512',
+	 * 		],
+	 * 	],
+	 * 	'INVALID_HASH' =>
+	 * 	[
+	 * 		'filename' => [
+	 * 			'expected' => 'expectedSHA512',
+	 * 			'current' => 'currentSHA512',
+	 * 		],
+	 * 	],
+	 * ]
+	 *
+	 * Array may be empty in case no problems have been found.
+	 *
+	 * @param string $appId
+	 * @return array
+	 */
+	public function verifyAppSignature($appId) {
+		try {
+			$path = $this->appLocator->getAppPath($appId);
+			$result = $this->verify(
+					$path . '/appinfo/signature.json',
+					$path,
+					$appId
+			);
+		} catch (\Exception $e) {
+			$result = [
+					'EXCEPTION' => [
+							'class' => get_class($e),
+							'message' => $e->getMessage(),
+					],
+			];
+		}
+		$this->storeResults($appId, $result);
+
+		return $result;
+	}
+
+	/**
+	 * Verify the signature of core. Returns an array with the following content:
+	 * [
+	 * 	'FILE_MISSING' =>
+	 * 	[
+	 * 		'filename' => [
+	 * 			'expected' => 'expectedSHA512',
+	 * 			'current' => 'currentSHA512',
+	 * 		],
+	 * 	],
+	 * 	'EXTRA_FILE' =>
+	 * 	[
+	 * 		'filename' => [
+	 * 			'expected' => 'expectedSHA512',
+	 * 			'current' => 'currentSHA512',
+	 * 		],
+	 * 	],
+	 * 	'INVALID_HASH' =>
+	 * 	[
+	 * 		'filename' => [
+	 * 			'expected' => 'expectedSHA512',
+	 * 			'current' => 'currentSHA512',
+	 * 		],
+	 * 	],
+	 * ]
+	 *
+	 * Array may be empty in case no problems have been found.
+	 *
+	 * @return array
+	 */
+	public function verifyCoreSignature() {
+		try {
+			$result = $this->verify(
+					$this->environmentHelper->getServerRoot() . '/core/signature.json',
+					$this->environmentHelper->getServerRoot(),
+					'core'
+			);
+		} catch (\Exception $e) {
+			$result = [
+					'EXCEPTION' => [
+							'class' => get_class($e),
+							'message' => $e->getMessage(),
+					],
+			];
+		}
+		$this->storeResults('core', $result);
+
+		return $result;
+	}
+
+	/**
+	 * Verify the core code of the instance as well as all applicable applications
+	 * and store the results.
+	 */
+	public function runInstanceVerification() {
+		$this->verifyCoreSignature();
+		$appIds = $this->appLocator->getAllApps();
+		foreach($appIds as $appId) {
+			// If an application is shipped a valid signature is required
+			$isShipped = $this->appManager->isShipped($appId);
+			$appNeedsToBeChecked = false;
+			if ($isShipped) {
+				$appNeedsToBeChecked = true;
+			} elseif ($this->fileAccessHelper->file_exists($this->appLocator->getAppPath($appId) . '/appinfo/signature.json')) {
+				// Otherwise only if the application explicitly ships a signature.json file
+				$appNeedsToBeChecked = true;
+			}
+
+			if($appNeedsToBeChecked) {
+				$this->verifyAppSignature($appId);
+			}
+		}
+	}
+}
diff --git a/lib/private/integritycheck/exceptions/invalidsignatureexception.php b/lib/private/integritycheck/exceptions/invalidsignatureexception.php
new file mode 100644
index 0000000000000000000000000000000000000000..9e05e5884f5c272cda08db5903f964fbd9366d8c
--- /dev/null
+++ b/lib/private/integritycheck/exceptions/invalidsignatureexception.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\IntegrityCheck\Exceptions;
+
+/**
+ * Class InvalidSignatureException is thrown in case the signature of the hashes
+ * cannot be properly validated. This indicates that either files
+ *
+ * @package OC\IntegrityCheck\Exceptions
+ */
+class InvalidSignatureException extends \Exception {}
diff --git a/lib/private/integritycheck/helpers/applocator.php b/lib/private/integritycheck/helpers/applocator.php
new file mode 100644
index 0000000000000000000000000000000000000000..b732cb808933193b24105948355a32eb3b997246
--- /dev/null
+++ b/lib/private/integritycheck/helpers/applocator.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\IntegrityCheck\Helpers;
+
+/**
+ * Class AppLocator provides a non-static helper for OC_App::getPath($appId)
+ * it is not possible to use IAppManager at this point as IAppManager has a
+ * dependency on a running ownCloud.
+ *
+ * @package OC\IntegrityCheck\Helpers
+ */
+class AppLocator {
+	/**
+	 * Provides \OC_App::getAppPath($appId)
+	 *
+	 * @param string $appId
+	 * @return string
+	 * @throws \Exception If the app cannot be found
+	 */
+	public function getAppPath($appId) {
+		$path = \OC_App::getAppPath($appId);
+		if($path === false) {
+
+			throw new \Exception('App not found');
+		}
+		return $path;
+	}
+
+	/**
+	 * Providers \OC_App::getAllApps()
+	 *
+	 * @return array
+	 */
+	public function getAllApps() {
+		return \OC_App::getAllApps();
+	}
+}
diff --git a/lib/private/integritycheck/helpers/environmenthelper.php b/lib/private/integritycheck/helpers/environmenthelper.php
new file mode 100644
index 0000000000000000000000000000000000000000..d7747dbb966405e6975dcb2c5b6f0e734bafe078
--- /dev/null
+++ b/lib/private/integritycheck/helpers/environmenthelper.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\IntegrityCheck\Helpers;
+
+/**
+ * Class EnvironmentHelper provides a non-static helper for access to static
+ * variables such as \OC::$SERVERROOT.
+ *
+ * @package OC\IntegrityCheck\Helpers
+ */
+class EnvironmentHelper {
+	/**
+	 * Provides \OC::$SERVERROOT
+	 *
+	 * @return string
+	 */
+	public function getServerRoot() {
+		return \OC::$SERVERROOT;
+	}
+}
diff --git a/lib/private/integritycheck/helpers/fileaccesshelper.php b/lib/private/integritycheck/helpers/fileaccesshelper.php
new file mode 100644
index 0000000000000000000000000000000000000000..23f592122dcaca0e255d79df4787b9975b3540f9
--- /dev/null
+++ b/lib/private/integritycheck/helpers/fileaccesshelper.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\IntegrityCheck\Helpers;
+
+/**
+ * Class FileAccessHelper provides a helper around file_get_contents and
+ * file_put_contents
+ *
+ * @package OC\IntegrityCheck\Helpers
+ */
+class FileAccessHelper {
+	/**
+	 * Wrapper around file_get_contents($filename, $data)
+	 *
+	 * @param string $filename
+	 * @return string|false
+	 */
+	public function file_get_contents($filename) {
+		return file_get_contents($filename);
+	}
+
+	/**
+	 * Wrapper around file_exists($filename)
+	 *
+	 * @param string $filename
+	 * @return bool
+	 */
+	public function file_exists($filename) {
+		return file_exists($filename);
+	}
+
+	/**
+	 * Wrapper around file_put_contents($filename, $data)
+	 *
+	 * @param string $filename
+	 * @param $data
+	 * @return int|false
+	 */
+	public function file_put_contents($filename, $data) {
+		return file_put_contents($filename, $data);
+	}
+}
diff --git a/lib/private/integritycheck/iterator/excludefilebynamefilteriterator.php b/lib/private/integritycheck/iterator/excludefilebynamefilteriterator.php
new file mode 100644
index 0000000000000000000000000000000000000000..c75554a7cc977643384d548f73072c03927a5a2f
--- /dev/null
+++ b/lib/private/integritycheck/iterator/excludefilebynamefilteriterator.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\IntegrityCheck\Iterator;
+
+/**
+ * Class ExcludeFileByNameFilterIterator provides a custom iterator which excludes
+ * entries with the specified file name from the file list.
+ *
+ * @package OC\Integritycheck\Iterator
+ */
+class ExcludeFileByNameFilterIterator extends \RecursiveFilterIterator {
+	/**
+	 * Array of excluded file names. Those are not scanned by the integrity checker.
+	 * This is used to exclude files which administrators could upload by mistakes
+	 * such as .DS_Store files.
+	 *
+	 * @var array
+	 */
+	private $excludedFilenames = [
+		'.DS_Store', // Mac OS X
+		'Thumbs.db', // Microsoft Windows
+		'.directory', // Dolphin (KDE)
+	];
+
+	/**
+	 * @return bool
+	 */
+	public function accept() {
+		if($this->isDir()) {
+			return true;
+		}
+
+		return !in_array(
+			$this->current()->getFilename(),
+			$this->excludedFilenames,
+			true
+		);
+	}
+}
diff --git a/lib/private/integritycheck/iterator/excludefoldersbypathfilteriterator.php b/lib/private/integritycheck/iterator/excludefoldersbypathfilteriterator.php
new file mode 100644
index 0000000000000000000000000000000000000000..43f194758626c81a2c9d5b5437abb744986cd963
--- /dev/null
+++ b/lib/private/integritycheck/iterator/excludefoldersbypathfilteriterator.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\IntegrityCheck\Iterator;
+
+class ExcludeFoldersByPathFilterIterator extends \RecursiveFilterIterator {
+	private $excludedFolders = [];
+
+	public function __construct(\RecursiveIterator $iterator) {
+		parent::__construct($iterator);
+
+		$appFolders = \OC::$APPSROOTS;
+		foreach($appFolders as $key => $appFolder) {
+			$appFolders[$key] = rtrim($appFolder['path'], '/');
+		}
+
+		$this->excludedFolders = array_merge([
+			rtrim(\OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data'), '/'),
+			rtrim(\OC::$SERVERROOT.'/themes', '/'),
+		], $appFolders);
+	}
+
+	/**
+	 * @return bool
+	 */
+	public function accept() {
+		return !in_array(
+			$this->current()->getPathName(),
+			$this->excludedFolders,
+			true
+		);
+	}
+}
diff --git a/lib/private/server.php b/lib/private/server.php
index de3324d2cce1290b420aaf40a701aa92642019a9..fe0d9dcf55dab6a0ec7b1cdd668d1560e0f728cd 100644
--- a/lib/private/server.php
+++ b/lib/private/server.php
@@ -51,6 +51,10 @@ use OC\Files\Node\HookConnector;
 use OC\Files\Node\Root;
 use OC\Files\View;
 use OC\Http\Client\ClientService;
+use OC\IntegrityCheck\Checker;
+use OC\IntegrityCheck\Helpers\AppLocator;
+use OC\IntegrityCheck\Helpers\EnvironmentHelper;
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
 use OC\Lock\DBLockingProvider;
 use OC\Lock\MemcacheLockingProvider;
 use OC\Lock\NoopLockingProvider;
@@ -409,6 +413,26 @@ class Server extends SimpleContainer implements IServerContainer {
 		$this->registerService('TrustedDomainHelper', function ($c) {
 			return new TrustedDomainHelper($this->getConfig());
 		});
+		$this->registerService('IntegrityCodeChecker', function (Server $c) {
+			// IConfig and IAppManager requires a working database. This code
+			// might however be called when ownCloud is not yet setup.
+			if(\OC::$server->getSystemConfig()->getValue('installed', false)) {
+				$config = $c->getConfig();
+				$appManager = $c->getAppManager();
+			} else {
+				$config = null;
+				$appManager = null;
+			}
+
+			return new Checker(
+					new EnvironmentHelper(),
+					new FileAccessHelper(),
+					new AppLocator(),
+					$config,
+					$c->getMemCacheFactory(),
+					$appManager
+			);
+		});
 		$this->registerService('Request', function ($c) {
 			if (isset($this['urlParams'])) {
 				$urlParams = $this['urlParams'];
@@ -1093,6 +1117,13 @@ class Server extends SimpleContainer implements IServerContainer {
 		return $this->query('NotificationManager');
 	}
 
+	/**
+	 * @return \OC\IntegrityCheck\Checker
+	 */
+	public function getIntegrityCodeChecker() {
+		return $this->query('IntegrityCodeChecker');
+	}
+
 	/**
 	 * @return \OC\Session\CryptoWrapper
 	 */
diff --git a/lib/private/templatelayout.php b/lib/private/templatelayout.php
index f5974128b73865046d8ac78b13e22658b422cef6..1a6a07ddc9fb5049074e5e21166bfd7c5333681b 100644
--- a/lib/private/templatelayout.php
+++ b/lib/private/templatelayout.php
@@ -78,8 +78,12 @@ class OC_TemplateLayout extends OC_Template {
 			// Update notification
 			if($this->config->getSystemValue('updatechecker', true) === true &&
 				OC_User::isAdminUser(OC_User::getUser())) {
-				$updater = new \OC\Updater(\OC::$server->getHTTPHelper(),
-					\OC::$server->getConfig(), \OC::$server->getLogger());
+				$updater = new \OC\Updater(
+						\OC::$server->getHTTPHelper(),
+						\OC::$server->getConfig(),
+						\OC::$server->getIntegrityCodeChecker(),
+						\OC::$server->getLogger()
+				);
 				$data = $updater->check();
 
 				if(isset($data['version']) && $data['version'] != '' and $data['version'] !== Array()) {
@@ -96,8 +100,13 @@ class OC_TemplateLayout extends OC_Template {
 				$this->assign('updateAvailable', false); // Update check is disabled
 			}
 
-			// Add navigation entry
+			// Code integrity notification
+			$integrityChecker = \OC::$server->getIntegrityCodeChecker();
+			if(!$integrityChecker->hasPassedCheck()) {
+				\OCP\Util::addScript('core', 'integritycheck-failed-notification');
+			}
 
+			// Add navigation entry
 			$this->assign( 'application', '');
 			$this->assign( 'appid', $appId );
 			$navigation = OC_App::getNavigation();
diff --git a/lib/private/updater.php b/lib/private/updater.php
index 1ff80863737ef6db6b7057cdd8170f2a943fe6cf..366ad2555a8b15c3736142947a977136a0d8695b 100644
--- a/lib/private/updater.php
+++ b/lib/private/updater.php
@@ -34,6 +34,8 @@
 namespace OC;
 
 use OC\Hooks\BasicEmitter;
+use OC\IntegrityCheck\Checker;
+use OC\IntegrityCheck\Storage;
 use OC_App;
 use OC_Installer;
 use OC_Util;
@@ -61,6 +63,9 @@ class Updater extends BasicEmitter {
 	/** @var IConfig */
 	private $config;
 
+	/** @var Checker */
+	private $checker;
+
 	/** @var bool */
 	private $simulateStepEnabled;
 
@@ -81,14 +86,17 @@ class Updater extends BasicEmitter {
 	/**
 	 * @param HTTPHelper $httpHelper
 	 * @param IConfig $config
+	 * @param Checker $checker
 	 * @param ILogger $log
 	 */
 	public function __construct(HTTPHelper $httpHelper,
 								IConfig $config,
+								Checker $checker,
 								ILogger $log = null) {
 		$this->httpHelper = $httpHelper;
 		$this->log = $log;
 		$this->config = $config;
+		$this->checker = $checker;
 		$this->simulateStepEnabled = true;
 		$this->updateStepEnabled = true;
 	}
@@ -335,6 +343,13 @@ class Updater extends BasicEmitter {
 			//Invalidate update feed
 			$this->config->setAppValue('core', 'lastupdatedat', 0);
 
+			// Check for code integrity on the stable channel
+			if(\OC_Util::getChannel() === 'stable') {
+				$this->emit('\OC\Updater', 'startCheckCodeIntegrity');
+				$this->checker->runInstanceVerification();
+				$this->emit('\OC\Updater', 'finishedCheckCodeIntegrity');
+			}
+
 			// only set the final version if everything went well
 			$this->config->setSystemValue('version', implode('.', \OC_Util::getVersion()));
 		}
diff --git a/resources/codesigning/core.crt b/resources/codesigning/core.crt
new file mode 100644
index 0000000000000000000000000000000000000000..475a59bddce7680f17b7c3e62ef865b099af98d6
--- /dev/null
+++ b/resources/codesigning/core.crt
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIEvjCCAqagAwIBAgIUc/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF
+BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw
+MzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s
+iOf4RwPXR6SE9bWZEm/b72SfWk//J6AbrD8WiOzBuT/ODy6k5T1arEdHO+Pux0W1
+MxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr/xolP3oD+eLbShPcblhdS
+VtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0
+klnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5/2riAzIssMFSCarWCx0AKYb54+d
+xLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77
+H87KFhYW8tKFFvF1V3AHl/sFQ9tDHaxM9Y0pZ2jPp/ccdiqnmdkBxBDqsiRvHvVB
+Cn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO/wAtd2vUW8UFiq
+s2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ/zrM0
+i8nfCFwTxWRxp3H9KoECzO/zS5R5KIS7s3/wq/w9T2Ie4rcecgXwDizwnn0C/aKc
+bDIjujpL1s9HO05pcD/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ
+Q238lC+A/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2
+AvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji
+oNCXUbExC/0iCPUqdHZIVb+Lc/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd
+9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb
+H+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th/55
+cf3Fovj6JJgbb9XFxrdnsOsDOu/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX
+uVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO//+TJtXRbyNgsf
+oMRZGi8DLGU2SGEAHcRH/QZHq/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1
+0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI/XUxEWOa2F
+K2EqhErgMK/N07U1JJJay5tYZRtvkGq46oP/5kQG8hYST0MDK6VihJoPpvCmAm4E
+pEYKQ96x6A4EH9Y9mZlYozH/eqmxPbTK8n89/p7Ydun4rI+B2iiLnY8REWWy6+UQ
+V204fGUkJqW5CrKy3P3XvY9X
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/resources/codesigning/core.key b/resources/codesigning/core.key
new file mode 100644
index 0000000000000000000000000000000000000000..4a588d47356abfa37d637cabefd6bff2764783ef
--- /dev/null
+++ b/resources/codesigning/core.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
\ No newline at end of file
diff --git a/resources/codesigning/root.crt b/resources/codesigning/root.crt
new file mode 100644
index 0000000000000000000000000000000000000000..539bd9a199f4874436203a4da08aa5c324bb3b93
--- /dev/null
+++ b/resources/codesigning/root.crt
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIE1DCCArygAwIBAgIUFgEnT7tUWNgEKfbMiRTOm3Z8figwDQYJKoZIhvcNAQEF
+BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMCAXDTE1MTEw
+MzIxMDMzMloYDzk5OTkxMjMxMjM1OTU5WjAjMSEwHwYDVQQKDBhvd25DbG91ZCBD
+b2RlIFNpZ25pbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCt
+rYEwiwpuhzRNx1L2SntkL/zalA7OkwEcsBn4Ysw47nEdvp648AVohT7d+U7+DWix
+O06xvlAJUhqYTXX7EG9n+mBnG+TIMq2zlei0Jj3uq1pEZE9elfGZael2uc8gRXMZ
+YFmSlTvzexqvfK4B3DwoZaMaWJecEO9iyuUyzHMBpE8bHtGDOUGy/oTO9WASbtl9
+Rfk38VLLV6csCPwKjii6Q3YZ+AYU0YqLg22BwZlqlTexUjWAmVIqaoxmSEuKa41X
+nuAIHfP65oiob86s4IvJXVr3r7NjdF2IJ/ZrwmjGKaWgSZKcoZBPLArZJeJiIQ4Y
+KzLZATwba5dWswV0mbDj8eP2BG+02NVKxylOBbeoBESnahZeZ5nJ5XKZr+ErJAUW
+b417fEnYaQNBlNnijjkqXaDipmTktUfnv4lm30sUAgho6I3Ga7gQrFPzKg9V4j2S
++LOTc1HmJOnR6Kfttx+yAHYLKtvV5yIMMpz+rZ2X5g/N2GdgleZj5VU9nmKzTPeG
+x6V+dBaIkqNe8/AXaVnxt9KSb03Q/+CFjKTNDtEN5fNJuXS0+h+oop6nhpktM2i7
+gCpxeLNEaQaeoxR5093VN00oOJOYBvQoVGEDftEwdG6dWbTZsIykBF7aK+p8DMy9
+tCdc2GnGMEuFlUNA9ucv2Rv7IcuPdspnK31CZoMNKwIDAQABMA0GCSqGSIb3DQEB
+BQUAA4ICAQBROWec0HOsnLPN40gkBQ62mNBUJgcAr5K2eMIEMSRFRD2ldEVOvmCO
+u1Q62umy9tiSRiFQTcG1J9k0zlOjy/hfpBl2G1Zce0OoEeuNkH7c0W/idHSloWRZ
+YlK3tVJD6DzY6s9VbO6e/ncecNsXkirkWp/cvMYquH2d4OmSl0/hW0VMdxOCLxkA
+xbW+3Dh05u7tgKVRD67/GRvLtg+xHaOJqiOh3MpMaHy+6xT5Fd5K2QC0pcGtZuqF
+EnnfdeUI/Dy76yQE8pBfjaUFf3TS1n1E6kun1Nkf0X4pvwi8W1goLsPu5sWDNNga
+1RGYj0o5OdIo27qebfmu76WX0fNNd47VabtzNV+W7Msj0yeZg+hxAtAvs0ZEyJh+
+4biWsv+ALSlqz4sSdoOVGUEBdnkUrWN19lou62ix9vTmuCrVEA3TuZA3PR0+hqqQ
+/A/DcmWwxWYKyaBgxwHc/nGo1qWrDh22P5Rp7++Zw3kOCY6QmeJkAiHFs6Crw4ub
+HKVMw3fV5H9oiUFjadPZoCU51uXKX4YRqKxWJ5djlp4r1GCEQHyxngTsmH3komnw
+kh4LsEQRqdhuT0A4sZN7CenMJfQiFqupL7RVSycJFQpgzwVFmOzjCVT4PT/W5ARv
+9YtqEkvyoRTwErwuN/FIVvhWnIP/C69Z1/T/nXyj86P2G7PMgnchIQ==
+-----END CERTIFICATE-----
diff --git a/resources/codesigning/root.key b/resources/codesigning/root.key
new file mode 100644
index 0000000000000000000000000000000000000000..557b0a26e3aabd55110bbcb6c3d7781cf6b1e751
--- /dev/null
+++ b/resources/codesigning/root.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/settings/application.php b/settings/application.php
index c876065fffa6115747a002927c971b783e6d117c..f9ee0121db0f9964459603e9a782609977c4c772 100644
--- a/settings/application.php
+++ b/settings/application.php
@@ -40,6 +40,7 @@ use OC\Settings\Middleware\SubadminMiddleware;
 use \OCP\AppFramework\App;
 use OCP\IContainer;
 use \OCP\Util;
+use OC\Server;
 
 /**
  * @package OC\Settings
@@ -154,7 +155,8 @@ class Application extends App {
 				$c->query('ClientService'),
 				$c->query('URLGenerator'),
 				$c->query('Util'),
-				$c->query('L10N')
+				$c->query('L10N'),
+				$c->query('Checker')
 			);
 		});
 
@@ -240,5 +242,10 @@ class Application extends App {
 		$container->registerService('CertificateManager', function(IContainer $c){
 			return $c->query('ServerContainer')->getCertificateManager();
 		});
+		$container->registerService('Checker', function(IContainer $c) {
+			/** @var Server $server */
+			$server = $c->query('ServerContainer');
+			return $server->getIntegrityCodeChecker();
+		});
 	}
 }
diff --git a/settings/controller/checksetupcontroller.php b/settings/controller/checksetupcontroller.php
index 491821b44c564bb072eac874bb67f06c0db3d8c0..5a77049479d2d6be67e00518f81e34c372e9e6a4 100644
--- a/settings/controller/checksetupcontroller.php
+++ b/settings/controller/checksetupcontroller.php
@@ -26,8 +26,12 @@
 namespace OC\Settings\Controller;
 
 use GuzzleHttp\Exception\ClientException;
+use OC\AppFramework\Http;
+use OC\IntegrityCheck\Checker;
 use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\DataDisplayResponse;
 use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\Http\RedirectResponse;
 use OCP\Http\Client\IClientService;
 use OCP\IConfig;
 use OCP\IL10N;
@@ -49,6 +53,8 @@ class CheckSetupController extends Controller {
 	private $urlGenerator;
 	/** @var IL10N */
 	private $l10n;
+	/** @var Checker */
+	private $checker;
 
 	/**
 	 * @param string $AppName
@@ -58,6 +64,7 @@ class CheckSetupController extends Controller {
 	 * @param IURLGenerator $urlGenerator
 	 * @param \OC_Util $util
 	 * @param IL10N $l10n
+	 * @param Checker $checker
 	 */
 	public function __construct($AppName,
 								IRequest $request,
@@ -65,13 +72,15 @@ class CheckSetupController extends Controller {
 								IClientService $clientService,
 								IURLGenerator $urlGenerator,
 								\OC_Util $util,
-								IL10N $l10n) {
+								IL10N $l10n,
+								Checker $checker) {
 		parent::__construct($AppName, $request);
 		$this->config = $config;
 		$this->clientService = $clientService;
 		$this->util = $util;
 		$this->urlGenerator = $urlGenerator;
 		$this->l10n = $l10n;
+		$this->checker = $checker;
 	}
 
 	/**
@@ -247,6 +256,72 @@ class CheckSetupController extends Controller {
 		return !(!extension_loaded('memcached') && extension_loaded('memcache'));
 	}
 
+	/**
+	 * @return RedirectResponse
+	 */
+	public function rescanFailedIntegrityCheck() {
+		$this->checker->runInstanceVerification();
+		return new RedirectResponse(
+			$this->urlGenerator->linkToRoute('settings_admin')
+		);
+	}
+
+	/**
+	 * @NoCSRFRequired
+	 * @return DataResponse
+	 */
+	public function getFailedIntegrityCheckFiles() {
+		$completeResults = $this->checker->getResults();
+
+		if(!empty($completeResults)) {
+			$formattedTextResponse = 'Technical information
+=====================
+The following list covers which files have failed the integrity check. Please read
+the previous linked documentation to learn more about the errors and how to fix
+them.
+
+Results
+=======
+';
+			foreach($completeResults as $context => $contextResult) {
+				$formattedTextResponse .= "- $context\n";
+
+				foreach($contextResult as $category => $result) {
+					$formattedTextResponse .= "\t- $category\n";
+					if($category !== 'EXCEPTION') {
+						foreach ($result as $key => $results) {
+							$formattedTextResponse .= "\t\t- $key\n";
+						}
+					} else {
+						foreach ($result as $key => $results) {
+							$formattedTextResponse .= "\t\t- $results\n";
+						}
+					}
+
+				}
+			}
+
+			$formattedTextResponse .= '
+Raw output
+==========
+';
+			$formattedTextResponse .= print_r($completeResults, true);
+		} else {
+			$formattedTextResponse = 'No errors have been found.';
+		}
+
+
+		$response = new DataDisplayResponse(
+			$formattedTextResponse,
+			Http::STATUS_OK,
+			[
+				'Content-Type' => 'text/plain',
+			]
+		);
+
+		return $response;
+	}
+
 	/**
 	 * @return DataResponse
 	 */
@@ -263,7 +338,9 @@ class CheckSetupController extends Controller {
 				'phpSupported' => $this->isPhpSupported(),
 				'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
 				'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
-				'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled()
+				'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
+				'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
+				'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
 			]
 		);
 	}
diff --git a/settings/routes.php b/settings/routes.php
index 95ad234c6990fef844761b168e8c480261b3eb2d..6b6b01501684dc78679bf3dd65606a18bcec4b41 100644
--- a/settings/routes.php
+++ b/settings/routes.php
@@ -53,6 +53,8 @@ $application->registerRoutes($this, [
 		['name' => 'LogSettings#getEntries', 'url' => '/settings/admin/log/entries', 'verb' => 'GET'],
 		['name' => 'LogSettings#download', 'url' => '/settings/admin/log/download', 'verb' => 'GET'],
 		['name' => 'CheckSetup#check', 'url' => '/settings/ajax/checksetup', 'verb' => 'GET'],
+		['name' => 'CheckSetup#getFailedIntegrityCheckFiles', 'url' => '/settings/integrity/failed', 'verb' => 'GET'],
+		['name' => 'CheckSetup#rescanFailedIntegrityCheck', 'url' => '/settings/integrity/rescan', 'verb' => 'GET'],
 		['name' => 'Certificate#addPersonalRootCertificate', 'url' => '/settings/personal/certificate', 'verb' => 'POST'],
 		['name' => 'Certificate#removePersonalRootCertificate', 'url' => '/settings/personal/certificate/{certificateIdentifier}', 'verb' => 'DELETE'],
 	]
diff --git a/tests/data/integritycheck/SomeApp.crt b/tests/data/integritycheck/SomeApp.crt
new file mode 100644
index 0000000000000000000000000000000000000000..a9bd4cd8a7cfea92844ddf6965ef49deb6dc2448
--- /dev/null
+++ b/tests/data/integritycheck/SomeApp.crt
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf/qTQvoyKAwDQYJKoZIhvcNAQEF
+BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw
+MzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk
+F8cAobMMi50qHCv9IrOn/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl/j
++ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+
+A+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M
+w0xDv30D5UkE/2N7Pa/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4
+GB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6
+dol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj
+t5O7Zn2wA7I4ddDS/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC
+cxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT/x/mqN3PfRmlnFBNACUw9bpZ
+SOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz
+tFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4
+6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf
+FWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS
+HVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70/4imPoKxbAVCpd/cveVcFyDC19j1yB
+Bapwu87oh+muoeaZxOlqQI4UxjBlR/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v
+0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3
+pPhEpo1dRpiXaF7WGIV1X6DI/ipWvfrF7CEy6I/kP1InY/vMDjQjeDnJ/VrXIWXO
+yZvHXVaN/m+1RlETsH7YO/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF
+49/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7
+7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW
+UO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS/P4RB1NkHA9+NTvmBpTonS
+SFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP/Y7xwxLv7
+4B+pXTAcRK0zECDEaX3npS8xWzrB
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/tests/data/integritycheck/SomeApp.key b/tests/data/integritycheck/SomeApp.key
new file mode 100644
index 0000000000000000000000000000000000000000..ce59eb1b86fa42f3c5c5458f6d1a10410eeee9c6
--- /dev/null
+++ b/tests/data/integritycheck/SomeApp.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
\ No newline at end of file
diff --git a/tests/data/integritycheck/app/AnotherFile.txt b/tests/data/integritycheck/app/AnotherFile.txt
new file mode 100644
index 0000000000000000000000000000000000000000..cf85999ddfcbee7ef57444cf42188ebf2ff154c9
--- /dev/null
+++ b/tests/data/integritycheck/app/AnotherFile.txt
@@ -0,0 +1 @@
+Another file with some Content.
diff --git a/tests/data/integritycheck/app/subfolder/file.txt b/tests/data/integritycheck/app/subfolder/file.txt
new file mode 100644
index 0000000000000000000000000000000000000000..69cd4e0afaf8f9d44560a6bea7e201ecd9ab9f5c
--- /dev/null
+++ b/tests/data/integritycheck/app/subfolder/file.txt
@@ -0,0 +1 @@
+A file with some content.
diff --git a/tests/data/integritycheck/appWithInvalidData/AnotherFile.txt b/tests/data/integritycheck/appWithInvalidData/AnotherFile.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c73a48924184b2a93f179865ad5b74f595a043f4
--- /dev/null
+++ b/tests/data/integritycheck/appWithInvalidData/AnotherFile.txt
@@ -0,0 +1 @@
+TAMPERED FILE.
\ No newline at end of file
diff --git a/tests/data/integritycheck/appWithInvalidData/UnecessaryFile b/tests/data/integritycheck/appWithInvalidData/UnecessaryFile
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/data/integritycheck/core.crt b/tests/data/integritycheck/core.crt
new file mode 100644
index 0000000000000000000000000000000000000000..475a59bddce7680f17b7c3e62ef865b099af98d6
--- /dev/null
+++ b/tests/data/integritycheck/core.crt
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIEvjCCAqagAwIBAgIUc/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF
+BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw
+MzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s
+iOf4RwPXR6SE9bWZEm/b72SfWk//J6AbrD8WiOzBuT/ODy6k5T1arEdHO+Pux0W1
+MxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr/xolP3oD+eLbShPcblhdS
+VtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0
+klnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5/2riAzIssMFSCarWCx0AKYb54+d
+xLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77
+H87KFhYW8tKFFvF1V3AHl/sFQ9tDHaxM9Y0pZ2jPp/ccdiqnmdkBxBDqsiRvHvVB
+Cn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO/wAtd2vUW8UFiq
+s2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ/zrM0
+i8nfCFwTxWRxp3H9KoECzO/zS5R5KIS7s3/wq/w9T2Ie4rcecgXwDizwnn0C/aKc
+bDIjujpL1s9HO05pcD/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ
+Q238lC+A/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2
+AvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji
+oNCXUbExC/0iCPUqdHZIVb+Lc/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd
+9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb
+H+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th/55
+cf3Fovj6JJgbb9XFxrdnsOsDOu/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX
+uVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO//+TJtXRbyNgsf
+oMRZGi8DLGU2SGEAHcRH/QZHq/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1
+0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI/XUxEWOa2F
+K2EqhErgMK/N07U1JJJay5tYZRtvkGq46oP/5kQG8hYST0MDK6VihJoPpvCmAm4E
+pEYKQ96x6A4EH9Y9mZlYozH/eqmxPbTK8n89/p7Ydun4rI+B2iiLnY8REWWy6+UQ
+V204fGUkJqW5CrKy3P3XvY9X
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/tests/data/integritycheck/core.key b/tests/data/integritycheck/core.key
new file mode 100644
index 0000000000000000000000000000000000000000..4a588d47356abfa37d637cabefd6bff2764783ef
--- /dev/null
+++ b/tests/data/integritycheck/core.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
\ No newline at end of file
diff --git a/tests/data/integritycheck/root.crt b/tests/data/integritycheck/root.crt
new file mode 100644
index 0000000000000000000000000000000000000000..539bd9a199f4874436203a4da08aa5c324bb3b93
--- /dev/null
+++ b/tests/data/integritycheck/root.crt
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIE1DCCArygAwIBAgIUFgEnT7tUWNgEKfbMiRTOm3Z8figwDQYJKoZIhvcNAQEF
+BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMCAXDTE1MTEw
+MzIxMDMzMloYDzk5OTkxMjMxMjM1OTU5WjAjMSEwHwYDVQQKDBhvd25DbG91ZCBD
+b2RlIFNpZ25pbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCt
+rYEwiwpuhzRNx1L2SntkL/zalA7OkwEcsBn4Ysw47nEdvp648AVohT7d+U7+DWix
+O06xvlAJUhqYTXX7EG9n+mBnG+TIMq2zlei0Jj3uq1pEZE9elfGZael2uc8gRXMZ
+YFmSlTvzexqvfK4B3DwoZaMaWJecEO9iyuUyzHMBpE8bHtGDOUGy/oTO9WASbtl9
+Rfk38VLLV6csCPwKjii6Q3YZ+AYU0YqLg22BwZlqlTexUjWAmVIqaoxmSEuKa41X
+nuAIHfP65oiob86s4IvJXVr3r7NjdF2IJ/ZrwmjGKaWgSZKcoZBPLArZJeJiIQ4Y
+KzLZATwba5dWswV0mbDj8eP2BG+02NVKxylOBbeoBESnahZeZ5nJ5XKZr+ErJAUW
+b417fEnYaQNBlNnijjkqXaDipmTktUfnv4lm30sUAgho6I3Ga7gQrFPzKg9V4j2S
++LOTc1HmJOnR6Kfttx+yAHYLKtvV5yIMMpz+rZ2X5g/N2GdgleZj5VU9nmKzTPeG
+x6V+dBaIkqNe8/AXaVnxt9KSb03Q/+CFjKTNDtEN5fNJuXS0+h+oop6nhpktM2i7
+gCpxeLNEaQaeoxR5093VN00oOJOYBvQoVGEDftEwdG6dWbTZsIykBF7aK+p8DMy9
+tCdc2GnGMEuFlUNA9ucv2Rv7IcuPdspnK31CZoMNKwIDAQABMA0GCSqGSIb3DQEB
+BQUAA4ICAQBROWec0HOsnLPN40gkBQ62mNBUJgcAr5K2eMIEMSRFRD2ldEVOvmCO
+u1Q62umy9tiSRiFQTcG1J9k0zlOjy/hfpBl2G1Zce0OoEeuNkH7c0W/idHSloWRZ
+YlK3tVJD6DzY6s9VbO6e/ncecNsXkirkWp/cvMYquH2d4OmSl0/hW0VMdxOCLxkA
+xbW+3Dh05u7tgKVRD67/GRvLtg+xHaOJqiOh3MpMaHy+6xT5Fd5K2QC0pcGtZuqF
+EnnfdeUI/Dy76yQE8pBfjaUFf3TS1n1E6kun1Nkf0X4pvwi8W1goLsPu5sWDNNga
+1RGYj0o5OdIo27qebfmu76WX0fNNd47VabtzNV+W7Msj0yeZg+hxAtAvs0ZEyJh+
+4biWsv+ALSlqz4sSdoOVGUEBdnkUrWN19lou62ix9vTmuCrVEA3TuZA3PR0+hqqQ
+/A/DcmWwxWYKyaBgxwHc/nGo1qWrDh22P5Rp7++Zw3kOCY6QmeJkAiHFs6Crw4ub
+HKVMw3fV5H9oiUFjadPZoCU51uXKX4YRqKxWJ5djlp4r1GCEQHyxngTsmH3komnw
+kh4LsEQRqdhuT0A4sZN7CenMJfQiFqupL7RVSycJFQpgzwVFmOzjCVT4PT/W5ARv
+9YtqEkvyoRTwErwuN/FIVvhWnIP/C69Z1/T/nXyj86P2G7PMgnchIQ==
+-----END CERTIFICATE-----
diff --git a/tests/data/integritycheck/root.key b/tests/data/integritycheck/root.key
new file mode 100644
index 0000000000000000000000000000000000000000..557b0a26e3aabd55110bbcb6c3d7781cf6b1e751
--- /dev/null
+++ b/tests/data/integritycheck/root.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/tests/lib/command/integrity/SignAppTest.php b/tests/lib/command/integrity/SignAppTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b7c34585c5c85a8cf6a68ff1db300aecc219e3be
--- /dev/null
+++ b/tests/lib/command/integrity/SignAppTest.php
@@ -0,0 +1,247 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace Test\Command\Integrity;
+
+use OC\Core\Command\Integrity\SignApp;
+use OC\IntegrityCheck\Checker;
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use Test\TestCase;
+
+class SignAppTest extends TestCase {
+	/** @var Checker */
+	private $checker;
+	/** @var SignApp */
+	private $signApp;
+	/** @var FileAccessHelper */
+	private $fileAccessHelper;
+
+	public function setUp() {
+		parent::setUp();
+		$this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
+			->disableOriginalConstructor()->getMock();
+		$this->fileAccessHelper = $this->getMockBuilder('\OC\IntegrityCheck\Helpers\FileAccessHelper')
+			->disableOriginalConstructor()->getMock();
+		$this->signApp = new SignApp(
+			$this->checker,
+			$this->fileAccessHelper
+		);
+	}
+
+	public function testExecuteWithMissingAppId() {
+		$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+		$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+		$inputInterface
+			->expects($this->at(0))
+			->method('getOption')
+			->with('appId')
+			->will($this->returnValue(null));
+		$inputInterface
+			->expects($this->at(1))
+			->method('getOption')
+			->with('privateKey')
+			->will($this->returnValue('PrivateKey'));
+		$inputInterface
+			->expects($this->at(2))
+			->method('getOption')
+			->with('certificate')
+			->will($this->returnValue('Certificate'));
+
+		$outputInterface
+			->expects($this->at(0))
+			->method('writeln')
+			->with('--appId, --privateKey and --certificate are required.');
+
+		$this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
+	}
+
+	public function testExecuteWithMissingPrivateKey() {
+		$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+		$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+		$inputInterface
+			->expects($this->at(0))
+			->method('getOption')
+			->with('appId')
+			->will($this->returnValue('AppId'));
+		$inputInterface
+			->expects($this->at(1))
+			->method('getOption')
+			->with('privateKey')
+			->will($this->returnValue(null));
+		$inputInterface
+			->expects($this->at(2))
+			->method('getOption')
+			->with('certificate')
+			->will($this->returnValue('Certificate'));
+
+		$outputInterface
+			->expects($this->at(0))
+			->method('writeln')
+			->with('--appId, --privateKey and --certificate are required.');
+
+		$this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
+	}
+
+	public function testExecuteWithMissingCertificate() {
+		$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+		$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+		$inputInterface
+			->expects($this->at(0))
+			->method('getOption')
+			->with('appId')
+			->will($this->returnValue('AppId'));
+		$inputInterface
+			->expects($this->at(1))
+			->method('getOption')
+			->with('privateKey')
+			->will($this->returnValue('privateKey'));
+		$inputInterface
+			->expects($this->at(2))
+			->method('getOption')
+			->with('certificate')
+			->will($this->returnValue(null));
+
+		$outputInterface
+			->expects($this->at(0))
+			->method('writeln')
+			->with('--appId, --privateKey and --certificate are required.');
+
+		$this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
+	}
+
+	public function testExecuteWithNotExistingPrivateKey() {
+		$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+		$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+		$inputInterface
+			->expects($this->at(0))
+			->method('getOption')
+			->with('appId')
+			->will($this->returnValue('AppId'));
+		$inputInterface
+			->expects($this->at(1))
+			->method('getOption')
+			->with('privateKey')
+			->will($this->returnValue('privateKey'));
+		$inputInterface
+			->expects($this->at(2))
+			->method('getOption')
+			->with('certificate')
+			->will($this->returnValue('certificate'));
+
+		$this->fileAccessHelper
+			->expects($this->at(0))
+			->method('file_get_contents')
+			->with('privateKey')
+			->will($this->returnValue(false));
+
+		$outputInterface
+			->expects($this->at(0))
+			->method('writeln')
+			->with('Private key "privateKey" does not exists.');
+
+		$this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
+	}
+
+	public function testExecuteWithNotExistingCertificate() {
+		$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+		$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+		$inputInterface
+			->expects($this->at(0))
+			->method('getOption')
+			->with('appId')
+			->will($this->returnValue('AppId'));
+		$inputInterface
+			->expects($this->at(1))
+			->method('getOption')
+			->with('privateKey')
+			->will($this->returnValue('privateKey'));
+		$inputInterface
+			->expects($this->at(2))
+			->method('getOption')
+			->with('certificate')
+			->will($this->returnValue('certificate'));
+
+		$this->fileAccessHelper
+			->expects($this->at(0))
+			->method('file_get_contents')
+			->with('privateKey')
+			->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key'));
+		$this->fileAccessHelper
+			->expects($this->at(1))
+			->method('file_get_contents')
+			->with('certificate')
+			->will($this->returnValue(false));
+
+		$outputInterface
+			->expects($this->at(0))
+			->method('writeln')
+			->with('Certificate "certificate" does not exists.');
+
+		$this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
+	}
+
+	public function testExecute() {
+		$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+		$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+		$inputInterface
+			->expects($this->at(0))
+			->method('getOption')
+			->with('appId')
+			->will($this->returnValue('AppId'));
+		$inputInterface
+			->expects($this->at(1))
+			->method('getOption')
+			->with('privateKey')
+			->will($this->returnValue('privateKey'));
+		$inputInterface
+			->expects($this->at(2))
+			->method('getOption')
+			->with('certificate')
+			->will($this->returnValue('certificate'));
+
+		$this->fileAccessHelper
+			->expects($this->at(0))
+			->method('file_get_contents')
+			->with('privateKey')
+			->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key'));
+		$this->fileAccessHelper
+			->expects($this->at(1))
+			->method('file_get_contents')
+			->with('certificate')
+			->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.crt'));
+
+		$this->checker
+			->expects($this->once())
+			->method('writeAppSignature');
+
+		$outputInterface
+			->expects($this->at(0))
+			->method('writeln')
+			->with('Successfully signed "AppId"');
+
+		$this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
+	}
+}
diff --git a/tests/lib/command/integrity/SignCoreTest.php b/tests/lib/command/integrity/SignCoreTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..885c6fc664eec087dbf4df21d2ff3bd6def1d45c
--- /dev/null
+++ b/tests/lib/command/integrity/SignCoreTest.php
@@ -0,0 +1,194 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace Test\Command\Integrity;
+
+use OC\Core\Command\Integrity\SignCore;
+use OC\IntegrityCheck\Checker;
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use Test\TestCase;
+
+class SignCoreTest extends TestCase {
+	/** @var Checker */
+	private $checker;
+	/** @var SignCore */
+	private $signCore;
+	/** @var FileAccessHelper */
+	private $fileAccessHelper;
+
+	public function setUp() {
+		parent::setUp();
+		$this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
+			->disableOriginalConstructor()->getMock();
+		$this->fileAccessHelper = $this->getMockBuilder('\OC\IntegrityCheck\Helpers\FileAccessHelper')
+			->disableOriginalConstructor()->getMock();
+		$this->signCore = new SignCore(
+			$this->checker,
+			$this->fileAccessHelper
+		);
+	}
+
+	public function testExecuteWithMissingPrivateKey() {
+		$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+		$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+		$inputInterface
+			->expects($this->at(0))
+			->method('getOption')
+			->with('privateKey')
+			->will($this->returnValue(null));
+		$inputInterface
+			->expects($this->at(1))
+			->method('getOption')
+			->with('certificate')
+			->will($this->returnValue('Certificate'));
+
+		$outputInterface
+			->expects($this->at(0))
+			->method('writeln')
+			->with('--privateKey and --certificate are required.');
+
+		$this->invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]);
+	}
+
+	public function testExecuteWithMissingCertificate() {
+		$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+		$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+		$inputInterface
+			->expects($this->at(0))
+			->method('getOption')
+			->with('privateKey')
+			->will($this->returnValue('privateKey'));
+		$inputInterface
+			->expects($this->at(1))
+			->method('getOption')
+			->with('certificate')
+			->will($this->returnValue(null));
+
+		$outputInterface
+			->expects($this->at(0))
+			->method('writeln')
+			->with('--privateKey and --certificate are required.');
+
+		$this->invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]);
+	}
+
+	public function testExecuteWithNotExistingPrivateKey() {
+		$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+		$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+		$inputInterface
+			->expects($this->at(0))
+			->method('getOption')
+			->with('privateKey')
+			->will($this->returnValue('privateKey'));
+		$inputInterface
+			->expects($this->at(1))
+			->method('getOption')
+			->with('certificate')
+			->will($this->returnValue('certificate'));
+
+		$this->fileAccessHelper
+			->expects($this->at(0))
+			->method('file_get_contents')
+			->with('privateKey')
+			->will($this->returnValue(false));
+
+		$outputInterface
+			->expects($this->at(0))
+			->method('writeln')
+			->with('Private key "privateKey" does not exists.');
+
+		$this->invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]);
+	}
+
+	public function testExecuteWithNotExistingCertificate() {
+		$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+		$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+		$inputInterface
+			->expects($this->at(0))
+			->method('getOption')
+			->with('privateKey')
+			->will($this->returnValue('privateKey'));
+		$inputInterface
+			->expects($this->at(1))
+			->method('getOption')
+			->with('certificate')
+			->will($this->returnValue('certificate'));
+
+		$this->fileAccessHelper
+			->expects($this->at(0))
+			->method('file_get_contents')
+			->with('privateKey')
+			->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key'));
+		$this->fileAccessHelper
+			->expects($this->at(1))
+			->method('file_get_contents')
+			->with('certificate')
+			->will($this->returnValue(false));
+
+		$outputInterface
+			->expects($this->at(0))
+			->method('writeln')
+			->with('Certificate "certificate" does not exists.');
+
+		$this->invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]);
+	}
+
+	public function testExecute() {
+		$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+		$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+		$inputInterface
+			->expects($this->at(0))
+			->method('getOption')
+			->with('privateKey')
+			->will($this->returnValue('privateKey'));
+		$inputInterface
+			->expects($this->at(1))
+			->method('getOption')
+			->with('certificate')
+			->will($this->returnValue('certificate'));
+
+		$this->fileAccessHelper
+			->expects($this->at(0))
+			->method('file_get_contents')
+			->with('privateKey')
+			->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key'));
+		$this->fileAccessHelper
+			->expects($this->at(1))
+			->method('file_get_contents')
+			->with('certificate')
+			->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.crt'));
+
+		$this->checker
+			->expects($this->once())
+			->method('writeCoreSignature');
+
+		$outputInterface
+			->expects($this->at(0))
+			->method('writeln')
+			->with('Successfully signed "core"');
+
+		$this->invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]);
+	}
+}
diff --git a/tests/lib/integritycheck/checkertest.php b/tests/lib/integritycheck/checkertest.php
new file mode 100644
index 0000000000000000000000000000000000000000..17117a4274b43b494afdd85ba479d2c270af4042
--- /dev/null
+++ b/tests/lib/integritycheck/checkertest.php
@@ -0,0 +1,671 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\IntegrityCheck;
+
+use OC\IntegrityCheck\Checker;
+use OC\Memcache\NullCache;
+use phpseclib\Crypt\RSA;
+use phpseclib\File\X509;
+use Test\TestCase;
+use OC\IntegrityCheck\Helpers\EnvironmentHelper;
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use OC\IntegrityCheck\Helpers\AppLocator;
+use OCP\IConfig;
+use OCP\ICacheFactory;
+use OCP\App\IAppManager;
+
+class CheckerTest extends TestCase {
+	/** @var EnvironmentHelper */
+	private $environmentHelper;
+	/** @var AppLocator */
+	private $appLocator;
+	/** @var Checker */
+	private $checker;
+	/** @var FileAccessHelper */
+	private $fileAccessHelper;
+	/** @var IConfig */
+	private $config;
+	/** @var ICacheFactory */
+	private $cacheFactory;
+	/** @var IAppManager */
+	private $appManager;
+
+	public function setUp() {
+		parent::setUp();
+		$this->environmentHelper = $this->getMock('\OC\IntegrityCheck\Helpers\EnvironmentHelper');
+		$this->fileAccessHelper = $this->getMock('\OC\IntegrityCheck\Helpers\FileAccessHelper');
+		$this->appLocator = $this->getMock('\OC\IntegrityCheck\Helpers\AppLocator');
+		$this->config = $this->getMock('\OCP\IConfig');
+		$this->cacheFactory = $this->getMock('\OCP\ICacheFactory');
+		$this->appManager = $this->getMock('\OCP\App\IAppManager');
+
+		$this->cacheFactory
+			->expects($this->any())
+			->method('create')
+			->with('oc.integritycheck.checker')
+			->will($this->returnValue(new NullCache()));
+
+		$this->checker = new Checker(
+			$this->environmentHelper,
+			$this->fileAccessHelper,
+			$this->appLocator,
+			$this->config,
+			$this->cacheFactory,
+			$this->appManager
+		);
+	}
+
+	/**
+	 * @expectedException \Exception
+	 * @expectedExceptionMessage Directory name must not be empty.
+	 */
+	public function testWriteAppSignatureOfNotExistingApp() {
+		$keyBundle = file_get_contents(__DIR__ .'/../../data/integritycheck/SomeApp.crt');
+		$rsaPrivateKey = file_get_contents(__DIR__ .'/../../data/integritycheck/SomeApp.key');
+		$rsa = new RSA();
+		$rsa->loadKey($rsaPrivateKey);
+		$x509 = new X509();
+		$x509->loadX509($keyBundle);
+		$this->checker->writeAppSignature('NotExistingApp', $x509, $rsa);
+	}
+
+	public function testWriteAppSignature() {
+		$expectedSignatureFileData = '{
+    "hashes": {
+        "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+        "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+    },
+    "signature": "Y5yvXvcGHVPuRRatKVDUONWq1FpLXugZd6Km\/+aEHsQj7coVl9FeMj9OsWamBf7yRIw3dtNLguTLlAA9QAv\/b0uHN3JnbNZN+dwFOve4NMtqXfSDlWftqKN00VS+RJXpG1S2IIx9Poyp2NoghL\/5AuTv4GHiNb7zU\/DT\/kt71pUGPgPR6IIFaE+zHOD96vjYkrH+GfWZzKR0FCdLib9yyNvk+EGrcjKM6qjs2GKfS\/XFjj\/\/neDnh\/0kcPuKE3ZbofnI4TIDTv0CGqvOp7PtqVNc3Vy\/UKa7uF1PT0MAUKMww6EiMUSFZdUVP4WWF0Y72W53Qdtf1hrAZa2kfKyoK5kd7sQmCSKUPSU8978AUVZlBtTRlyT803IKwMV0iHMkw+xYB1sN2FlHup\/DESADqxhdgYuK35bCPvgkb4SBe4B8Voz\/izTvcP7VT5UvkYdAO+05\/jzdaHEmzmsD92CFfvX0q8O\/Y\/29ubftUJsqcHeMDKgcR4eZOE8+\/QVc\/89QO6WnKNuNuV+5bybO6g6PAdC9ZPsCvnihS61O2mwRXHLR3jv2UleFWm+lZEquPKtkhi6SLtDiijA4GV6dmS+dzujSLb7hGeD5o1plZcZ94uhWljl+QIp82+zU\/lYB1Zfr4Mb4e+V7r2gv7Fbv7y6YtjE2GIQwRhC5jq56bD0ZB+I=",
+    "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf\/qTQvoyKAwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw\r\nDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk\r\nF8cAobMMi50qHCv9IrOn\/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl\/j\r\n+ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+\r\nA+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M\r\nw0xDv30D5UkE\/2N7Pa\/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4\r\nGB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6\r\ndol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj\r\nt5O7Zn2wA7I4ddDS\/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC\r\ncxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT\/x\/mqN3PfRmlnFBNACUw9bpZ\r\nSOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz\r\ntFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4\r\n6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf\r\nFWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS\r\nHVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70\/4imPoKxbAVCpd\/cveVcFyDC19j1yB\r\nBapwu87oh+muoeaZxOlqQI4UxjBlR\/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v\r\n0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3\r\npPhEpo1dRpiXaF7WGIV1X6DI\/ipWvfrF7CEy6I\/kP1InY\/vMDjQjeDnJ\/VrXIWXO\r\nyZvHXVaN\/m+1RlETsH7YO\/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF\r\n49\/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7\r\n7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW\r\nUO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS\/P4RB1NkHA9+NTvmBpTonS\r\nSFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP\/Y7xwxLv7\r\n4B+pXTAcRK0zECDEaX3npS8xWzrB\r\n-----END CERTIFICATE-----"
+}';
+		$this->appLocator
+			->expects($this->once())
+			->method('getAppPath')
+			->with('SomeExistingApp')
+			->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+		$this->fileAccessHelper
+			->expects($this->once())
+			->method('file_put_contents')
+			->with(
+					\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json',
+					$expectedSignatureFileData
+			);
+
+		$keyBundle = file_get_contents(__DIR__ .'/../../data/integritycheck/SomeApp.crt');
+		$rsaPrivateKey = file_get_contents(__DIR__ .'/../../data/integritycheck/SomeApp.key');
+		$rsa = new RSA();
+		$rsa->loadKey($rsaPrivateKey);
+		$x509 = new X509();
+		$x509->loadX509($keyBundle);
+		$this->checker->writeAppSignature('SomeExistingApp', $x509, $rsa);
+	}
+
+	public function testVerifyAppSignatureWithoutSignatureData() {
+		$expected = [
+			'EXCEPTION' => [
+					'class' => 'OC\IntegrityCheck\Exceptions\InvalidSignatureException',
+					'message' => 'Signature data not found.',
+			],
+		];
+		$this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
+	}
+
+	public function testVerifyAppSignatureWithValidSignatureData() {
+		$this->appLocator
+				->expects($this->once())
+				->method('getAppPath')
+				->with('SomeApp')
+				->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+		$signatureDataFile = '{
+    "hashes": {
+        "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+        "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+    },
+    "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
+    "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
+}';
+		$this->fileAccessHelper
+				->expects($this->at(0))
+				->method('file_get_contents')
+				->with(
+						\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json'
+				)
+				->will($this->returnValue($signatureDataFile));
+		$this->fileAccessHelper
+				->expects($this->at(1))
+				->method('file_get_contents')
+				->with(
+						'/resources/codesigning/root.crt'
+				)
+				->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+		$this->assertSame([], $this->checker->verifyAppSignature('SomeApp'));
+	}
+
+	public function testVerifyAppSignatureWithTamperedSignatureData() {
+		$this->appLocator
+				->expects($this->once())
+				->method('getAppPath')
+				->with('SomeApp')
+				->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+		$signatureDataFile = '{
+    "hashes": {
+        "AnotherFile.txt": "tampered",
+        "subfolder\/file.txt": "tampered"
+    },
+    "signature": "EL49UaSeyMAqyMtqId+tgOhhwgOevPZsRLX4j2blnybAB6fN07z0936JqZV7+eMPiE30Idx+UCY6rCFN531Kqe9vAOCdgtHUSOjjKyKc+lvULESlMb6YQcrZrvDlEMMjzjH49ewG7Ai8sNN6HrRUd9U8ws+ewSkW2DOOBItj\/21RBnkrSt+2AtGXGigEvuTm57HrCYDj8\/lSkumC2GVkjLUHeLOKYo4PRNOr6yP5mED5v7zo66AWvXl2fKv54InZcdxsAk35lyK9DGZbk\/027ZRd0AOHT3LImRLvQ+8EAg3XLlRUy0hOFGgPC+jYonMzgYvsAXAXi2j8LnLJlsLwpFwu1k1B+kZVPMumKZvP9OvJb70EirecXmz62V+Jiyuaq7ne4y7Kp5gKZT\/T8SeZ0lFtCmPfYyzBB0y8s5ldmTTmdVYHs54t\/OCCW82HzQZxnFNPzDTRa8HglsaMKrqPtW59+R4UvRKSWhB8M\/Ah57qgzycvPV4KMz\/FbD4l\/\/9chRKSlCfc2k3b8ZSHNmi+EzCKgJjWIoKdgN1yax94puU8jfn8UW+G7H9Y1Jsf\/jox6QLyYEgtV1vOHY2xLT7fVs2vhyvkN2MNjJnmQ70gFG5Qz2lBz5wi6ZpB+tOfCcpbLxWAkoWoIrmC\/Ilqh7mfmRZ43g5upjkepHNd93ONuY8=",
+    "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf\/qTQvoyKAwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw\r\nDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk\r\nF8cAobMMi50qHCv9IrOn\/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl\/j\r\n+ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+\r\nA+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M\r\nw0xDv30D5UkE\/2N7Pa\/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4\r\nGB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6\r\ndol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj\r\nt5O7Zn2wA7I4ddDS\/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC\r\ncxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT\/x\/mqN3PfRmlnFBNACUw9bpZ\r\nSOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz\r\ntFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4\r\n6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf\r\nFWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS\r\nHVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70\/4imPoKxbAVCpd\/cveVcFyDC19j1yB\r\nBapwu87oh+muoeaZxOlqQI4UxjBlR\/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v\r\n0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3\r\npPhEpo1dRpiXaF7WGIV1X6DI\/ipWvfrF7CEy6I\/kP1InY\/vMDjQjeDnJ\/VrXIWXO\r\nyZvHXVaN\/m+1RlETsH7YO\/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF\r\n49\/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7\r\n7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW\r\nUO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS\/P4RB1NkHA9+NTvmBpTonS\r\nSFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP\/Y7xwxLv7\r\n4B+pXTAcRK0zECDEaX3npS8xWzrB\r\n-----END CERTIFICATE-----"
+}';
+		$this->fileAccessHelper
+				->expects($this->at(0))
+				->method('file_get_contents')
+				->with(
+						\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json'
+				)
+				->will($this->returnValue($signatureDataFile));
+		$this->fileAccessHelper
+				->expects($this->at(1))
+				->method('file_get_contents')
+				->with(
+						'/resources/codesigning/root.crt'
+				)
+				->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+		$expected = [
+				'EXCEPTION' => [
+						'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
+						'message' => 'Signature could not get verified.',
+				],
+		];
+		$this->assertEquals($expected, $this->checker->verifyAppSignature('SomeApp'));
+	}
+
+	public function testVerifyAppSignatureWithTamperedFiles() {
+		$this->appLocator
+				->expects($this->once())
+				->method('getAppPath')
+				->with('SomeApp')
+				->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/'));
+		$signatureDataFile = '{
+    "hashes": {
+        "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+        "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+    },
+    "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
+    "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
+}';
+		$this->fileAccessHelper
+				->expects($this->at(0))
+				->method('file_get_contents')
+				->with(
+						\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json'
+				)
+				->will($this->returnValue($signatureDataFile));
+		$this->fileAccessHelper
+				->expects($this->at(1))
+				->method('file_get_contents')
+				->with(
+						'/resources/codesigning/root.crt'
+				)
+				->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+
+		$expected = [
+			'INVALID_HASH' => [
+				'AnotherFile.txt' => [
+						'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112',
+						'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c',
+				],
+			],
+			'FILE_MISSING' => [
+				'subfolder/file.txt' => [
+						'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b',
+						'current' => '',
+				],
+			],
+			'EXTRA_FILE' => [
+				'UnecessaryFile' => [
+						'expected' => '',
+						'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e',
+				],
+			],
+
+		];
+		$this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
+	}
+
+	public function testVerifyAppWithDifferentScope() {
+		$this->appLocator
+				->expects($this->once())
+				->method('getAppPath')
+				->with('SomeApp')
+				->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/'));
+		$signatureDataFile = '{
+    "hashes": {
+        "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+        "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+    },
+    "signature": "eXesvDm3pkek12xSwMG10y9suRES79Nye3jYNe5KYq1tTUPqRRNgxmMGAfcUro0zpLeAr2YgHeSMWtglblGOW7pmwGVPZ0O1Y4r1fE6jnep0kW+35PLIaqCorIOnCAtSzDNKBhwd1ow3zW2wC0DFouuEkIO8u5Fw28g8E8dp8zEk1xMblNPy+xtWkmYHrVJ\/dQgun1bYOF2ZFtAzatwndTI\/bGsy1i3Wsl+x6HyWKQdq8y8VObtOqKDH7uERBEpB9DHVyKflj1v1gQuEH6BhaRdATc7ee0MiQdGblraIySwYRdfo2d8i82OVKrenMB3SLwyCvDPyQ9iKpTOnSF52ZBqaqSXKM2N\/RAkweeBFQQCwcHhqxvB0cfbyHcbkOLeCZe\/tsh68IxwTiYgzvLfl7sOZ5arnZbzrPpZmB+hfV2omkoJ1tDwOWz9hEmLLNtfo2OxyUH1m0+XFaC+Gbn4WkVDgf7YZkwUcG+Qoa3oKDNMss8MEyZxewl2iDGZcf402dlidHRprlfmXbAYuVQ08\/a0HxIKYPGh\/nsMGmwnO15CWtFpAbhUA\/D5oRjsIxnvXaMDg0iAFpdu\/5Ffsj7g3EPdBkiQHNYK7YU1RRx609eH0bZyiIYHdUPw7ikLupvrebZmELqi3mqDFO99u4eISlxFJlUbUND3L4BtmWTWrKwI=",
+    "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIExjCCAq6gAwIBAgIUHSJjhJqMwr+3TkoiQFg4SVVYQ1gwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIzMjc1NVoXDTE2MTEwMzIzMjc1NVowFzEVMBMGA1UEAwwMQW5vdGhlclNjb3Bl\r\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA33npb5RmUkXrDT+TbwMf\r\n0zQ33SlzsjoGxCrbSwJOn6leGGInJ6ZrdzLL0WTi\/dTpg+Y\/JS+72XWm5NSjaTxo\r\n7OHc3cQBwXQj4tN6j\/y5qqY0GDLYufEkx2rpazqt9lBSJ72u1bGl2yoOXzYCz5i0\r\n60KsJXC9K44LKzGsarzbwAgskSVNkjAsPgjnCWZmcl6icpLi5Fz9rs2UMOWbdvdI\r\nAROsn0eC9E\/akmXTy5YMu6bAIGpvjZFHzyA83FQRbvv5o1V5Gsye\/VQLEgh7rqfz\r\nT\/jgWifP+JgoeB6otzuRZ3fFsmbBiyCIRtIOzQQflozhUlWtmiEGwg4GySuMUjEH\r\nA1LF86LO+ZzDQgd2oYNKmrQ8O+EcLqx9BpV4AFhEvqdk7uycJYPHs6yl+yfbzTeJ\r\n2Xd0yVAfd9r\/iDr36clLj2bzEObdl9xzKjcCIXE4Q0G4Pur41\/BJUDK9PI390ccQ\r\nnFjjVYBMsC859OwW64tMP0zkM9Vv72LCaEzaR8jqH0j11catqxunr+StfMcmxLTN\r\nbqBJbSEq4ER3mJxCTI2UrIVmdQ7+wRxgv3QTDNOZyqrz2L8A1Rpb3h0APxtQv+oA\r\n8KIZYID5\/qsS2V2jITkMQ8Nd1W3b0cZhZ600z+znh3jLJ0TYLvwN6\/qBQTUDaM2o\r\ng1+icMqXIXIeKuoPCVVsG7cCAwEAATANBgkqhkiG9w0BAQUFAAOCAgEAHc4F\/kOV\r\nHc8In5MmGg2YtjwZzjdeoC5TIPZczRqz0B+wRbJzN6aYryKZKLmP+wKpgRnJWDzp\r\nrgKGyyEQIAfK63DEv4B9p4N1B+B3aeMKsSpVcw7wbFTD57V5A7pURGoo31d0mw5L\r\nUIXZ2u+TUfGbzucMxLdFhTwjGpz9M6Kkm\/POxmV0tvLija5LdbdKnYR9BFmyu4IX\r\nqyoIAtComATNLl+3URu3SZxhE3NxhzMz+eAeNfh1KuIf2gWIIeDCXalVSJLym+OQ\r\nHFDpqRhJqfTMprrRlmmU7Zntgbj8\/RRZuXnBvH9cQ2KykLOb4UoCPlGUqOqKyP9m\r\nDJSFRiMJfpgMQUaJk1TLhKF+IR6FnmwURLEtkONJumtDQju9KaWPlhueONdyGi0p\r\nqxLVUo1Vb52XnPhk2GEEduxpDc9V5ePJ+pdcEdMifY\/uPNBRuBj2c87yq1DLH+U4\r\n3XzP1MlwjnBWZYuoFo0j6Jq0r\/MG6HjGdmkGIsRoheRi8Z8Scz5AW5QRkNz8pKop\r\nTELFqQy9g6TyQzzC8t6HZcpNe842ZUk4raEAbCZe\/XqxWMw5svPgNceBqM3fh7sZ\r\nBSykOHLaL8kiRO\/IS3y1yZEAuiWBvtxcTNLzBb+hdRpm2y8\/qH\/pKo+CMj1VzjNT\r\nD8YRQg0cjmDytJzHDrtV\/aTc9W1aPHun0vw=\r\n-----END CERTIFICATE-----"
+}';
+		$this->fileAccessHelper
+				->expects($this->at(0))
+				->method('file_get_contents')
+				->with(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json')
+				->will($this->returnValue($signatureDataFile));
+		$this->fileAccessHelper
+				->expects($this->at(1))
+				->method('file_get_contents')
+				->with(
+						'/resources/codesigning/root.crt'
+				)
+				->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+		$expected = [
+				'EXCEPTION' => [
+						'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
+						'message' => 'Certificate is not valid for required scope. (Requested: SomeApp, current: CN=AnotherScope)',
+					],
+		];
+		$this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
+	}
+
+	public function testVerifyAppWithDifferentScopeAndAlwaysTrustedCore() {
+		$this->appLocator
+				->expects($this->once())
+				->method('getAppPath')
+				->with('SomeApp')
+				->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+		$signatureDataFile = '{
+    "hashes": {
+        "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+        "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+    },
+    "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
+    "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
+}';
+		$this->fileAccessHelper
+				->expects($this->at(0))
+				->method('file_get_contents')
+				->with(\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json')
+				->will($this->returnValue($signatureDataFile));
+		$this->fileAccessHelper
+				->expects($this->at(1))
+				->method('file_get_contents')
+				->with(
+					'/resources/codesigning/root.crt'
+				)
+				->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+		$this->assertSame([], $this->checker->verifyAppSignature('SomeApp'));
+	}
+
+	public function testWriteCoreSignature() {
+		$expectedSignatureFileData = '{
+    "hashes": {
+        "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+        "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+    },
+    "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
+    "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
+}';
+		$this->environmentHelper
+				->expects($this->any())
+				->method('getServerRoot')
+				->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+		$this->fileAccessHelper
+				->expects($this->once())
+				->method('file_put_contents')
+				->with(
+						\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json',
+						$expectedSignatureFileData
+				);
+
+		$keyBundle = file_get_contents(__DIR__ .'/../../data/integritycheck/core.crt');
+		$rsaPrivateKey = file_get_contents(__DIR__ .'/../../data/integritycheck/core.key');
+		$rsa = new RSA();
+		$rsa->loadKey($rsaPrivateKey);
+		$x509 = new X509();
+		$x509->loadX509($keyBundle);
+		$this->checker->writeCoreSignature($x509, $rsa);
+	}
+
+	public function testVerifyCoreSignatureWithoutSignatureData() {
+		$expected = [
+			'EXCEPTION' => [
+				'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
+				'message' => 'Signature data not found.',
+			],
+		];
+		$this->assertSame($expected, $this->checker->verifyCoreSignature());
+	}
+
+	public function testVerifyCoreSignatureWithValidSignatureData() {
+		$this->environmentHelper
+				->expects($this->any())
+				->method('getServerRoot')
+				->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+		$signatureDataFile = '{
+    "hashes": {
+        "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+        "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+    },
+    "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
+    "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
+}';
+		$this->fileAccessHelper
+				->expects($this->at(0))
+				->method('file_get_contents')
+				->with(
+						\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json'
+				)
+				->will($this->returnValue($signatureDataFile));
+		$this->fileAccessHelper
+				->expects($this->at(1))
+				->method('file_get_contents')
+				->with(
+						\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt'
+				)
+				->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+		$this->assertSame([], $this->checker->verifyCoreSignature());
+	}
+
+	public function testVerifyCoreSignatureWithValidSignatureDataAndNotAlphabeticOrder() {
+		$this->environmentHelper
+				->expects($this->any())
+				->method('getServerRoot')
+				->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+		$signatureDataFile = '{
+    "hashes": {
+        "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+        "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+    },
+    "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
+    "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
+}';
+		$this->fileAccessHelper
+				->expects($this->at(0))
+				->method('file_get_contents')
+				->with(
+						\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json'
+				)
+				->will($this->returnValue($signatureDataFile));
+		$this->fileAccessHelper
+				->expects($this->at(1))
+				->method('file_get_contents')
+				->with(
+						\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt'
+				)
+				->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+		$this->assertSame([], $this->checker->verifyCoreSignature());
+	}
+
+	public function testVerifyCoreSignatureWithTamperedSignatureData() {
+		$this->environmentHelper
+				->expects($this->any())
+				->method('getServerRoot')
+				->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/'));
+		$signatureDataFile = '{
+    "hashes": {
+        "AnotherFile.txt": "tampered",
+        "subfolder\/file.txt": "tampered"
+    },
+    "signature": "eXesvDm3pkek12xSwMG10y9suRES79Nye3jYNe5KYq1tTUPqRRNgxmMGAfcUro0zpLeAr2YgHeSMWtglblGOW7pmwGVPZ0O1Y4r1fE6jnep0kW+35PLIaqCorIOnCAtSzDNKBhwd1ow3zW2wC0DFouuEkIO8u5Fw28g8E8dp8zEk1xMblNPy+xtWkmYHrVJ\/dQgun1bYOF2ZFtAzatwndTI\/bGsy1i3Wsl+x6HyWKQdq8y8VObtOqKDH7uERBEpB9DHVyKflj1v1gQuEH6BhaRdATc7ee0MiQdGblraIySwYRdfo2d8i82OVKrenMB3SLwyCvDPyQ9iKpTOnSF52ZBqaqSXKM2N\/RAkweeBFQQCwcHhqxvB0cfbyHcbkOLeCZe\/tsh68IxwTiYgzvLfl7sOZ5arnZbzrPpZmB+hfV2omkoJ1tDwOWz9hEmLLNtfo2OxyUH1m0+XFaC+Gbn4WkVDgf7YZkwUcG+Qoa3oKDNMss8MEyZxewl2iDGZcf402dlidHRprlfmXbAYuVQ08\/a0HxIKYPGh\/nsMGmwnO15CWtFpAbhUA\/D5oRjsIxnvXaMDg0iAFpdu\/5Ffsj7g3EPdBkiQHNYK7YU1RRx609eH0bZyiIYHdUPw7ikLupvrebZmELqi3mqDFO99u4eISlxFJlUbUND3L4BtmWTWrKwI=",
+    "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
+}';
+		$this->fileAccessHelper
+				->expects($this->at(0))
+				->method('file_get_contents')
+				->with(
+						\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//core/signature.json'
+				)
+				->will($this->returnValue($signatureDataFile));
+		$this->fileAccessHelper
+				->expects($this->at(1))
+				->method('file_get_contents')
+				->with(
+						\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//resources/codesigning/root.crt'
+				)
+				->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+		$expected = [
+				'EXCEPTION' => [
+						'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
+						'message' => 'Signature could not get verified.',
+				]
+		];
+		$this->assertSame($expected, $this->checker->verifyCoreSignature());
+	}
+
+	public function testVerifyCoreSignatureWithTamperedFiles() {
+		$this->environmentHelper
+				->expects($this->any())
+				->method('getServerRoot')
+				->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/'));
+		$signatureDataFile = '{
+    "hashes": {
+        "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+        "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+    },
+    "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
+    "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
+}';
+		$this->fileAccessHelper
+				->expects($this->at(0))
+				->method('file_get_contents')
+				->with(
+						\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//core/signature.json'
+				)
+				->will($this->returnValue($signatureDataFile));
+		$this->fileAccessHelper
+				->expects($this->at(1))
+				->method('file_get_contents')
+				->with(
+						\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//resources/codesigning/root.crt'
+				)
+				->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+		$expected = [
+				'INVALID_HASH' => [
+						'AnotherFile.txt' => [
+								'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112',
+								'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c',
+						],
+				],
+				'FILE_MISSING' => [
+						'subfolder/file.txt' => [
+								'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b',
+								'current' => '',
+						],
+				],
+				'EXTRA_FILE' => [
+						'UnecessaryFile' => [
+								'expected' => '',
+								'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e',
+						],
+				],
+
+		];
+		$this->assertSame($expected, $this->checker->verifyCoreSignature());
+	}
+
+	public function testVerifyCoreWithInvalidCertificate() {
+		$this->environmentHelper
+				->expects($this->any())
+				->method('getServerRoot')
+				->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+		$signatureDataFile = '{
+    "hashes": {
+        "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+        "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+    },
+    "signature": "eXesvDm3pkek12xSwMG10y9suRES79Nye3jYNe5KYq1tTUPqRRNgxmMGAfcUro0zpLeAr2YgHeSMWtglblGOW7pmwGVPZ0O1Y4r1fE6jnep0kW+35PLIaqCorIOnCAtSzDNKBhwd1ow3zW2wC0DFouuEkIO8u5Fw28g8E8dp8zEk1xMblNPy+xtWkmYHrVJ\/dQgun1bYOF2ZFtAzatwndTI\/bGsy1i3Wsl+x6HyWKQdq8y8VObtOqKDH7uERBEpB9DHVyKflj1v1gQuEH6BhaRdATc7ee0MiQdGblraIySwYRdfo2d8i82OVKrenMB3SLwyCvDPyQ9iKpTOnSF52ZBqaqSXKM2N\/RAkweeBFQQCwcHhqxvB0cfbyHcbkOLeCZe\/tsh68IxwTiYgzvLfl7sOZ5arnZbzrPpZmB+hfV2omkoJ1tDwOWz9hEmLLNtfo2OxyUH1m0+XFaC+Gbn4WkVDgf7YZkwUcG+Qoa3oKDNMss8MEyZxewl2iDGZcf402dlidHRprlfmXbAYuVQ08\/a0HxIKYPGh\/nsMGmwnO15CWtFpAbhUA\/D5oRjsIxnvXaMDg0iAFpdu\/5Ffsj7g3EPdBkiQHNYK7YU1RRx609eH0bZyiIYHdUPw7ikLupvrebZmELqi3mqDFO99u4eISlxFJlUbUND3L4BtmWTWrKwI=",
+    "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUPYoweUxCPqbDW4ntuh7QvgyqSrgwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDIwNloXDTE2MTEwMzIyNDIwNlowDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJui3nDbjOIjxNnthdBZplphujsN6u8K\r\nQ\/62zAuSwzXVp0+3IMgM\/2sepklVE8YfCyVJ5+SUJqnqHoUWVRVfs8jL0wW6nrHM\r\n\/lsscAguWCee4iAdNOqI9kq4+DUau8J45e62XA9mrAo\/8\/NKzFE2y2WduDoQZcm+\r\n8+dwcUUHXw2jl8dfrmvEMYSqTNDdb4rGmQpeV+dr9BLqr+x03U1Q08qCG9j7mSOz\r\ncvJENjOvC5uzAh5LCuCgxqG4o+mPzB0FtNnwoRRu6IsF3Y3KacRqPc30fB\/iXDn5\r\nBPr14uNxTTYWoZJ1F0tZrLzRbXdjJJOC+dnQurTtXWZ8WjPB1BWQYK7fW6t82mkN\r\n2Qe2xen99gs9nX5yY\/sHM3TKSJdM7AVCEv\/emW3gNjkvWTtRlN\/Nc7X2ckNwXcvo\r\n0yi3fSPjzXpDgLbhp1FzrMlHDn1VzmRT3r8wLByWa\/hsxrJDsBzwunMJYhXhmeKb\r\n3wX0tN\/EUJTWBntpwVOIGnRPD51oBoQUOMaEAq\/kz8PgN181bWZkJbRuf+FWkijQ\r\no+HR2lVF1jWXXst5Uc+s9HN81Uly7X4O9MMg0QxT4+wymtGDs6AOkwMi9rgBTrRB\r\n3tLU3XL2UIwRXgmd8cPtTu\/I6Bm7LdyaYtZ3yJTxRewq3nZdWypqBhD8uhpIYVkf\r\no4bxmGkVAQVTAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAKKAX5EHgU1grODnJ0of\r\nspFpgB1K67YvclNUyuU6NQ6zBJx1\/w1RnM7uxLcxiiWj1BbUhwZQ0ojmEHeUyi6O\r\nGrDVajwhTccDMmja3u5adhEncx65\/H+lD85IPRRkS2qBDssMDdJHhZ0uI+40nI7M\r\nMq1kFjl+6wiuqZXqps66DuLbk45g\/ZlrFIrIo3Ix5vj0OVqwT+gO4LYirJK6KgVS\r\nUttbcEsc\/yKU9ThnM8\/n4m2jstZXfzKPgOsJrQcZrFOtpj+CWmBzVElBSPlDT3Nh\r\nHSgOeTFJ8bQBxj2iG5dLA+JZJQKxyJ1gy2ZtxIJ2GyvLtSe8NUSqvfPWOaAKEUV2\r\ngniytnEFLr+PcD+9EGux6jZNuj6HmtWVThTfD5VGFmtlVU2z71ZRYY0kn6J3mmFc\r\nS2ecEcCUwqG5YNLncEUCyZhC2klWql2SHyGctCEyWWY7ikIDjVzYt2EbcFvLNBnP\r\ntybN1TYHRRZxlug00CCoOE9EZfk46FkZpDvU6KmqJRofkNZ5sj+SffyGcwYwNrDH\r\nKqe8m+9lHf3CRTIDeMu8r2xl1I6M6ZZfjabbmVP9Jd6WN4s6f1FlXDWzhlT1N0Qw\r\nGzJj6xB+SPtS3UV05tBlvbfA4e06D5G9uD7Q8ONcINtMS0xsSJ2oo82AqlpvlF\/q\r\noj7YKHsaTVGA+FxBktZHfoxD\r\n-----END CERTIFICATE-----"
+}';
+		$this->fileAccessHelper
+				->expects($this->at(0))
+				->method('file_get_contents')
+				->with(
+						\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json'
+				)
+				->will($this->returnValue($signatureDataFile));
+		$this->fileAccessHelper
+				->expects($this->at(1))
+				->method('file_get_contents')
+				->with(
+						\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt'
+				)
+				->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+		$expected = [
+				'EXCEPTION' => [
+						'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
+						'message' => 'Certificate is not valid.',
+				]
+		];
+		$this->assertSame($expected, $this->checker->verifyCoreSignature());
+	}
+
+	public function testVerifyCoreWithDifferentScope() {
+		$this->environmentHelper
+				->expects($this->any())
+				->method('getServerRoot')
+				->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+		$signatureDataFile = '{
+    "hashes": {
+        "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+        "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+    },
+    "signature": "EL49UaSeyMAqyMtqId+tgOhhwgOevPZsRLX4j2blnybAB6fN07z0936JqZV7+eMPiE30Idx+UCY6rCFN531Kqe9vAOCdgtHUSOjjKyKc+lvULESlMb6YQcrZrvDlEMMjzjH49ewG7Ai8sNN6HrRUd9U8ws+ewSkW2DOOBItj\/21RBnkrSt+2AtGXGigEvuTm57HrCYDj8\/lSkumC2GVkjLUHeLOKYo4PRNOr6yP5mED5v7zo66AWvXl2fKv54InZcdxsAk35lyK9DGZbk\/027ZRd0AOHT3LImRLvQ+8EAg3XLlRUy0hOFGgPC+jYonMzgYvsAXAXi2j8LnLJlsLwpFwu1k1B+kZVPMumKZvP9OvJb70EirecXmz62V+Jiyuaq7ne4y7Kp5gKZT\/T8SeZ0lFtCmPfYyzBB0y8s5ldmTTmdVYHs54t\/OCCW82HzQZxnFNPzDTRa8HglsaMKrqPtW59+R4UvRKSWhB8M\/Ah57qgzycvPV4KMz\/FbD4l\/\/9chRKSlCfc2k3b8ZSHNmi+EzCKgJjWIoKdgN1yax94puU8jfn8UW+G7H9Y1Jsf\/jox6QLyYEgtV1vOHY2xLT7fVs2vhyvkN2MNjJnmQ70gFG5Qz2lBz5wi6ZpB+tOfCcpbLxWAkoWoIrmC\/Ilqh7mfmRZ43g5upjkepHNd93ONuY8=",
+    "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf\/qTQvoyKAwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw\r\nDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk\r\nF8cAobMMi50qHCv9IrOn\/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl\/j\r\n+ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+\r\nA+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M\r\nw0xDv30D5UkE\/2N7Pa\/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4\r\nGB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6\r\ndol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj\r\nt5O7Zn2wA7I4ddDS\/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC\r\ncxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT\/x\/mqN3PfRmlnFBNACUw9bpZ\r\nSOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz\r\ntFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4\r\n6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf\r\nFWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS\r\nHVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70\/4imPoKxbAVCpd\/cveVcFyDC19j1yB\r\nBapwu87oh+muoeaZxOlqQI4UxjBlR\/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v\r\n0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3\r\npPhEpo1dRpiXaF7WGIV1X6DI\/ipWvfrF7CEy6I\/kP1InY\/vMDjQjeDnJ\/VrXIWXO\r\nyZvHXVaN\/m+1RlETsH7YO\/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF\r\n49\/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7\r\n7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW\r\nUO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS\/P4RB1NkHA9+NTvmBpTonS\r\nSFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP\/Y7xwxLv7\r\n4B+pXTAcRK0zECDEaX3npS8xWzrB\r\n-----END CERTIFICATE-----"
+}';
+		$this->fileAccessHelper
+				->expects($this->at(0))
+				->method('file_get_contents')
+				->with(
+						\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json'
+				)
+				->will($this->returnValue($signatureDataFile));
+		$this->fileAccessHelper
+				->expects($this->at(1))
+				->method('file_get_contents')
+				->with(
+						\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt'
+				)
+				->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+		$expected = [
+				'EXCEPTION' => [
+						'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
+						'message' => 'Certificate is not valid for required scope. (Requested: core, current: CN=SomeApp)',
+				]
+		];
+		$this->assertSame($expected, $this->checker->verifyCoreSignature());
+	}
+
+	public function testRunInstanceVerification() {
+		$this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
+			->setConstructorArgs([
+				$this->environmentHelper,
+				$this->fileAccessHelper,
+				$this->appLocator,
+				$this->config,
+				$this->cacheFactory,
+				$this->appManager,
+			])
+			->setMethods([
+				'verifyCoreSignature',
+				'verifyAppSignature',
+			])
+			->getMock();
+
+		$this->checker
+			->expects($this->at(0))
+			->method('verifyCoreSignature');
+		$this->appLocator
+			->expects($this->at(0))
+			->Method('getAllApps')
+			->will($this->returnValue([
+				'files',
+				'calendar',
+				'contacts',
+				'dav',
+			]));
+		$this->appManager
+			->expects($this->at(0))
+			->method('isShipped')
+			->with('files')
+			->will($this->returnValue(true));
+		$this->checker
+			->expects($this->at(1))
+			->method('verifyAppSignature')
+			->with('files');
+		$this->appManager
+			->expects($this->at(1))
+			->method('isShipped')
+			->with('calendar')
+			->will($this->returnValue(false));
+		$this->appLocator
+			->expects($this->at(1))
+			->method('getAppPath')
+			->with('calendar')
+			->will($this->returnValue('/apps/calendar'));
+		$this->fileAccessHelper
+			->expects($this->at(0))
+			->method('file_exists')
+			->with('/apps/calendar/appinfo/signature.json')
+			->will($this->returnValue(true));
+		$this->checker
+			->expects($this->at(2))
+			->method('verifyAppSignature')
+			->with('calendar');
+		$this->appManager
+				->expects($this->at(2))
+				->method('isShipped')
+				->with('contacts')
+				->will($this->returnValue(false));
+		$this->appLocator
+				->expects($this->at(2))
+				->method('getAppPath')
+				->with('contacts')
+				->will($this->returnValue('/apps/contacts'));
+		$this->fileAccessHelper
+				->expects($this->at(1))
+				->method('file_exists')
+				->with('/apps/contacts/appinfo/signature.json')
+				->will($this->returnValue(false));
+		$this->appManager
+			->expects($this->at(3))
+			->method('isShipped')
+			->with('dav')
+			->will($this->returnValue(true));
+		$this->checker
+			->expects($this->at(3))
+			->method('verifyAppSignature')
+			->with('dav');
+
+		$this->checker->runInstanceVerification();
+	}
+
+}
diff --git a/tests/lib/integritycheck/helpers/AppLocatorTest.php b/tests/lib/integritycheck/helpers/AppLocatorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..d0083298b22d4c7b4ef5b14e702cacc0af549714
--- /dev/null
+++ b/tests/lib/integritycheck/helpers/AppLocatorTest.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\IntegrityCheck\Helpers;
+
+use OC\IntegrityCheck\Helpers\AppLocator;
+use Test\TestCase;
+
+class AppLocatorTest extends TestCase {
+	/** @var AppLocator */
+	private $locator;
+
+	public function setUp() {
+		parent::setUp();
+		$this->locator = new AppLocator();
+	}
+
+	public function testGetAppPath() {
+		$this->assertSame(\OC_App::getAppPath('files'), $this->locator->getAppPath('files'));
+	}
+
+	/**
+	 * @expectedException \Exception
+	 * @expectedExceptionMessage App not found
+	 */
+	public function testGetAppPathNotExistentApp() {
+		$this->locator->getAppPath('aTotallyNotExistingApp');
+	}
+
+	public function testGetAllApps() {
+		$this->assertSame(\OC_App::getAllApps(), $this->locator->getAllApps());
+	}
+}
diff --git a/tests/lib/integritycheck/helpers/EnvironmentHelperTest.php b/tests/lib/integritycheck/helpers/EnvironmentHelperTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4a292188e7538eab2ebd1a803f42dbca45c918eb
--- /dev/null
+++ b/tests/lib/integritycheck/helpers/EnvironmentHelperTest.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\IntegrityCheck\Factories;
+
+use OC\IntegrityCheck\Helpers\EnvironmentHelper;
+use Test\TestCase;
+
+class EnvironmentHelperTest extends TestCase {
+	public function testGetServerRoot() {
+		$factory = new EnvironmentHelper();
+		$this->assertSame(\OC::$SERVERROOT, $factory->getServerRoot());
+	}
+}
diff --git a/tests/lib/integritycheck/helpers/FileAccessHelperTest.php b/tests/lib/integritycheck/helpers/FileAccessHelperTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..740b14e61c4632182904c47fe6b412bd453417c1
--- /dev/null
+++ b/tests/lib/integritycheck/helpers/FileAccessHelperTest.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace Test\IntegrityCheck\Helpers;
+
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use Test\TestCase;
+
+class FileAccessHelperTest extends TestCase {
+	/** @var FileAccessHelper */
+	private $fileAccessHelper;
+
+	public function setUp() {
+		parent::setUp();
+		$this->fileAccessHelper = new FileAccessHelper();
+	}
+
+	public function testReadAndWrite() {
+		$tempManager = \OC::$server->getTempManager();
+		$filePath = $tempManager->getTemporaryFile();
+		$data = 'SomeDataGeneratedByIntegrityCheck';
+
+		$this->fileAccessHelper->file_put_contents($filePath, $data);
+		$this->assertSame($data, $this->fileAccessHelper->file_get_contents($filePath));
+	}
+}
diff --git a/tests/lib/updater.php b/tests/lib/updater.php
index 1651fe1759d1630bc895c00b17f20830731689da..14ae3db3276bc2cd8e47e7f01150a8d8712039d5 100644
--- a/tests/lib/updater.php
+++ b/tests/lib/updater.php
@@ -24,6 +24,7 @@ namespace OC;
 
 use OCP\IConfig;
 use OCP\ILogger;
+use OC\IntegrityCheck\Checker;
 
 class UpdaterTest extends \Test\TestCase {
 	/** @var IConfig */
@@ -34,6 +35,8 @@ class UpdaterTest extends \Test\TestCase {
 	private $logger;
 	/** @var Updater */
 	private $updater;
+	/** @var Checker */
+	private $checker;
 
 	public function setUp() {
 		parent::setUp();
@@ -46,10 +49,14 @@ class UpdaterTest extends \Test\TestCase {
 		$this->logger = $this->getMockBuilder('\\OCP\\ILogger')
 			->disableOriginalConstructor()
 			->getMock();
+		$this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
+				->disableOriginalConstructor()
+				->getMock();
 
 		$this->updater = new Updater(
 			$this->httpHelper,
 			$this->config,
+			$this->checker,
 			$this->logger
 		);
 	}
@@ -158,11 +165,11 @@ class UpdaterTest extends \Test\TestCase {
 	 *
 	 * @param string $oldVersion
 	 * @param string $newVersion
+	 * @param string $allowedVersion
 	 * @param bool $result
 	 */
 	public function testIsUpgradePossible($oldVersion, $newVersion, $allowedVersion, $result) {
-		$updater = new Updater($this->httpHelper, $this->config, $this->logger);
-		$this->assertSame($result, $updater->isUpgradePossible($oldVersion, $newVersion, $allowedVersion));
+		$this->assertSame($result, $this->updater->isUpgradePossible($oldVersion, $newVersion, $allowedVersion));
 	}
 
 	public function testCheckInCache() {
diff --git a/tests/settings/controller/CheckSetupControllerTest.php b/tests/settings/controller/CheckSetupControllerTest.php
index ebba18de5fd6c59ff29215aa5da0aa6233940443..08d4135fa124ebd5da5815139bafe1bd88425d2c 100644
--- a/tests/settings/controller/CheckSetupControllerTest.php
+++ b/tests/settings/controller/CheckSetupControllerTest.php
@@ -21,7 +21,10 @@
 
 namespace OC\Settings\Controller;
 
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\DataDisplayResponse;
 use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\Http\RedirectResponse;
 use OCP\Http\Client\IClientService;
 use OCP\IConfig;
 use OCP\IL10N;
@@ -29,6 +32,7 @@ use OCP\IRequest;
 use OCP\IURLGenerator;
 use OC_Util;
 use Test\TestCase;
+use OC\IntegrityCheck\Checker;
 
 /**
  * Mock version_compare
@@ -63,6 +67,8 @@ class CheckSetupControllerTest extends TestCase {
 	private $util;
 	/** @var IL10N */
 	private $l10n;
+	/** @var Checker */
+	private $checker;
 
 	public function setUp() {
 		parent::setUp();
@@ -86,6 +92,8 @@ class CheckSetupControllerTest extends TestCase {
 			->will($this->returnCallback(function($message, array $replace) {
 				return vsprintf($message, $replace);
 			}));
+		$this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
+				->disableOriginalConstructor()->getMock();
 		$this->checkSetupController = $this->getMockBuilder('\OC\Settings\Controller\CheckSetupController')
 			->setConstructorArgs([
 				'settings',
@@ -95,6 +103,7 @@ class CheckSetupControllerTest extends TestCase {
 				$this->urlGenerator,
 				$this->util,
 				$this->l10n,
+				$this->checker,
 				])
 			->setMethods(['getCurlVersion'])->getMock();
 	}
@@ -351,6 +360,8 @@ class CheckSetupControllerTest extends TestCase {
 				'forwardedForHeadersWorking' => true,
 				'reverseProxyDocs' => 'reverse-proxy-doc-link',
 				'isCorrectMemcachedPHPModuleInstalled' => true,
+				'hasPassedCodeIntegrityCheck' => null,
+				'codeIntegrityCheckerDocumentation' => null,
 			]
 		);
 		$this->assertEquals($expected, $this->checkSetupController->check());
@@ -366,6 +377,7 @@ class CheckSetupControllerTest extends TestCase {
 				$this->urlGenerator,
 				$this->util,
 				$this->l10n,
+				$this->checker
 			])
 			->setMethods(null)->getMock();
 
@@ -596,4 +608,441 @@ class CheckSetupControllerTest extends TestCase {
 			->will($this->returnValue([]));
 		$this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated'));
 	}
+
+	public function testRescanFailedIntegrityCheck() {
+		$this->checker
+			->expects($this->once())
+			->method('runInstanceVerification');
+		$this->urlGenerator
+			->expects($this->once())
+			->method('linkToRoute')
+			->with('settings_admin')
+			->will($this->returnValue('/admin'));
+
+		$expected = new RedirectResponse('/admin');
+		$this->assertEquals($expected, $this->checkSetupController->rescanFailedIntegrityCheck());
+	}
+
+	public function testGetFailedIntegrityCheckFilesWithNoErrorsFound() {
+		$this->checker
+			->expects($this->once())
+			->method('getResults')
+			->will($this->returnValue([]));
+
+		$expected = new DataDisplayResponse(
+				'No errors have been found.',
+				Http::STATUS_OK,
+				[
+						'Content-Type' => 'text/plain',
+				]
+		);
+		$this->assertEquals($expected, $this->checkSetupController->getFailedIntegrityCheckFiles());
+	}
+
+	public function testGetFailedIntegrityCheckFilesWithSomeErrorsFound() {
+		$this->checker
+				->expects($this->once())
+				->method('getResults')
+				->will($this->returnValue(array ( 'core' => array ( 'EXTRA_FILE' => array('/testfile' => array()), 'INVALID_HASH' => array ( '/.idea/workspace.xml' => array ( 'expected' => 'f1c5e2630d784bc9cb02d5a28f55d6f24d06dae2a0fee685f3c2521b050955d9d452769f61454c9ddfa9c308146ade10546cfa829794448eaffbc9a04a29d216', 'current' => 'ce08bf30bcbb879a18b49239a9bec6b8702f52452f88a9d32142cad8d2494d5735e6bfa0d8642b2762c62ca5be49f9bf4ec231d4a230559d4f3e2c471d3ea094', ), '/lib/private/integritycheck/checker.php' => array ( 'expected' => 'c5a03bacae8dedf8b239997901ba1fffd2fe51271d13a00cc4b34b09cca5176397a89fc27381cbb1f72855fa18b69b6f87d7d5685c3b45aee373b09be54742ea', 'current' => '88a3a92c11db91dec1ac3be0e1c87f862c95ba6ffaaaa3f2c3b8f682187c66f07af3a3b557a868342ef4a271218fe1c1e300c478e6c156c5955ed53c40d06585', ), '/settings/controller/checksetupcontroller.php' => array ( 'expected' => '3e1de26ce93c7bfe0ede7c19cb6c93cadc010340225b375607a7178812e9de163179b0dc33809f451e01f491d93f6f5aaca7929685d21594cccf8bda732327c4', 'current' => '09563164f9904a837f9ca0b5f626db56c838e5098e0ccc1d8b935f68fa03a25c5ec6f6b2d9e44a868e8b85764dafd1605522b4af8db0ae269d73432e9a01e63a', ), ), ), 'bookmarks' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'dav' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'encryption' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'external' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'federation' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files_antivirus' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files_drop' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files_external' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files_pdfviewer' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files_sharing' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files_trashbin' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files_versions' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files_videoviewer' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'firstrunwizard' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'gitsmart' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'logreader' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature could not get verified.', ), ), 'password_policy' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'provisioning_api' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'sketch' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'threatblock' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'two_factor_auth' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'user_ldap' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'user_shibboleth' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), )));
+
+		$expected = new DataDisplayResponse(
+				'Technical information
+=====================
+The following list covers which files have failed the integrity check. Please read
+the previous linked documentation to learn more about the errors and how to fix
+them.
+
+Results
+=======
+- core
+	- EXTRA_FILE
+		- /testfile
+	- INVALID_HASH
+		- /.idea/workspace.xml
+		- /lib/private/integritycheck/checker.php
+		- /settings/controller/checksetupcontroller.php
+- bookmarks
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- dav
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- encryption
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- external
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- federation
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- files
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- files_antivirus
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- files_drop
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- files_external
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- files_pdfviewer
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- files_sharing
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- files_trashbin
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- files_versions
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- files_videoviewer
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- firstrunwizard
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- gitsmart
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- logreader
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature could not get verified.
+- password_policy
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- provisioning_api
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- sketch
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- threatblock
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- two_factor_auth
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- user_ldap
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+- user_shibboleth
+	- EXCEPTION
+		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
+		- Signature data not found.
+
+Raw output
+==========
+Array
+(
+    [core] => Array
+        (
+            [EXTRA_FILE] => Array
+                (
+                    [/testfile] => Array
+                        (
+                        )
+
+                )
+
+            [INVALID_HASH] => Array
+                (
+                    [/.idea/workspace.xml] => Array
+                        (
+                            [expected] => f1c5e2630d784bc9cb02d5a28f55d6f24d06dae2a0fee685f3c2521b050955d9d452769f61454c9ddfa9c308146ade10546cfa829794448eaffbc9a04a29d216
+                            [current] => ce08bf30bcbb879a18b49239a9bec6b8702f52452f88a9d32142cad8d2494d5735e6bfa0d8642b2762c62ca5be49f9bf4ec231d4a230559d4f3e2c471d3ea094
+                        )
+
+                    [/lib/private/integritycheck/checker.php] => Array
+                        (
+                            [expected] => c5a03bacae8dedf8b239997901ba1fffd2fe51271d13a00cc4b34b09cca5176397a89fc27381cbb1f72855fa18b69b6f87d7d5685c3b45aee373b09be54742ea
+                            [current] => 88a3a92c11db91dec1ac3be0e1c87f862c95ba6ffaaaa3f2c3b8f682187c66f07af3a3b557a868342ef4a271218fe1c1e300c478e6c156c5955ed53c40d06585
+                        )
+
+                    [/settings/controller/checksetupcontroller.php] => Array
+                        (
+                            [expected] => 3e1de26ce93c7bfe0ede7c19cb6c93cadc010340225b375607a7178812e9de163179b0dc33809f451e01f491d93f6f5aaca7929685d21594cccf8bda732327c4
+                            [current] => 09563164f9904a837f9ca0b5f626db56c838e5098e0ccc1d8b935f68fa03a25c5ec6f6b2d9e44a868e8b85764dafd1605522b4af8db0ae269d73432e9a01e63a
+                        )
+
+                )
+
+        )
+
+    [bookmarks] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [dav] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [encryption] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [external] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [federation] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [files] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [files_antivirus] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [files_drop] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [files_external] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [files_pdfviewer] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [files_sharing] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [files_trashbin] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [files_versions] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [files_videoviewer] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [firstrunwizard] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [gitsmart] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [logreader] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature could not get verified.
+                )
+
+        )
+
+    [password_policy] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [provisioning_api] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [sketch] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [threatblock] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [two_factor_auth] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [user_ldap] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+    [user_shibboleth] => Array
+        (
+            [EXCEPTION] => Array
+                (
+                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+                    [message] => Signature data not found.
+                )
+
+        )
+
+)
+',
+				Http::STATUS_OK,
+				[
+						'Content-Type' => 'text/plain',
+				]
+		);
+		$this->assertEquals($expected, $this->checkSetupController->getFailedIntegrityCheckFiles());
+	}
 }