2016-09-27 31 views
3

BouncyCastle暗号化プロバイダを使用してJavaでECDSA署名を検証する必要があります。これまでのところ、BouncyCastleは署名の検証に失敗しました。BouncyCastle ECDSA署名検証に失敗prime256v1とSHA256withECDSAアルゴリズムを使用

署名はで作成されます。Atmel AT88CK590暗号化認証モジュールと公開鍵をモジュールから取得できます。ここで、公開鍵は64オクテット長の、C/C++形式で、次のとおりです。

uint8_t pubKey[] = { 
    // X coordinate of the elliptic curve. 
    0xc1, 0x71, 0xCB, 0xED, 0x65, 0x71, 0x82, 0x2E, 0x8F, 0x8A, 0x43, 0x8D, 0x72, 0x56, 0xD1, 0xC8, 
    0x86, 0x3C, 0xD0, 0xBC, 0x7F, 0xCC, 0xE3, 0x6D, 0xE7, 0xB7, 0x17, 0xED, 0x29, 0xC8, 0x38, 0xCB, 

    // Y coordinate of the elliptic curve. 
    0x80, 0xCD, 0xBE, 0x0F, 0x1D, 0x5C, 0xC5, 0x46, 0x99, 0x24, 0x8F, 0x6E, 0x0A, 0xEA, 0x1F, 0x7A, 
    0x43, 0xBA, 0x2B, 0x03, 0x80, 0x90, 0xE9, 0x25, 0xB2, 0xD0, 0xE6, 0x48, 0x93, 0x91, 0x64, 0x83 
}; 

元のメッセージ、Base64エンコーディングでの署名と公開鍵:

// Raw message to sign 
private static final String tokenStr = "123456789"; 
// Base64 encoded 
private static final String pubKeyStr = "wXHL7WVxgi6PikONclbRyIY80Lx/zONt57cX7SnIOMuAzb4PHVzFRpkkj24K6h96
Q7orA4CQ6SWy0OZIk5Fkgw=="; 
// Base64 encoded 
private static final String signatureStr = "XF2WossFTA82ndYFGEH0FPqAldkFQLGd/Bv/Qh8UYip7sXUvCUnFgi1YXjN3WxLn
IwSo3OaHLCOzGAtIis0b3A=="; 

公開鍵を変換するには、私は次を使用しています:

private static PublicKey getPublicKeyFromBytes(byte[] pubKey, String ecSpec, String provider) 
     throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException { 
    ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec(ecSpec); 
    KeyFactory kf = KeyFactory.getInstance(ECDSA_CRYPTO, provider); 
    ECNamedCurveSpec params = new ECNamedCurveSpec(ecSpec, spec.getCurve(), spec.getG(), spec.getN()); 
    ECPoint pubPoint = ECPointUtil.decodePoint(params.getCurve(), pubKey); 
    ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(pubPoint, params); 
    PublicKey publicKey = kf.generatePublic(pubKeySpec); 

    return publicKey; 
} 

私は次のように使用していDER形式に署名を変換するには:

private static byte[] toDERSignature(byte[] tokenSignature) throws IOException { 
    byte[] r = Arrays.copyOfRange(tokenSignature, 0, tokenSignature.length/2); 
    byte[] s = Arrays.copyOfRange(tokenSignature, tokenSignature.length/2, tokenSignature.length); 
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 
    DEROutputStream derOutputStream = new DEROutputStream(byteArrayOutputStream); 
    ASN1EncodableVector v = new ASN1EncodableVector(); 

    v.add(new ASN1Integer(new BigInteger(1, r))); 
    v.add(new ASN1Integer(new BigInteger(1, s))); 
    derOutputStream.writeObject(new DERSequence(v)); 

    byte[] derSignature = byteArrayOutputStream.toByteArray(); 

    return derSignature; 
} 

ここで署名を検証するためのコードです:

Security.addProvider(new BouncyCastleProvider()); 

byte[] tokenBytes = tokenStr.getBytes("UTF-8"); 
String urlDecodePubKeyStr = pubKeyStr.replace(newlineHtml, ""); 
byte[] pubKeyBytes = DatatypeConverter.parseBase64Binary(urlDecodePubKeyStr); 
String urlDecodeSignatureStr = signatureStr.replace(newlineHtml, ""); 
byte[] signBytes = DatatypeConverter.parseBase64Binary(urlDecodeSignatureStr); 
byte[] derSignature = toDERSignature(signBytes); 
ByteBuffer bb = ByteBuffer.allocate(pubKeyBytes.length + 1); 

bb.put((byte)4); 
bb.put(pubKeyBytes); 

PublicKey ecPublicKey = getPublicKeyFromBytes(bb.array(), "prime256v1", "BC"); 

System.out.println("\nSignature: " + Hex.toHexString(signBytes).toUpperCase()); 
System.out.println("DER Signature: " + Hex.toHexString(derSignature).toUpperCase()); 
System.out.println(ecPublicKey.toString()); 

Signature signature = Signature.getInstance("SHA256withECDSA", "BC"); 

signature.initVerify(ecPublicKey); 
signature.update(tokenBytes); 

boolean result = signature.verify(derSignature); 

System.out.println("BC Signature Valid: " + result); 

出力:

Signature: 5C5D96A2CB054C0F369DD6051841F414FA8095D90540B19DFC1BFF421F14622A7BB1752F0949C5822D585E33775B12E72304A8DCE6872C23B3180B488ACD1BDC 
DER Signature: 304402205C5D96A2CB054C0F369DD6051841F414FA8095D90540B19DFC1BFF421F14622A02207BB1752F0949C5822D585E33775B12E72304A8DCE6872C23B3180B488ACD1BDC 
EC Public Key 
      X: c171cbed6571822e8f8a438d7256d1c8863cd0bc7fcce36de7b717ed29c838cb 
      Y: 80cdbe0f1d5cc54699248f6e0aea1f7a43ba2b038090e925b2d0e64893916483 

BC Signature Valid: false 

は、誰もが前に同じ問題に遭遇したことがありますか?私はここで何を欠場したのですか?

+1

newlineHtmlは次のように定義されています。 private static final String newlineHtml = " "; – Petaluma007

+1

入力データ(別名メッセージまたは平文)のバイナリ比較を実行しましたか?公開鍵と秘密鍵がペアになっていることを確認しましたか?ハッシュアルゴリズムはどうですか?そしてカーブ? –

答えて

2

この署名値は、というハッシュでは計算されず、データの上に直接表示されていますが、です。次のいずれかの

  • アトメルは、あなたが最初のハッシュとハッシュが

  • アトメルハッシュ必要がありますが、あなたには、いくつかの必要なオプションを設定していないかを署名するotuput提供しなければなりませんでしたので、ハッシュはありません何か

  • あなたは実際にはハッシュのない非独創的な「生の」署名を望んでいます。これは、データを32バイトまたは31バイトに制限することに注意してください。署名する値が敵によって影響され、署名に使用されるランダムジェネレータが弱い場合、私はあなたの秘密鍵を回復する攻撃を与えると思います(私はそれをやっていませんが)。それが危険であるとしても、私は避けるだろう。いずれにしても

、あなたが持っている値は、スキームNoneWithECDSAを使用して検証することができます。

また、これをテストする際に、コードにいくつかの改善点がありました。まず、固定フォーマットのX、Yを使用すれば、ポイントを準符号化してデコードする必要はありません。次に、署名をDERエンコードする必要がありますが、より簡単に行うことができます。

public static void main (String[] args) throws Exception { 
    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 
    // test data; real data would come from outside 
    byte[] dataBytes = "123456789".getBytes("UTF-8"); 
    String sigString = "XF2WossFTA82ndYFGEH0FPqAldkFQLGd/Bv/Qh8UYip7sXUvCUnFgi1YXjN3WxLn
IwSo3OaHLCOzGAtIis0b3A=="; 
    String pubkeyString = "wXHL7WVxgi6PikONclbRyIY80Lx/zONt57cX7SnIOMuAzb4PHVzFRpkkj24K6h96
Q7orA4CQ6SWy0OZIk5Fkgw=="; 
    String ecSpec="prime256v1"; int size=32; // bytes for x,y in pubkey also r,s in sig 

    byte[] pubkeyBytes = DatatypeConverter.parseBase64Binary(pubkeyString.replaceAll("
","")); 
    KeyFactory kf = KeyFactory.getInstance ("ECDSA", "BC"); 
    ECNamedCurveParameterSpec cspec = ECNamedCurveTable.getParameterSpec(ecSpec); 
    BigInteger x = new BigInteger(1, Arrays.copyOfRange(pubkeyBytes,0,size)); 
    BigInteger y = new BigInteger(1, Arrays.copyOfRange(pubkeyBytes,size,size*2)); 
    ECPublicKeySpec kspec = new ECPublicKeySpec (cspec.getCurve().createPoint(x, y), cspec); 
    PublicKey k = kf.generatePublic(kspec); 

    byte[] sigBytes = DatatypeConverter.parseBase64Binary(sigString.replaceAll("
","")); 
    ASN1EncodableVector v = new ASN1EncodableVector(); 
    v.add(/*r*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(sigBytes,0,size)))); 
    v.add(/*s*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(sigBytes,size,size*2)))); 
    byte[] sigDer = new DERSequence(v).getEncoded(); 

    Signature sig = Signature.getInstance("NoneWithECDSA", "BC"); // NOTE None instead of a hash 
    sig.initVerify (k); sig.update (dataBytes); 
    System.out.println ("verify="+sig.verify(sigDer)); 
} 

PS:ここに私のバージョンでは、わかりやすくするために、単一の線状ブロックに、両方の変更で、だあなたはX [Y] coordinate of the elliptic curveとして公開半分をコメント。それらは上の点の座標であり、それはpublickeyです。曲線自体には座標がありません。

+0

それはDaveだった。私はちょうどチップが供給された入力をハッシュしないことを私に伝えるAtmelからの確認を受け取った。今、Java側のNonewithECDSAを使用して署名が正常に検証されました。 – Petaluma007

関連する問題