Moris' Note Book

本博客所有内容皆收集于网上,非本人原创,非心情日记,非研究心得,只是自己浏览的东西的收集
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Encryption in Flex Applications 1 - Simulate EncryptedLocalStore(RT)

Posted on 2008-06-20 13:08  moris  阅读(229)  评论(0)    收藏  举报

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