Obfuscate / Encrypt a String (NSString)

Wed, Aug 4

Apple Keychain Services offer a secure means to store sensitive information. Through the keychain, all the hardwork is managed for you to store and retrieve content. As powerful as the keychain services are, I was recently tinkering with some code to see if I could obfuscate content within an application.

I had a few strings defined as constants and I was interested to see if there was a painless way to store the values as obfuscated strings, and when running the application, un-obfuscate the strings and use the same within the application.

Using Exclusive-Or (XOR) to Obfuscate an NSString

The basic concept is that for each character in a string, I XOR the value against a key value, replacing the original character with the new XOR’d character. This will create a string that is unrecognizable from the original. To get the original string back, perform the same XOR operation with the same key. The obfuscate method is shown below.

- (NSString *)obfuscate:(NSString *)string withKey:(NSString *)key
{
  // Create data object from the string
  NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
 
  // Get pointer to data to obfuscate
  char *dataPtr = (char *) [data bytes];
 
  // Get pointer to key data
  char *keyData = (char *) [[key dataUsingEncoding:NSUTF8StringEncoding] bytes];
 
  // Points to each char in sequence in the key
  char *keyPtr = keyData;
  int keyIndex = 0;
 
  // For each character in data, xor with current value in key
  for (int x = 0; x < [data length]; x++) 
  {
    // Replace current character in data with 
    // current character xor'd with current key value.
    // Bump each pointer to the next character
    *dataPtr = *dataPtr++ ^ *keyPtr++; 
 
    // If at end of key data, reset count and 
    // set key pointer back to start of key value
    if (++keyIndex == [key length])
      keyIndex = 0, keyPtr = keyData;
  }
 
  return [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
}
Test Obfuscation

Pass in the string to obfuscate as well as a key which will provide the characters to use in the XOR process. To un-obfuscate a string, call the method with the obfuscated string and the same key.

Below is a short test of the obfuscation:

NSString *str = @"iPhoneDeveloperTips"; 
NSLog(@"Input string:%@", str);		
 
// Obfuscate string
NSString *obfuscatedStr = [self obfuscate:str withKey:@"+@$"];
NSLog(@"Obfuscated string:%@", obfuscatedStr);	
 
// Run obfuscate again to get the original string
str = [self obfuscate:obfuscatedStr withKey:@"+@$"];
NSLog(@"Final string:%@", str);

The output will look as follows:

Note: The resulting obfuscated string may include non-printable characters, so if you print to the console as shown above, don’t be surprised if the output contains what looks to be spaces or newline characters.

Caveat

For any number of reasons, including the key is stored as part of the application, this approach is by no means a substitution for real encryption. If you have sensitive data you need to protect, use the Keychain services. With that said, there are times when storing a string in a format other than clear text in your application may be of interest.

7 comments

Are you sure that you aren’t going to create strings which are not valid UTF8 encodings?

For example, if the last byte in your obfuscated result is a 0xE0 through 0xEF, NSString is going to fail because it expects that byte to be followed by two more bytes both of which must have the top bits of 1 and zero respectively.

http://www.fileformat.info/info/unicode/utf8.htm

If all your characters (in data and key) are 7-bit ascii, you are safe because you’ll never xor on the top bit.

by Pedant on Aug 5, 2010. Reply #

Pedant, thanks, good info. For those interested, here is a link to more information on ASCII

by John Muchow on Aug 5, 2010. Reply #

Thanks for the original (but sometimes nonworking code).
Thanks for the explaination and catching the problem.
Thanks for the link regarding tons of info about UTF8.
Thanks for the link regarding tons of info about ASCII.

But….

How would you change the code (keeping the overall concept and simplicity) so that it will always work????

by Alice on Dec 30, 2010. Reply #

Is there a specific key value that you are using that doesn’t work as expected? Things may not appear to work as expected (if you print the results to the console) if there are non-printable characters created through the encryption process.

by John Muchow on Dec 31, 2010. Reply #

Use NSMacOSRomanStringEncoding to create and read your NSData. — all 256 8-bit characters are valid.

Of course, that means that you must constrain your input string, or allow lossy conversion.

Best technique — leave the obfuscated text as NSData in the app, and just decode it as required.

Have some debug-mode-only code that takes your strings, XORs them, and then saves them as a text file of C-source that inits C arrays with the appropriate hex values to reproduce the obfuscated strings. Incorporate the C source into your project. Now your strings have been reduced so some garble in a DATA segment in the binary.

by Kirk Kerekes on Feb 14, 2011. Reply #

Hi,

I found out that there is a problem if you have the compiler set to LLVM compiler 2.0. For some reason the decoding does not work properly.

I found that the problem is the line where you replace the characters and xor them with the key
*dataPtr = *dataPtr++ ^ *keyPtr++;

If you replace this line with
*dataPtr = *dataPtr ^ *keyPtr;
dataPtr++;
keyPtr++;

everything works with LLVM compiler 2.0, LLVM GCC and GCC. But I honestly don’t understand why this happens, because both ways should compile to the exact same thing.

by Martin Reichart on Apr 16, 2011. Reply #

Somehow that function doesn’t work on the big NSStrings.
After I tested it a while, looks like it
[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]
on the last string returns nil at about 300 symbols.

Anyone noticed that?
That is so strange, trying to fix that half of the day for now=\

by Ivan on Sep 22, 2011. Reply #

Leave a Comment