6

ikev2 プロトコルに関連するプログラムのテスト ハーネスを Java で作成しています。この一環として、ECDSA 署名を計算できるようにする必要があります (具体的には NIST P-256 曲線を使用)。

RFC 4754は、IKEv2 での ECDSA の使用について説明し、一連のテスト ベクトルを提供します (必要な p256 曲線を含む)。

次のコードを使用して、Java の ECDSA 署名実装を介して ECDSA-256 テスト ベクトル値 ( RFC のセクション 8.1 ) を実行しようとしています。

//"abc" for the input
byte[] input = { 0x61, 0x62, 0x63 };

//Ugly way of getting the ECParameterSpec for the P-256 curve by name as opposed to specifying all the parameters manually.
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec kpgparams = new ECGenParameterSpec("secp256r1");
kpg.initialize(kpgparams);
ECParameterSpec params = ((ECPublicKey) kpg.generateKeyPair().getPublic()).getParams();

//Create the static private key W from the Test Vector
ECPrivateKeySpec static_privates = new ECPrivateKeySpec(new BigInteger("DC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F", 16), params);
KeyFactory kf = KeyFactory.getInstance("EC");
ECPrivateKey spriv = (ECPrivateKey) kf.generatePrivate(static_privates);

//Perfrom ECDSA signature of the data with SHA-256 as the hash algorithm
Signature dsa = Signature.getInstance("SHA256withECDSA");
dsa.initSign(spriv);
dsa.update(input);

byte[] output = dsa.sign();
System.out.println("Result: " + new BigInteger(1, output).toString(16));

結果は次のようになります。

CB28E099 9B9C7715 FD0A80D8 E47A7707 9716CBBF 917DD72E 97566EA1 C066957C 86FA3BB4 E26CAD5B F90B7F81 899256CE 7594BB1E A0C89212 748BFF3B 3D5B0315

代わりに私は得る:

30460221 00dd9131 edeb5efd c5e718df c8a7ab2d 5532b85b 7d4c012a e5a4e90c 3b824ab5 d7022100 9a8a2b12 9e10a2ff 7066ff79 89aa73d5 ba37c868 8e 7d7fa6e 2e 613

長さの違いは、署名の Java ASN.1 エンコードが原因であることを知っています。しかし、残りの部分は完全に間違っており、その理由がわかりません。

どんな助けやアドバイスも大歓迎です!

PS私はECDSAまたはJava暗号の専門家ではないので、おそらく私が犯している愚かな間違いです

4

2 に答える 2

13

プログラムを実行するたびに、同じ平文 (署名対象) の入力に対して異なる署名値が得られると思います。

ECDSA は、ランダムで一時的な ECDSA 秘密鍵が署名ごとに生成されることを指定します。そのためにSignature.getInstance("SHA256withECDSA")、一時的なキーを指定することはできません (これは良いことです。多くのセルフ ショットを防止するためです!)。代わりに、出力を非決定的にする独自の SecureRandom インスタンスを取得します。

Signature.getInstance()これはおそらく、テスト ベクターの検証にJCE ( ) を使用できないことを意味します。

あなたができることはSecureRandom、決定論的なデータを返すように拡張することです。明らかに、実際の展開ではこれを使用しないでください。

public class FixedSecureRandom extends SecureRandom {
    private static boolean debug = false;
    private static final long serialVersionUID = 1L;
    public FixedSecureRandom() { }
    private int nextBytesIndex = 0;

    private byte[] nextBytesValues = null;

    public void setBytes(byte[] values) {
        this.nextBytesValues = values; 
    }

    public void nextBytes(byte[] b) {
        if (nextBytesValues==null) { 
            super.nextBytes(b);
        } else if (nextBytesValues.length==0) { 
            super.nextBytes(b);
        } else {
            for (int i=0; i<b.length; i++) {
                b[i] = nextBytesValues[nextBytesIndex];
                nextBytesIndex = (nextBytesIndex + 1) % nextBytesValues.length;
            }
        }
    }
}

ふぅ。これで、既知のバイト数を返し、その後実際の SecureRandom にフォールバックする SecureRandom クラスができました。もう一度言います (大声ですみません) - これを本番環境で使用しないでください!

次に、独自の SecureRandom を指定できる ECDSA 実装を使用する必要があります。ECDSASignerこの目的でBouncyCastle を使用できます。ここを除いて、独自の偽造された FixedSecureRandom を指定して、 を呼び出したときに必要secureRandom.getBytes()なバイトを取得します。これにより、テスト ベクトルで指定されたものと一致するように一時キーを制御できます。ECDSASigner要求するものと一致するように、実際のバイトをマッサージする必要がある場合があります (たとえば、事前にゼロのパディングを追加する) 。

ECPrivateKeyParameters ecPriv = ...; // this is the user's EC private key (not ephemeral)

FixedSecureRandom fsr_k = new FixedSecureRandom();
fsr_k.setBytes(tempKeyK);

ECDSASigner signer = new ECDSASigner();
ParametersWithRandom ecdsaprivrand = new ParametersWithRandom(ecPriv, fsr_k);
signer.init(true, ecdsaprivrand);

BCECDSASignerはハッシュではなく、EC 署名部分のみを実装することに注意してください。独自のハッシュを行う必要があります (入力データが にあると仮定しますdata)。

Digest md = new SHA256Digest()
md.reset();
md.update(data, 0, data.length);
byte[] hash = new byte[md.getDigestSize()];
md.doFinal(hash, 0);

ECDSA 署名を作成する前に:

BigInteger[] sig = signer.generateSignature(hash);

最後に、これBigInteger[](length==2 である必要があります) は (r,s) 値です。ASN.1 DER エンコードする必要があります。これにより、探しているドロイドバイトが得られます。

于 2013-11-11T13:50:34.013 に答える
1

これは、BouncyCastle を使用しているが、古き良き JCA API に固執している tsechin のソリューションに続く私の完全なテストです。

    byte[] input = { 0x61, 0x62, 0x63 };

    //Create the static private key W from the Test Vector
    ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
    org.bouncycastle.jce.spec.ECPrivateKeySpec privateKeySpec = new org.bouncycastle.jce.spec.ECPrivateKeySpec(new BigInteger("DC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F", 16), parameterSpec);
    KeyFactory kf = KeyFactory.getInstance("EC");
    ECPrivateKey spriv = (ECPrivateKey) kf.generatePrivate(privateKeySpec);

    //Perfrom ECDSA signature of the data with SHA-256 as the hash algorithm
    Signature dsa = Signature.getInstance("SHA256withECDSA", "BC");
    FixedSecureRandom random = new FixedSecureRandom();
    random.setBytes(Hex.decode("9E56F509196784D963D1C0A401510EE7ADA3DCC5DEE04B154BF61AF1D5A6DECE"));
    dsa.initSign(spriv, random);
    dsa.update(input);
    byte[] output = dsa.sign();

    // compare the signature with the expected reference values
    ASN1Sequence sequence = ASN1Sequence.getInstance(output);
    DERInteger r = (DERInteger) sequence.getObjectAt(0);
    DERInteger s = (DERInteger) sequence.getObjectAt(1);
    Assert.assertEquals(r.getValue(), new BigInteger("CB28E0999B9C7715FD0A80D8E47A77079716CBBF917DD72E97566EA1C066957C", 16));
    Assert.assertEquals(s.getValue(), new BigInteger("86FA3BB4E26CAD5BF90B7F81899256CE7594BB1EA0C89212748BFF3B3D5B0315", 16));
于 2013-11-11T16:38:06.090 に答える