Exchange ActiveSync Building Blocks – AS-WBXML

Index for the series:
Exchange ActiveSync Building Blocks – Intro

Background reading (not a pre-requisite):
[MS-ASWBXML] – Code Pages
Exchange 2010 SP1 IRM – Preamble
Understanding IRM in Exchange ActiveSync
EAS MD – Testing IRM
EAS MD 1.0 – Support for Device Info

I have referred to WBXML a couple of times in this series, but so far not going into any detail other than generically describing it as "ActiveSync language". I’m not attempting to make this seem like "magic" or anything, but when I started hacking around with the Exchange ActiveSync protocol myself I felt that I had to get comfortable with the basic concepts before going into this.

If you recall when we made our first sync attempt with the FolderSync command in a POST I included the following variable as the content of the POST:

//the wbxml required for initial FolderSync
byte[] postBytes = 
new byte[13]{03,01,106,00,00,07,86,82,03,48,00,01,01};

I have no idea why I thought it logical to define the bytes with their decimal value rather than the hexadecimal value (which I have used in later WBXMLs). Still; this is a rather short and sweet snippet of WBXML so we should be able to decode it manually by using a lookup table. I’ve converted the values to hex, and "translated" each byte to the plain xml representation.

FolderSync

//Decoding FolderSync
0x03 - WBXML Version number 1.3
0x01 - Unknown public identifier
0x6A - Character set: utf-8
0x00 - String table length
0x00, 0x07 - Switch to code page 7 (FolderHierarchy)
0x56 - FolderSync
0x52 - SyncKey
0x03 - Start value
0x30 - 0 (value of SyncKey in Ascii)
0x00 - End value
0x01 - /SyncKey
0x01 - /FolderSync

So, while originally partially cryptic we see that it makes sense.

(As explained better than me by commenter "chkan" as to why we’re using 0x56 & 0x52 instead of 0x16 & 0x12 as per the code page would seem to be the correct values: "Bit 6 Indicates whether this tag begins an element containing content. If this bit is zero, the tag contains no content and no end tag. If this bit is one, the tag is followed by any content it contains and is terminated by an END token. Without bit 6 on (ie, sending 0×16 instead of 0×56) you’re telling Exchange that there’s no content in your element, which isn’t true, so it rejects the request." )

How about another example, this time doing an encode rather than a decode? I need to build something of equal length to the above, so I decided to encode an IRM "Get Policy" request. And IRM means exactly what? It stands for Information Restriction Management. I’ve covered it before (link in top paragraph), and there is a test included in EAS MD, but I understand that it might not be on top of everyone’s to-do list. That’s ok, I just wanted some WBXML that isn’t too hardcore.

IRM Policies

byte[] postBytes = new byte[12];

//intro
postBytes[0] = 0x03;
postBytes[1] = 0x01;
postBytes[2] = 0x6a;
postBytes[3] = 0x00;
            
//switch to code page 18
postBytes[4] = 0x00;
postBytes[5] = 0x12;
            
//Settings -> Get -> ihsManagementInformation
postBytes[6] = 0x45; //(0x05 + 0x45)
postBytes[7] = 0x6b; //(0x2B + 0x40)
postBytes[8] = 0x47; //(0x07 + 0x40)

//close tags
postBytes[9] = 0x01;
postBytes[10] = 0x01;
postBytes[11] = 0x01;

I’ve used the same "padding trick" adding 0x40 to the values indicated in the code page. Note that this does not apply to selecting which code page you’re using or the closing tags.

It becomes fairly obvious fairly quickly that it is not feasible to sit around and do this manually as we do now unless you have a masochistic streak. I’ll just show one more example where we combine manual labor, yet inserting variables dynamically.

A requirement for provisioning in Exchange 2010 SP1, (if the client indicates supporting ActiveSync Protocol version 14.1), is that the server insists the client send some information back to the server about its properties; like operating system, IMEI, Mobile Operator, etc. (Screenshot from OWA shown in EAS MD 1.0 post linked to in the background reading section.)

Device Info

//WBXML for DeviceInfo
byte[] bModel       = utf8Encoding.GetBytes(txtDevModel.Text);
byte[] bIMEI        = utf8Encoding.GetBytes(txtDevIMEI.Text);
byte[] bFriendly    = utf8Encoding.GetBytes(txtDevFriendly.Text);
byte[] bOS          = utf8Encoding.GetBytes(txtDevOS.Text);
byte[] bOSLang      = utf8Encoding.GetBytes(txtDevOSLang.Text);
byte[] bPhoneNr     = utf8Encoding.GetBytes(txtDevPhoneNr.Text);
byte[] bMO          = utf8Encoding.GetBytes(txtDevMO.Text);
byte[] bUserAgent   = utf8Encoding.GetBytes(txtDevUA.Text);

byte[] polType = utf8Encoding.GetBytes("MS-EAS-Provisioning-WBXML");

//let's define a byte array with the wbxml for the device info
int ProvXmlLength = bModel.Length + bIMEI.Length + bFriendly.Length 
+ bOS.Length + bOSLang.Length + bPhoneNr.Length + bMO.Length 
+ bUserAgent.Length + polType.Length + 58;
byte[] postBytes = new byte[ProvXmlLength];

                        
postBytes[0] = 0x03;
postBytes[1] = 0x01;
postBytes[2] = 0x6A;
postBytes[3] = 0x00;
postBytes[4] = 0x00;
postBytes[5] = 0x0E;
postBytes[6] = 0x45;
postBytes[7] = 0x00;
postBytes[8] = 0x12;
postBytes[9] = 0x56;
postBytes[10] = 0x48;
postBytes[11] = 0x57;                        
            
int outerCount = 12;
int innerCount = 0;

//Fill in DeviceModel
postBytes[outerCount] = 0x03;
outerCount++;
for (innerCount = 0; innerCount < bModel.Length; innerCount++)
{
    postBytes[outerCount] = bModel[innerCount];
    outerCount++;
}           
postBytes[outerCount] = 0x00;
outerCount++;

postBytes[outerCount] = 0x01;
postBytes[outerCount + 1] = 0x58;
outerCount += 2;

//Fill in Device IMEI
(...)

//Fill in Device Friendly Name
(...)

//Fill in Device OS
(...)

//Fill in Device OS Language
(...)

//Fill in Device Phone Number
(...)

//Fill in Mobile Operator
(...)

//Fill in User Agent
(...)
            
postBytes[outerCount] = 0x46;
postBytes[outerCount+1] = 0x47;
postBytes[outerCount+2] = 0x48;

outerCount += 3;

//Switch to provision to include 
//MS-EAS-Provisioning-WBXML as the Policy type
postBytes[outerCount] = 0x03;
outerCount++;            

for (innerCount = 0; innerCount < polType.Length; innerCount++)
{
    postBytes[outerCount] = polType[innerCount];
    outerCount++;
}
postBytes[outerCount] = 0x00;
outerCount++;

postBytes[outerCount] = 0x01;
postBytes[outerCount+1] = 0x01;
postBytes[outerCount+2] = 0x01;
postBytes[outerCount+3] = 0x01;
postBytes[outerCount+4] = 0x01;

I’ve stripped out parts of the code and replaced it with the placeholder “(…)” – otherwise it would have taken up a lot more lines of the space here, and you could have called it slightly repetitive. It was mind-numbing to write this code as well; I had the coding window on one monitor and the code page reference on the other. If your response to this is that I must be stupid or something for doing this manually you wouldn’t be entirely wrong. But I did this for a reason – yes, it’s boring, no you wouldn’t implement a full ActiveSync client this way, but doing it the hard way means you learn it. If you want to do the exercise yourself you can fill in the missing parts so that all device properties are included. (Unrolling the loops serves no grander purpose  – I just didn’t want a lot of loops as my initial feeling was that if felt less readable that way.)

Either way – whether we are comfortable with building WBXML the hard way or not it is quite clear that the current approach does not scale. When you see all the code lines required just to send some simple info to the server you get the feeling this might get out of hand quickly. The proper thing to do would be to build a library handling all the WBXML stuff, or build some methods for encoding and decoding it. While WBXML is a generic concept not specific to Exchange ActiveSync, the AS part of AS-WBXML is more specific to this context. I have seen some dlls out there for the generic flavor, but have not seen exactly what I need specifically for ActiveSync communication. The “right” thing to do would as such be to build one myself, but I have not done so. It would take a lot of time, and so far I have not had the need for it either. (EAS MD has not required me to do so yet.) If I’ll build one later? Who knows? Smile

No code for you!

I can’t come up with any good code samples for this article (other than the snippets already shown). I guess I could have wrapped the fetching of IRM policies into a small sample app, but unless you happen to be running Exchange 2010 SP1 it doesn’t apply, and even then you’d need to have the rest of the infrastructure ready as well. If you are in control of an Exchange that does have support for this you can test it with my EAS MD utility instead, or hack together something based on the byte array I described here, along with helpings of previous code snippets in this series.

2 thoughts on “Exchange ActiveSync Building Blocks – AS-WBXML”

  1. I am following your tutorials and able to get the response from the server but now i am not sure how to parse the response as the response which i am getting is as hex code.

    03016a00 0007564c 03310001 52033100 014e5703 31310001 4f480331 00014903 30000147 0343616c 656e6461 7200014a 03380001 014f4803 32000149 03300001 4703436f 6e746163 74730001 4a033900 01014f48 03330001 49033000 01470344 656c6574 65642049 74656d73 00014a03 34000101 4f480334 00014903 30000147 03447261 66747300 014a0333 0001014f 48033500 01490330 00014703 496e626f 7800014a 03320001 014f4803 36000149 03300001 47034a6f 75726e61 6c00014a 03313100 01014f48 03370001 49033000 0147034a 756e6b20 456d6169 6c00014a 03313200 01014f48 03380001 49033000 0147034e 6f746573 00014a03 31300001 014f4803 39000149 03300001 47034f75 74626f78 00014a03 36000101 4f480331 30000149 03300001 47035365 6e742049 74656d73 00014a03 35000101 4f480331 31000149 03300001 47035461 736b7300 014a0337 00010101 01

    Can you please help me? I have gone through the AES code pages but still confused.

Leave a Reply

Your email address will not be published. Required fields are marked *

*