TLS 1.2更改密码规范的“解密错误”,但可以正确读取MAC

时间:2018-09-27 14:11:57

标签: 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实验室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;
  

更新:FWIW,我添加了Wireshark日志,其中包含https://example.com的1.2读取失败,以及正在运行运行相同代码的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警报来指示填充错误。”,这是可以预期的。)如果我在来自以下位置的消息中破坏了client-iv我以前加密的内容(只是在传输版本中放了一个坏字节),这样做还给了我不良记录MAC。我希望这也会破坏解密。

所以我很困惑可能是什么问题:

  • 服务器演示了有效MAC识别与否,因此必须已解密。如何获得正确的MAC并发生解密错误?
  • 密码套件是旧的(TLS_RSA_WITH_AES_256_CBC_SHA),但是我一次只解决一个问题...如果我没记错的话,那没关系。

具有相关经验的人是否有理论认为TLS 1.2可以如何将代码插入可以在TLS 1.1中运行的代码? (也许有人对代码库进行了类似的更新,并必须进行更多的更改,才能使其正常工作?)我是否错过了另一个关键的技术更改?我必须采取什么措施才能找出导致服务器不满意的原因?

2 个答案:

答案 0 :(得分:3)

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

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

从第二个链接:

  

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


“希望某人有将TLS 1.0或1.1更新到1.2的经历,并且可能由于未更改超过P_SHA256 MAC和更改版本号而遇到了类似的问题”

他们只提到了在RFC 5246的"changes from TLS 1.1" section中更新MD5 / SHA1组合所需的三个位置中的两个:

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

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

  •   

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

在该部分中他们没有提及的是MD5 / SHA-1组合更改的第三位置,该位置是{的verify_data {1}}消息。但是,这也与TLS 1.1有所不同,在section 7.4.9的文档中对此进行了更详细的介绍:

  

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

对于正式的规范,它们在“用作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计算机上:

  • Finished
  • 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。使用以下方法针对静态库进行编译:

  • make

我按照此处生成证书的说明进行操作,但是将AA更改为gcc -g -O0 simple.c -o simple -lssl -lcrypto -ldl -lpthread

openSSL sign https_client certificate with CA

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

key.pem => rootCA.key

并成功获得wget https://localhost:4433 --no-check-certificate 作为回应。因此,仅需查看我的客户在哪里造成了故障。

答案 1 :(得分:1)

我可以想到两种导致这种问题的情况:

  1. 发送错误的IVIV仅影响CBC模式解密中的第一个块,因此,如果您的内容超过16个字节(AES块大小),则MAC部分数据将被解密正确。
  2. 如果使用不正确的填充结构,则解密时可能会出错(因为填充验证失败),但是内容将被正确解密。