.Net无法连接到Apple推送通知服务

时间:2017-06-08 12:05:23

标签: c# ios asp.net

我正在使用.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();
        }
    }

3 个答案:

答案 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#?