diff --git a/core/core.services.yml b/core/core.services.yml
index 2f7da56..7b94487 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -278,6 +278,9 @@ services:
   config.manager:
     class: Drupal\Core\Config\ConfigManager
     arguments: ['@entity.manager', '@config.factory', '@config.typed', '@string_translation', '@config.storage', '@event_dispatcher']
+  config.comparator:
+    class: Drupal\Core\Config\ConfigComparator
+    arguments: ['@config.storage']
   config.factory:
     class: Drupal\Core\Config\ConfigFactory
     tags:
diff --git a/core/lib/Drupal/Core/Config/ConfigComparator.php b/core/lib/Drupal/Core/Config/ConfigComparator.php
new file mode 100644
index 0000000..e8b1a32
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/ConfigComparator.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Drupal\Core\Config;
+
+use Drupal\Component\Utility\Crypt;
+
+/**
+ * The ConfigComparator provides helper functions for the configuration system.
+ */
+class ConfigComparator implements ConfigComparatorInterface {
+
+  /**
+   * The active configuration storage.
+   *
+   * @var \Drupal\Core\Config\StorageInterface
+   */
+  protected $activeStorage;
+
+  /**
+   * Creates ConfigComparator objects.
+   *
+   * @param \Drupal\Core\Config\StorageInterface $active_storage
+   *   The active configuration storage.
+   */
+  public function __construct(StorageInterface $active_storage) {
+    $this->activeStorage = $active_storage;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isModified($config_name) {
+    $active = $this->activeStorage->read($config_name);
+
+    if (!$active) {
+      throw new ConfigNameException(
+        sprintf('Configuration does not exist for "%s".', $config_name)
+      );
+    }
+
+    // Get the hash created when the config was installed.
+    $original_hash = $active['_core']['default_config_hash'];
+
+    // Remove export keys not used to generate default config hash.
+    unset($active['uuid']);
+    unset($active['_core']);
+    $active_hash = Crypt::hashBase64(serialize($active));
+
+    return $original_hash !== $active_hash;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Config/ConfigComparatorInterface.php b/core/lib/Drupal/Core/Config/ConfigComparatorInterface.php
new file mode 100644
index 0000000..66a3fcc
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/ConfigComparatorInterface.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Drupal\Core\Config;
+
+/**
+ * Provides an interface for configuration comparator.
+ */
+interface ConfigComparatorInterface {
+
+  /**
+   * Checks if a config item has been modified since its installation.
+   *
+   * @param string $config_name
+   *   The configuration item's full name.
+   *
+   * @return bool
+   *   Returns TRUE is modified, FALSE if original configuration.
+   *
+   * @throws ConfigNameException
+   *   When configuration is not found.
+   */
+  public function isModified($config_name);
+
+}
diff --git a/core/modules/config/src/Tests/ConfigModifiedTest.php b/core/modules/config/src/Tests/ConfigModifiedTest.php
new file mode 100644
index 0000000..70d9c48
--- /dev/null
+++ b/core/modules/config/src/Tests/ConfigModifiedTest.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Drupal\config\Tests;
+
+use Drupal\Component\Utility\Crypt;
+use Drupal\Core\Config\ConfigNameException;
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * Check is a configuration has been modified.
+ *
+ * @group config
+ */
+class ConfigModifiedTest extends KernelTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['config_test', 'system'];
+
+  /**
+   * Verify config is not modified, modify it. Verify shows up as modified.
+   */
+  public function testIsModified() {
+    $this->installConfig(['config_test']);
+
+    /* @var \Drupal\Core\Config\ConfigComparatorInterface $comparator */
+    $comparator = $this->container->get('config.comparator');
+    $config_name = 'config_test.system';
+
+    /* @var \Drupal\Core\Config\Config $editable_config */
+    $editable_config = $this->container->get('config.factory')
+      ->getEditable($config_name);
+
+    // Not changed config.
+    $this->assertFalse($comparator->isModified($config_name), 'Configuration is not changed after install.');
+
+    // After config is change.
+    $editable_config
+      ->set('404', 'user/login')
+      ->save();
+    $this->assertTrue($comparator->isModified($config_name), 'Configuration is modified after last install.');
+
+    // After config is updated.
+    $active = $editable_config->getRawData();
+    unset($active['uuid']);
+    unset($active['_core']);
+    $editable_config
+      ->set('_core.default_config_hash', Crypt::hashBase64(serialize($active)))
+      ->save();
+    $this->assertFalse($comparator->isModified($config_name), 'Configuration is not changed after last update.');
+
+    // After config is removed.
+    $editable_config->delete();
+    $this->setExpectedException(ConfigNameException::class, sprintf('Configuration does not exist for "%s".', $config_name));
+    $comparator->isModified($config_name);
+  }
+
+  /**
+   * Verify that not existing config will throw exception.
+   */
+  public function testIsModifiedNotExisting() {
+    $this->installConfig(['config_test']);
+
+    /** @var \Drupal\Core\Config\ConfigComparatorInterface $comparator */
+    $comparator = $this->container->get('config.comparator');
+
+    $not_existing_config = 'config_test.not_existing';
+    $this->setExpectedException(ConfigNameException::class, sprintf('Configuration does not exist for "%s".', $not_existing_config));
+    $comparator->isModified($not_existing_config);
+  }
+
+}
