Java与Python HMAC-SHA256不匹配

Dan*_*anH 11 python java android hmac

根据最近对这个问题的反馈和发现,我重写了摆脱噪音的问题.

我有两个单独的代码路径,一个在Java(Android),一个和Python,它们完成以下目的,用于协商Android设备和Python/Django之间的配对.

Java的:

  • 生成syncKey
  • 使用presharedKey(包括syncKey)散列各种值的串联字符串
  • 使用presharedKey加密syncKey
  • 将哈希,加密的syncKey,DeviceId和任意变量发送到Web服务器

蟒蛇

  • 从deviceId获取presharedKey
  • 解密加密的syncKey
  • 使用presharedKey(包括解密的syncKey)散列各种值的串联字符串
  • 确保哈希匹配,确认syncKey已成功解密,并且deviceId保存正确的presharedKey.

现在,如果我发送未加密的syncKey,此过程将起作用.最终的哈希匹配,证明deviceId具有正确的预共享密钥,但是只要我将en/decryption添加到进程中,哈希就不再匹配,尽管syncKey和连接字符串看起来都完全匹配Java/Python的调试输出中的字符字符.

这个过程的一个怪癖是AES256加密算法需要256位密钥,因此我将512bit预共享密钥切成两半.在整个电路板上只使用256位密钥的替代方案是要求我在密钥encode('ascii')端传递密钥,否则在使用较短密钥进行散列时会抛出错误.

这是相关代码:

Java的:

String presharedKey = getKey();
// f8250b0d5960444e4de6ecc3a78900bb941246a1dece7848fc72b90092ab3ecd0c1c8e36fddba501ef92e72c95b47e07f98f7fd9cb63da75c008a3201124ea5d

String deviceId = getDeviceId();
// 1605788742789230

SyncKey syncKey = generateSyncKey();
// 824C1EE9EF507B52EA28362C71BD4AD512A5F82ACFAE80DEF531F73AC124CA814BA30CE805A157D6ADB9EC04FC99AAE6FDC4238FCD76B87CE22BC2FE33B2E5C9

String concat = syncKey.hexString();
// 824C1EE9EF507B52EA28362C71BD4AD512A5F82ACFAE80DEF531F73AC124CA814BA30CE805A157D6ADB9EC04FC99AAE6FDC4238FCD76B87CE22BC2FE33B2E5C9

String ALGORITHM = "HmacSHA256";
String hash = null;
try {
    SecretKeySpec keySpec = new SecretKeySpec(
        presharedKey.getBytes(),
        ALGORITHM);
    Mac mac = Mac.getInstance(ALGORITHM);
    mac.init(keySpec);
    byte[] result = mac.doFinal(concat.getBytes());
    hash = Base64.encodeToString(result, Base64.DEFAULT);
    // FpDE2JLmCBr+/rW+n/jBHH13F8AV80sUM2fQAY2IpRs=
} catch (NoSuchAlgorithmException x) {
} catch (InvalidKeyException x) {
}

String encKey = presharedKey.substring(0, presharedKey.length() / 2);
// f8250b0d5960444e4de6ecc3a78900bb941246a1dece7848fc72b90092ab3ecd

int len = encKey.length();
byte[] encKeyBytes = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
    encKeyBytes[i / 2] = (byte) ((Character.digit(encKey.charAt(i), 16) << 4)
            + Character.digit(encKey.charAt(i+1), 16));
}

String encryptedSyncKey = null;
try {
    byte[] iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
    SecretKeySpec encKeySpec = new SecretKeySpec(encKeyBytes, "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, encKeySpec, ivSpec);
    byte[] encryptedSyncKeyBytes = cipher.doFinal(syncKey.hexString().getBytes());
    encryptedSyncKey = Base64.encodeToString(encryptedSyncKeyBytes, Base64.DEFAULT);
    /*
        Yrl0/SuTUUTC6oJ8o4TCOy65EwO0JzoXfEi9kLq0AOlf6rH+nN7+BEc0s5uE7TIo1UlJb/DvR2Ca
        ACmQVXXhgpZUTB4sQ0eSo+t32lg0EEb9xKI5CZ4l9QO5raw0xBn7r/tfIdVm8AIFkN9QCcthS0DF
        KH3oWhpwNS+tfEuibLPgGqP/zGTozmido9U9lb4n
    */
} catch (InvalidAlgorithmParameterException e) {
} catch (NoSuchAlgorithmException e) {
} catch (NoSuchPaddingException e) {
} catch (InvalidKeyException e) {
} catch (IllegalBlockSizeException e) {
} catch (BadPaddingException e) {
}

sendStuffToWeb(encryptedSyncKey, deviceId, hash);
Run Code Online (Sandbox Code Playgroud)

蟒蛇:

hash = getHash(request)
# hash from Java: FpDE2JLmCBr+/rW+n/jBHH13F8AV80sUM2fQAY2IpRs=

encrypted_sync_key = getEncSyncKey(request)
# encryptedSyncKey from Java:
# Yrl0/SuTUUTC6oJ8o4TCOy65EwO0JzoXfEi9kLq0AOlf6rH+nN7+BEc0s5uE7TIo1UlJb/DvR2Ca
# ACmQVXXhgpZUTB4sQ0eSo+t32lg0EEb9xKI5CZ4l9QO5raw0xBn7r/tfIdVm8AIFkN9QCcthS0DF
# KH3oWhpwNS+tfEuibLPgGqP/zGTozmido9U9lb4n

device_id = getDeviceId(request)
# 1605788742789230

preshared_key = getPresharedKeyFromDevice(deviceId)
# f8250b0d5960444e4de6ecc3a78900bb941246a1dece7848fc72b90092ab3ecd0c1c8e36fddba501ef92e72c95b47e07f98f7fd9cb63da75c008a3201124ea5d

enc_key = preshared_key[:len(preshared_key)/2]
# f8250b0d5960444e4de6ecc3a78900bb941246a1dece7848fc72b90092ab3ecd

aes = AES.new(enc_key.decode('hex'), AES.MODE_CBC, IV="\x00"*16)
sync_key = aes.decrypt(base64.b64decode(encrypted_sync_key))
# 824C1EE9EF507B52EA28362C71BD4AD512A5F82ACFAE80DEF531F73AC124CA814BA30CE805A157D6ADB9EC04FC99AAE6FDC4238FCD76B87CE22BC2FE33B2E5C9

concat = sync_key
# 824C1EE9EF507B52EA28362C71BD4AD512A5F82ACFAE80DEF531F73AC124CA814BA30CE805A157D6ADB9EC04FC99AAE6FDC4238FCD76B87CE22BC2FE33B2E5C9

import hashlib
from hmac import new as hmac

verify_hash = hmac(preshared_key, concat, hashlib.sha256).digest().encode('base64')
# IoSc2w2sQ4/fwhJTdUQHw/Hdyjy+ranzQ1z3J5LfYbA=
Run Code Online (Sandbox Code Playgroud)

从下面的调试输出中,您可以看到syncKey已成功加密和解密,并且concat完全相同.然而,结果hash最终会有所不同.

agf*_*agf 8

你的Python代码是错误的.我可以用Python重现你在Java中得到的答案.

如果我使用你的输入:

>>> preshared_key_hex
b'f8250b0d5960444e4de6ecc3a78900bb941246a1dece7848fc72b90092ab3ecd0c1c8e36fddba501ef92e72c95b47e07f98f7fd9cb63da75c008a3201124ea5d'
>>> concat_hex
b'824C1EE9EF507B52EA28362C71BD4AD512A5F82ACFAE80DEF531F73AC124CA814BA30CE805A157D6ADB9EC04FC99AAE6FDC4238FCD76B87CE22BC2FE33B2E5C9'
Run Code Online (Sandbox Code Playgroud)

我得到了与Java相同的价值:

>>> base64.b64encode(hmac.new(preshared_key_hex, concat_hex, hashlib.sha256).digest())
b'FpDE2JLmCBr+/rW+n/jBHH13F8AV80sUM2fQAY2IpRs='
Run Code Online (Sandbox Code Playgroud)

但是,这个价值可能也是错误的.您几乎肯定会对输入值进行十六进制解码.

我无法重现Python中的内容; 你传递的价值之一hmac.new并不是你想象的那样.print他们在打电话之前hmac.new,你应该看到不匹配的东西.

  • @DanH我根本无法重现Python中的内容.我只是提到这些值应该是十六进制解码.这里没有任何Python 2/3的差异.无论编码如何,你都会在Python中犯错误.如果在调用`hmac.new`之前打印出这些值,你会发现它们与你在Java中传递的值不匹配 - 因为如果你传递这些值,就像我一样,你得到的答案与Java的.所以你的问题在于Python. (3认同)