我正在使用.NET 4.5 c#连接到仅支持TLS 1.2的服务器。 (Apple推送通知服务器)。
我的代码在这里失败了:
sslStream.AuthenticateAsClient(server, myAppleCertificates, SslProtocols.Tls12, true);
我做了很多测试和谷歌搜索,发现了以下内容:
1)如果我使用10个窗口,此代码可以正常工作,但如果我在开发服务器或生产服务器上运行此代码则不行。 Windows Server 2008 R2。
2)所有的PC和服务器都在运行.net 4.6 +
3)我可以添加“ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;”这一行。 - 它仍然表现完全一样。
4)我可以在Windows 10上复制该问题,只需将其更改为: sslStream.AuthenticateAsClient(server,myAppleCertificates,SslProtocols。 Tls11 ,true);
5)我添加了以下注册表项并重新启动服务器将无效:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client]
"Enabled"=dword:00000001
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client]
"DisabledByDefault"=dword:00000000
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server]
"Enabled"=dword:00000001
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server]
"DisabledByDefault"=dword:00000000
5)服务器是最新的Windows更新。 (实际上......我不是100%在这一个)
6)IISCrypto表示协议存在,并正确设置为默认值。
7)我可以使用Chrome连接到APN,在我的电脑和服务器上都没有问题。
8)如果我使用Fiddler进行测试,在我在fiddler设置中启用TLS12设置后,它的行为与我的应用程序完全相同。我想这是因为Fiddler是在.net 4中构建的。
就好像即使你强制.NET使用TLS1.2一样,它根本就没有,并且还原为1.1。有没有人在这里有任何想法。
由于
这是我的测试代码:
public class SslTcpClient
{
private static Hashtable certificateErrors = new Hashtable();
public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None) return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
return false;
}
public static X509Certificate SelectLocalCertificate(object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
{
Console.WriteLine("Client is selecting a local certificate.");
if (acceptableIssuers != null && acceptableIssuers.Length > 0 && localCertificates != null && localCertificates.Count > 0)
{
// Use the first certificate that is from an acceptable issuer.
foreach (X509Certificate certificate in localCertificates)
{
string issuer = certificate.Issuer;
if (Array.IndexOf(acceptableIssuers, issuer) != -1) return certificate;
}
}
if (localCertificates != null && localCertificates.Count > 0) return localCertificates[0];
return null;
}
public static void RunClient(string server)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
TcpClient client = new TcpClient(server, 443);
Console.WriteLine("Client connected.");
SslStream sslStream = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
Console.WriteLine("SSL stream obtained");
try
{
X509Certificate localCertificate = new X509Certificate("VOIPPush.p12", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
Console.WriteLine("Local Certificte features");
Console.WriteLine("=========================");
Console.WriteLine("Issuer: " + localCertificate.Issuer);
Console.WriteLine("Subject: " + localCertificate.Subject);
Console.WriteLine("CertHash: " + localCertificate.GetCertHashString());
Console.WriteLine("EffectiveDate: " + localCertificate.GetEffectiveDateString());
Console.WriteLine("ExpirationDate: " + localCertificate.GetExpirationDateString());
Console.WriteLine("Format: " + localCertificate.GetFormat());
Console.WriteLine("HashCode: " + localCertificate.GetHashCode().ToString());
Console.WriteLine("KeyAlgorithm: " + localCertificate.GetKeyAlgorithm());
Console.WriteLine("KeyAlgorithmParameters: " + localCertificate.GetKeyAlgorithmParametersString());
Console.WriteLine("PublicKey: " + localCertificate.GetPublicKeyString());
Console.WriteLine("SerialNumber: " + localCertificate.GetSerialNumberString());
X509CertificateCollection localCertificates = new X509CertificateCollection();
localCertificates.Add(localCertificate);
sslStream.AuthenticateAsClient(server, localCertificates, SslProtocols.Tls12, true);
Console.WriteLine("Authenticated as client");
X509Certificate remoteCertificate = sslStream.RemoteCertificate;
Console.WriteLine("Remote Certificte features");
Console.WriteLine("==========================");
Console.WriteLine("Issuer: " + remoteCertificate.Issuer);
Console.WriteLine("Subject: " + remoteCertificate.Subject);
Console.WriteLine("CertHash: " + remoteCertificate.GetCertHashString());
Console.WriteLine("EffectiveDate: " + remoteCertificate.GetEffectiveDateString());
Console.WriteLine("ExpirationDate: " + remoteCertificate.GetExpirationDateString());
Console.WriteLine("Format: " + remoteCertificate.GetFormat());
Console.WriteLine("HashCode: " + remoteCertificate.GetHashCode().ToString());
Console.WriteLine("KeyAlgorithm: " + remoteCertificate.GetKeyAlgorithm());
Console.WriteLine("KeyAlgorithmParameters: " + remoteCertificate.GetKeyAlgorithmParametersString());
Console.WriteLine("PublicKey: " + remoteCertificate.GetPublicKeyString());
Console.WriteLine("SerialNumber: " + remoteCertificate.GetSerialNumberString());
}
catch (AuthenticationException e)
{
Console.WriteLine("Exception: {0}", e.ToString());
client.Close();
return;
}
byte[] messsage = Encoding.UTF8.GetBytes(@"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");
// no use in writing more, this is just a test...
Console.WriteLine("Writing to sslStream...");
sslStream.Write(messsage);
Console.WriteLine("Writing done");
sslStream.Flush();
Console.WriteLine("Reading Response...");
string serverMessage = ReadMessage(sslStream);
Console.WriteLine("Server says: {0}", serverMessage);
client.Close();
Console.WriteLine("Client closed.");
}
static string ReadMessage(SslStream sslStream)
{
// Read the message sent by the server.
// The end of the message is signaled using the
// "<EOF>" marker.
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do
{
bytes = sslStream.Read(buffer, 0, buffer.Length);
// Use Decoder class to convert from bytes to UTF8
// in case a character spans two buffers.
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
// Check for EOF.
if (messageData.ToString().IndexOf("<EOF>") != -1)
{
break;
}
} while (bytes != 0);
return messageData.ToString();
}
public static void Main(string[] args)
{
SslTcpClient.RunClient("api.push.apple.com");
Console.ReadLine();
}
}
答案 0 :(得分:1)
在我使用的Windows Server 2008 R2生产服务器上:
_iPhoneSslStream.AuthenticateAsClient(hostname, certificatesCollection, SslProtocols.Tls, false);
完整的身份验证源代码:
String hostname = "gateway.push.apple.com";
X509Certificate2 clientCertificate = new X509Certificate2(_iPhoneCertificate, _iPhonePassword);
X509Certificate2Collection certificatesCollection = new X509Certificate2Collection(clientCertificate);
_iPhoneClient = new TcpClient(hostname, 2195);
_iPhoneSslStream = new SslStream(_iPhoneClient.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
_iPhoneSslStream.AuthenticateAsClient(hostname, certificatesCollection, SslProtocols.Tls, false);
和
public static bool ValidateServerCertificate(object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
// Do not allow this client to communicate with unauthenticated servers.
return false;
}
PS我仍在使用Legacy Binary Provider API: https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/BinaryProviderAPI.html
答案 1 :(得分:1)
好的,就这样,如果有人偶然发现了......这就是交易。
无论Windows更新多少,修补,注册等等...... Windows Server 2008根本无法建立与Apple的LTS 1.2(仅)连接。所以我们尝试了Windows Server 2012 ....
开箱即用......同样的问题。没工作! O.M.W.无论如何...两天的Windows更新后,Service Pack 2(也包括.Net 4.6.2)......它工作了!
我们最终使用了HttpTwo https://github.com/Redth/HttpTwo/ 为了帮助构建框架,但现在一切正常。
答案 2 :(得分:0)
“目前,在标准.NET框架中,HttpClient不支持HTTP / 2.但是,在.NET Core框架中,它是..但它需要在Windows 10上运行(或者,我假设Windows Server 2016)“
参考:Is there any way to use the new http/2 api to send push notifications in C#?