Moris' Note Book

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

Encryption in Flex Applications 3 - NitroLM SWF Encryption(RT)

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

Encryption in Flex Applications 3 - NitroLM SWF Encryption

AddThis Social Bookmark Button

NitroLM (Nitromation License Manager) is a commercial product developed by Simplified Logic, Inc. (SLI) for managing users, products, notifications, and source code protection for applications. It started off as an internal tool to protect SLI’s 3D engineering design and automation software. It quickly grew into a commercial application after some of SLI’s Fortune 500 customers got an appreciation for the design and philosophy behind the NitroLM solution. NitroLM has an API for C/C++, Java, .NET, and recently announced support for Adobe Flex/AIR at 360Flex in Atlanta along with a new marketing video entitled "The Pain of Software Piracy".

 

NitroLM_architecture.jpg

The architecture and implementation are fairly straight-forward. SLI has redundant license servers placed in locations around the continental US. SLI provides you with a SWC file to include in your application that communicates with license servers over a secure channel. The interface allows you to manage user registration, various license options (demo/time-limited/floating/checked-out), support/enhancement requests, and sign-on. The only end-user requirement is an Internet connection in order to retrieve a license. A more complete list of features can be found at http://nitrolm.com/Features.html.

You administer a pool of licenses for your company and create and manage your products through a Flex application developed by SLI. The NitroLM API is for communication-only, so the look and feel is up to you. It’s also important to decide what licensing process and features you want to enable in your applications.

Encryption in NitroLM goes beyond what I’ve been able to show in the previous two articles. NitroLM stores decryption keys on the server that can only be accessed after a valid license has been retrieved. The encryption is public/private key encryption as opposed to simple shared-secret encryption.

The process for encrypting an application using NitroLM is to first develop an application you want to protect. I’m going to be lazy and download some example code and call the project UnencryptedFlexApp.

UnencryptedFlexApp example

In order to encrypt this application, we first need to create a wrapper project that will handle the login to NitroLM, download a decryption key over a secure connection, and decrypt and display our SWF file.

NitroEncryptedWrapper_projectstructure.png

I’ve created a new project called NitroEncryptedWrapper. Main.swf from UnencryptedFlexApp has been copied into my src folder along with any loose assets and data used by it.

 

 

 

 

nitroadmin_productkeys.png

NitroAdmin is the tool used to administer the NitroLM solution. First, I create a new product in the system along with a license key and an encryption key. Both keys are downloaded and placed into my project. The .ser file is a public key which is used to secure the communication to NitroLM. It is unique to each product in the system, and only the server can decrypt messages sent to it with the private key. This prevents any potential interception of the NitroLM server communication. The .vser file is the private key used for encrypting main.swf. We’ll download this key at runtime over a secure connection in order to decrypt our app only after a successful NitroLM authentication has happened. We do not want to distribute either of these files with our application as assets, so pay careful attention during deployment.

nitroadmin_poolsetup.png

Next, I add my product to an existing license pool. If we were setting this up for a new customer, we would have created a new pool of licenses.

 

 

 

 

swfencrypter.png

The next order of business is to encrypt main.swf using SLI’s SWFEncrypter AIR application. I drag/drop the .ser, .vser, and main.swf onto the SWFEncrypter and click Run.

 

 

 

 

 

 

In the code, I need to embed the encrypted main.swf file and create a connection to NitroLM by using the LicenseClientFactory class. This will be used to create an ILicenseClient compatible object. All communication to NitroLM happens through the ILicenseClient interface. The constructor for LicenseClientFactory takes an event handler function as a parameter. This function will be called once everything NitroLM-related is initialized and ready for use. I also create a ByteArray called decryptedSwfBytes. This ByteArray is bound to a SWFLoader object in mxml with autoLoad=false. We don’t want to load up the embedded swf file until we’re completely done decrypting.

 

//Put the Flex App's SWF file here that you want to decrypt and run
[Embed(source="main.swf", mimeType="application/octet-stream")]
private var encryptedSwfFile:Class;
private var mainClassName:String = "main";
			
private var encryptedSwfBytes:ByteArray = new ByteArray();
[Bindable]
private var decryptedSwfBytes:ByteArray = new ByteArray();
			
private var serPublicKey:ByteArray = new ByteArray();
private var publicKey:RSAKey = null;
private var productName:String = null;
			
private var licenseClientFactory:LicenseClientFactory = new LicenseClientFactory(initNitroLM);
private var licenseClient:ILicenseClient = null;
NitroEncryptedWrapper_beforelogin.png

In initNitroLM(), we validate our license and retrieve a decryption key if we have a license. Otherwise, we pop up a Login dialog box to retrieve a license.

 
/**
 * Load the swf file into a ByteArray and check for a license on startup
 */
private function initNitroLM(event:Event):void
{
 	serPublicKey.length = 0;
 	encryptedSwfBytes.length = 0;
 	if(encryptedBlobBytes != null)
 		encryptedBlobBytes.length = 0;
 				
 	//get the basic data from the encrypted blob
 	var encryptedBlobBytes:ByteArray = new encryptedSwfFile();
 	productName = encryptedBlobBytes.readUTF();
 	var keyLength:int = encryptedBlobBytes.readInt();
 	encryptedBlobBytes.readBytes(serPublicKey, 0, keyLength);
 	encryptedBlobBytes.readBytes(encryptedSwfBytes);
 
 	//save the public key in the NitroLM Interface
 	//so we can communicate to the server over an
 	//encrypted channel
 	ProductKeys.putPublicKey(productName, serPublicKey);
 				
 	licenseClient = licenseClientFactory.getInstance();
 				
 	var licenseValues:HashMap = new HashMap();
 	var response:int = licenseClient.validate("0.1", productName, licenseValues);
 	if(response == NLMConstants.RESPONSE_OK)
 	{
  		trace(licenseValues.dump());
  		//we already have a license, get the decryption key from server
  		licenseClient.addEventListener(LicenseClientEvent.LICENSE_RESPONSE, handleRequestKey);
  		licenseClient.requestKey(productName, "0.1");
  	}
 	else
 	{
  		//retrieve a license.  pop up dialog box here
  		var login:Login = new Login();
  		login.licenseClient = licenseClient;
  		login.product_name = productName;
  		login.addEventListener("success", loginSuccess); //re-run init
  		login.addEventListener("failure", loginFailure);
  					
  		PopUpManager.addPopUp(login, this, true);
  		PopUpManager.centerPopUp(login);
  		login.email.setFocus();
  	}
}
NitroEncryptedWrapper_afterlogin.png

If we get a valid license, we request a key and handle the event that comes back from the server. We then decrypt and set the SWFLoader to visible.

 

/**
 * This method retrieves a decryption key from the server based on
 * our valid and authenticated license we retrieved
 */
private function handleRequestKey(event:LicenseClientEvent):void
{
 	licenseClient.removeEventListener(LicenseClientEvent.LICENSE_RESPONSE, handleRequestKey);
 	if(event.response == NLMConstants.RESPONSE_OK)
 	{
  		var retVals:HashMap = event.data;
  		var key:ByteArray = retVals.find("key");
  		key.position = 0;
  		var rsaKey:RSAKey = ProductKeys.readRSAPublicKey(key);
  		decryptedSwfBytes.length = 0;
  					
  		//now decrypt and load the swf file
  		rsaKey.verify(encryptedSwfBytes, decryptedSwfBytes, 128);
  					
  		decryptedSwfBytes.writeBytes(encryptedSwfBytes, encryptedSwfBytes.position);
  
  		loader.load();
  		loader.visible = true;
  	}
 	else
 	{
  		Alert.show(NLMConstants.responseToString(event.response), "Error");
  	}
}

See the source code for additional details and to view the Login dialog box code.
NitroEncryptedWrapper example