Home  >  

Encryption in Flex Applications 2 - SWC AS3 Library Encryption

AddThis Social Bookmark Button

In article 1, I showed a trivial example of using the AS3Crypto API for encrypting data. In this article, we’ll go over an example using Interfaces, Factories, and Encryption to protect code in a SWC library from being easily decompiled.

article2_img1.png

The first order of business is to create a Flex Library project to hold our protected code, and another Flex Library project to hold the Interface and Factory to access it. In this case, our super-duper-ultra-mega-secret code we want to protect is how to calculate the circumference and area of a circle. I’ve created projects called CircleCalculator and CircleCalculatorInterface. CircleCalculator should reference CircleCalculatorInterface so we can implement the interface we’ll create.

In CircleCalculatorInterface, create an Interface class to define what we want our end-user to be able to do. We’re hiding the code because we don’t want them to be able to decompile our code to see how circle areas and circumferences are calculated.

 
package com.company
{
 	public interface ICircleCalculator
 	{
  		function calcArea(radius:Number):Number;
  		function calcCircumference(radius:Number):Number;
  	}
}

Next, in CircleCalculator, create our super-duper-ultra-mega-secret code that implements the ICircleCalculator interface.

 
package com.company
{
 	import com.company.ICircleCalculator;
 	
 	public class CircleCalculator implements ICircleCalculator
 	{
  		public function CircleCalculator()
  		{
   		}
  		
  		public function calcArea(radius:Number):Number
  		{
   			return Math.PI * radius * radius;
   		}
  		
  		public function calcCircumference(radius:Number):Number
  		{
   			return 2 * Math.PI * radius;
   		}
  	}
}
article2_img2.png
article2_img3.png

Now that we have the CircleCalculator project in a state that is compiling, open up the bin folder and extract library.swf from CircleCalculator.swc using the zip tool of your choice. Then copy library.swf to the root folder of CircleCalculatorInterface. Library.swf contains the actual code that we’re going to be encrypting.

In order to encrypt library.swf, I’ve created a tool called LibraryEncrypter as an AIR application. It takes any dropped file and encrypts it using blowfish. The key is stored at the beginning of the encrypted bytes. In a real-world scenario, you’d obviously want to do more obfuscation of the key or store it server-side for increased protection.

 

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"  width="400" height="300" layout="vertical" horizontalAlign="center" verticalAlign="middle"
	nativeDragEnter="handleDragEnter(event)" 
	nativeDragDrop="handleDropSwf(event)"
	>
	<mx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import com.hurlant.crypto.Crypto;
			import com.hurlant.crypto.symmetric.ICipher;
			import com.hurlant.crypto.prng.Random;
			
			private function handleDragEnter(event:NativeDragEvent):void
			{
 				NativeDragManager.acceptDragDrop(this);
 			}
			
			private function handleDropSwf(event:NativeDragEvent):void
			{
 				var filelist:Array = event.clipboard.getData("air:file list") as Array;
 				for each(var file:File in filelist)
 				{
  					//do simple blowfish encryption for the swf file
  					var fs:FileStream = new FileStream();
  					fs.open(file, FileMode.READ);
  					
  					var fileBytes:ByteArray = new ByteArray();
  					fs.readBytes(fileBytes);
  					fs.close();
  					
  					//generate a random key
  					var key:ByteArray = new ByteArray();
  					var random:Random = new Random();
  					random.nextBytes(key, 8);
  					
  					var aes:ICipher = Crypto.getCipher("blowfish-ecb", key, Crypto.getPad("pkcs5"));
  					aes.encrypt(fileBytes);
  					
  					//rewrite the file with the encrypted blob
  					fs.open(file, FileMode.WRITE);
  					fs.writeBytes(key);
  					fs.writeBytes(fileBytes);
  					fs.truncate();
  					fs.close();
  					fs = null;
  					
  					Alert.show("Encryption Completed", "Results");
  				}
 			}
		]]>
	</mx:Script>
	
	<mx:Text text="Drop SWF here to encrypt"/>
</mx:WindowedApplication>
article2_img4.png
article2_img5.png

 

 

Now that that’s done, launch the app and drop library.swf from CircleCalculatorInterface onto it.

 

 

 

If you’re curious, you can drop the encrypted swf into a web browser, right-click and see that “movie not loaded…” will appear. Another test is to use a decompiler tool to ensure the encryption worked ok.

The final step in the process of creating an encrypted swc library is to create a Factory class in CircleCalculatorInterface that can interpret and understand our encrypted library.swf file. You’ll also need to check the box in CircleCalculatorInterface properties for including the AIR libraries. We do this so that our library can be used by either Flex or AIR when we release it to a client.

 

package com.company
{
 	import com.hurlant.crypto.Crypto;
 	import com.hurlant.crypto.symmetric.ICipher;
 	
 	import flash.display.Loader;
 	import flash.events.Event;
 	import flash.events.EventDispatcher;
 	import flash.system.ApplicationDomain;
 	import flash.system.LoaderContext;
 	import flash.utils.ByteArray;
 	
 	import mx.core.ByteArrayAsset;
 	
 	public class CircleCalculatorFactory extends EventDispatcher 
 	{
  		//this is the CircleCalculator library.swf file (encrypted with LibraryEncrypter of course)
  		[Embed (source="library.swf", mimeType="application/octet-stream")]
  		private var encryptedSwf:Class;
  
  		private var _circleCalculator:ICircleCalculator=null;
  
  		public function CircleCalculatorFactory(completeHandler:Function)
  		{
   			super();
   			
   			this.addEventListener(Event.COMPLETE, completeHandler);
   			
   			//load up the swf file that contains the CircleCalculator class
   			var fileData:ByteArrayAsset = ByteArrayAsset(new encryptedSwf());
   
   			var key:ByteArray = new ByteArray();
   			fileData.readBytes(key, 0, 8);
   			var encryptedBytes:ByteArray = new ByteArray();
   			fileData.readBytes(encryptedBytes);
   			
   			//decrypt library.swf
   			var aes:ICipher = Crypto.getCipher("blowfish-ecb", key, Crypto.getPad("pkcs5"));
   			aes.decrypt(encryptedBytes);
   			
   			//load the swf bytes into the current application domain
   			var ldr:Loader = new Loader();
   			var ldrContext:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
   			
   			//do this for AIR support
   			if(ldrContext.hasOwnProperty("allowLoadBytesCodeExecution"))
   				ldrContext.allowLoadBytesCodeExecution = true;
   				
   			ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, loadSwfComplete);
   			ldr.loadBytes(encryptedBytes, ldrContext);
   		}
  		
  		private function loadSwfComplete(event:Event):void
  		{
   			var cc:Class = ApplicationDomain.currentDomain.getDefinition("com.company.CircleCalculator") as Class;
   			_circleCalculator = new cc();
   			dispatchEvent(new Event(Event.COMPLETE));
   		}
  		
  		/**
  		 * @return an object implementing the ICircleCalculator interface
  		 */
  		public function getInstance():ICircleCalculator
  		{
   			return _circleCalculator;
   		}
  	}
}

Lastly, let’s create an example Flex application that loads and uses our CircleCalculatorInterface library project.

 

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:degrafa="com.degrafa.*" xmlns:paint="com.degrafa.paint.*" xmlns:geometry="com.degrafa.geometry.*"
	backgroundGradientColors="[#666666, #222222]"
	layout="absolute" viewSourceURL="srcview/index.html">
	<mx:Script>
		<![CDATA[
			import com.company.ICircleCalculator;
			import com.company.CircleCalculatorFactory;
			
			private var circleCalcFactory:CircleCalculatorFactory = new CircleCalculatorFactory(initComplete);
			private var circleCalculator:ICircleCalculator = null;
						
			[Bindable]
			private var circumference:Number=0;
			
			[Bindable]
			private var area:Number=0;
			
			private function initComplete(event:Event):void
			{
 				circleCalculator = circleCalcFactory.getInstance();
 				calculateCircle();
 			}
			
			private function calculateCircle():void
			{
 				circumference = circleCalculator.calcCircumference(radiusSlider.value);
 				area = circleCalculator.calcArea(radiusSlider.value);
 			}
		]]>
	</mx:Script>
	<mx:VBox x="20" y="20">
		<mx:Label text="Radius:"/>
		<mx:HSlider id="radiusSlider" value="100" minimum="10" maximum="400" width="200" change="calculateCircle()" liveDragging="true"/>
		<mx:Spacer height="20"/>
		<mx:Text text="Circumference: {circumference}"/>
		<mx:Text text="Area: {area}"/>
	</mx:VBox>
	
	<degrafa:Surface horizontalCenter="0" verticalCenter="0">
        <degrafa:fills>
            <paint:SolidFill    id="blue"
                                color="#9999ff"/>
        </degrafa:fills>
        
        <degrafa:strokes>
            <paint:SolidStroke  id="white"
                                color="#FFFFFF"
                                alpha="1"
                                weight="2"/>
        </degrafa:strokes>
        
        <degrafa:GeometryGroup>
        
			<geometry:Circle    fill="{blue}"
			                    stroke="{white}"
			                    radius="{radiusSlider.value}"/>
            
        </degrafa:GeometryGroup>
	</degrafa:Surface>
</mx:Application>

CircleCalculatorExample

Read more from Andrew Westberg. Andrew Westberg's Atom feed andrewwestberg on Twitter

Comments

15 Comments

Bob said:

Hi,

Cool article!
Where could we find the AIR LibraryEncrypter application?

Thanks

The LibraryEncrypter application source code is available in the article.

kev said:

Hi Andrew,

This series of articles has been an excellent insight into some methods of protecting swf assets. I agree the Nitro solution sounds like the easiest and most complete to implement. However, the licensing agreements do not suit all applications.

Take for example a project I am soon to be commencing - it will not be mass-marketed or widely deployed and I am less concerned about people decompiling the swfs to see how I 'make things work'. But more so concerned about them seeing how I communicate to backend servers.

Using the information you've provided, I can see how it would be easy enough to encrypt the most sensitive parts of the app and store the key on a remote server. But access to the key, in the application init is where I am a little confused.

Using a username & password is a given but these can easily be shared. Once they have been, decompiling the app init swf and authenticating with these credentials to get the key via a php page or remote object would be relatively easy for those that are interested in decompiling anyway.

The app init could be encrypted also but as AIR lacks the ability to deploy an encrypted local store with the application, I am at a loss as to how I could keep the call to get the key a secret.

Does this make sense?

Do you have any insights you'd like to share?


Thanks for any help!

@kev,
Using the Nitro-LM solution, talking to the server "on-your-own" without using their encrypted library over an encrypted channel to retrieve keys isn't a trivial task as you suggest. The decryption keys coming down from the server are themselves buried under ~5 layers of encryption that can only be interpreted by Simplified Logic's encrypted SWC.

Honestly, with any interpreted language like Java or the Flash player, it's not possible to have something be 100% secure. Nitro-LM was designed to be about 99.5% secure, and so complex that the other 0.5% of people would become quickly frustrated. Along with each layer of its encrypted communication, there are also a number of proprietary obfuscation techniques employed.

kev said:

Hi Andrew, apologies if I mislead you - I didn't mean to suggest that I aim to write something as robust as the Nitro-LM solution. But merely make it more difficult for the contents of a remote database to be accessed. I was hoping there might be another more secure way of accessing remote data or hiding the calls in the swf that I'm failing to think of at present. Sometimes I get sidetracked and 'can't see the forest for the trees' as they say...

I guess another way might be to employ multiple levels of encryption to make it more 'annoying' to decrypt.

Cheers

@kev,
In Nitro-LM, we protect server calls by requiring that they pass an app-id value along with the request. An app can retrieve this from the LM server after a valid authentication and pass it along with any call to your server-side code over https. On the server-side, you can add calls to validate the app-id.

In a sense, the app-id is a token you can pass to the server code so that it knows the request is coming from your own application that has been authenticated using Nitro-LM.

kev said:

Thaks Andrew - some great food for thought there. I'll be sure to offer the Nitro-LM solution as the safest option.

Cheers

Tim said:

This is very interesting. What stops someone decompiling the decrypter to obtain the decryption algorithm and key - even if obfuscation or a service call is used - and using that to decrypt and then decompile the embedded asset?

@Tim,

Good question.

If the individual trying to crack your application is on the inside (having successfully authenticated before using the service call to retrieve a key), there isn't much you can do if the person is sufficiently skilled enough. This is mainly a limitation of the Flash, .NET, and Java platforms in that the bytecode they produce are inherently easy to reverse-engineer. The solution I've presented is designed to keep out 90-95+% of people. A really skilled reverse-engineer hellbent on getting your code with lots of free time is probably going to be successful in their goals.

Don said:

tim has a good point, your code is still in the swf and can be extracted easily. Especially since your are using an automated tool, takes a few minutes to enhance any reverseengineering too to detect this and extract the real code.

If you want to be sure no one steals your code, you still have to use a tool like irrfuscator for flex or flashncrypt for flash.

@Don,

Thanks for your comments.

While obfuscation can be good for small projects, it quickly can become a babysitting job for a large project. It requires you double-test your code since the code you write isn't the code you run. You have to ensure that the obfuscator hasn't introduced an issue into your code.

In the above example, you're correct that this isn't terribly hard to reverse engineer for a skilled programmer. What I was referring to in the comments was a different method of storing the key server-side (as is used in the Nitro-LM tools). It's delivered over an encrypted channel and decrypts Flex/AIR modules in memory. There's no opportunity for a hacker to capture the key so your code stays secure.

I really should write a new article on Module encryption since the three I've done here so far are becoming somewhat dated.

Oscar said:

Dear Andrew,
I'm thinking of using the encryption as you explained it in the example above. I would like to make an "envelope swf" that only reads, decrypts and runs my large main.swf (encrypted) application file.
Is that possible/smart/stupid?
Can you give a hint on how I can best structure the envelope swf?
Thanks.
Oscar

@Oscar,

It is possible to create a wrapper swf that simply decrypts and runs a main swf file. It's better than nothing, but it does have some drawbacks. The key must be stored server-side and protected by other security measures, otherwise, your swf is vulnerable to decompilation. You'll also likely suffer a bit of a performance hit unless your application is very small.

In Nitro-LM, we recommend to create a modular application (mx:Module), encrypt them, and use our EncryptedModuleLoader. Asymmetric decryption keys are stored on the server behind the Nitro-LM licensing and authentication system.

This article is becoming quite dated. Take a look at the three video posts about encrypting modules on the Nitro-LM blog.

http://www.simplifiedlogic.com/nitrolm/blog

We're working on a new administration tool. I hope to get some new content and how-tos posted here once that is closer to completed.

Oscar said:

@Andrew,

Thanks for your reply.

I've made the wrapper (using the code from http://blogs.soph-ware.com/?p=14), and it works (sort of) but only to conclude that it doesn't help me much.
In my case, I distribute the "wrapper swf" and "encrypted swf" to be installed and run locally on top of a c++ application. When decompiling the wrapper, it simply loads and decrypts the encrypted swf et voila, everything is in the open again ... Could have thought of that before ...
It was very educational for me, but I'll have to do the decryption from the c++ application.

(Next to that I had problems to get a 100% display of the original swf. Fonts and layout were different at certain points and there was some padding I couldn't get rid of.)

@Oscar,

I'm not associated with the soph-ware website. If you want a true C++/Flex solution, I'd check out Nitro-LM (disclaimer, I consult for them). They have either a C++ dll, or a Flex SWC you can hook into to retrieve keys from the server-side to handle encryption/decryption. It's a public/private key system, and the keys are delivered over a secure connection after the user authenticates, so nothing is ever in the clear.

Leave a comment


Tag Cloud

Question of the Week: Dream App

If you had an unlimited budget and unlimited resources what application would you build and why would you build it?

Answer

Latest Features

Recommended for You

@InsideRIA on Twitter

Archives

  • Or, visit our complete archive.  

About This Site

Welcome to the premiere community site for all things RIA sponsored by O'Reilly Media and Adobe Systems Incorporated.