簡體   English   中英

Java Mac HMAC與C ++ OpenSSL hmac

[英]Java Mac HMAC vs C++ OpenSSL hmac

這將是一個很長的問題,但是我有一個非常奇怪的錯誤。 我在C ++中使用OpenSSL來計算HMAC,並將它們與使用javax.crypto.Mac的模擬實現進行比較。 對於某些鍵,HMAC計算是正確的,而對於另一些鍵,HMAC有差異。 我相信當鑰匙變大時會出現問題。 這是詳細信息。

這是C ++最重要的代碼:

void computeHMAC(std::string message, std::string key){
    unsigned int digestLength = 20;
    HMAC_CTX hmac_ctx_;
    BIGNUM* key_ = BN_new();;

    BN_hex2bn(&key_, key); 

    unsigned char convertedKey[BN_num_bytes(key_)];
    BIGNUM* bn = BN_new();

    HMAC_CTX_init(&hmac_ctx_);

    BN_bn2bin(bn, convertedKey);
    int length = BN_bn2bin(key_, convertedKey);

    HMAC_Init_ex(&hmac_ctx_, convertedKey, length, EVP_sha1(), NULL);

/*Calc HMAC */
    std::transform( message.begin(), message.end(), message.begin(), ::tolower);
    unsigned char digest[digestLength];

    HMAC_Update(&hmac_ctx_, reinterpret_cast<const unsigned char*>(message.c_str()),
      message.length());
    HMAC_Final(&hmac_ctx_, digest, &digestLength);
    char mdString[40];
    for(unsigned int i = 0; i < 20; ++i){
        sprintf(&mdString[i*2], "%02x", (unsigned int)digest[i]);
    }
     std::cout << "\n\nMSG:\n" << message << "\nKEY:\n" + std::string(BN_bn2hex(key_)) + "\nHMAC\n" + std::string(mdString) + "\n\n";
}

Java測試如下所示:

public String calculateKey(String msg, String key) throws Exception{

    HMAC = Mac.getInstance("HmacSHA1");

    BigInteger k = new BigInteger(key, 16);

    HMAC.init(new SecretKeySpec(k.toByteArray(), "HmacSHA1"));

    msg = msg.toLowerCase();
    HMAC.update(msg.getBytes());
    byte[] digest = HMAC.doFinal();

    System.out.println("Key:\n" + k.toString(16) + "\n");
    System.out.println("HMAC:\n" + DatatypeConverter.printHexBinary(digest).toLowerCase() + "\n");

    return DatatypeConverter.printHexBinary(digest).toLowerCase();
}

某些測試使用不同的鍵運行(所有字符串均解釋為十六進制):


鍵1:736A66B29072C49AB6DC93BB2BA41A53E169D14621872B0345F01EBBF117FCE48EEEA2409CFC1BD92B0428BA0A34092E3117BEB4A8A14F03391C661994863DAC1A75ED437C1394DA0741B16740D018CA243A800DA25311FDFB9CA4361743E8511E220B79C2A3483FCC29C7A54F1EB804481B2DC87E54A3A7D8A94253A60AC77FA4584A525EDC42BF82AE2A1FD6E3746F626E0AFB211F6984367B34C954B0E08E3F612590EFB8396ECD9AE77F15D5222A6DB106E8325C3ABEA54BB59E060F9EA0

訊息:測試

Hmac OpenSSL:b37f79df52afdbbc4282d3146f9fe7a254dd23b3

Hmac Java Mac:b37f79df52afdbbc4282d3146f9fe7a254dd23b3


重點2:636A66B29072C49AB6DC93BB2BA41A53E169D14621872B0345F01EBBF117FCE48EEEA2409CFC1BD92B0428BA0A34092E3117BEB4A8A14F03391C661994863DAC1A75ED437C1394DA0741B16740D018CA243A800DA25311FDFB9CA4361743E8511E220B79C2A3483FCC29C7A54F1EB804481B2DC87E54A3A7D8A94253A60AC77FA4584A525EDC42BF82AE2A1FD6E3746F626E0AFB211F6984367B34C954B0E08E3F612590EFB8396ECD9AE77F15D5222A6DB106E8325C3ABEA54BB59E060F9EA0

訊息:測試

Hmac OpenSSL:bac64a905fa6ae3f7bf5131be06ca037b3b498d7

Hmac Java Mac:bac64a905fa6ae3f7bf5131be06ca037b3b498d7


重點3:836A66B29072C49AB6DC93BB2BA41A53E169D14621872B0345F01EBBF117FCE48EEEA2409CFC1BD92B0428BA0A34092E3117BEB4A8A14F03391C661994863DAC1A75ED437C1394DA0741B16740D018CA243A800DA25311FDFB9CA4361743E8511E220B79C2A3483FCC29C7A54F1EB804481B2DC87E54A3A7D8A94253A60AC77FA4584A525EDC42BF82AE2A1FD6E3746F626E0AFB211F6984367B34C954B0E08E3F612590EFB8396ECD9AE77F15D5222A6DB106E8325C3ABEA54BB59E060F9EA0

訊息:測試

Hmac OpenSSL:c189c637317b67cee04361e78c3ef576c3530aa7

Hmac Java Mac:472d734762c264bea19b043094ad0416d1b2cd9c

如數據所示,當鑰匙變大時,會發生錯誤。 如果不知道哪個實現是錯誤的。 我也嘗試過使用較大的鍵和較小的鍵。 我尚未確定確切的閾值。 誰能發現問題? 有沒有人能夠通過使用不同的軟件進行仿真來告訴我,在最后一種情況下哪個HMAC是不正確的,或者有人可以告訴我可以使用哪種第三種實現檢查我的?

親切的問候,

魯爾風暴

在Java中將十六進制字符串轉換為BigInt時,它將假定數字為正(除非字符串包含-符號)。

但是它的內部表示是二進制補碼。 表示將一位用於符號。

如果要轉換以007F含)之間的十六進制開頭的值,那么這不是問題。 它可以直接轉換字節,因為最左邊的位是零,這意味着該數字被視為正數。

但是,如果您要轉換以80FF開頭的值,則最左邊的位是1,這將被視為負數。 為避免這種情況,並保持BigInteger值與提供的值完全相同,它在開頭添加了另一個零字節。

因此,在內部,數字的轉換(例如7ABCDE是字節數組

0x7a 0xbc 0xde

但是,諸如FABCDE之類的數字(只有第一個字節不同!)的轉換是:

0x00 0xfa 0xbc 0xde

這意味着對於以80-FF范圍內的字節開頭的鍵, BigInteger.toByteArray()不會產生與C ++程序產生的數組相同的數組,而是會再增加一個字節的數組。

有幾種方法可以解決此問題-例如使用您自己的十六進制到字節數組解析器或在某個庫中找到現有的解析器。 如果要使用BigInteger生產的產品,可以執行以下操作:

BigInteger k = new BigInteger(key, 16);
byte[] kByteArr = k.toByteArray();
if ( kByteArr.length > (key.length() + 1) / 2 ) {
    kByteArr = Arrays.copyOfRange(kByteArr,1,kByteArr.length);
}

現在,您可以使用kByteArr正確執行該操作。

您應該注意的另一個問題是長度為奇數的鍵。 通常,您不應使用長度為奇數的十六進制八位字節字符串。 F8ACB這樣的字符串實際上是0F8ACB (這不會在BigInteger中引起額外的字節),因此應該這樣解釋。 這就是為什么我在公式中寫入(key.length() + 1) -如果key為奇數長度,則應將其解釋為更長的八位字節。 注意,如果您編寫自己的十六進制至字節數組轉換器,這一點也很重要-如果長度為奇數,則應在開始轉換之前在開頭添加一個零。

暫無
暫無

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

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