TLS 1.2 中更改密码规范时出现“解密错误”的问题,但正确读取 MAC。

13

我正在尝试将一个老的TLS 1.0实现(不是我编写的)更新以支持TLS 1.2。

作为第一步,我集成了TLS 1.1中的更改,将明文初始化向量放入记录。没有问题,看起来工作正常,我可以在TLS 1.1下读取https://example.com以及SSL Labs viewMyClient.html

然后我适应了TLS 1.2中的伪随机函数更改,使用(对于大多数实际目的)P_SHA256代替(更复杂和奇怪的)半MD5 / SHA1混杂。第一次做错了,得到了一个无效的MAC错误,但这基本上是我的一个打字错误,我修复了它。然后无效的MAC错误就消失了。

但是尽管如此,在发送ClientKeyExchange->ChangeCipherSpec消息之后,我从服务器那里收到了一个“解密错误”的返回消息(无论我尝试什么,都是相同的Alert,https://google.com或其他任何内容)。我了解到ChangeCipherSpec消息仅加密一个字节,将其放入具有填充和MAC等内容的消息中。

如果我随机调整MAC一个字节,它会再次抱怨无效的MAC。 令我困惑的是MAC本身作为GenericBlockCipher的一部分进行了加密:

struct {
    opaque IV[SecurityParameters.record_iv_length];
    block-ciphered struct {
        opaque content[TLSCompressed.length];
        opaque MAC[SecurityParameters.mac_length]; // <-- server reads this fine!
        uint8 padding[GenericBlockCipher.padding_length];
        uint8 padding_length;
    };
} GenericBlockCipher;

更新: 顺便说一下,我已经添加了一个Wireshark记录文件,记录了1.2版本的https://example.com失败读取情况,以及一个运行相同代码(不包括P_SHA256 MAC更新)的成功的1.1版本会话的日志:

http://hostilefork.com/media/shared/stackoverflow/example-com-tls-1.2.pcapng (失败) http://hostilefork.com/media/shared/stackoverflow/example-com-tls-1.1.pcapng (成功)

那么,到底是什么导致了解密出现问题呢?填充似乎是正确的,如果对字节进行加减1操作,就会收到无效MAC错误提示。(规范中指出,“接收方必须检查这个填充,并使用bad_record_mac警报来指示填充错误”,因此这是可以预料到的。)如果我在从加密过程中使用的消息中损坏了客户端iv(只需将传输版本中的一个错误的字节放入即可),这样做也会导致Bad Record MAC。我预计这也将破坏解密过程。

因此,我对问题感到困惑:

  • 服务器演示了对有效MAC和无效MAC的区分,因此它必须解密成功。 它如何获得正确的MAC并出现解密错误?
  • 密码套件是一个旧版本(TLS_RSA_WITH_AES_256_CBC_SHA),但我只是逐步解决一个问题......如果我没有弄错,这不应该有影响。

有没有经验丰富的人有关于TLS 1.2如何使本来在TLS 1.1中工作的代码失败的理论? (也许是那些已经对代码库进行过类似更新并不得不更改更多而不仅仅是我所做的两个事情的人?) 我是否忽略了其他重要的技术变化?我有什么方法可以找出是什么让服务器不开心?


嗯...只是猜测,但你是否使用了正确的填充类型?如果使用了错误的填充,解密时会出现错误,但解密后的数据是正确的。MAC 将进行验证,但解密会创建错误。 - Afshin
1
刚刚检查了一下,填充模式本身并没有改变。但是“SHOULD”的术语已经改为“MUST”。在TLS 1.0的情况下,没有规则可以在填充不正确时返回错误。你从TLS 1.0开始,所以中间可能有些东西丢失了。但总的来说,这只是一个猜测。请参考https://www.ietf.org/rfc/rfc4346.txt和https://tools.ietf.org/html/rfc5246。 - Afshin
无论如何,填充机制很简单。因此,您可以快速检查它。 - Afshin
顺便提一下,如果您将IV错误地发送到服务器端,可能会遇到同样的问题。因为您正在使用CBC加密模式,如果发送了不正确的IV,则只有第一个块会出现错误。因此,在解密时,您将拥有正确的MAC(假设内容超过16个字节)。 - Afshin
1
CBC模式下,不正确的IV仅导致第1个块的解密错误。虽然奇怪的是它在1.1版本中能够工作但在1.2中不能,但没有调试信息,我只能想到这2个问题。 - Afshin
显示剩余2条评论
2个回答

4
实际上,ChangeCipherSpec消息并没有任何问题。问题实际上出在Finished消息上。它抱怨该消息中解密的verify_data与预期哈希不匹配(尽管加密/解密本身是正确的)。
但是,在Wireshark日志中令人困惑的是,Finished消息出现在同一日志行上,但使用"EncryptedHandshakeMessage"名称。这使其看起来像是描述ChangeCipherSpec的某种标签或标识,但实际上不是。该消息实际上根本没有被加密。

来自第二个链接:

实际上,您将看到未加密的客户端Hello、服务器Hello、证书、服务器密钥交换、证书请求、证书验证和客户端密钥交换消息。由于Finished握手消息发生在Change Cipher Spec消息之后,因此它是加密的。


希望有经验更新TLS 1.0或1.1到1.2的人,可能由于仅更改了P_SHA256 MAC并增加版本号而导致类似问题,他们只提到了RFC 5246中"从TLS 1.1更改"部分的三个位置中的两个:
- 伪随机函数(PRF)中的MD5 / SHA-1组合已替换为特定于密码套件的PRF。本文档中的所有密码套件均使用P_SHA256。 - 数字签名元素中的MD5 / SHA-1组合已替换为单个哈希值。签名元素现在包括一个明确指定所使用哈希算法的字段。
(注:第二个适用于证书,如果您还没有进行证书检查,则尚未到达该点。)
在那个部分没有提到的是MD5/SHA-1组合发生变化的第三个位置,这是种子中使用的哈希值,用于“Finished”消息的“verify_data”。然而,这一点也是从TLS 1.1开始发生变化的,在文档的7.4.9节中更详细地描述:
“哈希表示握手消息的哈希值。对于第5节定义的PRF,哈希必须是作为PRF基础使用的哈希。任何定义不同PRF的密码套件也必须定义用于完成计算的哈希。”
对于正式规范,他们有些含糊不清,“作为PRF基础使用的哈希”(是HMAC还是普通哈希?),但它是普通哈希。因此,除非密码套件的规范另有规定,否则为SHA256。
(还要注意的是,密码套件可以指定verify_data的长度超过12个字节,尽管规范中未提及任何这样的情况。)

"我有什么办法可以找出是什么让服务器不开心?"

因人而异。但我所做的就是构建OpenSSL作为静态调试库,并将其链接到简单的服务器上。然后我添加了断点和插装来查看它的问题所在。(由于某些原因,GDB无法进入共享库。)

大约在2018年9月30日,在一个普通的Linux机器上:

  • git://git.openssl.org/openssl.git
  • ./config no-shared no-asm -g3 -O0 -fno-omit-frame-pointer -fno-inline-functions no-ssl2 no-ssl3
  • make

我使用的简单服务器来自Simple TLS Server。使用以下命令编译静态库:

  • gcc -g -O0 simple.c -o simple -lssl -lcrypto -ldl -lpthread
我按照这里生成证书的说明进行操作,但将AAs更改为localhost

使用CA签署https_client证书的openSSL

然后我在简单服务器代码中更改了cert.pem => rootCA.pemkey.pem => rootCA.key。 我成功完成了:

wget https://localhost:4433 --no-check-certificate

然后成功地获得test作为响应。所以接下来只需找到我的客户端导致失败的原因。


2
优秀的知识分享经验的示例! - giuliolunati

1
我可以想到两种不同的情况会导致这个问题:
  1. 发送了错误的 IVIV 只影响解密 CBC 模式中的第一个块,因此如果您的内容超过16字节(AES 块大小),则您数据的 MAC 部分将被正确解密。
  2. 如果您使用了错误的填充结构,则在解密时可能会出现错误(因为填充验证失败),但内容将被正确解密。

看起来填充是正确的...如果我改变它,我会得到一个无效的MAC错误(它说“接收方必须检查此填充,并使用bad_record_mac警报指示填充错误。”,所以这是可以预料的)。我在消息中损坏了客户端IV(只是在传输版本中放入了一个错误的字节),这样做也会给我带来Bad Record MAC,这正是我所期望的。:-/ Grrr. - HostileFork says dont trust SE
感谢你的帮助。我已经为这个问题设置了赏金,但由于我自己解决了它,所以你因为是一个好人而获得了这些分数。偶尔会有回报 :-) - HostileFork says dont trust SE
@HostileFork 哈哈,谢谢,我突然惊讶地看到了一大堆积分:D 不管怎样,我想知道问题是什么,与我的回复有关吗? - Afshin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接