EAPOL 802.1X上的TLS握手
我正在建立一个EAP-TLS身份验证客户端(802.1x EAPOL)。到目前为止,要求只是EAP-TL。我正在使用Freeradius Server进行测试,并且它正在使用TLS 1.1,因此这是我开发的传输版本。
因为这个求职者使用的是自定义的网络堆栈,并且在一个小的嵌入式设备上,我无法使用openssl libs,因为它们将所有握手都用作BlackBox插座级别进行通信。另外,我发现的所有恳求者都包含与AAA和身份验证者密切相互交织的代码。我没有太多的空间来添加所有这些来源(除了使支持更加困难之外),
无论如何都可以学习。
因此,当我挖掘时,我看到与RFC不一致或根本不定义的事物。
在询问有关试图“滚动我自己的”问题的WPA-Supplicant邮寄问题之前,我首先想礼貌地问“这是一个问技术问题的好地方,还是还有其他资源”。我被礼貌地忽略了。所以我在这里发布。
咨询RFC 3579、3748、4346、5216等,我对服务器进行了MD5挑战身份验证。在理解EAP,以太网数据包,片段等方面的成功
,我已经成功接收,组装并解析了TLS Server Hello Herthake。 (RFC 5216仅在EAP上定义了TLS标头,而RFC 4346解释了完整的TLS握手,但是EAP使用了它的子集。)由于我可以访问测试服务器证书和钥匙,所以我还验证了使用Premaster Secret secret ofer of Test Sertial Secre公钥,并用私钥正确地解密。
现在,我正在尝试逐步建立完整的客户握手,并在消息中添加块。并找到我无法解决的事情。
下面,我指的是RFC 4346,以获取以下TLS 1.1消息。
在第4.3节中,向量用特定的“演示语言”定义。使用[]用于固定已知长度,< ..>对于必须包含指示大小的领先值的变量长度。
第7.4.7节定义客户端密钥交换。就我而言,这只是一个RSA,因此是“ eNcryptedPremasterSecret”。第7.4.7.1节定义了RSA的EncryptedPremasterSecret,这是版本和随机数,总数为48个字节。
该定义对此没有任何主张是可变向量。但是,如果Freeradius的调试信息没有两个字节的主机订单值的长度值,则拒绝它。
(27) eap_tls: TLS-Client-Cert-X509v3-Basic-Constraints += "CA:FALSE"
(27) eap_tls: TLS_accept: SSLv3/TLS read client certificate
(27) eap_tls: <<< recv TLS 1.0 Handshake [length 0104], ClientKeyExchange
(27) eap_tls: >>> send TLS 1.0 Alert [length 0002], fatal decode_error
(27) eap_tls: ERROR: TLS Alert write:fatal:decode error
tls: TLS_accept: Error in error
(27) eap_tls: ERROR: Failed in __FUNCTION__ (SSL_read): error:1419F09F:SSL routines:tls_process_cke_rsa:length mismatch
(27) eap_tls: ERROR: System call (I/O) error (-1)
(27) eap_tls: ERROR: TLS receive handshake failed during operation
(27) eap_tls: ERROR: [eaptls process] = fail
有趣的是,Wireshark似乎不介意是否缺少。
通过添加两个字节长度,我已经过去了这个故障。但是,我不喜欢它不遵循我阅读的规范。
这是我缺少的其他地方吗?,
所以我似乎已经越过了前销钉,并转到了证书验证消息。至于它,第7.4.8节定义了包含MD5和SHA哈希的证书验证,指的是7.4.3节。 7.4.3中的定义定义了一个“签名”是什么,并且对此是可变向量没有任何主张。
实际上,第7.4.3节非常清楚地表明它是已知的长度向量(IE使用固定长度[16]和[20])。然而,Wireshark期望在这里也有两个字节标头,如果不存在,则报告错误。
所以我添加了两个字节标头,Wireshark很高兴。
但这仍然没有遵循规范。已知的最大长度为36个字节,适合一个8位。因此,需要两个字节违反了第4.3节中的规范:
The length will be in the form of a number consuming as many bytes as required to hold the vector’s specified maximum (ceiling) length.
但是,即使进行了更改,服务器仍在抱怨。
(13) eap_tls: TLS-Client-Cert-X509v3-Basic-Constraints += "CA:FALSE"
(13) eap_tls: TLS_accept: SSLv3/TLS read client certificate
(13) eap_tls: <<< recv TLS 1.0 Handshake [length 0106], ClientKeyExchange
(13) eap_tls: TLS_accept: SSLv3/TLS read client key exchange
(13) eap_tls: <<< recv TLS 1.0 Handshake [length 002a], CertificateVerify
(13) eap_tls: >>> send TLS 1.0 Alert [length 0002], fatal decrypt_error
(13) eap_tls: ERROR: TLS Alert write:fatal:decrypt error
tls: TLS_accept: Error in error
(13) eap_tls: ERROR: Failed in __FUNCTION__ (SSL_read)
(13) eap_tls: ERROR: error:04091077:rsa routines:int_rsa_verify:wrong signature length
(13) eap_tls: ERROR: error:1417B07B:SSL routines:tls_process_cert_verify:bad signature
服务器说“ Decrypt_error”。是否应该加密此验证消息?规格不这么说。 greppping服务器源,我在任何地方都找不到该短信。它被很好地隐藏了,很难找到拒绝它的功能。
如果应该加密,则使用什么键?客户端私钥还是服务器公钥?
再次,这是否描述了我缺少的其他地方?它没有在两个方面(使用可变长度,两个字节)遵循规范。
在第7.4.9节中,完成的消息是使用包含“ [0..11]”的演示语言定义的,第4节中未在任何地方定义描述。这是错别字的,旨在是可变的长度向量&lt; 0 .. 11&gt;?还是[0..11]在这里意味着什么?
下一个主要问题:
我是否使它变得太难了?
是否有openssl呼叫,只需重新组装后的TLS握手,创建客户端握手回复,将其填充到提供的缓冲区中吗?同样,由于嵌入式设备上的求职客户端使用其自己的网络堆栈,所以我无法将OpenSSL的内部套接字调用进行握手。
在许多领域缺少OpenSSL文档,如果存在这样的API,我没有偶然发现它。
感谢您的任何答案和建议。
-Scott
I am building a EAP-TLS authentication client (802.1X EAPOL). The requirement thus far is just EAP-TLS. I am using the FreeRadius server to test against, and it is using TLS 1.1, so that is the transport version I'm developing to.
Because this supplicant is using a network stack that is custom, and on a small embedded device, I cannot use the OpenSSL libs, since they do all the handshaking as blackbox socket level for communication. Also, the supplicants I found all contain code that is strongly intertwined with the AAA and Authenticator. I don't have that much space to add all that source (in addition to making support more difficult)
It's good to learn while rolling your own anyway.
So, as I dig in, I am seeing things that are not consistent with the RFC's or simply not defined.
Before asking the WPA-Supplicant mailing questions about trying to "roll my own" I first wanted to politely ask simply "Is this a good place to ask technical questions, or is there another resource". I was politely ignored. So I am posting here.
Consulting RFC 3579, 3748, 4346, 5216 and others, I have performed MD5 challenge authentication to the server. Success with understanding EAP, Ethernet packets, fragments, etc.
On to TLS, I have successfully received, assembled and parsed the TLS Server Hello handshake. (RFC 5216 only defines a TLS header over EAP, while RFC 4346 explains the full TLS handshake, but EAP uses a subset of it.) Since I have access to the test server cert and key, I have also verified ciphering a premaster secret with the public key, and it deciphers correctly with the private key.
Now I am trying to build the full Client handshake, piece by piece, adding blocks to the message. And finding things that I cannot resolve.
Below, I am referring to RFC 4346 for the following TLS 1.1 messages.
In Section 4.3, vectors are defined with the specific "Presentation language". Using [] for fixed known lengths, and <..> for variables lengths that must contain a leading value indicating the size.
Section 7.4.7 defines the Client Key Exchange. In my case, it is simply an RSA, therefore is a "EncryptedPreMasterSecret". Section 7.4.7.1 defines the EncryptedPreMasterSecret for RSA, which is the version and random numbers, totaling 48 bytes in length.
The definition does not make any claim about this being a variable vector. And yet, the debug information from FreeRadius rejects it if it does not have a two byte host order value of the length.
(27) eap_tls: TLS-Client-Cert-X509v3-Basic-Constraints += "CA:FALSE"
(27) eap_tls: TLS_accept: SSLv3/TLS read client certificate
(27) eap_tls: <<< recv TLS 1.0 Handshake [length 0104], ClientKeyExchange
(27) eap_tls: >>> send TLS 1.0 Alert [length 0002], fatal decode_error
(27) eap_tls: ERROR: TLS Alert write:fatal:decode error
tls: TLS_accept: Error in error
(27) eap_tls: ERROR: Failed in __FUNCTION__ (SSL_read): error:1419F09F:SSL routines:tls_process_cke_rsa:length mismatch
(27) eap_tls: ERROR: System call (I/O) error (-1)
(27) eap_tls: ERROR: TLS receive handshake failed during operation
(27) eap_tls: ERROR: [eaptls process] = fail
Interestingly enough, Wireshark doesn't seem to mind if it's missing.
By adding the two byte length, I am past this failure. However, I don't like that it's not following the specification I read.
Is this described someplace else that I am missing?
So I seem to have gotten past the PremasterSecret, and have moved on to the Certificate Verify message. As for it, section 7.4.8 defines the certificate verify which contains the MD5 and SHA hashes, referring back to section 7.4.3. The definition in 7.4.3 defines what a "Signature" is, and does not make any claim about this being a variable vector.
In fact, section 7.4.3 very clearly indicates that is it a known length vector (i.e. uses fixed lengths [16] and [20]). And yet, Wireshark expects a two byte header here too and reports an error if it is not present.
So I added it the two byte header, Wireshark is happy.
But that is still not following the specification. The known max length is 36 bytes, which fits in one 8 bit number. So requiring two bytes violates the specification which says in section 4.3:
The length will be in the form of a number consuming as many bytes as required to hold the vector’s specified maximum (ceiling) length.
However even with that change, the server is still complaining.
(13) eap_tls: TLS-Client-Cert-X509v3-Basic-Constraints += "CA:FALSE"
(13) eap_tls: TLS_accept: SSLv3/TLS read client certificate
(13) eap_tls: <<< recv TLS 1.0 Handshake [length 0106], ClientKeyExchange
(13) eap_tls: TLS_accept: SSLv3/TLS read client key exchange
(13) eap_tls: <<< recv TLS 1.0 Handshake [length 002a], CertificateVerify
(13) eap_tls: >>> send TLS 1.0 Alert [length 0002], fatal decrypt_error
(13) eap_tls: ERROR: TLS Alert write:fatal:decrypt error
tls: TLS_accept: Error in error
(13) eap_tls: ERROR: Failed in __FUNCTION__ (SSL_read)
(13) eap_tls: ERROR: error:04091077:rsa routines:int_rsa_verify:wrong signature length
(13) eap_tls: ERROR: error:1417B07B:SSL routines:tls_process_cert_verify:bad signature
The server says "decrypt_error". Is this verification message supposed to be encrypted? The spec doesn't say so. Grepping the server source, I cannot find that text message anywhere. It's been hidden very well, making it difficult to find the function that is rejecting it.
And if it is supposed to be encrypted, what key is used? The client private key or the server public key?
Again, is this described someplace else that I am missing? It's not following the specification on two fronts (using a variable length, and two bytes where one is sufficient).
In section 7.4.9 the finished message is defined using the Presentation language containing "[0..11]", which description is not defined anywhere in section 4. Is it a typo meant to be a variable length vector <0..11>? Or what does [0..11] mean here?
Next major question:
Am I making this too hard?
Are there OpenSSL calls which will simply take the reassembled TLS handshake, and create the client handshake reply, populating it into a supplied buffer? Again, because the supplicant client on an embedded device uses it's own network stack, I can't use OpenSSL's internal socket call for the handshake.
The OpenSSL documentation is lacking in many areas, and if such a API exists I have not stumbled across it.
Thanks for any answers and advice.
-Scott
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
尽管我没有所有原始问题的答案,但我已经解决了这个原始问题。
具体来说,我将回答“我让我太难了”这个问题。这个答案是是。
事实证明,我正在寻找的OpenSSL API是 bio api,我学到的是一种有效的API,它允许您直接处理缓冲区,而不是使用OpenSSL插座进行通信。通过将您的代码插入发送接收电话之间,将其视为“中间人”。
显然,生物API也可以用于文件IO等其他事物。
就我而言,这是原始的以太网数据包,但假设您需要在没有TCP/IP支持的I2C线上执行此加密。在这种情况下,您将在流中读取缓冲区,将其复制到输入生物中,然后进行OpenSSL调用。然后将数据从输出生物中获取并发送。
这是一些应该解释所有内容的特定代码片段:
首先,标题。我只是扔了所有内容,因为我使用的是此处未显示的代码中的许多其他功能(OpenSSL页面提供了此信息,但是API DOC不会告诉您原型在哪里,非常让我烦人)。一些标题名称是有关其贡献的线索:
下面的这些代码片段包裹在班级中,并使用匈牙利铅“ m_”表示法表示它们是班级的成员。首先,您需要几个生物手柄,其中一个用于您收到的东西并将其传递到OpenSSL中,另一个用于OpenSSL的答复。似乎没有任何要求您提前知道缓冲区的大小。
声明BIOS
并初始化它们,
现在假设SSL已配置,CERT,CAS,KEYS等...全部加载并创建了上下文(此答案不是SSL调用的解释)
创建BIOS。进出是相同创建的。
现在很酷的部分。告诉OpenSSL,它将使用这些生物作为缓冲区,因此您可以将数据放入并取出。
这很重要:我发现完成后,您无需致电bio_free()。因为SSL已将它们连接的事实意味着它将为您释放它们。有点烦人的功能,没有告诉任何人。但是,我到处崩溃了,直到我弄清楚了。
以下假设您拥有数据,您会读取诸如原始数据包或I2C电线之类的内容。请注意,当您初始化TLS握手时,第一个消息尚不存在(TLS首先您向另一侧发送clienthello消息)。因此,请在第一个呼叫上将输入bio空为空,然后拨打ssl_connect(m_ssl)开始。
SSL_Connect()调用将导致故障。但是故障是错误:ssl_error_want_read,这仅表示它正在等待更多读取。因此,在呼叫后,请复制OpenSL在输出生物中放置的内容:
在第一个通过时,您将获得“ client_hello”消息。
因此,现在您将消息放在缓冲区中,将其发送到服务器,然后阅读服务器的回复。将其回复并将其存储在输入生物中,再次致电SSL_Connect(),然后读取Bio中的响应。只要继续前进,直到握手完成。
另外,如上所示,每次调用SSL_Connect时,您都必须刷新SSL句柄中堆积的所有错误:
对于我的EAPOL客户端,此过程非常简单。复杂的部分是处理来回走动的消息的碎片,因为某些TLS握手可以拥有许多证书并且很大。并且重要的是,将服务器的整个消息重新组装到将其放入生物中并调用下一个SSL_Connect。
我希望这有助于下一个人尝试同样的事情。
I have this original problem solved, although I don't have answers to all my original questions.
Specifically I will answer the question "Am I making this too hard". That answer is yes.
Turns out the OpenSSL api I am looking for is the BIO api, which I learned is effectively an API that allows you to handle the buffers directly, rather than using the OpenSSL sockets to communicate. Think of this as "man in the middle" by inserting your code in between the sending an receiving calls.
Apparently the BIO api can be used to other things like file IO as well.
In my case this was EAPOL which are raw ethernet packets, but suppose you need to perform some encryption like this TLS handshake over a I2C wire, which has no TCP/IP support. In this case, you would read in the stream into a buffer, copy it into an input BIO, and make the OpenSSL call. Then get the data out of the output BIO and send it.
Here are some specific code fragments which should explain everything:
First, headers. I just threw everything at it, because I am using many of the other features in the code not shown here (OpenSSL pages give this information, but API doc that doesn't tell you where the prototypes are is very annoying to me). Some of the header names are clues as to what they contribute:
These code fragments below are wrapped in a class, and use the Hungarian notation of a lead "m_" to indicate they are member of the class. First you need a couple of BIO handles, one for what you receive and are passing into OpenSSL, and one for what OpenSSL replies with. There does not appear to be any requirement that you know the buffer size ahead of time.
Declare the BIOS
And initialize them
Now assume SSL is configured, Cert, CAs, Keys, etc... are all loaded and a context has been created (this answer is not meant to be an explanation of SSL calls)
Create the BIOs. In and Out are created identically.
Now the cool part. Tell OpenSSL that it will use these BIO's as buffers, so you can put data in and take it out.
This is important: I discovered that when you are done, you do NOT need to call BIO_free(). Because the fact that SSL has connected them means it will free them for you. Kind of an annoying feature that's in there without telling anyone. But I was getting "multiple buffer freed" crashes all over the place until I figured that out.
The following assumes that you have data you read off something like the raw packet or I2C wire, or whatever. Note that when you are initialing a TLS handshake, the first message does not yet exist (TLS starts with you sending a ClientHello message to the other side). So leave the input BIO empty on the first call and make a call SSL_connect(m_ssl) to get started.
The SSL_connect() call WILL give a failure. But the failure is the error: SSL_ERROR_WANT_READ, which simply means that it is waiting for more to reads. So, right after the call, copy out what OpenSSL put in the output BIO:
On the first pass, you will have the beginning "CLIENT_HELLO" message.
So, now you have the message in your buffer, send it to the server, and read the reply from the server. Take that reply and store it in the input BIO again, call SSL_connect() again, and read the response out of the BIO. Just keep going back and forth until the handshake completes.
Also, as shown in the code fragment above, each time you call SSL_connect, you will have to flush out all the errors that are piled in the SSL handle:
For my EAPOL client, this process turned out to be quite simple. The complex part was handling the fragmentation of the messages that were going back and forth, because some TLS handshakes can have many certificates and be quite large. And it is important that the entire message from the server be reassembled before putting it into the BIO and calling the next SSL_connect.
I hope this helps the next person trying to so the same thing.