diff --git a/core/lib/Drupal/Core/Console/Command/HashPasswordApplication.php b/core/lib/Drupal/Core/Console/Command/HashPasswordApplication.php
new file mode 100644
index 0000000..a884e5a
--- /dev/null
+++ b/core/lib/Drupal/Core/Console/Command/HashPasswordApplication.php
@@ -0,0 +1,66 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Console\Command\HashPassword.
+ */
+
+namespace Drupal\Core\Console\Command;
+
+use Drupal\Core\Password\PasswordInterface;
+use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+
+/**
+ * Hashes a password.
+ */
+class HashPasswordApplication extends Application {
+
+  /**
+   * The password hashing service object.
+   *
+   * @var \Drupal\Core\Password\PasswordInterface
+   */
+  protected $password;
+
+  /**
+   * @param \Drupal\Core\Password\PasswordInterface $password
+   *   The password service object.
+   */
+  public function __construct(PasswordInterface $password) {
+    $this->password = $password;
+    parent::__construct();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getCommandName(InputInterface $input) {
+    return 'hash-password';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getDefaultCommands() {
+    // Even though this is a single command, keep the HelpCommand (--help).
+    $default_commands = parent::getDefaultCommands();
+    $default_commands[] = new HashPasswordCommand($this->password);
+    return $default_commands;
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * Overridden so the application doesn't expect the command name as the first
+   * argument.
+   */
+  public function getDefinition() {
+    $definition = parent::getDefinition();
+    // Clears the normal first argument (the command name).
+    $definition->setArguments([new InputArgument('raw')]);
+    return $definition;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Console/Command/HashPasswordCommand.php b/core/lib/Drupal/Core/Console/Command/HashPasswordCommand.php
new file mode 100644
index 0000000..1ec03a7
--- /dev/null
+++ b/core/lib/Drupal/Core/Console/Command/HashPasswordCommand.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Console\Command\HashPasswordCommand.
+ */
+
+namespace Drupal\Core\Console\Command;
+
+use Drupal\Core\Password\PasswordInterface;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class HashPasswordCommand extends Command {
+
+  /**
+   * The password hashing service object.
+   *
+   * @var \Drupal\Core\Password\PasswordInterface
+   */
+  protected $password;
+
+  /**
+   * Generates a HashPasswordCommand object.
+   *
+   * @param \Drupal\Core\Password\PasswordInterface $password
+   *   The password hashing service object.
+   */
+  public function __construct(PasswordInterface $password) {
+    parent::__construct();
+    $this->password = $password;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function configure() {
+    $this->setName('hash-password')
+      ->setDescription('Hash a password');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function execute(InputInterface $input, OutputInterface $output) {
+    $output->writeln($this->password->hash($input->getFirstArgument()));
+  }
+
+}
diff --git a/core/scripts/bootstrap.php b/core/scripts/bootstrap.php
new file mode 100644
index 0000000..dd7b151
--- /dev/null
+++ b/core/scripts/bootstrap.php
@@ -0,0 +1,12 @@
+<?php
+
+use Drupal\Core\DrupalKernel;
+use Drupal\Core\Site\Settings;
+use Symfony\Component\HttpFoundation\Request;
+
+// Bootstrap.
+$autoloader = require __DIR__ . '/../../autoload.php';
+require_once __DIR__ . '/../includes/bootstrap.inc';
+$request = Request::createFromGlobals();
+Settings::initialize(dirname(dirname(__DIR__)), DrupalKernel::findSitePath($request), $autoloader);
+$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod')->boot();
diff --git a/core/scripts/dump-database-d8-mysql.php b/core/scripts/dump-database-d8-mysql.php
index b50f4c0..ebe78b7 100755
--- a/core/scripts/dump-database-d8-mysql.php
+++ b/core/scripts/dump-database-d8-mysql.php
@@ -3,21 +3,12 @@
 
 use Drupal\Core\Command\DbDumpApplication;
 use Drupal\Core\Database\Database;
-use Drupal\Core\DrupalKernel;
-use Drupal\Core\Site\Settings;
-use Symfony\Component\HttpFoundation\Request;
 
 if (PHP_SAPI !== 'cli') {
   return;
 }
 
-// Bootstrap.
-$autoloader = require __DIR__ . '/../../autoload.php';
-require_once __DIR__ . '/../includes/bootstrap.inc';
-$request = Request::createFromGlobals();
-Settings::initialize(dirname(dirname(__DIR__)), DrupalKernel::findSitePath($request), $autoloader);
-$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod')->boot();
-
+include_once __DIR__ . '/bootstrap.php';
 // Run the database dump command.
 $application = new DbDumpApplication(Database::getConnection(), \Drupal::moduleHandler());
 $application->run();
diff --git a/core/scripts/password-hash.php b/core/scripts/password-hash.php
new file mode 100644
index 0000000..9b69de5
--- /dev/null
+++ b/core/scripts/password-hash.php
@@ -0,0 +1,13 @@
+#!/usr/bin/env php
+<?php
+
+use Drupal\Core\Console\Command\HashPasswordApplication;
+
+if (PHP_SAPI !== 'cli') {
+  return;
+}
+
+include_once __DIR__ . '/bootstrap.php';
+// Run the hash password command.
+$application = new HashPasswordApplication($kernel->getContainer()->get('password'));
+$application->run();
diff --git a/core/scripts/password-hash.sh b/core/scripts/password-hash.sh
deleted file mode 100755
index cf5e9e3..0000000
--- a/core/scripts/password-hash.sh
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/env php
-<?php
-
-/**
- * Drupal hash script - to generate a hash from a plaintext password
- *
- * @param password1 [password2 [password3 ...]]
- *  Plain-text passwords in quotes (or with spaces backslash escaped).
- */
-
-use Drupal\Core\DrupalKernel;
-use Symfony\Component\HttpFoundation\Request;
-
-if (PHP_SAPI !== 'cli') {
-  return;
-}
-
-if (version_compare(PHP_VERSION, '5.4.5') < 0) {
-  $version  = PHP_VERSION;
-  echo <<<EOF
-
-ERROR: This script requires at least PHP version 5.4.5. You invoked it with
-       PHP version {$version}.
-\n
-EOF;
-  exit;
-}
-
-$script = basename(array_shift($_SERVER['argv']));
-
-if (in_array('--help', $_SERVER['argv']) || empty($_SERVER['argv'])) {
-  echo <<<EOF
-
-Generate Drupal password hashes from the shell.
-
-Usage:        {$script} [OPTIONS] "<plan-text password>"
-Example:      {$script} "mynewpassword"
-
-All arguments are long options.
-
-  --help      Print this page.
-
-  "<password1>" ["<password2>" ["<password3>" ...]]
-
-              One or more plan-text passwords enclosed by double quotes. The
-              output hash may be manually entered into the
-              {users_field_data}.pass field to change a password via SQL to a
-              known value.
-
-
-EOF;
-  exit;
-}
-
-// Password list to be processed.
-$passwords = $_SERVER['argv'];
-
-$autoloader = require __DIR__ . '/../../autoload.php';
-
-$request = Request::createFromGlobals();
-$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod', FALSE);
-$kernel->boot();
-
-$password_hasher = $kernel->getContainer()->get('password');
-
-foreach ($passwords as $password) {
-  print("\npassword: $password \t\thash: ". $password_hasher->hash($password) ."\n");
-}
-print("\n");
-
diff --git a/core/tests/Drupal/Tests/Core/Console/Command/HashPasswordCommandTest.php b/core/tests/Drupal/Tests/Core/Console/Command/HashPasswordCommandTest.php
new file mode 100644
index 0000000..e926aad
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Console/Command/HashPasswordCommandTest.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Console\Command\HashPasswordCommandTest.
+ */
+
+namespace Drupal\Tests\Core\Console\Command;
+
+use Drupal\Core\Console\Command\HashPasswordCommand;
+use Drupal\Core\Password\PasswordInterface;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Console\Command\HashPasswordCommand
+ * @group Console
+ */
+class HashPasswordCommandTest extends UnitTestCase {
+
+  /**
+   * @covers ::execute
+   */
+  public function testExecute() {
+    $password = $this->randomMachineName();
+    $hash = $this->randomMachineName();
+
+    $input = $this->prophesize(InputInterface::CLASS);
+    $input->getFirstArgument()->willReturn($password)->shouldBeCalled();
+
+    $output = $this->prophesize(OutputInterface::CLASS);
+    $output->writeln($hash)->shouldBeCalled();
+
+    $hasher = $this->prophesize(PasswordInterface::CLASS);
+    $hasher->hash($password)->willReturn($hash)->shouldBeCalled();
+
+    $command = new HashPasswordCommand($hasher->reveal());
+
+    $method = new \ReflectionMethod($command, 'execute');
+    $method->setAccessible(TRUE);
+    $method->invoke($command, $input->reveal(), $output->reveal());
+  }
+
+}
