1

As per apple notification, https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns/ I am trying to upgrade my server side push notification code into standard .net farmworker 4.6.2 to support HTTP/2 and TLS 1.2 based request.
Below is the peace of code which I am trying:

public async Task<ApnsResponse> PushMessage_Http2(string Title, string Message, string DeviceToken, string Badge, string Custom_Field, string PUSH_MODULE, string ITEM_CODE)
    {
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
        
        var url = string.Format("https://api.push.apple.com:443/3/device/{0}", DeviceToken);
        List<string> Key_Value_Custom_Field = new List<string>();
        
        int iBadge = 1;

        if (!string.IsNullOrEmpty(Badge))
            iBadge = Convert.ToInt32(Badge);

        using (var httpHandler = new Http2CustomHandler() { SslProtocols = SslProtocols.Tls12})// { SslProtocols = SslProtocols.Tls12 })
        {
            httpHandler.ServerCertificateValidationCallback = (message, cert, chain, errors) => { return true; };
            httpHandler.ClientCertificates.Add(getServerCert());

            using (var httpClient = new HttpClient(httpHandler, true))
            using (var request = new HttpRequestMessage(HttpMethod.Post, url))
            {
                request.Headers.Add("apns-id", Guid.NewGuid().ToString("D"));
                request.Headers.Add("apns-push-type", "alert");
                request.Headers.Add("apns-priority", "10");
                ////request.Headers.Add("apns-topic", topic);
                
                string msg = "";

                msg = "{";
                msg += "\"aps\": {";
                msg += "\"badge\": " + iBadge.ToString() + ", ";
                msg += "\"alert\": \"" + Message + "\", ";//Apple side alert title is not in use
                msg += "\"sound\": \"default\" ";

                msg += "}, ";

                msg += "\"data\": {";
                msg += "\"item_code\": \"" + ITEM_CODE + "\", ";
                msg += "\"push_title\":\"" + Title + "\", ";//This item is not in use into app side
                msg += "\"push_module\":\"" + PUSH_MODULE + "\" ";
                msg += "} ";

                String PayloadMess = "";
                if (string.IsNullOrWhiteSpace(Custom_Field) == false)
                {
                    List<string> list_Custom_Field = Custom_Field.Split(';').ToList();

                    if (list_Custom_Field.Count > 0)
                    {
                        for (int indx = 0; indx < list_Custom_Field.Count; indx++)
                        {
                            Key_Value_Custom_Field = list_Custom_Field[indx].Split('=').ToList();
                            if (Key_Value_Custom_Field.Count > 1)
                            {
                                if (PayloadMess != "") PayloadMess += ", ";
                                PayloadMess += "'" + Key_Value_Custom_Field[0].ToString() + "':'" + Key_Value_Custom_Field[1].ToString() + "'";
                            }
                        }
                    }
                }

                if (PayloadMess != "")
                {
                    msg += ", " + PayloadMess;
                }

                msg += "}";

                request.Content = new StringContent(msg);
                request.Version = new Version(2, 0);
                try
                {
                    using (var response = await httpClient.SendAsync(request, new System.Threading.CancellationToken()))
                    {
                        var succeed = response.IsSuccessStatusCode;
                        var statuscode = response.StatusCode;
                        var content = await response.Content.ReadAsStringAsync();
                        var error = content;
                        //var error = JsonHelper.Deserialize<ApnsError>(content);

                        return new ApnsResponse
                        {
                            IsSuccess = succeed,
                            Error = "status code: " + statuscode + " error: " + error
                        };
                    }

                }
                catch (HttpRequestException ex)//Exception
                {
                        
                    return new ApnsResponse
                    {
                        IsSuccess = false,
                        Error = ex.Message + "  " + ex.InnerException.Message + " " + ex.InnerException.InnerException + " " + ex.StackTrace.ToString()
                    
                    };
                }
            }
        }
    }
private X509Certificate getServerCert()
    {
        X509Certificate test = new X509Certificate();

        //Open the cert store on local machine        
        X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine); //##### Try either StoreLocation.CurrentUser or StoreLocation.LocalMachine

        if (store != null)
        {
            // store exists, so open it and search through the certs for the Apple Cert        
            store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
            X509Certificate2Collection certs = store.Certificates;

            if (certs.Count > 0)
            {
                int i;
                for (i = 0; i < certs.Count; i++)
                {
                    X509Certificate2 cert = certs[i];

                    if (cert.FriendlyName.Contains(ProductionKeyFriendName))
                    {
                        //Cert found, so return it.
                        return certs[i];
                    }
                }
                for (i = 0; i < certs.Count; i++)
                {
                    X509Certificate2 cert = certs[i];

                    if (cert.FriendlyName.Contains(FriendName))
                    {
                        //Cert found, so return it.
                        return certs[i];
                    }
                }
            }
            return test;
        }

        return test;
    }
public class Http2CustomHandler : WinHttpHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            request.Version = new Version("2.0");
            return base.SendAsync(request, cancellationToken);
        }
    }
    public class ApnsResponse
    {
        public bool IsSuccess { get; set; }
        public string Error { get; set; }
        //public ApnsError Error { get; set; }
    }

But I am getting below error while final request call:

Error while copying content to a stream. The write operation failed, see inner exception. System.Net.Http.WinHttpException (0x80072F78): Error 12152 calling WinHttpWriteData, 'The server returned an invalid or unrecognized response'. at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Net.Http.WinHttpHandler.d__136.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Net.Http.WinHttpHandler.d__109.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at DIS_AdminPortal.ApplePNS.d__8.MoveNext()

Note: Above error we are facing into two different production server which is having windows server 2016 installed but same code when I try with different dev server which is having windows server 2019 then it's working fine.so does this issue related to server configuration or anything related?

Please let me know any help around this issue, Thanks in advance.

  • You need to publish application and install with the setup.exe that is created from the publish option. A net application only works if the build and deploy machines have exactly same version of Net installed. So either you need to install same version of Net or publish like commercial software. The Net library uses Windows dlls. The publish updates the wijndows dlls in installed machine so the are compatible with the build machine. – jdweng Mar 15 '21 at 18:24
  • @jdweng , Actually I have web application project in which i am using above code so for that I am publishing latest code along with dlls and hosted that web application within iis. – user2500948 Mar 15 '21 at 18:27
  • IIS doesn't allow users to access the File System which is probably giving "Error while copying content to a stream". You should place any files that are needed by application on a network drive that all users will have access instead on the IIS. If you really need access to a file you should write a separate dll that only does read/write to file system and set dll to "Run As Admin". The call the dll from your current app. You want to limit access of any app on IIS to File System or Resources. – jdweng Mar 15 '21 at 18:36
  • @jdweng we can access file through the code via assigning the IIS user permission so I think that's not related to this. – user2500948 Mar 15 '21 at 18:47
  • A web app uses GUEST privileges by default. For more access you need to set the role : https://learn.microsoft.com/en-us/aspnet/web-forms/overview/older-versions-security/roles/creating-and-managing-roles-cs – jdweng Mar 15 '21 at 18:53
  • I am facing the same issue. Any luck @user2500948 – saravana Jan 10 '23 at 10:46

0 Answers0