LoginSignup
22

More than 5 years have passed since last update.

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

Posted at

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

サンプルコード

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

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アルゴリズムの鍵ペアを生成します。
注: key.getAlgorithm()は、「DiffieHellman」ではなく「DH」を返します。
DSA デジタル署名アルゴリズムの鍵ペアを生成します。
RSA RSAアルゴリズム(Signature/Cipher)の鍵ペアを生成します。
EC Elliptic Curveアルゴリズムの鍵ペアを生成します。

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から乱数を取得します。

Signatureアルゴリズム

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

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

参考元

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22