Node.jsで暗号化とハッシュ


## 暗号化とハッシュ
- ハッシュとは、平文から固定長の疑似乱数を生成する演算手法。
- 暗号化とは、第3者に内容を知られないように、規則に従ってデータを変換すること。

## Node.jsでの暗号化とハッシュ

### モジュールの読み込み

Node.jsをインストールすれば、cryptoモジュールが使えます。
次の様にモジュールを読み込みます。

```javascript
var crypto = require("crypto");
```
### cryptoがサポートする暗号化アルゴリズム
cryptoで使える暗号化アルゴリズムは、crypto.getCiphers()メソッドで取得できます。

```javascript
// cryptoで使える暗号化アルゴリズムを配列で取得する
var cipers = crypto.getCiphers();

console.log(cipers);
```

実行すると、

```bash
$ node app.js
[ 'CAST-cbc',
  'aes-128-cbc',
 :
 :
  'seed-ecb',
  'seed-ofb' ]
```

### cryptoがサポートするハッシュアルゴリズム

cryptoで使えるハッシュアルゴリズムは、crypto.getHashes()メソッドで取得できます。

```javascript
// サポートするハッシュアルゴリズムを配列で取得する。
var hashes = crypto.getHashes();

console.log(hashes);
```

実行すると、

```bash
$ node app.js
[ 'DSA',
  'DSA-SHA',
 :
 :
  'ssl3-sha1',
  'whirlpool' ]
```

### cryotoで暗号化

以下、cryptoでAES192アルゴリズムを使用した暗号化と復号化です。

```javascript
var crypto = require("crypto");

var planeText = 'This is Plane Text';
var passowrd = 'passw0rd';

console.log('暗号化するテキスト : ' + planeText);
console.log('暗号化キー        : ' + passowrd);

// 暗号化
var cipher = crypto.createCipher('aes192', passowrd);
cipher.update(planeText, 'utf8', 'hex');
var cipheredText = cipher.final('hex');

console.log('暗号化(AES192) :');
console.log(cipheredText);

// 復号
var decipher = crypto.createDecipher('aes192', passowrd);
decipher.update(cipheredText, 'hex', 'utf8');
var dec = decipher.final('utf8');

console.log('復号化(AES192) : ');
console.log(dec);
``` 

#### crypto.createCipher(algorithm, password)
引数にアルドリズムとキーを与えて、暗号オブジェクトを作成する。

- 引数
  - algorithm 暗号化するアルゴリズムで、このアルゴリズムはOpenSSLに依存します。
  - password 暗号化・復号化で使用するキー

- 返り値
暗号オブジェクト

#### cipher.update(data, [input_encoding], [output_encoding])
dataで暗号を更新する。

- 引数
  - data 更新するデータ
  - input_encoding 'utf8', 'ascii' or 'binary'が指定できる
  - output_encoding 暗号化されたデータの出力形式。'binary', 'base64' or 'hex'が指定できる。

#### cipher.final([output_encoding])
暗号化されたデータを取得する。
※ finalメソッドが呼び出された後、暗号オブジェクトは使えません。

- 引数
  - output_encoding 'binary', 'base64' or 'hex'を指定できる。

- 返り値
暗号化されたデータ

#### crypto.createDecipher(algorithm, password)
引数にアルゴリズムとパスワードを与えて、復号オブジェクトを作成する。

- 引数
  - algorithm 復号化するアルゴリズムで、このアルゴリズムはOpenSSLに依存します。
  - password 暗号化・復号化で使用するキー

- 返り値
復号オブジェクト

#### decipher.update(data, [input_encoding], [output_encoding])
エンコードされた復号データを更新する。

- 引数
  - data 更新するデータ
  - input_encoding 'utf8', 'ascii' or 'binary'が指定できる
  - output_encoding 復号化されたデータの出力形式。'binary', 'base64' or 'hex'が指定できる。

#### decipher.final([output_encoding])
復号化されたプレーンテキストを取得する。
※ finalメソッドが呼び出された後、復号オブジェクトは使えません。

- 引数
  - output_encoding 'binary', 'ascii' or 'utf8'を指定できる。

- 返り値
復号化されたデータ

実際に実行すると

```bash
$ node app-cipher.js
暗号化するテキスト : ThisisPlaneText
暗号化キー        : passw0rd
暗号化(AES192) :
df8fba3165b241ccf50aaa2c3aabc0ca
復号化(AES192) : 
ThisisPlaneText

```

### cryptoでハッシュ値を求める

以下、cryptpでsha512アプゴリズムを使用たハッシュ値の算出

```javascript
var crypto = require("crypto");

var password ='passowrd';

var sha512 = crypto.createHash('sha512');
sha512.update(password)
var hash = sha512.digest('hex')

console.log(hash);
```

#### crypto.createHash(algorithm)

指定したアルゴリズムでハッシュオブジェクトを作成する。

- 引数
  - algorithm ハッシュ化するアルゴリズム。OpenSSL のバージョンでサポートされている利用可能なアルゴリズムに依存。

- 返り値

ハッシュオブジェクト

#### hash.update(data, [input_encoding])
与えられた data でハッシュの内容を更新します。

- 引数
  - input_encoding 'utf8', 'ascii' or 'binary'を指定できる。

- 返り値
ハッシュオブジェクト

#### hash.digest([encoding])
ハッシュするために渡されたすべてのデータのダジェストを計算する。
※ digest()メソッドが呼ばれた後、ハッシュオブジェクトは使えない。

- 引数
  - encoding 'hex', 'binary' or 'base64'が指定できる。

- 返り値
ハッシュ化された値

実行すると、

```bash
$ node app-hash.js 
91583dac4a1574f0fba6a71decb149101b9ed67402b84f1bcfbf529a742948e4381960ec00715b6b7c6aa721b811a703fc8b13ebc3d3f90641e00848ef06e080
```
## BCryptでハッシュ化

MD5やSHAは高速に実行できる様に設計されていて、レインボーテーブルの作成が容易で攻撃者が１秒間に何回ものブルートフォースを試みることができるらしい。だからあまりセキュアでないとのこと。  
Bcryptアルゴリズムは、汎用的なアルゴリズム（SHA、MD5など）より意図的に計算量を増やしてて、「オフライン総あたり攻撃」に強い特性を持っているとのこと。
よりセキュアにハッシュ化できるアルゴリズムがBCryptの様です。とりあえず使ってみる。

### [Bcrypt](https://github.com/ncb000gt/node.bcrypt.js/)のインストール

```bash
npm install bcrypt
```

このモジュールのもつAPIには同期で処理できるものと非同期で処理できるものがある様です。
詳細はGitHubのページに記載してあります。

とりあえず、非同期で試してみました。

```javascript
var bcrypt = require('bcrypt');

bcrypt.genSalt(10, function(err, salt) {
    bcrypt.hash("B4c0/\/", salt, function(err, hash) {

        console.log(hash);

        // To check a password:   
        bcrypt.compare("B4c0/\/", hash, function(err, res) {
            // res == true
            console.log("equal")
            console.log(res);
        });

        bcrypt.compare("not_bacon", hash, function(err, res) {
            // res == false
            console.log("not equal");
            console.log(res);
        });
    });
});

// Auto-gen a salt and hash:
bcrypt.hash('bacon', 8, function(err, hash) {
    console.log(hash);
});
```

#### genSalt(rounds, cb)
非同期でSALTを取得する。

- 引数
- rounds - [オプション] - データの処理コスト. (default - 10)
- cb - [必須] - SALTが生成された後、発火されるコールバック。
  - コールバックの引数
     - err - 最初のパラメータはエラーの詳細
     - salt - 2つ目のパラメータは生成されたSALTを提供する。

#### hash(data, salt, cb)
与えられえたデータとSALTからハッシュを計算する。

- 引数
  - data - [必須] - 暗号化するデータ
  - salt - [オプション] - ハッシュするために使用するSALT。
  - cb - [必須] - データがハッシュ化された時に発火するコールバック
     - コールバックの引数 
         - err - 最初のパラメータはエラーの詳細
         - encrypted - 2つ目のパラメータはハッシュ化された値

####  compare(data, encrypted, cb)

データとハッシュ化された値を比較する。

- 引数
  - data - [必須] - 比較するデータ
  - encrypted - [必須] - 比較するデータ（ハッシュ値）
  - cb - [必須] - 比較した結果、コールバックが発火する。
     - コールバックの引数
         - err - 最初のパラメータはエラーの詳細
         - same - 2つ目のパラメータは比較した結果[true or false]

実行すると、


```
$ node app-bcrypt.js 
$2a$08$vj/8fY70VkEmVab/czGJ8euRvsGo0q0T5ETDKUusCXMXytYXeCKkC
$2a$10$LLsJqwOAirlXQW5DVt1pDeyPTWM5qoooqHjkAjA68iIp2mpcCfdZ2
not equal
false
equal
true

```