TLS 1.2 change-cipher-spec的"解密错误",但正确读取MAC

Hos*_*ork 12 encryption ssl tls1.2

我正在尝试将最新的TLS 1.0实现(我没写过)更新为TLS 1.2.

作为第一步,我集成了将明文初始化向量放入记录的TLS 1.1更改.那不是问题.它似乎工作得很好,我可以阅读https://example.comTLS 1.1,以及SSL Labs viewMyClient.html.

然后我适应了伪随机函数的TLS 1.2更改(对于大多数实际用途)P_SHA256而不是(更复杂和奇怪的)一半和一半MD5/SHA1 rigamarole.我第一次做错了,得到了一个无效的MAC错误,但这或多或少是我的一个错字,我修复了它.然后无效的MAC错误消失了.

但尽管如此,在发送ClientKeyExchange-> ChangeCipherSpec消息后,我从服务器返回"解密错误" (无论是相同的警报,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;
Run Code Online (Sandbox Code Playgroud)

更新: FWIW,我添加了一个失败的1.2读取的Wireshark日志https://example.com,以及一个运行1.1会话的日志,运行相同的代码,不包括P_SHA256 MAC更新:

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或减1一样,我得到了无效的MAC错误. (规范说"接收方必须检查这个填充并且必须使用bad_record_mac警告来指示填充错误.",这是可以预期的.) 如果我在消息中损坏客户端-iv来自我以前加密的内容(只是在发送的版本中放入一个坏字节),这样做也给了我错误记录MAC.我希望破坏解密也是如此.

所以我对可能出现的问题感到疑惑:

  • 服务器演示了对有效MAC的辨别力,因此它必须已经解密.如何获得正确的MAC - 并且有解密错误?
  • 密码套件是一个旧套件(TLS_RSA_WITH_AES_256_CBC_SHA),但我只是一次解决一个问题......如果我没有弄错,那应该不重要.

有相关经验的人是否有理论知道TLS 1.2如何在TLS 1.1中起作用的代码? (也许有人对代码库进行了类似的更新,并且不得不改变我改变的两件事以使其工作?) 我是否错过了另一项重要的技术变革?我有什么办法可以找出让服务器不开心的原因?

Hos*_*ork 4

其实这条消息并没有什么问题ChangeCipherSpec。实际上是Finished消息有问题。它抱怨verify_data该消息内的解密内容与预期的哈希值不匹配(尽管加密/解密本身是正确的)。

但 Wireshark 日志中令人困惑的是,该Finished消息显示在同一日志行上,但名称为“ EncryptedHandshakeMessage ,这使得它看起来像是某种描述 ChangeCipherSpec 的标记或标签,但事实并非如此。该消息实际上根本没有加密。

来自第二个链接:

实际上,您将看到未加密的客户端问候、服务器问候、证书、服务器密钥交换、证书请求、证书验证和客户端密钥交换消息。完成的握手消息是加密的,因为它发生在更改密码规范消息之后。


“希望有人有将 TLS 1.0 或 1.1 更新到 1.2 的经验,并且可能已经看到过类似的问题,因为没有更改超过 P_SHA256 MAC 并增加了版本号”

他们只提到了 RFC 5246 的“TLS 1.1 的更改”部分中需要更新 MD5/SHA1 组合的三个位置中的两个:

  • 伪随机函数 (PRF) 中的 MD5/SHA-1 组合已替换为密码套件指定的 PRF。本文档中的所有密码套件均使用 P_SHA256。

  • 数字签名元素中的 MD5/SHA-1 组合已替换为单个哈希值。签名元素现在包含一个明确指定所使用的哈希算法的字段。

(注意:第二个适用于证书,如果您还没有进行证书检查,那么您还没有进行到这一点。)

他们在该部分中没有提到的是MD5/SHA-1 组合更改的第三个verify_data位置,这是消息种子中使用的哈希值Finished然而,这一点也是 TLS 1.1 的一个变化,在文档第 7.4.9 节中进一步描述:

“哈希表示握手消息的哈希。对于第 5 节中定义的 PRF,哈希必须是用作 PRF 基础的哈希。任何定义不同 PRF 的密码套件也必须定义在完成后使用的哈希。计算。”

对于正式规范,他们对“用作 PRF 基础的哈希”有点模糊(是 HMAC 还是只是普通哈希?)但它是普通哈希。所以 SHA256,除非密码套件的规范另有说明。

(另请注意,密码套件可以将 verify_data 的长度规定为超过 12 个字节,尽管规范中没有提到这样做。)


“我必须采取什么措施才能找出是什么让服务器不满意?”

YMMV。但我所做的只是将 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

我按照此处生成证书的说明进行操作,但将 AA 更改为localhost

openSSL 使用 CA 签署 https_client 证书

然后我更改了简单服务器代码中的cert.pem => rootCA.pem和。key.pem => rootCA.key我能够做到:

wget https://localhost:4433 --no-check-certificate
Run Code Online (Sandbox Code Playgroud)

并成功得到test回复。所以接下来的问题就是看看我的客户在哪里造成了失败。

  • 分享通过经验获得的知识的绝佳例子! (2认同)