簡體   English   中英

Java的BouncyCastle並不總是驗證OpenSSL ECDSA簽名

[英]Java's BouncyCastle doesn't always verify OpenSSL ECDSA signature

我使用OpenSSL(在C ++中)簽署文本,但是我的Java程序並不總是驗證簽名的消息(只有約5個中的1個被驗證)。 有趣的是, https: //kjur.github.io/jsrsasign/sample/sample-ecdsa.html不會驗證其中任何一個:

曲線名稱: secp256k1簽名算法: SHA256withECDSA

專用密鑰

431313701ec60d303fa7d027d5f1579eaa57f0e870b23e3a25876e61bed2caa3

公鑰

035bcefc4a6ca257e394e82c20027db2af368474afb8917273713644f11a7cecb3

失敗

text to sign=
    pcax2727gRo8M6vf9Vjhr1JDrQ3rdPYu6xx81000pcax273z8kaV5Ugsiqz3tvWGo8Gg6sch6V4912341535867163229

signature=
    3044022061dff8e39f9324b0794ec2c58abda971898f694ca980baf3c2a4045a9048b441022054a2fb8ef3d383fd7eeb31425dba440e2fd2053778d4ab3725046385c7845cff0000

成功

text to sign=
    pcax2727gRo8M6vf9Vjhr1JDrQ3rdPYu6xx81000pcax273z8kaV5Ugsiqz3tvWGo8Gg6sch6V4912341535867122614

signature=
    3046022100f200d0fb9e86a16bd46ee2dd11f1840a436d0a5c6823001a516e975a44906fcf022100d062a60611fc0f21d81fa3140741c8b6e650fff33d2c48aef69a3a40d7c7b3ca

Java的

private static final String SHA256WITH_ECDSA = "SHA256withECDSA";

public static boolean isValidSignature(PublicKey pub, byte[] dataToVerify, byte[] signature) {

    try {

        Signature sign = Signature.getInstance(SHA256WITH_ECDSA, BouncyCastleProvider.PROVIDER_NAME);

        sign.initVerify(pub);

        sign.update(dataToVerify);

        return sign.verify(signature);

    } catch (Exception e) {
        log.error("Error: " + e.getMessage());
    }

    return false;

}

C ++

std::vector<unsigned char> utils::crypto::sign(std::string& private_key_58, std::string& message) {

    auto priv_bytes = utils::base58::decode_base(private_key_58);

    auto digest = utils::crypto::sha256(message);

    auto key = utils::crypto::ec_new_keypair(priv_bytes);

    auto signature = ECDSA_do_sign(digest.data(), digest.size(), key);

    auto der_len = ECDSA_size(key);
    auto der = (uint8_t*) calloc(der_len, sizeof(uint8_t));
    auto der_copy = der;
    i2d_ECDSA_SIG(signature, &der_copy);

    std::vector<unsigned char> s (der, der+der_len);

    return s;

}

std::vector<unsigned char> utils::crypto::sha256(std::string& str) {

    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    SHA256_Update(&sha256, str.c_str(), str.size());
    SHA256_Final(hash, &sha256);

    std::vector<unsigned char> data(hash, hash + SHA256_DIGEST_LENGTH);

    return data;

}

EC_KEY *utils::crypto::ec_new_keypair(std::vector<unsigned char>& priv_bytes) {

    EC_KEY *key = nullptr;
    BIGNUM *priv = nullptr;
    BN_CTX *ctx = nullptr;
    const EC_GROUP *group = nullptr;
    EC_POINT *pub = nullptr;

    key = EC_KEY_new_by_curve_name(NID_secp256k1);

    if (!key) {
        std::cerr << "Can't generate curve secp256k1\n";
        std::abort();
    }

    priv = BN_new();
    BN_bin2bn(priv_bytes.data(), 32, priv);
    EC_KEY_set_private_key(key, priv);

    ctx = BN_CTX_new();
    BN_CTX_start(ctx);

    group = EC_KEY_get0_group(key);
    pub = EC_POINT_new(group);
    EC_POINT_mul(group, pub, priv, NULL, NULL, ctx);
    EC_KEY_set_public_key(key, pub);

    EC_POINT_free(pub);
    BN_CTX_end(ctx);
    BN_CTX_free(ctx);
    BN_clear_free(priv);

    return key;
}

Neardupes ECDSA簽名長度以及如何為java.security.Signature簽名方法指定簽名長度 (以及更多鏈接)

ASN.1 DER編碼對於除了某些非常有限的數據之外的所有數據都是可變大小的,特別是對於ECDSA(或DSA)簽名。 ECDSA_size返回給定鍵可能的最大長度,但每個實際簽名可以是該長度或更短,具體取決於簽名中值r和s的二進制表示,出於您的目的,可以將其基本上視為隨機數。

如果實際簽名比ECDSA_size短,您仍然會編碼整個緩沖區並將其傳遞給Java; 注意“失敗”示例末尾的兩個零字節(十六進制為0000 )? DER解碼器可以忽略尾隨垃圾,當我在較舊的BouncyCastle和SunEC提供程序上測試這樣的情況時,它實際上工作正常,但是從BouncyCastle 1.54開始它失敗 - 有一個相當明顯的異常, java.security.SignatureException: error decoding signature bytes. - 和SunEC從8u121開始,其原因或異常類似於java.security.SignatureException: Invalid encoding for signature

在對“松散”編碼進行一些成功攻擊(包括比特幣中的secp256k1簽名)之后,許多實現最近使DER解碼更加嚴格 - 請參閱https://bitcoin.stackexchange.com/questions/51706/what-c​​an-be-changed- in-signed-bitcoin-transactionhttps://en.bitcoin.it/wiki/Transaction_malleability 這在Oracle Java 8u121發行說明項“添加到DER編碼解析代碼中的更多檢查”中提到,盡管我沒有看到類似於Bouncy的任何內容。

由於secp256k1是Certicom / X9'prime'(Fp)曲線組,因此其輔助因子為1,其順序非常接近於基礎字段大小,而后者又非常接近256位,即8的倍數,因此簽名為這個組將DER編碼到最大長度(和工作)幾乎正好1/4(25%)的時間; 剩下的時間他們都會失敗。

官方和最佳解決方案是使用指針中的更新值,此處為der_copy ,由(任意) i2d*例程輸出,以確定編碼的長度,並使用該長度。 如果由於某種原因無法處理可變長度,則可以傳輸整個緩沖區,但在傳遞給BouncyCastle(或SunEC)之前使用2+signature[1]作為有效長度進行截斷 - 但如果更改為大於約480位的曲線; 以上它是不同的,更復雜的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM