ssl - TLS 1.2 change-cipher-spec 的 "decrypt error",但正确读取 MAC

标签 ssl encryption tls1.2

我正在尝试更新旧的 TLS 1.0 实现(不是我写的)以支持 TLS 1.2。

作为第一步,我集成了 putting the plaintext initialization vector in the record 的 TLS 1.1 更改.那没问题。它似乎工作得很好,我可以阅读 TLS 1.1 中的 https://example.com,以及 SSL Labs viewMyClient.html .

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

但尽管如此,在发送 ClientKeyExchange->ChangeCipherSpec 消息后,我从服务器返回了一个“解密错误”(不管怎样,都是相同的警报,https://google.com 或我尝试的任何东西)。我收集到 ChangeCipherSpec 消息仅加密一个字节,将其放入带有填充和 MAC 等的消息中。

如果我随机调整 MAC 一个字节,它会返回到提示无效 MAC。 令我困惑的是the MAC itself is encrypted as part of 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;

UPDATE: FWIW, I've added a Wireshark log of the failing 1.2 read of https://example.com, as well as a log of a functioning 1.1 session running what is the same code, not counting the P_SHA256 MAC update:

http://hostilefork.com/media/shared/stackoverflow/example-com-tls-1.2.pcapng (fails) http://hostilefork.com/media/shared/stackoverflow/example-com-tls-1.1.pcapng (succeeds)

那么,究竟是什么原因造成解密困难?填充看起来是正确的,就好像在字节上加或减 1 我得到一个无效的 MAC 错误。 (规范说“接收方必须检查此填充并且必须使用 bad_record_mac 警报来指示填充错误。”,所以这是预料之中的。) 如果我在来自的消息中破坏了 client-iv我以前加密的内容(只是在传输版本中放了一个坏字节),这样做也会给我坏记录 MAC。我希望这也会破坏解密。

所以我对可能是什么问题感到困惑:

  • 服务器展示了对有效 MAC 与无效 MAC 的辨别力,因此它必须已经解密。它如何获得正确的 MAC - 并且 - 出现解密错误?
  • 密码套件是旧的 (TLS_RSA_WITH_AES_256_CBC_SHA),但我一次只处理一个问题......如果我没记错的话,那应该没关系。

有相关经验的人是否有关于 TLS 1.2 如何破坏 TLS 1.1 中正常运行的代码的理论? (也许有人对代码库进行了类似的更新,并且必须改变比我改变的两件事更多的东西才能让它工作?)我是否错过了另一个关键的技术变化?我有什么办法可以找出导致服务器不愉快的原因?

最佳答案

ChangeCipherSpec 消息实际上没有任何问题。它实际上是 Finished message那有问题。它提示该消息中已解密的 verify_data,它与预期的哈希值不匹配(尽管加密/解密本身是正确的)。

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

来自第二个链接:

In practice, you will see unencrypted Client Hello, Server Hello, Certificate, Server Key Exchange, Certificate Request, Certificate Verify and Client Key Exchange messages. The Finished handshake message is encrypted since it occurs after the Change Cipher Spec message.


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

"changes from TLS 1.1" section 中,他们只提到了您需要更新 MD5/SHA1 组合的三个地方中的两个。 RFC 5246 的:

  • The MD5/SHA-1 combination in the pseudorandom function (PRF) has been replaced with cipher-suite-specified PRFs. All cipher suites in this document use P_SHA256.

  • The MD5/SHA-1 combination in the digitally-signed element has been replaced with a single hash. Signed elements now include a field that explicitly specifies the hash algorithm used.

(注意:第二个适用于证书,如果您还没有进行证书检查,您就不会到那个时候。)

他们在那个部分没有提到的是 第三个地方的 MD5/SHA-1 组合变化,这是在 verify_data 的种子中使用的散列Finished 消息。然而,这一点也是 TLS 1.1 的一个变化,在 section 7.4.9 中的文档中进行了更深入的描述。 :

"Hash denotes a Hash of the handshake messages. For the PRF defined in Section 5, the Hash MUST be the Hash used as the basis for the PRF. Any cipher suite which defines a different PRF MUST also define the Hash to use in the Finished computation."

对于正式规范,他们在“用作 PRF 基础的散列”(是 HMAC 还是纯散列?)方面有点含糊,但它是纯散列。所以 SHA256,除非密码套件的规范另有说明。

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


“我有什么办法可以找出服务器不满意的原因?”

YMMV。但我所做的只是将 OpenSSL 构建为静态调试库,并将其链接到一个简单的服务器。然后我添加了断点和检测以查看它对什么感到不安。 ( GDB wasn't letting me step into the shared library, for some reason .)

大约 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
  • 制作

我使用的简单服务器来自Simple TLS Server .编译静态库:

  • gcc -g -O0 simple.c -o simple -lssl -lcrypto -ldl -lpthread

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

openSSL sign https_client certificate with CA

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

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

并成功返回 test 作为响应。因此,接下来只需看看我的客户在哪里导致失败。

关于ssl - TLS 1.2 change-cipher-spec 的 "decrypt error",但正确读取 MAC,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52539066/

相关文章:

ssl - jboss 服务器中的自签名 CA 避免 SEC_ERROR_UNKNOWN_ISSUER

amazon-web-services - 如何排查 AWS 目标组中的不健康状态代码

php - 加密数字 URL 参数,结果不能比原来长

c# - 向使用 SocketAsyncEventArgs 构建的服务器添加加密

node.js - 在 Node JS TLS 中将密码从 SSLv3 (DHE-RSA-AES256-SHA) 添加/启用到 TLS 1.2

web-services - HTTP 错误代码 : 302 when calling https webservice

ssl - crypt-protocol 值为 '-' 的 IIS 日志表示什么?

Python 凯撒密码更改给定输入字符串的大小写

java - AES 256 位加密

java - openJdk 升级到 8u292 会破坏我的 aosp 构建系统吗?