jsSHAとjava.security.MessageDigestを試してみる

## はじめに

[jsSHA](http://caligatio.github.io/jsSHA/)とjava.security.MessageDigestを使ってSHA-256を試してみます。

[jsSHA](http://caligatio.github.io/jsSHA/)にデモがあります。まず、このデモを使って、SHA-256の値を用意します。この記事では、入力テキストをtokyo、入力タイプをTEXT、SHAバージョンをSHA-256、出力タイプをBase64とします。もちろん、他のケースで試すこともできます。

出力ハッシュ値は__r+BFeQBASczOGEOkC9+euF86SDk5A3XjXQAYp/mqcMg=__です。

![スクリーンショット 2015-08-17 21.36.50.png](https://qiita-image-store.s3.amazonaws.com/0/29884/fe04f837-3607-aa97-f9eb-396f5718e090.png)

java.security.MessageDigestのほうはScalaでSHA-256の値を用意します。

この記事では、jsSHAとjava.security.MessageDigestのSHA-256の値を確認して、気を付けることなどを紹介します。

## 準備

Base64を使うために、下記のようなbuild.sbtを用意します。

```build.sbt
libraryDependencies += "commons-codec" % "commons-codec" % "1.10"
```

REPL起動のためのコマンドラインは、__$ sbt console__です。

Javaのバージョンが古い場合、java.security.MessageDigestが使えないかもしれません。Javaのバージョンは1.8を使いました。

## REPL

REPLで試してみましたが、シンタックスハイライトが効かないこともあり、読み難いです。

```shell-session
Type in expressions to have them evaluated.
Type :help for more information.

scala> import java.security.MessageDigest
import java.security.MessageDigest

scala> MessageDigest.getInstance("SHA-256")
res0: java.security.MessageDigest =
SHA-256 Message Digest from SUN, <initialized>


scala> res0.digest
                                         def digest(): Array[Byte]                def digest(Array[Byte]): Array[Byte]     def digest(Array[Byte], Int, Int): Int

scala> res0.digest("tokyo".getBytes)
res1: Array[Byte] = Array(-81, -32, 69, 121, 0, 64, 73, -52, -50, 24, 67, -92, 11, -33, -98, -72, 95, 58, 72, 57, 57, 3, 117, -29, 93, 0, 24, -89, -7, -86, 112, -56)

scala> import org.apache.commons.codec.binary.Base64
import org.apache.commons.codec.binary.Base64

scala> Base64.encodeBase64(res1)
res2: Array[Byte] = Array(114, 43, 66, 70, 101, 81, 66, 65, 83, 99, 122, 79, 71, 69, 79, 107, 67, 57, 43, 101, 117, 70, 56, 54, 83, 68, 107, 53, 65, 51, 88, 106, 88, 81, 65, 89, 112, 47, 109, 113, 99, 77, 103, 61)

scala> Base64.encodeBase64String(res1)
res3: String = r+BFeQBASczOGEOkC9+euF86SDk5A3XjXQAYp/mqcMg=

scala> Base64.encodeBase64URLSafeString(res1)
res4: String = r-BFeQBASczOGEOkC9-euF86SDk5A3XjXQAYp_mqcMg

scala> Base64.encodeBase64ChunkedString(res1)
<console>:12: error: value encodeBase64ChunkedString is not a member of object org.apache.commons.codec.binary.Base64
              Base64.encodeBase64ChunkedString(res1)
                     ^

scala> Base64.encodeBase64Chunked(res1)
res6: Array[Byte] = Array(114, 43, 66, 70, 101, 81, 66, 65, 83, 99, 122, 79, 71, 69, 79, 107, 67, 57, 43, 101, 117, 70, 56, 54, 83, 68, 107, 53, 65, 51, 88, 106, 88, 81, 65, 89, 112, 47, 109, 113, 99, 77, 103, 61, 13, 10)

scala> new String(Base64.encodeBase64Chunked(res1))
res7: String =
"r+BFeQBASczOGEOkC9+euF86SDk5A3XjXQAYp/mqcMg=
"

scala> Base64.decodeBase64(res3.getBytes)
res8: Array[Byte] = Array(-81, -32, 69, 121, 0, 64, 73, -52, -50, 24, 67, -92, 11, -33, -98, -72, 95, 58, 72, 57, 57, 3, 117, -29, 93, 0, 24, -89, -7, -86, 112, -56)

scala> new String(res8)
res9: String = ��Ey?@I��?C�
                           ߞ�_:H99?u�]??���p�

scala> Base64.decodeBase64(res4.getBytes)
res10: Array[Byte] = Array(-81, -32, 69, 121, 0, 64, 73, -52, -50, 24, 67, -92, 11, -33, -98, -72, 95, 58, 72, 57, 57, 3, 117, -29, 93, 0, 24, -89, -7, -86, 112, -56)

scala> new String(res10)
res11: String = ��Ey?@I��?C�
                            ߞ�_:H99?u�]??���p�

scala> Base64.decodeBase64(res6)
res12: Array[Byte] = Array(-81, -32, 69, 121, 0, 64, 73, -52, -50, 24, 67, -92, 11, -33, -98, -72, 95, 58, 72, 57, 57, 3, 117, -29, 93, 0, 24, -89, -7, -86, 112, -56)

scala> new String(res12)
res13: String = ��Ey?@I��?C�
                            ߞ�_:H99?u�]??���p�

scala>
```

コードも用意してみます。実行するためのコマンドラインは__$ sbt run__です。

```scala
object Main extends App {
  import java.security.MessageDigest
  val hash = MessageDigest.getInstance("SHA-256").digest("tokyo".getBytes)

  println("------------------------------")
  println(hash)

  import org.apache.commons.codec.binary.Base64

  println("------------------------------")
  val base = Base64.encodeBase64(hash)
  println(new String(base))
  //=> r+BFeQBASczOGEOkC9+euF86SDk5A3XjXQAYp/mqcMg=

  println("------------------------------")
  val baseSafe = Base64.encodeBase64URLSafe(hash)
  println(new String(baseSafe))
  //=> r-BFeQBASczOGEOkC9-euF86SDk5A3XjXQAYp_mqcMg

  println("------------------------------")
  val baseChunked = Base64.encodeBase64Chunked(hash)
  println(new String(baseChunked))
  //=> "r+BFeQBASczOGEOkC9+euF86SDk5A3XjXQAYp/mqcMg=
  //   "

  println("------------------------------")
  println(Base64.decodeBase64(base) sameElements hash)
  //=> true

  println("------------------------------")
  println(Base64.decodeBase64(baseSafe) sameElements hash)
  //=> true

  println("------------------------------")
  println(Base64.decodeBase64(baseChunked) sameElements hash)
  //=> true

}
```

まず、ハッシュ値を生成しています。hashがハッシュ値です。

次に、そのハッシュ値をBase64でエンコードして、String型のオブジェクトを生成しています。エンコードのためのメソッドはいくつかありますが、ここでは__encodeBase64__、__encodeBase64URLSafe__、__encodeBase64Chunked__を使っています。デコードのためのメソッドは__decodeBase64__です。

さて、エンコードは複数あります。どのエンコードで生成した値がjsSHAで生成した値と同じ値になるのでしょうか？目を細めて確認してみますと、__encodeBase64__で生成した値がjsSHAで生成した値と同じ値になるようです。ほかのエンコードで生成した値は少し違う値になるようです。

余談ですが、配列(Array)の比較は__==__ではなく、__sameElements__を使います。

```shell-session
scala> Seq() == Seq(1)
res0: Boolean = false

scala> Seq() == Seq()
res1: Boolean = true

scala> Vector() == Vector(1)
res2: Boolean = false

scala> Vector() == Vector()
res3: Boolean = true

scala> Array() == Array(1)
res4: Boolean = false

scala> Array() == Array()
res5: Boolean = false

scala> Array() sameElements Array(1)
res6: Boolean = false

scala> Array() sameElements Array()
<console>:8: error: polymorphic expression cannot be instantiated to expected type;
 found   : [T]Array[T]
 required: scala.collection.GenIterable[?]
              Array() sameElements Array()
                                        ^

scala> Array(1) sameElements Array(1)
res8: Boolean = true

scala>
```

SeqまたはVectorでは__==__で比較することができますが、Arrayでは__==__で比較すると問答無用でfalseになります。

## まとめ

話が長くなりましたが、気を付けることは用途に合わせてエンコードを選択することとArrayの比較では__==__ではなく__sameElements__を使うことです。

## 参考URL

- [Apache Commons Codec で Base64 処理を行う (Java) | WEB ARCH LABO](http://weblabo.oscasierra.net/java-apache-commons-codec-base64-1/)
- [serialization - Howto base64 encode an java object using org.apache.commons.codec.binary.base64? - Stack Overflow](http://stackoverflow.com/questions/10744715/howto-base64-encode-an-java-object-using-org-apache-commons-codec-binary-base64)
- [Library providing various hash algorithms (MD5, SHA1, SHA256, etc) in Java? - Stack Overflow](http://stackoverflow.com/questions/6210449/library-providing-various-hash-algorithms-md5-sha1-sha256-etc-in-java)
- [SHA！ えびばーでぃ！ - おしりんブログ](http://shioriwest.hatenablog.com/entry/2014/07/22/000111)
- [Scala配列メモ(Hishidama's Scala Array Memo)](http://www.ne.jp/asahi/hishidama/home/tech/scala/array.html)