iosiphonesslapplepayjs

Sandbox apple pay testing handshake failure


I am having trouble validating the merchant in my apple pay sandbox environment. Taken from https://developer.apple.com/reference/applepayjs/applepaysession#2166532, once my server then calls the Start Session endpoint at the provided URL, I get a 500 error.

I've dug around and this 500 error is happening somewhere in the network layer. As listed on the apple page (https://developer.apple.com/reference/applepayjs/), I need the following requirements met:

  1. All pages that include Apple Pay must be served over HTTPS. done, server has ssl/https sitewide
  2. To enable merchant validation, your server must allow access over HTTPS (TCP over port 443) to the Apple Pay IP addresses provided in Listing 1 below. done, server is open to all ips on port 443
  3. Your server must support the Transport Layer Security (TLS) 1.2 protocol and one of the cipher suites listed in Table 1. server does support tls 1.2, since I send requests on tls 1.2 to apple pay's development server (below)

I've been using Wireshark to check what's going on, and I seem to be failing once the server is in the ChangeCipherSpec phase, after the server sends back the cipher spec to the client. (Reference for ssl procedure: https://support.f5.com/csp/article/K15292). As you can see from my image, I'm communicating to the apple pay sandbox server, passing in the same supported tls protocol and cipher suite that the error would suggest -> Handshake Failure (40), so something else is going on and I don't know where to look

enter image description here

If you look at the ServerHello message, you can see the server found and accepted the cipher suite that matches the client, which also matches one of the required ciphers that apple pay supports enter image description here

enter image description here

I can add other details as necessary


Solution

  • The issue was that our server did not have TLS 1.2 enabled by default. Enabling TLS 1.2 and disabling TLS 1.0 fixed the issue - Win 2008

    edit

    There are a few things that needed to happen. Our server was on .net 4.5, which does not use tls 1.2 by default (apple requires tls 1.2 to be used). So, we upgraded our solution to .net 4.6, and also forced tls 1.2 for our request. Additionally, we have to include the merchant id certificate in our request to apple (which isn't mentioned in the docs very well).

    You can find the github repo of the source I used here (https://github.com/justeat/ApplePayJSSample), but here is my code that I needed to put in my solution to make things work (I also had to export my merchant certificate from my mac's keychain that gave me a .p12 file. I imported this .p12 file into my server's computer certificate store)

    [System.Web.Http.HttpPost]
        public async Task<ContentResult> GetApplePaySession([FromBody] string url)
        {
            // http://stackoverflow.com/a/36912392/1837080
            System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
    
            // Load the merchant certificate for two-way TLS authentication with the Apple Pay server.
            var certificate = LoadMerchantCertificate();
    
            // Get the merchant identifier from the certificate to send in the validation payload.
            var merchantIdentifier = GetMerchantIdentifier(certificate);
    
            // Create the JSON payload to POST to the Apple Pay merchant validation URL.
            var payload = new ApplePayRequest()
            {
                merchantIdentifier = merchantIdentifier,
                domainName = System.Web.HttpContext.Current.Request.Url.Host,
                displayName = "[display name from apple developer portal]"
            };
    
            JObject merchantSession;
    
            // Create an HTTP client with the merchant certificate
            // for two-way TLS authentication over HTTPS.
            using (var httpClient = CreateHttpClient(certificate))
            {
                var jsonPayload = JsonConvert.SerializeObject(payload);
    
                using (var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json"))
                {
                    // POST the data to create a valid Apple Pay merchant session.
                    using (var response = await httpClient.PostAsync(url, content))
                    {
                        response.EnsureSuccessStatusCode();
    
                        // Read the opaque merchant session JSON from the response body.
                        var merchantSessionJson = await response.Content.ReadAsStringAsync();
                        merchantSession = JObject.Parse(merchantSessionJson);
                    }
                }
            }
    
            // Return the merchant session as JSON.
            return Content(merchantSession.ToString(), "application/json");
        }
    
        #region Apple Pay helper methods
    
        private X509Certificate2 LoadMerchantCertificate()
        {
            X509Certificate2 certificate;
    
            // Load the certificate from the current user's certificate store. This
            // is useful if you do not want to publish the merchant certificate with
            // your application, but it is also required to be able to use an X.509
            // certificate with a private key if the user profile is not available,
            // such as when using IIS hosting in an environment such as Microsoft Azure.
            using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
            {
                store.Open(OpenFlags.ReadOnly);
    
                // when using thumbprint from mmc, look at:
                // http://stackoverflow.com/a/14852713
                // there is a hidden character that you must delete
                var certificates = store.Certificates.Find(
                    X509FindType.FindByThumbprint,
                    "[thumbprint]",                    
                    validOnly: false);
    
                if (certificates.Count < 1)
                {
                    throw new InvalidOperationException(
                        // ReSharper disable once UseStringInterpolation
                        string.Format(
                            "Could not find Apple Pay merchant certificate with thumbprint '{0}' from store '{1}' in location '{2}'.",
                            "‎[thumpprint]", store.Name, store.Location));
                }
    
                certificate = certificates[0];
            }
    
            return certificate;
        }
    
        private string GetMerchantIdentifier(X509Certificate2 certificate)
        {
            // This OID returns the ASN.1 encoded merchant identifier
            var extension = certificate.Extensions["1.2.840.113635.100.6.32"];
    
            // Convert the raw ASN.1 data to a string containing the ID
            return extension == null ? string.Empty : Encoding.ASCII.GetString(extension.RawData).Substring(2);            
        }
    
        private HttpClient CreateHttpClient(X509Certificate2 certificate)
        {
            var handler = new WebRequestHandler();
            handler.ClientCertificates.Add(certificate);
    
            return new HttpClient(handler, disposeHandler: true);
        }
    
        #endregion