gpt4 book ai didi

java - Bouncy CaSTLe 在尝试解密 AES 消息时抛出 "mac check in OCB failed"

转载 作者:行者123 更新时间:2023-12-05 01:49:59 29 4
gpt4 key购买 nike

我需要解密 AES 消息。我能够让它在 python 上工作(使用 pycryptodome 库)但我在 kotlin/java 上没有成功。由于解密方式是OCB,Java默认不支持,只好使用Bouncy CaSTLe库。

这是有效的 python 代码:

def decrypt_aes_message(shared_key, encrypted_message):
encrypted_msg = b64decode(encrypted_message["encryptedMessage"].encode())
tag = b64decode(encrypted_message["tag"].encode())
nonce = b64decode(encrypted_message["nonce"].encode())
cipher = AES.new(shared_key.encode(), AES.MODE_OCB, nonce=nonce)
return cipher.decrypt_and_verify(encrypted_msg, tag).decode()

Java代码如下:

fun decryptAesMessage2(sharedKey: String, encryptedMessageData: Map<String, String>): ByteArray {
var encryptedMessage = encryptedMessageData["encryptedMessage"]!!.utf8Base64Decode()
var tag = encryptedMessageData["tag"]!!.utf8Base64Decode()
var nonce = encryptedMessageData["nonce"]!!.utf8Base64Decode()

var key = KeyParameter(sharedKey.toByteArray(Charsets.UTF_8))
var params = AEADParameters(key, tag.size*8, nonce)
var cipher = OCBBlockCipher(AESEngine(), AESEngine())

cipher.init(false, params)

val out = ByteArray(cipher.getOutputSize(encryptedMessage.size))
var offset = cipher.processBytes(encryptedMessage, 0, encryptedMessage.size, out, 0)
offset += cipher.doFinal(out, offset) // Throwing exception here

return out
}

Java 代码在 cipher.doFinal 上抛出异常 org.bouncycaSTLe.crypto.InvalidCipherTextException: mac check in OCB failed

文件debug.zip有完整的问题重现器。在 zip 文件中,您会发现:

  • py_working_code.py - 工作 python 脚本(需要 pycryptodome 才能工作。您可以使用 pip install pycryptodome 安装 pycryptodome)
  • bc-debug - 重现问题的 gradle 项目

最佳答案

有两个问题,一个是你的 Kotlin 代码中的错误,另一个是库错误:

Kotlin 代码中的错误

虽然 PyCryptodome 分别处理密文和标签,但 BC/Kotlin 期望按顺序将两者串联:ciphertext|tag
因此,必须在 Kotlin 代码中添加 encryptedMessage += tag 行:

fun decryptAesMessage2(sharedKey: String, encryptedMessageData: Map<String, String>): ByteArray {
var encryptedMessage = encryptedMessageData["encryptedMessage"]!!.utf8Base64Decode()
var tag = encryptedMessageData["tag"]!!.utf8Base64Decode()
encryptedMessage += tag // Fix
var nonce = encryptedMessageData["nonce"]!!.utf8Base64Decode()

var key = KeyParameter(sharedKey.toByteArray(Charsets.UTF_8))
var params = AEADParameters(key, tag.size*8, nonce)
var cipher = OCBBlockCipher(AESEngine(), AESEngine())

cipher.init(false, params)

val out = ByteArray(cipher.getOutputSize(encryptedMessage.size))
var offset = cipher.processBytes(encryptedMessage, 0, encryptedMessage.size, out, 0)
offset += cipher.doFinal(out, offset) // Throwing exception here

return out
}

测试:下面,使用Python代码和固定的Kotlin代码成功解密相同的测试数据:

python :

encrypted_message = {
'encryptedMessage': 'LzoelJ9Nv4cruj0JUlxFrNR+mqyO2rvwqDHYwnj0OkvJ+BBvug+ORYVkxA==',
'tag': 'hl56drXePWiLkVavVwF3/w==',
'nonce': b64encode(b'012345678901').decode()
}
dt = decrypt_aes_message('01234567890123456789012345678901', encrypted_message)
print(dt) # The quick brown fox jumps over the lazy dog

Kotlin :

val encrypted_message = mutableMapOf<String, String>()
encrypted_message["encryptedMessage"] = "LzoelJ9Nv4cruj0JUlxFrNR+mqyO2rvwqDHYwnj0OkvJ+BBvug+ORYVkxA=="
encrypted_message["tag"] = "hl56drXePWiLkVavVwF3/w=="
encrypted_message["nonce"] = Base64.getEncoder().encodeToString("012345678901".toByteArray(Charsets.UTF_8))
val dt = decryptAesMessage2("01234567890123456789012345678901", encrypted_message)
println(String(dt, Charsets.UTF_8)) // The quick brown fox jumps over the lazy dog

库错误

另一个问题是,对于 120 位(15 字节)的 nonce 长度,OCB 允许的最大 nonce 长度(参见 RFC 7253, 4.2. Encryption: OCB-ENCRYPT),两种实现都会产生不同的结果:

以下测试使用固定 key 和明文,并比较 Python 和 BC/Kotlin 代码在随机数长度为 13、14 和 15 字节时的结果:

Key: b'01234567890123456789012345678901'
Plaintext: b'testmessage'

Nonce Länge 13 bytes 14 bytes 15 bytes
Nonce b'0123456789012' b'01234567890123' b'012345678901234'
Python (ct|tag) 0xb35a69a245ab18fe3b6bae38b179c2a43b341f67c0451256b76bd7 0xff9be97fcb6e1ac57e6997bc3e84598a83ab70947ccac500fcf75e 0xff9be97fcb6e1ac57e6997bc3e84598a83ab70947ccac500fcf75e
BC/Kotlin 0xb35a69a245ab18fe3b6bae38b179c2a43b341f67c0451256b76bd7 0xff9be97fcb6e1ac57e6997bc3e84598a83ab70947ccac500fcf75e 0xa4355068324065f2ad194b058bdb86caa67c225b99021dbd588034

Python 实现为随机数长度 14 和 15 返回相同的密文/标签,而 BC/Kotlin 的结果不同。这表明 Python 实现中存在错误。

遗憾的是,RFC 7253, Appendix A. Sample Results只提供了nonce大小均为12字节的测试 vector ,所以无法更明确地分配bug。

即如果您使用 15 字节的随机数,则两种实现都不兼容,问题很可能是由 Python 实现引起的。


编辑

链接示例分析:

您受到这两个错误的影响。根据您的评论,您已经修复了 Kotlin 代码中的错误(密文和标签的串联)。由于您在示例中使用的随机数是 15 个字节(knQgYf1MsOs8smx9GtWM 对应于 Base64 解码为 0x92742061fd4cb0eb3cb26c7d1ad58c),我的答案第二部分中描述的错误是问题的原因。此错误不在您的代码中,而是在其中一个库中,很可能在 Python 库中。因此,您无法修复它(至少不能不付出更大的努力)。

解决方法

如上测试所示,15 字节长度的 Python 代码似乎简单地忽略了第 15 个字节,即如果您使用 14 字节的随机数 knQgYf1MsOs8smx9GtU= (0x92742061fd4cb0eb3cb26c7d1ad5 ) 在您的 Python 代码中,Python 代码返回与 15 字节随机数 knQgYf1MsOs8smx9GtWM 相同的密文和 相同 标签,这就是为什么也可以使用 14 字节的随机数进行解密:

key = "f009Cip5hM4Obbb6E2MT5npJBHlc82vD"
message_data = {"encryptedMessage":"XMQx/xbVVTbMdpMiTXVp5XPICm11Vw2pgALpVI0NgbdqLLmikhPuu9M+qQzyOVZlZZBRlscijpyAZDsLGcTSPP54O35oKNp//PuOrWsN/ZZMkCByKCSBysJLRiZV1OjZDg01gi5/nYNbUgGGd8uRGKfBaKjjXngZ1J89GOvDeWPQcjbfbdzd9w+jbZGZ5jnAIChOL1Uqohf+6KHtjR/H06fFTHwB1abzAQrGbCNBNXBmN9+zEu7Auy3NPWKrZ+SL5Nk=","tag":"ZcqXSBqYU5TjgdMC+bMeUQ==","nonce":"knQgYf1MsOs8smx9GtU="}

decrypted_message = decrypt_aes_message (key, message_data)
print (decrypted_message) # https://app.passiv.com/snapTrade/redeemToken?token=v9uJsXYsi%2B6s9kyohisc6DFntJ/yD6m/2zhmO5xp6Vmezcyi8nwx63YtkqnnaogZvFmqs7L99EtZ0mxN9mAQTNoThHj3GaypXXUdiQIzig%3D%3D&clientId=SCANZ&broker=ALPACA

如果在Kotlin代码中使用这个14字节的nonce knQgYf1MsOs8smx9GtU=,同样解密成功。这是此特定示例的解决方法!

一般的解决方法,只要库错误没有被修复,就是不使用 15 字节的随机数,而是最多只使用 14 字节的随机数!


this website 上还有列出了 15 字节随机数的测试 vector 。正如预期的那样,PyCryptodome 实现不满足这些要求。我已经基于 15 字节随机数测试 vector 之一在 PyCryptodome 上提交了一个问题:Issue #664

关于java - Bouncy CaSTLe 在尝试解密 AES 消息时抛出 "mac check in OCB failed",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73557729/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com