Spring Boot + Spring Securityでパスワードをハッシュ化(ソルト付、ストレッチングあり)

Spring SecurityのPasswordEncoderを利用すると、ソルト付 + ストレッチングされたハッシュ値を容易に生成することが可能です。本記事はそのサンプルになります。

# 環境
JDK：1.8
Spring Boot：1.5.3.RELEASE
Spring Security：4.2.2.RELEASE

# Spring Security PasswordEncoder Class Diagram
見づらいかもしれませんが、Spring Security4.2.2.RELEASEのPasswordEncoderのクラス図は以下のようになってます。

![Spring Security PasswordEncoder Class Diagram1.png](https://qiita-image-store.s3.amazonaws.com/0/23101/4da20c1b-d8a0-de0a-e1cf-c804dd57cc0b.png)

PasswordEncoderインターフェースの実装クラスが6つあります。

|PasswordEncoderの実装クラス|概要|
|:--|:--|
|AbstractPasswordEncoder|-|
|NoOpPasswordEncoder|ハッシュ化を行わないエンコーダ(テスト用)|
|Pbkdf2PasswordEncoder|設定可能な反復回数とランダムな8バイトランダムソルト値を使用してPBKDF2を使用するPasswordEncoderの実装|
|StandardPasswordEncoder|SHA-256アルゴリズム + 1024回のストレッチでハッシュ化を行うエンコーダ|
|BCryptPasswordEncoder|bcryptアルゴリズムでハッシュ化を行うエンコーダ|
|SCryptPasswordEncoder|scryptアルゴリズムでハッシュ化を行うエンコーダ|

本記事ではBCryptPasswordEncoderを使ってハッシュ化してみます。

# BCryptPasswordEncoderを使ってハッシュ化

Spring Securityの依存関係を追加します。

```pom.xml
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-config</artifactId>
</dependency>
```

今回はCommandLineRunnerをimplementsしてCLIとしています。

```SpringBootEncodeApplication.java
package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@SpringBootApplication
public class SpringBootEncodeApplication implements CommandLineRunner {

	@Autowired
	PasswordEncoder passwordEncoder;

	public static void main(String[] args) {
		SpringApplication.run(SpringBootEncodeApplication.class, args);
	}

	@Bean
	PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	@Override
	public void run(String... args) {

		String password = "#fe?3d31";
		String digest = passwordEncoder.encode(password);
		System.out.println("ハッシュ値 = " + digest);

		if (passwordEncoder.matches(password, digest)) {
			System.out.println("一致したよ");
			return;
		}
		System.out.println("一致しなかったよ");
	}
}
```

平文パスワード(password)をPasswordEncoderのencodeメソッドを使ってハッシュ化します。ハッシュ化した結果がdigestです。
次に、PasswordEncoderのmatchesメソッドを使って平文パスワード(password)とencodeメソッドで得られたハッシュ値(digest)を照合します。

```実行結果.
ハッシュ値 = $2a$10$im98CLRwtWohvPE6wZkYk.Os.RXZVF0iROJXL8vUn7TGrfWoixTdq
一致したよ
```

# ハッシュ値の見方

上記で生成したハッシュ値は以下のようになりました。

```
$2a$10$im98CLRwtWohvPE6wZkYk.Os.RXZVF0iROJXL8vUn7TGrfWoixTdq
```

見方は以下の通りです。

|文字場所|文字列|概要|
|:--|:--|:--|
|1文字目～3文字目|$2a|bcryptのバージョン番号。|
|4文字目～6文字目|$10|ストレッチング回数(ハッシュ化演算の繰り返し回数)になります。回数は2のn乗回です。そのため、この例だと2^10=1024回となります。|
|7文字目～29文字目|$im98CLRwtWohvPE6wZkYk.|ソルト値。|
|30文字目～最後|Os.RXZVF0iROJXL8vUn7TGrfWoixTdq|パスワード本体。|

※ソルト値、ストレッチング等については、本記事の内容とは外れてしまうのでここでは省略します。

以上のように、Spring SecurityのPasswordEncoderを使えばソルト + ストレッチングされたハッシュ値が生成でき、また、平文パスワードとの照合も容易にできます。
※bcryptの話になってしまいました。