我正在尝试使用旧的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。我希望这也会破坏解密。
所以我很困惑可能是什么问题:
具有相关经验的人是否有理论认为TLS 1.2可以如何将代码插入可以在TLS 1.1中运行的代码? (也许有人对代码库进行了类似的更新,并必须进行更多的更改,才能使其正常工作?)我是否错过了另一个关键的技术更改?我必须采取什么措施才能找出导致服务器不满意的原因?
答案 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
然后,我在简单的服务器代码中更改了localhost
和cert.pem => rootCA.pem
。我能够做到:
key.pem => rootCA.key
并成功获得wget https://localhost:4433 --no-check-certificate
作为回应。因此,仅需查看我的客户在哪里造成了故障。
答案 1 :(得分:1)
我可以想到两种导致这种问题的情况:
IV
。 IV
仅影响CBC
模式解密中的第一个块,因此,如果您的内容超过16个字节(AES
块大小),则MAC
部分数据将被解密正确。