问题描述
我正在尝试更新应用程序的 TcpClient
,以将TLS与 SslStream
一起使用,而不是常规的 Stream
(我正在使用的代码)因为这似乎在Unity之外可行,但在集成到我的Unity 2019.1.8(也已在2018年和2017年测试)项目中失败.
I'm trying to update my application's TcpClient
to use TLS with SslStream
instead of the normal Stream
, the code i'm using for this seems to work outside of Unity, but fails when integrated in my Unity 2019.1.8 (tested on 2018 and 2017 as well) project.
要建立连接并打开新的 SslStream
,请使用以下代码:
To establish a connection and open a new SslStream
I use the following code:
public static void InitClient(string hostName, int port, string certificateName)
{
client = new TcpClient(hostName, port);
if (client.Client.Connected)
{
Debug.LogFormat("Client connected succesfully");
}
else
{
Debug.LogErrorFormat("Client couldn't connect");
return;
}
stream = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
try
{
stream.AuthenticateAsClient(certificateName);
}
catch (AuthenticationException e)
{
Debug.LogErrorFormat("Error authenticating: {0}", e);
if (e.InnerException != null)
{
Debug.LogErrorFormat("Inner exception: {0}", e);
}
Debug.LogErrorFormat("Authentication failed - closing connection");
stream.Close();
client.Close();
}
}
用于验证证书
public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Debug.LogErrorFormat("Certificate error: {0}", sslPolicyErrors);
return false;
}
在Unity 2019.1.8中,客户端连接并尝试验证远程证书,该证书失败并显示错误 TlsException:握手失败-错误代码:UNITYTLS_INTERNAL_ERROR,验证结果:UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED.
In Unity 2019.1.8 the client connects and will attempt to validate the remote certificate, which fails with the error TlsException: Handshake failed - error code: UNITYTLS_INTERNAL_ERROR, verify result: UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED.
使 ValidateServerCertificate
始终返回 true
,让我的客户端连接没有问题.
Making ValidateServerCertificate
always return true
lets my client connect without issue.
我尝试使用完全相同的代码在针对.net Framework 4.7.1的独立C#控制台应用程序中复制问题.在此应用程序中启动客户端将通过 sslPolicyErrors == SslPolicyErrors.None
检查从 ValidateServerCertificate
返回 true
.
I tried replicating the issue in a standalone C# Console Application targeting .net framework 4.7.1 using the exact same code. Launching the client in this application will return true
from ValidateServerCertificate
from the sslPolicyErrors == SslPolicyErrors.None
check.
我知道证书是由受信任的CA颁发的有效证书(已通过控制台应用程序接受该证书,并且该证书在浏览器中具有挂锁的事实证明了这一点.)
I know that the certificate is a valid cert, issued by a trusted CA (as verified by the fact that the cert is accepted from a console app, and it having a padlock in browsers).
为什么验证在Unity中失败,但在其他地方没有?
Why does the validation fail in Unity, but nowhere else?
推荐答案
为什么Unity无法验证选项1:
尽管该证书有效且正确(例如,在Web浏览器中使用时有效),但它不包括到根CA的中间证书链.结果是无法形成信任链(Unity不缓存/检索中间对象),从而导致设置了 UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED
标志.
Why Unity can't validate option 1:
Although the certificate is valid and correct (e.g. it works when using it in a web browser), it did not include the intermediate certificates chain to the root CA. This has as a result that no chain of trust can be formed (Unity doesn't cache/retrieve intermediates), resulting in the UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED
flag being set.
要解决此问题,我需要将证书链附加到我的叶子证书上,以便Unity可以验证整个链,直到根CA.要为您的证书找到证书链,可以使用"TLS证书链组成器".
To fix this I needed to append the certificate chain to my leaf certificate so that Unity can verify the entire chain up to the root CA. To find the certificate chain for your certificate you can use a "TLS certificate chain composer".
根据所使用的软件,您可能需要在证书中包含该链,或者将其保存在单独的文件中.来自 whatsmychaincert (我绝不隶属于本网站,仅使用它):
Depending on what software you use you may either need to include the chain in your certificate, or keep it in a seperate file. From whatsmychaincert (i'm in no way affiliated to this site, merely used it):
为什么Unity无法验证选项2:
当您的服务器的SSL证书过期时,Unity将抛出完全相同的 UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED
错误,而无需提供证书已过期的附加信息,因此请确保有效期"日期是将来的日期(这也会导致证书在Web浏览器中使用时被拒绝).
Why Unity can't validate option 2:
When your server's SSL certificate has expired Unity will throw the exact same UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED
error without additional information that the certificate has been expired, so make sure the "Valid to" date is in the future (this would also cause the certificate to be denied when used in a web browser).
为什么浏览器/控制台应用程序 可以验证:
对于如何处理不完整的链,软件可以有不同的实现方式.它可能会抛出错误,指出链已断开,因此无法信任(如Unity那样),或者缓存并保存中间物以供以后使用/从以前的会话中检索(如浏览器和Microsoft的.net).(核心)).
如此答案(强调我的答案)
Why a browser/Console app can validate:
Software can have different implementation on how it deals with incomplete chains. It can either throw an error, stating that the chain is broken and can thus not be trusted (as is the case with Unity), or cache and save the intermediates for later use/retrieve it from previous sessions (as browsers and Microsoft's .net(core) do).
As explained in this answer (emphasis mine)
如[发行问题1115214的答案] [3]中所述,由于跨平台的兼容性,Unity故意在不提供任何链的情况下不信任证书:
Not trusting the certificate when no chain is provided is a deliberate decision made by Unity due to cross-platform compatability, as stated in an [answer to issue 1115214][3]:
在问这个问题时,我发现这是针对我的特殊情况的解决方案,因此决定自行回答以供将来参考.但是 UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED
可能有多种原因,而这只是其中一种.
I figured out this was a solution to my particular situation while asking this question, so decided to self-answer it for future references. However UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED
can have all kind of causes, this just being one of them.
这篇关于Unity TlsException:握手失败UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!