楕円曲線暗号の署名検証をJavaで実装してみる


暗号理論の中で、RSAと同じ公開鍵暗号に属する楕円曲線暗号（Elliptic Curve Cryptography: ECC）ですが、RSAちゃんよりできる子と言われ続けて早15年くらい？
このまま秘密兵器のまま終わるのかと思いきや、ようやっと使われ始める兆しがあったりなかったりなので、Javaでの実装方法について調べてみました。
Javaでは、JDK 7以上で楕円曲線暗号のネイティブ・プロバイダが追加されたようです。

# サンプルコード
楕円曲線DSA（Elliptic Curve Digital Signature Algorithm: ECDSA）での、署名検証サンプルコードです。
鍵ペアの生成、署名の作成、署名の検証を順に実施しています。
実際に使う場合には、署名作成する人と署名検証をする人は別の人になりますが、サンプルでは同じメソッド内でやってます。

```java
package com.example;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;

import javax.xml.bind.DatatypeConverter;

public class ECDSAExample {

	/**
	 * 楕円曲線DSA 署名検証サンプル
	 */
	public static void main(String[] args) throws Exception {
        /*
         *  楕円曲線暗号 鍵ペア生成
         */
		// 鍵ペア生成器
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); // Elliptic Curve
        // 乱数生成器
        SecureRandom randomGen = SecureRandom.getInstance("SHA1PRNG");
        // 鍵サイズと乱数生成器を指定して鍵ペア生成器を初期化
        int keySize = 256;
        keyGen.initialize(keySize, randomGen);

        // 鍵ペア生成
        KeyPair keyPair = keyGen.generateKeyPair();
        // 秘密鍵
        PrivateKey privateKey = keyPair.getPrivate();
        // 公開鍵
        PublicKey publicKey = keyPair.getPublic();

        /*
         * 署名生成
         */
        String originalText = "This is string to sign";

        // 署名生成アルゴリズムを指定する
        Signature dsa = Signature.getInstance("SHA1withECDSA");
        // 初期化
        dsa.initSign(privateKey);
        // 署名生成
        dsa.update(originalText.getBytes("UTF-8"));
        // 生成した署名を取り出す
        byte[] signature = dsa.sign();
        System.out.println("Signature: " + DatatypeConverter.printHexBinary(signature));

        /*
         * 署名検証
         */
        // 初期化
        dsa.initVerify(publicKey);
        // 署名検証する対象をセットする
        dsa.update(originalText.getBytes("UTF-8"));
        // 署名検証
        boolean verifyResult = dsa.verify(signature);
        System.out.println("Verify: " + verifyResult);
    }
}

```

## KeyPairGeneratorアルゴリズム
鍵ペアジェネレータの引数には、他に以下のものを指定することが出来ます。

| アルゴリズム名 | 説明 |
|:------------|:-----------|
|DiffieHellman|Diffie-Hellman KeyAgreementアルゴリズムの鍵ペアを生成します。<br>注: key.getAlgorithm()は、「DiffieHellman」ではなく「DH」を返します。|
|DSA|デジタル署名アルゴリズムの鍵ペアを生成します。|
|RSA|RSAアルゴリズム(Signature/Cipher)の鍵ペアを生成します。|
|EC|Elliptic Curveアルゴリズムの鍵ペアを生成します。|
http://docs.oracle.com/javase/jp/8/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator

## SecureRandom乱数生成アルゴリズム
乱数生成ジェネレータの引数には、他に以下のものを指定することが出来ます。

| アルゴリズム名 | 説明 |
|:------------|:-----------|
|NativePRNG|基盤となるネイティブOSから乱数を取得します。乱数生成のブロック性については何も表明されません。|
|NativePRNGBlocking|基盤となるネイティブOSから乱数を取得し、必要に応じてブロック化します。たとえば、UNIX系システムの/dev/randomなど。|
|NativePRNGNonBlocking|基盤となるネイティブOSから乱数を取得しますが、アプリケーションの速度低下を避けるためブロック化しません。たとえば、UNIX系システムの/dev/urandomなど。|
|PKCS11|基盤となるインストールおよび構成済みのPKCS11ライブラリから乱数を取得します。|
|SHA1PRNG|Sunプロバイダが提供する擬似乱数生成(PRNG)アルゴリズム。このアルゴリズムは、PRNGの基盤としてSHA-1を使用します。各操作につき値が1増加する64ビット・カウンタを使って鎖状につながった真にランダムなシード値から、SHA-1ハッシュを計算します。160ビットのSHA-1出力のうち、64ビットだけが使用されます。|
|Windows-PRNG|基盤となるWindows OSから乱数を取得します。|
http://docs.oracle.com/javase/jp/8/docs/technotes/guides/security/StandardNames.html#SecureRandom

## Signatureアルゴリズム
署名アルゴリズムには、他に以下のものを指定することが出来ます。

| アルゴリズム名 | 説明 |
|:------------|:-----------|
|NONEwithRSA|RSA操作を行う前にダイジェスト・アルゴリズム(MD5/SHA1など)を使用しないRSA署名アルゴリズム。RSA署名アルゴリズムについては、PKCS#1を参照してください。|
|MD2withRSA<br>MD5withRSA|PKCS#1で定義された、RSA暗号を使用したMD2/MD5署名アルゴリズム。MD2/MD5ダイジェスト・アルゴリズムおよびRSAを使用してRSAデジタル署名を作成および検証します。|
|SHA1withRSA<br>SHA224withRSA<br>SHA256withRSA<br>SHA384withRSA<br>SHA512withRSA|OSI Interoperability Workshopで定義された、SHA-*およびRSA暗号化アルゴリズムを使用した署名アルゴリズム。PKCS#1に記述されているパディング規則を使用します。|
|NONEwithDSA|FIPS PUB 186-2で定義されたデジタル署名アルゴリズム。このデータの長さは正確に20バイトである必要があります。このアルゴリズムは、rawDSAとも呼ばれています。|
|SHA1withDSA<br>SHA224withDSA<br>SHA256withDSA|FIPS PUB 186-3で定義された、SHA-1、SHA-224またはSHA-256ダイジェスト・アルゴリズムを使用してデジタル署名を作成および検証するDSA署名アルゴリズム。|
|NONEwithECDSA<br>SHA1withECDSA<br>SHA224withECDSA<br>SHA256withECDSA<br>SHA384withECDSA<br>SHA512withECDSA<br>(ECDSA)|ANSI X9.62で定義されたECDSA署名アルゴリズム。<br>注:「ECDSA」は「SHA1withECDSA」アルゴリズムのあいまいな名前であるため、使用しないでください。代わりに、正式な名前「SHA1withECDSA」を使用します。|
|&lt;digest&gt;with&lt;encryption&gt;|	この形式を使用して、特定のメッセージ・ダイジェスト(MD2、MD5など)とアルゴリズム(RSA、DSAなど)を使用する署名アルゴリズムの名前を指定します。このセクションで紹介した明示的に定義されている標準名(MD2withRSAなど)も同じ形式で指定されています。<br>PKCS#1 v2.0で定義された新しい署名方式の場合は、&lt;digest&gt;with&lt;encryption&gt;の形式では不十分なため、&lt;digest&gt;with&lt;encryption&gt;and&lt;mgf&gt;の形式を使用して名前を指定できます。&lt;mgf&gt;は、MGF1などのマスク生成機能に置き換える必要があります。例: MD5withRSAandMGF1。|
http://docs.oracle.com/javase/jp/8/docs/technotes/guides/security/StandardNames.html#Signature

# 参考元
java - Tutorial of ECDSA algorithm to sign a string - Stack Overflow
https://stackoverflow.com/questions/11339788/tutorial-of-ecdsa-algorithm-to-sign-a-string
Java暗号化アーキテクチャ標準アルゴリズム名のドキュメント(JDK 8用) - #KeyPairGeneratorアルゴリズム
http://docs.oracle.com/javase/jp/8/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator
Java暗号化アーキテクチャ標準アルゴリズム名のドキュメント(JDK 8用) - #SecureRandom乱数生成アルゴリズム
http://docs.oracle.com/javase/jp/8/docs/technotes/guides/security/StandardNames.html#SecureRandom
Java暗号化アーキテクチャ標準アルゴリズム名のドキュメント(JDK 8用) - #Signatureアルゴリズム
http://docs.oracle.com/javase/jp/8/docs/technotes/guides/security/StandardNames.html#Signature
「Java 暗号化アーキテクチャ API 仕様 & リファレンス」の付録 A（PRNG アルゴリズムの標準的な名前）
https://docs.oracle.com/javase/jp/1.4/guide/security/CryptoSpec.html#AppA
Elliptical curve cryptography in java
http://rahulatjava.blogspot.jp/2014/02/elliptical-curve-cryptography-in-java.html
bc-java/ECIESTest.java at master · bcgit/bc-java
https://github.com/bcgit/bc-java/blob/master/prov/src/test/java/org/bouncycastle/jce/provider/test/ECIESTest.java
Encryption and Decryption of Data using Elliptic Curve Cryptography( ECC ) with Bouncy Castle C# Library
https://www.codeproject.com/Tips/1071190/Encryption-and-Decryption-of-Data-using-Elliptic-C