Encrypting Flex RSLs and Libraries

Encrypting Flex RSLs and Libraries

I have been working on a new ThunderBay SaaS product for months now. And two of the myriad issues I have had to consider is that of user management and licensing and intellectual property protection (IPP). Now many people will argue that one should not protect source code as IP, but rather business processes as IP. When it comes to online Flex products, however, I could not disagree more![1]

Do you know how ridiculously easy it is to reverse-engineer/decompile a Flex 3 application and its AVM byte code?

Try it: simply use SWFDecompiler to open any SWF. Even if that .swf file has been optimized for production release, you can still see the classes, methods, and logic. If you are developing an online Flex solution, then you had better worry about protecting your source code. Why lose your invest of time and effort and let competitors steal your ideas and solutions? Patents and trademarks are not the issue here. Those are ex post facto protection mechanisms. At the very least, using source-code IPP will present an immediate “barrier-to-entry” to your competitors.

Now, do not misunderstand the intent of this blog. I am a huge proponent of mentoring, open-source, and the community-driven evolution of software. Google and blog entries have been radical saviors to many technical roadblocks and bugs that I have encountered. In return, I reciprocate by blogging solutions and contributing to open-source (Cairngorm Extensions/CGX, Mate Localizations, Flex Behaviour Injection, etc.) I do not, however, open source ALL my software or solutions.

Do you deserve to be bitch slapped?

As a consultant or developer working on a disruptive solution you would be have to be an idiot to not protect your core IP and your core source.

Consequences of Not Encrypting
Figure 1: Consequences... if you do not protect!!

So how do you protect your source code? Really only two (2) viable solutions exist for Flash/Flex solutions: code obfuscation or encryption. Source or byte code obfuscation works well for some purposes; but may present real problems with non-trivial Flex architectures. Encryption, however, is more secure and also provides opportunities for you to track customer usage and authorize access.

You can "grow-your-own" encryption mechanism and embed the decryption key in your code. Or you can use a client-cloud model to request your decryption key from a cloud-based service. Nitro-LM is a hosted, commercial technology platform that provides user management, product licenses, and software protection (encryption) for Java, .NET, Adobe Flex/AIR, C/C++ software. Nitro-LM administration, user authorization, and key creation/access is all managed online as a hosted software-as-a-service (SaaS). And while the license services are offered as an annual subscription, the yearly cost to provide RSA encryption for my software product IPP is trivial. Granted, if you have a free product, then a subscription-based licensing model may not work for you. If you have a free product, I would contend that you probably do not have a viable revenue model in the first place… so why protect it?

You say: “I can implement my own encryption scheme!”

Anyone who has ever tried to write user management or user license modules recalls that those features and the associated issues of maintenance & availability quickly become a "full-time job". The broad suite of features and functionality offered by Nitro-LM is amazing and the support is excellent (shout out to Andrew Westberg). So sign-up for a webinar and check it out for yourself; and no I do NOT get any compensation for my promotion.

For the purpose of this blog, I will be presenting a solution for IP source-code protection using Nitro-LM and "encryption of Flex RSLs". Nitro-LM already publishes solutions for encrypting your Flex application or a Flex "module". But nothing was available for encrypting a Flex RSL (aka shared library). So I created that solution, released it as open-source to SimplifiedLogic, and now present it to you. Shown below is a live demo of a Flex app using a custom preloader [display] and dynamic RSL decryption:

Custom Preloader with Dynamic RSL Decryption
Figure 2: Custom Preloader with Dynamic RSL Decryption (click to launch)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
< ?xml version="1.0" encoding="utf-8"?>
<mx:application layout="absolute" 
				minWidth="1024" minHeight="768" 
				backgroundColor="#777777"  backgroundGradientColors="[#777777,#BABABA]"
				preloader="tb.preloader.ThunderPreloader"				creationComplete="onAppReady();"
				xmlns:mx="http://www.adobe.com/2006/mxml" 
				xmlns:adobe="http://www.adobe.com/2006/cairngorm">
 
 <!--
	Note the ThunderBay loading indicator is shown because
    of the preloader reference to ThunderPreloader; which is NOT
    a preloader but a subclass of DownloadProgressBar
 -->
 
 <mx:script>
	< ![CDATA[
		import mx.controls.Alert;
 
		import tb.utils.DelayedCall;
 
		private function onAppReady():void {
			DelayedCall.schedule( showStatus,[],600);
 
			function showStatus():void {
				var msg : String = 'Finished: CGX encrypted RSL was decrypted & loaded using Nitro-LM!';
				mx.controls.Alert.show(msg,'Encrypted RSLs Loaded');	
			}
		}
	]]>
 </mx:script>
 
 <!-- A class from the encrypted RSL CairngormExtensions.swf -->
 <adobe:frontcontroller id="myController" />
 
</mx:application>

This demo loads an encrytped CaignormExtension RSL. Download this encrypted RSL directly and try to decompile it! Now download and decompile the standard version that is NOT encrypted. The latter version you can get access to class code and logic. The former is completely locked and unreadable. I love this feature; encryption is awesome!

Why should I use RSLs with my Flex/AIR application?

Flex RSLs act as shared libraries (similar to DLLs on Windows), require only a single download [to the browser/flashplayer] and can be shared among multiple Flex applications. These RSLs can be optimized for small footprint sizes to achieve faster download times. And best of all, you can update an RSL with patches, upgrades, etc… without requiring new downloads of any other RSLs. This power comes with a small price to start-up time and complexities to builds and source-code organization. For a SaaS solutions, especially, I think RSLs are critical requirements. For deeper learning, check out

  1. Adobe LiveDocs "Introduction to RSLs"
  2. Adobe Labs "Flex 3 RSLs"

You need to know how RSLs are loaded.

To understand the process of RSL encryption/decryption, you must first be familiar with how RSLs are loaded. First, do you need to know an interesting secret about a Flex application! A Flex application is constructed as a 2-frame movie. The first frame of a Flex application movie contains on the very critical classes including the SystemManager, the Preloader, the DownloadProgressBar and some glue helper classes.[2] The second frame of a Flex SWF contains the rest of the Flex framework code, your application code and all of your application assets like embedded fonts, images, etc. This means it takes a lot longer to get all of the frame 2 downloaded than the small stuff in frame 1.

The .swf format is a progressive download format

Which means that the Flash player can access content on downloaded frames without having to wait for subsequent frames/the entire file to download. By creating a 2-frame movie, Flex applications can take advantage of the streaming support built into the Flash Player and a preloader can appear before all of the Flex framework code and your application code are downloaded. The visible benefit of this whole idea is the loading bar. Even on a large app, Flex quickly shows that it is loading (Frame1 downloaded) and gives continual feedback to the user while the rest of the app downloads (Frame 2).

So here are the steps involved in the startup of a Flex application:

  1. First, enough bytes for frame 1 are streamed down to the Flash Player.
  2. The Flash Player executes those bytes by creating a SystemManager instance.
  3. SystemManager instruct the Flash Player to stop at the end of frame 1.
  4. SystemManager then goes on to create the Preloader which creates the DownloadProgressBar control and pops that up on the client screen.
  5. The Preloader then starts tracking the rest of the bytes streaming in from the Flex SWF (or from external SWFs).
    Once all the bytes for the Flex framework and application code are in, the System Manager goes on to frame 2 and instantiates the Application instance.

    • All RSLs and their associated classes are now loaded [into the ApplicationDomain]; note that the load order is in the order defined during app compile time.[3]
    • All class definitions used by the application (views, logic, etc) must be loaded prior to Frame 2. It is here, in Frame1, that your preloader code can even install patches to the Flex framework BEFORE the framework classes themselves are loaded.
    • This technique is called "monkey-patching" and will be used later
      when we are discussing the decryption process.
  6. Once the Application instance has been created, the SystemManager sets Application.systemManager to itself. This is how you, the application developer, can access the SystemManager at a later time.
  7. The Application dispatches the preinitialize event at the beginning of the initialization process.
  8. Application goes on to create its children. The method createChildren() is called on the application. At this point each of the application’s components is being constructed, and each component’s createChildren() will be also called. For detail, look at component lifecycle section.
  9. The Application dispatches the initialize event, which indicates that all application’s components have been initialized. However, at this state, all the components are not yet laid out.
  10. Eventually, once all the Application child controls and containers have been created, sized and positioned, the Application dispatches the creationComplete event.
  11. Once the creationComplete event has been dispatched, the Preloader removes the DownloadProgressBar control and the SystemManager adds the Application instance to the Flash Player display list. (The Flash Player display list is basically the tree of visible or potentially visible objects that make up your application. When you add and remove child components to your application, your basically just adding and removing them from the display list).
  12. Once the Application is added to the Flash Player display list, the Application dispatches its applicationComplete event
  13. The Application has been created and is up on the screen ready to be interacted with.
Better RUX using custom preloaders.

A well-known technique for displaying your own startup splash screen is to subclass the DownloadProgressBar control [or implement the IPreloaderDisplay interface]. Your custom display class can be used during the startup and that class can listen for 6 types of events:

  • ProgressEvent.PROGRESS
  • Event.COMPLETE
  • FlexEvent.INIT_PROGRESS
  • FlexEvent.INIT_COMPLETE
  • RSLEvent.RSL_PROGRESS
  • RSLEvent.RSL_ERROR

The last two are the interesting part… with those you can listen to events for RSL loading and errors. And, it is during the preloader event dispatching phase that you will manage/decrypt 1 or more encrypted RSLs. Here are the steps involved:

  1. The FlashPlayer loads an encrypted RSL/swf and dispatches an RSLError event; normally this kills the preload process and your application "hangs".
  2. Intercept that event and kill all event propogation to any other listeners; which effectively pauses all subsequent loading of other RSL still in the pending queue.
  3. Using Nitro-LM, asynchronously request a RSA decryption key
  4. Reload the encrypted RSL (in order to get clear access to the swf bytecode array)
  5. Decrypt the byte array
  6. Load the decrypted bytecode into the AVM
  7. Notify the Preloader to resume loading all remaining pending RSLs

So let’s look at the AS3 code for the custom display class that also supports RSL decryption:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package tb.preloader
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.ProgressEvent;
 
import mx.core.RSLItem;
import mx.core.RSLListLoader;
import mx.core.mx_internal;
import mx.events.RSLEvent;
import mx.preloaders.Preloader;
 
import tb.preloader.nitrolm.NitroDecryptor;
 
use namespace mx_internal;
 
public class ThunderPreloader extends SplashDisplay
{
/**
 * Constructor for a Preloader subclass that supports on-demand RSL decryption
 * 
 */
public function ThunderPreloader() {
	super();
}
 
 
/**
 * The code in the preloader setter below uses listeners to intercept RSLErrors and 
 * handles the loading and decryptiong of encrypted RSLs. 
 * 
 * 		Note: The parent class is responsibile for the 
 * 			  PROGRESS, COMPLETE, INIT_PROGRESS, INIT_COMPLETE events and handlers
 * 
 * @param value IPreloaderDisplay subclasses used as interface to startup processes; that have been initiated by SystemManager. 
 */
override public function set preloader(value:Sprite):void {
	super.preloader = value;
 
	_preloader = (value as Preloader);
	_preloader.addEventListener(RSLEvent.RSL_PROGRESS, 	onRSLDownloadProgress );
	_preloader.addEventListener(RSLEvent.RSL_ERROR, 	onRSLEncryptionDetected, false, 999);
 
}
 
// **************************************************************
//  RSL Event Handlers
// **************************************************************
 
/**
 * Called during initial RSLEvent.RSL_ERROR; in our case
 * this is expected because the RSL is encrypted and cannot be properly loaded.
 * Kill the event propogation and invoke the NitroLM decryptor to reload a decrypted version 
 * of the RSL.
 * 
 * @param event  Initial RSLEvent.RSL_ERROR
 * 
 */
private function onRSLEncryptionDetected(event:RSLEvent):void {
	// Could be encrypted so reload it and check.
	event.stopImmediatePropagation();
	_decryptor.loadAndDecrypt(event.url, onRSLDecryptedAndLoaded);}
 
/**
 * Method invoked when the encrypted RSL has been decrypted and loaded into the AVM.
 * This causes the next RSL to start loading (if any available). 
 * @param event
 * 
 */
private function onRSLDecryptedAndLoaded(event:Event):void {
	// We must manually force the Preloader to continue load all subsequent RSLs
	// that are in the queue (after the current RSL has processed/loaded).
	Preloader(_preloader).mx_internal::resume();	
}
 
/**
 * Show the progress of the current RSL download relative to the total percentage 
 * Not relevant to 
 * @param event RSLEvent to update progress of load each RSL
 * 
 */
private function onRSLDownloadProgress(event:RSLEvent):void {
	// Now that are tracking on RSL loads, remove generic listener...
	_preloader.removeEventListener(ProgressEvent.PROGRESS, 	onDownload_Progress);
 
	centerOnScreen();
	progressBar.progress = calculatePercentDone(event);
	if (progressBar.visible && !isReady) progressBar.visible = false;
}
 
// **************************************************************
//  Private Attributes
// **************************************************************
 
private function calculatePercentDone(event:RSLEvent):Number {
	var amountPerRSL   : Number = DOWNLOAD_PERCENTAGE / event.rslTotal;
	var alreadyLoaded  : Number = amountPerRSL * event.rslIndex; 
	var currentLoading : Number = amountPerRSL * (event.bytesLoaded / event.bytesTotal);
 
	return  alreadyLoaded + currentLoading;
}
 
 
/**
 * Decryptor facade class that hides complexities of asynchronous decryption 
 * of encrtyped RSL.
 * 
 * @private 
 */
private var _decryptor      : NitroDecryptor = new NitroDecryptor();
 
// **************************************************************
//  Monkey-Patch Attributes (to force linker to load the patched classes)
// **************************************************************
 
private var _preloader     : Preloader;
private var _unused4       : RSLListLoader;	
}
}

Because the Flex 3 & 4 mx framework never planned for the use of encrypted RSLs, any RSLError event immediately stops the current and all other RSL load processes. In our situation, however, we do not consider an "encryption error" to be a real error… so we have to "fix" or monkey-patch the Flex codebase to support new "resume()" functionality.

Don’t worry, I have provided all that "patch" code in source code for this blog! In order to employ RSL encryption within your own Flex application(s), you will need:

  • A Nitro-LM account and product encryption keyset[4]
  • An application configured to load 1 or more RSLs during startup
  • A custom DownloadProgressBar display with support for RSL decryption
  • An Ant script to encrypt your RSL/swf (shown below)

Below is a sample ANT script that you can use to encrypt a target Flex library using a Nitro-LM RSA private key:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
< ?xml version="1.0" encoding="utf-8"?>
<project name="Deploy EncryptedRSL Demo" basedir="." default="encrypt.compiled.swc" >
 
	<property name="library.name" 	value="CairngormExtensions" />
 
	<property name="flexsdk.dir" 	value="/Users/thomasburleson/Documents/dev/flexSDK" />
	<property name="bin.dir" 		value="/Users/thomasburleson/Documents/dev/frameworks/CGX/build" />
	<property name="deploy.dir" 	value="/Users/thomasburleson/Documents/dev/samples/nitroLM/Flex4_encryptedRSL/bin-debug" />
	<property name="build.dev" 		value="/Users/thomasburleson/Documents/dev/samples/nitroLM/Flex4_encryptedRSL/build" />
	<property name="tools.dir" 		value="${build.dev}/tools/build" />
 
	<target name="encrypt.compiled.swc" depends="">
		<encryptswc rsl-dir="${bin.dir}/rsl" swc-dir="${bin.dir}" swc-name="${library.name}" deploy-dir="${deploy.dir}" />		<delete dir="${bin.dir}/rsl" quiet="true"/>
 
		<echo message="Finished build and encryption of: ${deploy.dir}/${library.name}.swf "/>
	</target>
 
 
		<!--
			Ant Definitions and Macros
		-->	
 
		<taskdef name="nitrolm-encrypt" classname="com.simplifiedlogic.nitrolm.LMEncryptAsset" classpath="${tools.dir}/jars/AssetEncrypterX.jar"/>
 
		<macrodef name="encryptSWC">
			<attribute name="rsl-dir"/>
			<attribute name="swc-dir"/>
			<attribute name="swc-name"/>
			<attribute name="version-stamp" default="" />
 
			<attribute name="deploy-dir"       default="${deploy.dir}" />
			<attribute name="encrypt-keys-dir" default="${tools.dir}/nitro_lm/keys" />
 
			<sequential>
				<!--   Process
 
					1) Extract the SWF from the SWC (RSL that should be encrypted)
				    2) Optimize the RSL (remove debug info and compress)
				    3) Encrypt the SWF/RSL with Nitro-LM tools and keys
				    4) Deploy the encrypted RSL
				    5) Update the target SWC digest with the encrypted RSL checksum 
				       - The "parent" app that loads the RSL uses the SWC/updated digest value to
				       - insure that the RSL is the "expected" RSL and not a pirated one!
				-->
 
				<unzip src="@{swc-dir}/@{swc-name}.swc"
					dest="@{rsl-dir}">
					<patternset>
	                   <include name="library.swf" />
					</patternset> 
				</unzip>
 
				<java jar="${flexsdk.dir}/lib/optimizer.jar" fork="true" failonerror="true">
					<jvmarg line="-ea -DAS3 -DAVMPLUS -Dflexlib=${flexsdk.dir}/frameworks -Xms32m -Xmx384m -Dsun.io.useCanonCaches=false"/>
					<arg line="'@{rsl-dir}/library.swf' --output '@{rsl-dir}/library.swf' --keep-as3-metadata='Bindable,Managed,ChangeEvent,NonCommittingChangeEvent,Transient' "/>
				</java>
 
				<!-- encrypt the swf to protect 
					 Here, I am using my private key downloaded from my Nitro-LM account
					 @ https://license.nitromation.com/NitroAdminLite/index.html
				-->
				<nitrolm -encrypt
					filename="@{rsl-dir}/library.swf"
					product="6LMLulqf9VN9AmbFzKky"
					library="6LMLulqf9VN9AmbFzKky"
					keydir="@{encrypt-keys-dir}" />
 
 
				<delete file="@{deploy-dir}/@{swc-name}.swf" />
				<copy file="@{rsl-dir}/library.swf" tofile="@{deploy-dir}/@{swc-name}@{version-stamp}.swf" />
 
				<!-- Update the digest value in the .swc so linking applications will use the updated digest value 
				     Do not worry about the library.swf inside the SWC being the original version...
				-->
				<java jar="${flexsdk.dir}/lib/digest.jar" fork="true" failonerror="true">
					<jvmarg line="-ea -DAS3 -DAVMPLUS -Dflexlib=${flexsdk.dir}/frameworks -Xms32m -Xmx384m -Dsun.io.useCanonCaches=false"/>
					<arg line="--digest.rsl-file  @{rsl-dir}/library.swf --digest.swc-path  @{swc-dir}/@{swc-name}.swc"/>
				</java>
 
 
			</sequential>
		</macrodef>
 
 
</project>

Click here to download the FlashBuilder 4 Demo project (and ant build script) used for the demo above. You can even use the Custom Preloader display solutions to rapidly create your own.

So what is next? Well, if you have non-trivial flex applications you may want to deliver some of the following support:

  • want to partition your code into multiple RSLs ?
  • need to provide on-demand compiles and encryption of multiple RSLs[5] ?
  • want to version stamp those RSLs based on tags in SVN, CVS, or GIT ?
  • want to optimize filesizes for all deployed, production components ?
  • want to auto-deploy components with version-stamps[6] ?
  • want to configure CruiseControl for automated builds and deploys ?

If you need help with these, contact me. My professional life is as an RIA Flex/Flash consultant, entrepreneur, developer, architect, and mentor. So give me a shout out at Thomas dot Burleson at ThunderbaySoft dot com.

Share this:
  1. A quote from Bill Gates: “Who can afford to do professional work for nothing? What hobbyist can put three man-years into programming, finding all bugs, documenting his product, and distributing it for free?” []
  2. Remember, the Preloader is what creates the DownloadProgressBar control which displays the progress bar of a Flex application downloading and being initialized. []
  3. Better yet, you should use ANT scripts to explicity define the load order and resolve any RSL dependencies. []
  4. You can try to implement your own home-grown encryption algorithm. But is it secure? Is it worth the non-trivial efforts ? []
  5. Sophisticated ANT scripts are required for full compiles, optimization, encrytption of multiple RSL and apps. []
  6. Using version-stamped names for your RSLs will force refreshes to browser/cached versions []

7 Responses to “ “Encrypting Flex RSLs and Libraries”

  1. This has to rate as one of the most useful Flex blog posts I’ve ever read, excellent work and thanks. Did Adobe make any changes in Flex 4 to smooth the decryption process, or are the same patch techniques still valid?

  2. Simon Bailey says:

    Excellent article Tom a very good read. Thanks for the clear code solutions also!

    Cheers,

    Simon

  3. Matt Chotin says:

    Tom, have you submitted the monkey patches in JIRA yet? Please make sure we’re actually aware of the work :-)

    • thomasb says:

      Matt,
      Thanks for the reminder about JIRA. I created bug #SDK-24076 with upload/attached code for the monkey patches to mx.core.*.

      Thanks again.

  4. Wow Tom. You really took RSL encryption to the next level.

    • thomasb says:

      Andrew,
      The great thing about this solution is that is works [without any changes] for encrypted or standard RSLs. So even in development mode, it works auto-magically.
      Now to get Adobe to incorporate the “monkey patches” into Flex 4.

Trackbacks/Pingbacks

  1. Tweets that mention RSA Encryption for Flex 4 RSLs - GridLinked to RUX -- Topsy.com - [...] This post was mentioned on Twitter by Gilles Guillemin and Arul Kumaran, Flash Builder Wiki. Flash Builder Wiki said: ...
  2. uberVU - social comments - Social comments and analytics for this post... This post was mentioned on Twitter by 360Flex: awesome blog post on protecting IP, ...

Leave a Reply

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

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">

Please leave these two fields as-is: