Recently I’ve been asked a question about how the AES encryption envelope can be used with a PCL build of Data Abstract, the issue being that PCL doesn’t expose the needed cryptography API. Although I don’t known the reason for this, I am still going to provide a solution.
Before we dive into the technical details, I have to say that relying on AES message encryption alone is not the best option. Once someone steals your password, they will be able to intercept, read and modify any client-server traffic. So keeping passwords safe and not storing them in non-encrypted form is an absolute must. But you should still consider SSL to protect your communication channels.
Let’s go back to AES for now.
For most of the platforms supported by RemObjects SDK “natively”, the AESEncryptionEnvelope can be instantiated in the application and then this instance can be added to the .Envelopes collection of the Message instance created in the PCL communication library (this is the magic of PCL, yes). Unfortunately, there are two quite important platforms that are supported only by the PCL build of RemObjects SDK. So a solution is needed.
Assume we have a very simple PCL library that calls a RemObjects SDK server and that the server has AESEncryptionEnvelope enabled, so we have to enable it on the client side as well. The client is a Xamarin.iOS application (but the Xamarin.Android approach is exactly the same). Below, I’ll be talking about ‘native’ code, by which I mean code that directly references Xamarin libraries, opposed to code that references PCL.
So the solution is dead simple: In the ‘native’ code part, wrap Crypto API into a form that the PCL code part can understand and provide this wrapped instance to the RemObjects SDK message instance. Obviously, the best way to provide something is to wrap it into a form that RemObjects SDK understands. In our case, we’ll derive a class from the abstract class MessageEnvelope. For further reference I’ll provide the code entirely:
using System; using System.ComponentModel; using System.IO; using System.Text; using RemObjects.SDK.Helpers; using System.Security.Cryptography; namespace RemObjects.SDK { public class AesEncryptionEnvelope : MessageEnvelope { #region Default values private const String CATEGORY_ENCRYPTION = "Encryption"; private const String IV = "@1B2c3D4e5F6g7H8"; private const Int32 BUFFER_SIZE = 65536;//64k private const Int32 BLOCK_SIZE = 128 / 8; #endregion #region Private fields private Byte[] fEncodedPassword; #endregion public AesEncryptionEnvelope() : base() { } public AesEncryptionEnvelope(String password) : this() { this.Password = password; } #region Properties public String Password { get { return this.fPassword; } set { this.fPassword = value; this.EncodePassword(); } } private String fPassword; public override String DefaultEnvelopeMarker { get { return "AES"; } } #endregion protected override MemoryStream InternalWrap(Stream source, Byte[] header) { // Size of the original stream Int64 lStreamLength = source.Length - source.Position; Byte[] lStreamSizeBuffer = BinHelpers.Int32ToBuffer((Int32)lStreamLength); // Create bytes array containing // [... original header ...][stream size] // This is cheaper than creating MemoryStream and then converting it to Byte[] Byte[] lHeader = new Byte[header.Length + 4]; Array.Copy(header, lHeader, header.Length); Array.Copy(lStreamSizeBuffer, 0, lHeader, header.Length, 4); return this.InternalEncrypt(source, lHeader); } protected override MemoryStream InternalUnwrap(Stream source) { // Read size of the original stream Byte[] lBuffer = new Byte[BinHelpers.SIZE_INT32]; Int32 lBytesRead = source.Read(lBuffer, 0, BinHelpers.SIZE_INT32); if (lBytesRead != BinHelpers.SIZE_INT32) BinHelpers.UnexpectedEndOfStream(); Int32 lStreamSize = BinHelpers.Int32FromBuffer(lBuffer); MemoryStream lDecryptedData = new MemoryStream(); this.InternalDecrypt(source, lDecryptedData); if (lDecryptedData.Length (You'll have to reference the RemObjects SDK assemblies to get this code compiled.)
Then provide an instance of this AesEncryptionEnvelope class to the code part where the communication components are instantiated and add it there to the Envelopes collection:
message.Envelopes.Add(envelopeInstance, true);
And that's it. AES encryption is enabled.
I also want to mention that while theoretically it is possible to implement AES encryption/decryption completely in managed PCL code, there are reasons NOT to do this. Re-implementing part of the base class library leads to code bloating. What's more important is that cryptography APIs implemented by Xamarin platforms might utilize low-level platform methods for better performance and security – something that a PCL managed code-only implementation won't be able to provide by definition.
PS.: The above-mentioned approach can be extended. More powerful (and resources-consuming) cryptography algorithms can be used instead of AES, like asymmetric encryption. Custom envelopes based on them can be used in RemObjects SDK-powered applications – all you need to do is to properly override the abstract methods of the MessageEnvelope class.