Monday, December 20, 2010

Unity3D Parse Server Public Key and Send Encrypted Strings

Here we are to finish out our return of the server's public key for this user. Open Unity3D and open our ConnectionHandler class. We need to add in the handlers. Now there is a problem with using blogs in compose mode through blogger. Anything in <> gets chopped off unless you escape it. So first we need to update a few lines. Search for Dictionary in ConnectionHandler, there should be 2 cases of it, the declaration and the initialization in the constructor. Please go in and fix these two places so it says Dictionary<string, ExtensionHandler>
Secondly we need to do the same thing in OnExtensionResponse. Go down to the foreach and it should read:
foreach(KeyValuePair<string, ExtensionHandler> entry in handlers)
With these 2 changes we are ready to receive our "publickey" message from the server. Save the ConnectionHandler and open up LobbyGUI.


In the Awake() function after the smartFox.AddLogListener(LogLevel.DEBUG, OnDebugMessage); add the following line:
handlers.Add("publickey", provider.ServerPublicKeyFromSFSObject);
This adds a handler to our list of handlers so that any time we receive a message with the key "publickey" it will call our provider object, pass it the SFS data and our ServerPublicKeyFromSFSObject will handle reading it and putting it in place. Now we need to create this function. To do so, save this file and open up EncryptionProvider and add the following function after the ClientPublicKeyToSFSObject function:

    public void ServerPublicKeyFromSFSObject(ISFSObject data)
    {
        HasServerPK = true;
        Debug.Log("Got Server Public Key");
        ISFSObject playerData = data.GetSFSObject("key");
        ServerRSA = new RSACryptoServiceProvider();
        RSAParameters param = new RSAParameters();
        param.Modulus = playerData.GetByteArray("mod").Bytes;
        param.Exponent = playerData.GetByteArray("exp").Bytes;
        ServerRSA.ImportParameters(param);


        afterServerPKRecieved();
    }
Just like you saw on the Java side, we take the data, get the object called "key" and ready our the "mod" and "exp" bytes and put them into our key information.


The last thing you will see is a function call to afterServerPKRecieved(). We are creating this as an "event handler". What this means is, just like our handlers in the messaging, we are going to do something when this function gets called. Because we are working with asynchronous messages, there is no guarantee that we will get a key as the first message back from the server. In fact for security you may want to change keys every so often. So when we do get the Key we want to do something. In this case the moment we get a public key back from the server, we want to package up our login and password and send it to the server to be validated. In other scenes we may want to just update our key so we continue to encrypt data correctly. So up in the declarations we want to define the following:

    public delegate void AfterServerPKRecieved();
    public AfterServerPKRecieved afterServerPKRecieved;
again we see a delegate being set up. Now any time we receive a primary key it will call our delegate. Go ahead and save/close EncryptionProvider and go back to LobbyGUI.

In LobbyGUI after our handler is added in the Awake function, lets add the following line:
provider.afterServerPKRecieved += Login_AfterServerPKRecieved;
This tells the compiler that any time we call afterServerPKReceived, we want that function call to call Login_AfterServerPKRecieved(); By using a += you can chain as many functions on to that event as you want as long as it matches the function signature of void FunctionName(); So lets add our new function now. In LobbyGUI add:

    public void Login_AfterServerPKRecieved()
    {
        Debug.Log("sending login");
        ISFSObject tr = new SFSObject();
        tr.PutByteArray("user", provider.EncryptString(username));
        tr.PutByteArray("password", provider.EncryptString(password));


        ISFSObject data = new SFSObject();
        data.PutSFSObject("login", tr);


        ExtensionRequest request = new ExtensionRequest("login", data);
        smartFox.Send(request);
    }
So, now when we get our server's key, we are going to encrypt our login string and password string and send it to the server in an object called login, with a message key of login. The last thing we are going to add today is the EncryptString(string) function so we can send our data encrypted with the server's public key so that people can't see the text we sent along. Save LobbyGUI and open EncryptionProvider. And lets add the last function to today, EncryptString:
    public ByteArray EncryptString(string strToEncrypt)

    {
         System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
         return new ByteArray(ServerRSA.Encrypt(enc.GetBytes(strToEncrypt), false));
    }
All this function does is takes a string, uses our server's public key and encrypts it and passes it back as a ByteArray.

That concludes this chunk. We now receive our server's public key and encrypt data back to the server so prying eyes can't see our login/password.

No comments: