1
\$\begingroup\$

I am trying to post an image captured through my game to Facebook through their SDK, and as it requires a URI, I wrote a simple AWS Lambda function to take a byte array and upload it to an AWS bucket. The AWS function takes a call and then returns a URL to call PUT to with the image data... So the idea is, if the response is 200, use that URL to post to FB.

But - using Unity 5.2.x to 5.3.4, and Android 4.4 - 6.1 - it always gives me a 403 response, while working fine on iOS.

So, I have a method that takes a byte[] then does this:

    WWW w = new WWW("https://my-amazon-function-to-call");
    yield return w;
    if (!string.IsNullOrEmpty(w.error)) {
        Debug.LogError(w.error);
    }
    else 
    {
             var dict = Json.Deserialize(w.text) as Dictionary<string,object>;
             string oneTimeUploadUrl = (string)dict["oneTimeUploadUrl"]; // Private URL
             string resultUrl = (string)dict["resultUrl"]; // Public URL

            UnityWebRequest aws = UnityWebRequest.Put(oneTimeUploadUrl, bytes);
            yield return aws.Send();
            if(aws.isError) 
            {
                Debug.LogError("AWS ERROR: " + aws.error);
            }
            if(aws.responseCode == 200)
            {
                FeedShare(new Uri(resultUrl), _cachedMessage);    // FB call
            }
    }

Pretty simple, right? On iOS, yes it is. But Android it continually gives me a 403 response on the PUT operation.

So, I've taken to wrapping this in an iOS-specific #ifdef and trying something more natively C-Sharp-ish for Android... Eg.:

    WWW w = new WWW("https://my-amazon-function-to-call"); // tried pure old Http too
    yield return w;
    if (!string.IsNullOrEmpty(w.error)) {
        Debug.LogError(w.error);
    }
    else {
        var dict = Json.Deserialize(w.text) as Dictionary<string,object>;
        string oneTimeUploadUrl = (string)dict["oneTimeUploadUrl"];
        string resultUrl = (string)dict["resultUrl"];

    #if UNITY_IPHONE
        UnityWebRequest aws = UnityWebRequest.Put(oneTimeUploadUrl, bytes);
        yield return aws.Send();
        if(aws.isError) 
        {
            Debug.LogError("AWS ERROR: " + aws.error);
        }
        if(aws.responseCode == 200)
        {
            FeedShare(new Uri(resultUrl), _cachedMessage);    
        }
    #else             

    // Various Security Callback Tests
        //ServicePointManager.ServerCertificateValidationCallback = (p1, p2, p3, p4) => true;
        //ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback;
        ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(AcceptAllCertifications);


        HttpWebRequest wreq = (HttpWebRequest)WebRequest.Create(oneTimeUploadUrl);
        //wreq.AuthenticationLevel = System.Net.Security.AuthenticationLevel.None;
        //wreq.PreAuthenticate = false;
        wreq.Method = "PUT";
        wreq.ContentType = "image/png";
        wreq.ContentLength = bytes.Length;

        Stream newStream = wreq.GetRequestStream();
        newStream.Write(bytes, 0, bytes.Length);
        newStream.Close();

        HttpWebResponse response = (HttpWebResponse)wreq.GetResponse();
        if((int)response.StatusCode == 200)
        {
            FeedShare(new Uri(resultUrl), _cachedMessage);
        }
     #endif
    }

... but that also gives me a 403. I've tried a few different options, as you can see from the commented-out code, but no love. For the record, here are the SSL Policy functions that are used as the ServerCertificateValidationCallbacks:

public bool AcceptAllCertifications(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certification, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
    return true;
}

public bool MyRemoteCertificateValidationCallback(System.Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
    bool isOk = true;
    if (sslPolicyErrors != SslPolicyErrors.None) {
        for (int i=0; i<chain.ChainStatus.Length; i++) {
            if (chain.ChainStatus [i].Status != X509ChainStatusFlags.RevocationStatusUnknown) {
                chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
                chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
                chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan (0, 1, 0);
                chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
                bool chainIsValid = chain.Build ((X509Certificate2)certificate);
                if (!chainIsValid) {
                    isOk = false;
                }
            }
        }
    }
    print("MyRemoteCertificateValidationCallback: " + isOk);
    return isOk;
}

This is the actual StackTrace:

04-01 11:14:57.325: I/Unity(22979):  
04-01 11:14:57.325: I/Unity(22979): (Filename: ./artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 37)
04-01 11:14:57.825: I/Unity(22979): WebException: The remote server returned an error: (403) Forbidden.
04-01 11:14:57.825: I/Unity(22979):   at System.Net.HttpWebRequest.CheckFinalStatus (System.Net.WebAsyncResult result) [0x00000] in <filename unknown>:0 
04-01 11:14:57.825: I/Unity(22979):   at System.Net.HttpWebRequest.SetResponseData (System.Net.WebConnectionData data) [0x00000] in <filename unknown>:0 
04-01 11:14:57.825: I/Unity(22979):  
04-01 11:14:57.825: I/Unity(22979): (Filename:  Line: -1)

At this point, I'm totally stuck... my only thought is to maybe write a native plugin... but would love to NOT have to do that...

Thoughts ???

\$\endgroup\$

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.