Convert Byte Array to String

Hi all, I'm not sure what I'm doing wrong but I'm having trouble converting a ByteArray to a string. I've tried various methods and seem to fail in every one of them.

My code is taking a string and running it through HashBasedMessageAuthenticationCode which I think is going fine and in any case is certainly returning a Byte Array which I can print to console. If I convert this to String though, I seem to get a string of the Byte Array rather than a string with the values of the byte array. So rather than "ab" I'm getting "[26, 27]". I'm using this to create a SAS token for a web service.

I've struggled to find examples of any cryptography code in Monkey C so it's taken a while to get to this stage.


equivalent c# code which I'm trying to convert is:

               TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
               var week = 60 * 60 * 24 * 7;
               var expiry = Convert.ToString((int)sinceEpoch.TotalSeconds + week);

               string stringToSign = HttpUtility.UrlEncode(textBoxURL.Text) + "\n" + expiry;
               HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(textBoxSASKey.Text));
               var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
               textBoxSASToken.Text = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}", HttpUtility.UrlEncode(textBoxURL.Text), HttpUtility.UrlEncode(signature), expiry, textBoxSASKeyName.Text);


Monkey C code so far is:

   var mySASKey = Application.Properties.getValue("eHSASKey");
        
       var keyConvertOptions = {
        :fromRepresentation => StringUtil.REPRESENTATION_STRING_PLAIN_TEXT,
        :toRepresentation => StringUtil.REPRESENTATION_BYTE_ARRAY,
        :encoding => StringUtil.CHAR_ENCODING_UTF8
        };
        //Convert SAS Key to Byte Array
        var mySASKeyByteArray = StringUtil.convertEncodedString(mySASKey, keyConvertOptions);
        
        //Set up string to convert
        //current time
        var UTCNow = Time.now().value();
        //duration for key to last
        var duration = 2678400; //month
        var keyExpiry = (UTCNow + duration).toString();
        
        var stringToConvert = Comm.encodeURL(url) + keyExpiry;
        
        var bytesToConvert = StringUtil.convertEncodedString(stringToConvert, keyConvertOptions);
        //Set up HMAC (HashBasedMessageAuthenticationCode)
        var HMACOptions = {
            :algorithm => Cryptography.HASH_SHA256,
            :key => mySASKeyByteArray
        };

        var HMAC = new Cryptography.HashBasedMessageAuthenticationCode(HMACOptions);
        //convert the string    
        HMAC.update(bytesToConvert);
        var encryptedBytes = HMAC.digest();
        System.println(encryptedBytes.toString());

  • I've tried

    • encryptedBytes.toString() - produces the array as a string
    • StringUtil.encodeBase64(encryptedBytes.toString()); - produces a string that's way too long. Presumably is encoding the string representation of the array
    • StringUtil.encodeBase64(encryptedBytes); - fails as expecting a string input
    •         var keyConvertOptions2 = {
              :toRepresentation => StringUtil.REPRESENTATION_STRING_PLAIN_TEXT,
              :fromRepresentation => StringUtil.REPRESENTATION_BYTE_ARRAY,
              :encoding => StringUtil.CHAR_ENCODING_UTF8
              };
              //Convert SAS Key to Byte Array
              var stringvariable = StringUtil.convertEncodedString(encryptedBytes, keyConvertOptions2); - fails for no apparent reason with no real error that I can debug
  • This will convert a ByteArray ( in "value") into a sting:

    			var str="";
    			var sz=value.size();
    			for(var i=0;i<sz;i++) {str=str+value[i].toChar();}

  • Thanks Jim, I think that may be the answer. Still not working but it's looking closer than it did!

  • I use that when I get a ByteArray over BLE when it really just represents a string.

    If you're ByteArray represents a number, decodeNumber() can be used with something like NUMBER_FORMAT_UINT32 to do 4 bytes

  • I'm able to convert from a byte array that represents UTF-8 code points back to byte array without any issues using the provided StringUtil.convertEncodedString() function.

    hidden function test() {
        var byte_array = test_string_to_byte_array("Monkey C is a programming language.");
        var plain_text = test_byte_array_to_string(byte_array);
    }
        
    hidden function test_string_to_byte_array(plain_text) {
        var options = {
    		:fromRepresentation => StringUtil.REPRESENTATION_STRING_PLAIN_TEXT,
            :toRepresentation => StringUtil.REPRESENTATION_BYTE_ARRAY,
            :encoding => StringUtil.CHAR_ENCODING_UTF8
        };
        
        System.println(Lang.format("Converting '$1$' to ByteArray", [ plain_text ]));
        var result = StringUtil.convertEncodedString(plain_text, options);
        System.println(Lang.format("           '$1$'..", [ result ]));
        
        return result;
    }
        
        
    hidden function test_byte_array_to_string(byte_array) {
        var options = {
            :fromRepresentation => StringUtil.REPRESENTATION_BYTE_ARRAY,
            :toRepresentation => StringUtil.REPRESENTATION_STRING_PLAIN_TEXT,
    		:encoding => StringUtil.CHAR_ENCODING_UTF8
        };
    
    	System.println(Lang.format("Converting '$1$' to String", [ byte_array ]));
        var result = StringUtil.convertEncodedString(byte_array, options);
        System.println(Lang.format("           '$1$'..", [ result ]));
    
        return result; 
    }

    produces this:

    Converting 'Monkey C is a programming language.' to ByteArray
               '[77, 111, 110, 107, 101, 121, 32, 67, 32, 105, 115, 32, 97, 32, 112, 114, 111, 103, 114, 97, 109, 109, 105, 110, 103, 32, 108, 97, 110, 103, 117, 97, 103, 101, 46]'..
    Converting '[77, 111, 110, 107, 101, 121, 32, 67, 32, 105, 115, 32, 97, 32, 112, 114, 111, 103, 114, 97, 109, 109, 105, 110, 103, 32, 108, 97, 110, 103, 117, 97, 103, 101, 46]' to String
               'Monkey C is a programming language.'..
    

  • It seems like this translation of the code should be pretty close:

    hidden function test() {
    	var textBoxSASKey_Text = "eHSASKey"; //Application.Properties.getValue("eHSASKey");
    	var textBoxURL_Text = "https://blah.blah.blah";
    	var textBoxSASKeyName_Text = "what the what";
    	
    	// TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
    	// var week = 60 * 60 * 24 * 7;
    	// var expiry = Convert.ToString((int)sinceEpoch.TotalSeconds + week);
    	var expiry = Time.now().add(new Time.Duration(Gregorian.SECONDS_PER_DAY * 7)).value().toString();
    	
    	// string stringToSign = HttpUtility.UrlEncode(textBoxURL.Text) + "\n" + expiry;
    	var stringToSign = Communications.encodeURL(textBoxURL_Text) + "\n" + expiry;
    	
    	//HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(textBoxSASKey.Text));
    	var hmac = new_HMACSHA256(Encoding_UTF8_GetBytes(textBoxSASKey_Text));
    	
    	// var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
    	var signature = Convert_ToBase64String(ComputeHash(hmac, Encoding_UTF8_GetBytes(stringToSign)));
    	
    	//textBoxSASToken.Text = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}", HttpUtility.UrlEncode(textBoxURL.Text), HttpUtility.UrlEncode(signature), expiry, textBoxSASKeyName.Text);
    	var textBoxSASToken_Text = Lang.format("SharedAccessSignature sr=$1$&sig=$2$&se=$3$&skn=$4$", [
    	    Communications.encodeURL(textBoxURL_Text),
    	    Communications.encodeURL(signature),
    	    expiry,
    	    textBoxSASKeyName_Text
    	]);
    	System.println(textBoxSASToken_Text);
    }
    
    
    hidden function Encoding_UTF8_GetBytes(plain_text)
    {
        return StringUtil.convertEncodedString(plain_text, {
            :fromRepresentation => StringUtil.REPRESENTATION_STRING_PLAIN_TEXT,
            :toRepresentation => StringUtil.REPRESENTATION_BYTE_ARRAY
        });
    }
    
    hidden function ComputeHash(hmac, byte_array)
    {
        hmac.update(byte_array);
        return hmac.digest();
    }
    
    hidden function Convert_ToBase64String(byte_array)
    {
        return StringUtil.convertEncodedString(byte_array, {
            :fromRepresentation => StringUtil.REPRESENTATION_BYTE_ARRAY,
            :toRepresentation => StringUtil.REPRESENTATION_STRING_BASE64
        });
    }
    
    hidden function new_HMACSHA256(byte_array)
    {
        return new Cryptography.HashBasedMessageAuthenticationCode({
            :algorithm => Cryptography.HASH_SHA256,
            :key => byte_array
        });
    }

  • Thanks both for your help. I managed to get this working in the end and have now updated my app code at https://github.com/davedoesdemos/ConnectIQ-Watch-IoT to include settings and in watch SAS token generation. These changes will allow this app to be configurable to any Azure Event Hub to send data easily to the cloud for processing or machine learning. I've also, as a result, finally submitted the app to the app store so that others can play with IoT without writing their own apps Slight smile

    Thanks again for the help!

  • with ConnectIQ-Version 4.1.3 and

    value[i].toChar()

    i run into

    Error: Symbol Not Found Error
    Details: "Could not find symbol 'toChar'"

    ??

  • What is "value"?  It needs to be a ByteArray in this case

  • i replied to your example:

    var str="";
    var sz=value.size();
    for(var i=0;i<sz;i++) {str=str+value[i].toChar();}

    and yes, value is a ByteArray in my test case.