Reader Level:
Articles

Asset Encryption & XNB Security

By Ibrahim Ersoy on March 03, 2010
In this article I’ll tell you how you can Encrypt your Assets - that ContentManager in XNA builds automatically - in a simple way.
  • 0
  • 0
  • 9200
Download Files:
 

In this article I'll tell you how you can "Encrypt" your Assets -which ContentManager in XNA builds automatically- in a simple way.

There will be 2 kinds of applications we'll be developing...

1) "Find&Replace Text Algorithm" 2) Rijndael algorithm. While I was developing with Managed DirectX 3 years ago(MDX's best years :P),I used this Find&Replace Algorithm for encrypting my  3D Models(.x files).We will be out-of-standards while writing a Find&Replace Algorithm, because of not using Content Importers' way

Let's get back to our subject, shall we? If you would like to protect your assets that you have created by working lots of time on it, then make sure listen what I'll tell you in this article. There file types that don't have Content Importer by default. You can write your own Importer but in our article we will build our own Sound System to call MP3 files and then encrypt these files. You can also encrypt your video files in which XNA 3.1 supports it with a Video Content Importer. But we shall write our own Video System as well...

File Types we will be encrypting and their algorithms

.FX(Shader files) - Rijndael algorithm
.DDS(Texture Dosyası) - Rijndael algorithm
.X(DirectX Model) - Find&Replace and Rijndael algorithm
.FBX(Autodesk 3D Model) - Rijndael algorithm
.MP3(Music File) - Rijndael algorithm
.WMV(Video File) - Rijndael algorithm
.LUA(Lua Script) - Rijndael algorithm

Alright then let's start...

First of all because of we will be using "Find&Replace Algorithm" we will be working with ".X File". And after that we will be working with Rijndael Algorithm in which we're going to encrypt any kind of file.

First of all let me show you how to export a 3D Model as an ".x" File Type:

Step-1: Download 3DS MAX {version}  ".x Exporter" from andytather.co.uk

1.gif

http://www.andytather.co.uk/Panda/directxmax_downloads.aspx

Above is the shortcut to DirectX Exporter for 3DS MAX...

Step-2: Copy the "Exporter" in the specified location

After extracting archive copy the "PandaDirectXMaxExporter_x86.dle" file to your "C:\Program Files\Autodesk\3ds Max {versiyon}\stdplugs" directory. After that 3DS MAX will recognize the plug-in.

Step-3: Exporting 3D Model

2.gif

From Max's menu Click "Export->Export" & then wait for the Save FileDialog to open

3.gif

Choose Panda DirectX  and assign a name to the file. After clicking the save we will have this kind of Dialog Window.

4.gif

You can set anything here. After finishing, click on the OK button which will export the 3D ".x" model.

I showed you how to export  an ".x" 3d model here which is a very old 3D Model.

Open the .X file in a Notepad-Like application.
xof 0303txt 0032
template ColorRGBA {
 <35ff44e0-6c7c-11cf-8f52-0040333594a3>
 FLOAT red;
 FLOAT green;
 FLOAT blue;
 FLOAT alpha;
}

This filetype is one of the most easy to read 3D Model Type that's why we will have no problem using Find&Replace Algorithm.

Now  let's setup our XNA Application...

Step-1: Creating our Application

5.gif

Step-2:  Add reference to "System.Windows.Forms"  and use it on your project

using
System.Windows.Forms;

Step-3: Setting Control System

Declaring Variables:

MenuStrip
menuStrip1 = new MenuStrip();
ToolStripMenuItem encodeStripMenuItem = new ToolStripMenuItem();
ToolStripMenuItem encodeopenStripMenuItem = new ToolStripMenuItem();
ToolStripMenuItem decodeStripMenuItem = new ToolStripMenuItem();
ToolStripMenuItem decodeopenStripMenuItem = new ToolStripMenuItem();

and then in the "Initialize()" function assign its properties

menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            encodeStripMenuItem,decodeStripMenuItem});
            menuStrip1.Location = new System.Drawing.Point(0, 0);
            menuStrip1.Name = "menuStrip1";
            menuStrip1.Size = new System.Drawing.Size(877, 24);
            menuStrip1.TabIndex = 0;
            menuStrip1.Text = "menuStrip1";
            menuStrip1.BackColor = System.Drawing.Color.CornflowerBlue;

            // Encode

            encodeStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {

            encodeopenStripMenuItem});

            encodeStripMenuItem.Name = "encodeStripMenuItem";

            encodeStripMenuItem.Size = new System.Drawing.Size(37, 20);

            encodeStripMenuItem.Text = "Encoding";

            // Encode Open

            encodeopenStripMenuItem.Name = "encodeopenStripMenuItem";

            encodeopenStripMenuItem.Size = new System.Drawing.Size(152, 22);

            encodeopenStripMenuItem.Text = "Encode";

            encodeopenStripMenuItem.Click += new EventHandler(encodeopenStripMenuItem_Click);

            // Decode

            decodeStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {

            decodeopenStripMenuItem});

            decodeStripMenuItem.Name = "decodeStripMenuItem";

            decodeStripMenuItem.Size = new System.Drawing.Size(37, 20);

            decodeStripMenuItem.Text = "Decoding";

            // Decode Open

            decodeopenStripMenuItem.Name = "decodeopenStripMenuItem";

            decodeopenStripMenuItem.Size = new System.Drawing.Size(152, 22);

            decodeopenStripMenuItem.Text = "Decode";
decodeopenStripMenuItem.Click += new EventHandler(decodeopenStripMenuItem_Click);      

Control.FromHandle(Window.Handle).Controls.Add(menuStrip1);

Step-4: Adding an .X file(With its Textures)

6.gif

As you can see we have added the files by Right clicking Project and then  "Add Existing Items"

Step-5: Building Find&Replace Algorithm

Let's explain simply what this algorithm really does. Find&Replace Algorithm; searches in a file or string variable & if found replacing the oldvalue with newvalue. We can say that it has two parameters.

How this algorithm works?

When it gets "encode" as a parameter it replaces list_x with pass_x and all we see in the ".x" file is the pass_x list we have build below. If it takes "decode" as a parameter then it makes the reverse which pass_x is replaced with list_x. Let me explain with codes...

Let's write our Class for more details.

Step-6: Writing Find&Replace Algorithm

Create a new class and name it process_x.cs.

using System.IO;

add "System.IO" namespace to your class

Declare 2 string based array:

string
[] list_x = new string[8];
string[] pass_x = new string[8];

Create a function and as they get parameters "encode" or "decode", create a parameter in the function as "val_x" which will store the values. And create another parameter as "file_x" which we will store the ".x" file's path with its filename and extension...

public
void convert(string val_x,string file_x)
{
}

Yes, we have created our function which will convert the files as our Find&Replace Text Algorithm meant to be.

Let's write some code in our function:

int
a = 0;

"a" is an int variable which will help us in list iteration for "encode" and "decode".

string
file_old,file_new;

we declare 2 string variables which will store the replaced file in text.

TextReader
tr = new StreamReader(file_x);
file_old = tr.ReadToEnd();
tr.Close();

by creating a Textreader object we open the file in Read-Mode and storing the files innertext in our variable named "file_old". Then closing our object to finish.

list_x[0]  = "template";
list_x[1]  = "ColorRGBA";
list_x[2]  = "Material";
list_x[3]  = "TextureFilename";
list_x[4]  = "Frame";
list_x[5]  = "Matrix4x4";
list_x[6]  = "Vector";
list_x[7]  = "MeshFace";

pass_x[0]  = "qwe";
pass_x[1]  = "ewq";
pass_x[2]  = "rty";
pass_x[3]  = "ytr";
pass_x[4]  = "uop";
pass_x[5]  = "pou";
pass_x[6]  = "bnm";
pass_x[7]  = "mnb";

This is our list of "encoded" and "decoded" values. For example; if we would like to encode all the "list_x" array values will be replaced by "pass_x" array values: "template" will be "qwe"

do
 {
  file_new = file_old;
  if (val_x == "encode")
     {
      file_old = file_new.Replace(list_x[a], pass_x[a]);
     }
  else if (val_x == "decode")
     {
      file_old = file_new.Replace(pass_x[a], list_x[a]);
     }               
      a++;
 }
while (a <= 7);

This do..while structure will readtoend of the specified file then change the values if its "encode" or "decode". if encode; pass_x values will replace list_x values and it will mean "encoding", if decode; the reverse will happen...If ".x" file is encoded then it won't be able to open until its "decoded"...

TextWriter tw = new StreamWriter(file_x);
tw.WriteLine(file_old);
tw.Close();

and here we are re-building our ".x" file..

Step-7: Finalizing the application

First of all we will be creating a new instance of the class we built previously:

process_x new_process = new process_x();

Then writing its event handlers...

protected
void encodeopenStripMenuItem_Click(Object sender, EventArgs e)
{
    new_process.convert("encode", "fish.x");            
}
protected void decodeopenStripMenuItem_Click(object sender, EventArgs e)
{
     new_process.convert("decode", "fish.x"); 
}

Step-8: Running the Application & see the results

7.gif


Click the "Encoding" then click "Encode". After that see the results for the ".x" file...

8.gif

Decode/Normal:
xof 0303txt 0032
template ColorRGBA {
 <35ff44e0-6c7c-11cf-8f52-0040333594a3>
 FLOAT red;
 FLOAT green;
 FLOAT blue;
 FLOAT alpha;
}
template ColorRGB {
 <d3e16e81-7835-11cf-8f52-0040333594a3>
 FLOAT red;
 FLOAT green;
 FLOAT blue;
}
....

Encode/Crypted:
xof 0303txt 0032
qwe ewq {
 <35ff44e0-6c7c-11cf-8f52-0040333594a3>
 FLOAT red;
 FLOAT green;
 FLOAT blue;
 FLOAT alpha;
}
qwe ColorRGB {
 <d3e16e81-7835-11cf-8f52-0040333594a3>
 FLOAT red;
 FLOAT green;
 FLOAT blue;
}
....

We have successfully built our Find&Replace Text Algorithm...

We have applied Find&Replace algorithm for an ".x" file and encrypted it then decrypted to be able used.

Now we'll learn more about File Encryption.

But before that wish to create a class for using in our next project

For displaying Videos and playing music files in XNA we will build Video&Sound System without using Content Importers in a much more out-of-standard way.

Let's create our project:

9.gif

Let's add a new class Project-> Add New Item-> Class which we'll be naming it as "VidSnd.cs".

10.gif

Now let's add some references:

By clicking Project->Add Reference we will be adding DirectX & AudioVideoPlayback namespaces to our project. And don't forget to add "System.Windows.Forms" as well

11.gif

Now set up our class as you see below...

using
System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.DirectX.AudioVideoPlayback;
using System.Windows.Forms;

namespace
CryptAsset
{
    class VidSnd
    {
        public static Video vd;
        public static Audio ad;
        public VidSnd()
        {
        }
        public static void movie_open(string file_name,IntPtr ownervid)
        {
            vd =new Video(file_name);
            vd.Owner = Control.FromHandle(ownervid);                      
        }
        public static void movie_play()
        { 
           vd.Play();
        }
        public static void movie_stop()
        {
            vd.Stop();
        }
        public static void movie_pause()
        {
            vd.Pause();
        }
        public static string sound_open(string soundfile)
        {
            ad = new Audio(soundfile,false);
            return soundfile;
        }
        public static void sound_play()
        {
            ad.Play();
        }
        public static void sound_stop()
        {
            ad.Stop();
        }
        public static void sound_pause()
        {
            ad.Pause();
        }
    }
}

As you can see we simply built a Video&Sound System by the help of AudioVideoPlayback which will play, stop & pause Video & Sound files...

Add a video file to your project and call it just like that in your Game class;

VidSnd
.movie_open("Sample.wmv", Window.Handle);
VidSnd.movie_play();

That's it! You will be able to see your Video playing inside your XNA Window ;)

As you covered it, we didn't use any Content Importer which isn't standard by default.

Now let's add much more assets to our project in which we will be encrypting & decrypting assets by using RijnDael Algorithm(AES).

.FX(Shader Files)
.DDS(Texture Files)
.X(DirectX Model Files)
.FBX(Autodesk Model File)
.MP3(Sound Files)
.WMV(Video Files)
.LUA(Lua Script Files)

Let's setup some folders for these files as seen below :

12.gif

Geri kalan Assetlerimizi de projemize dahil edelim

Textures:

These are image files that can be used for covering 3D models, sprite animations. We are adding a simple texture in our project.

Shaders:

We will be adding 2 kinds of shader files; HLSL ve CGFX

HLSL; DirectX & XNA's official shader file type.CGFX is being used in OpenGL by the help of CGFX API.CGFX is designed by Nvidia and we can say that it's Nvidia's official shader file from that point.

We will be adding 2 effects in our file. They both have "Post Glow" Effect.

Both of them are built with Nvidia FX Composer 2.5

Models:

We will be adding 2 kinds of model files. One of them is FBX which is Autodesk Model File and the other one is DirectX ".x" Model.FBX file is house model, .x file is a fish :)

Scripts:

This LUA script is a simple Flag with "LUAEdit".

Flag.lua
local r, g, b
 local x, y
 for y = 0, height - 1 do
   for x = 0, width - 1 do
      set_rgb(x, y, 1,0,0)
   end
   progress(y / height)
 end
 for y = 0.4 * height, 0.6 * height do
   for x = 0.2 * width, 0.8 * width do
      set_rgb(x, y, 1,1,1)
   end
   progress(y / height)
 end
 for y = 0.2 * height, 0.8 * height do
   for x = 0.4 * width, 0.6 * width do
      set_rgb(x, y, 1,1,1)
   end
   progress(y / height)
 end

Let's see how our Solution looks like right now:

13.gif

Set the entire Assets' "Copy To Output Directory" property as "Copy always". Adding different kinds of Asset Types in our project; because of showing you can encrypt & decrypt them in a single Algorithm(Rijndael).These assets are added to be encrypted or decrypted. Sorry for not showing  how to display them together in this sample. Let's build our Rijndael Algorithm...

What's Rijndael Algorithm?

AES was announced by National Institute of Standards and Technology (NIST) as U.S. FIPS PUB 197 (FIPS 197) on November 26, 2001 after a 5-year standardization process in which fifteen competing designs were presented and evaluated before Rijndael was selected as the most suitable (see Advanced Encryption Standard process for more details). It became effective as a Federal government standard on May 26, 2002 after approval by the Secretary of Commerce. It is available in many different encryption packages. AES is the first publicly accessible and open cipher approved by the NSA for top secret information.

The Rijndael cipher was developed by two Belgian cryptographers, Joan Daemen and Vincent Rijmen, and submitted by them to the AES selection process. Rijndael is a portmanteau of the names of the two inventors.

With Rijndael your assets are in safe. And that's the reason I'm writing this article.

Well then after a short review of what's a Rijndael Algorithm is, let's write some code to understand it clearer and encrypt-decrypt our assets...

We will be working on Game1.cs file:

First of all we need to add some namespaces to our project:

using
System.IO;
using System.Security.Cryptography;
using System.Text;

For file-based operations "IO", for Encrypting "Security.Cryptography" & for Text-String based operations "Text" namespaces must be added in our project.

private
string password = "Sample";
MenuStrip menuStrip1 = new MenuStrip();
ToolStripMenuItem encodeStripMenuItem = new ToolStripMenuItem();
ToolStripMenuItem encodeopenStripMenuItem = new ToolStripMenuItem();
ToolStripMenuItem decodeStripMenuItem = new ToolStripMenuItem();
ToolStripMenuItem decodeopenStripMenuItem = new ToolStripMenuItem();
Label fileenc = new Label();
Label filede = new Label();

We are declaring our MenuSystem's variables and some other variables for encryption & decryption which are password & filepaths...

Update your Initialize function as seen below:

menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            encodeStripMenuItem,decodeStripMenuItem});
            menuStrip1.Location = new System.Drawing.Point(0, 0);
            menuStrip1.Name = "menuStrip1";
            menuStrip1.Size = new System.Drawing.Size(877, 24);
            menuStrip1.TabIndex = 0;
            menuStrip1.Text = "menuStrip1";
            menuStrip1.BackColor = System.Drawing.Color.CornflowerBlue; 

            // Encode

            encodeStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {

            encodeopenStripMenuItem});

            encodeStripMenuItem.Name = "encodeStripMenuItem";

            encodeStripMenuItem.Size = new System.Drawing.Size(37, 20);

            encodeStripMenuItem.Text = "Encoding";

            // Encode Open

            encodeopenStripMenuItem.Name = "encodeopenStripMenuItem";

            encodeopenStripMenuItem.Size = new System.Drawing.Size(152, 22);

            encodeopenStripMenuItem.Text = "Encode";

            encodeopenStripMenuItem.Click += new EventHandler(encodeopenStripMenuItem_Click);

            // Decode

             decodeStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {

            decodeopenStripMenuItem});

            decodeStripMenuItem.Name = "decodeStripMenuItem";

            decodeStripMenuItem.Size = new System.Drawing.Size(37, 20);

            decodeStripMenuItem.Text = "Decoding";

             // Decode Open

             decodeopenStripMenuItem.Name = "decodeopenStripMenuItem";

            decodeopenStripMenuItem.Size = new System.Drawing.Size(152, 22);

            decodeopenStripMenuItem.Text = "Decode";
decodeopenStripMenuItem.Click += new EventHandler(decodeopenStripMenuItem_Click);

Control.FromHandle(Window.Handle).Controls.Add(menuStrip1);

And now we will be writing our Encryption code:

private
void EncryptFile(string encpath)
 {

   int position = 0, t = 0;

   while (encpath.IndexOf('\\', t) != -1)

    {

      position = encpath.IndexOf('\\', t);

      t = position;

    }

   string outfile = encpath.Substring(position);

   outfile = outfile.Replace('.', '_');

   fileenc.Text = outfile + ".ibo";

   try

    {

      if (password.Length > 8)

      password = password.Substring(0, 8);

      else if (password.Length < 8)

       {

         int add = 8 - password.Length;

         for (int i = 0; i < add; i++)

           password = password + i;

       }

      UnicodeEncoding UE = new UnicodeEncoding();

      byte[] key = UE.GetBytes(password);

      string cryptFile = fileenc.Text;

      FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create);

      RijndaelManaged RMCrypto = new RijndaelManaged();
      CryptoStream cs = new CryptoStream(fsCrypt,

                    RMCrypto.CreateEncryptor(key, key),

      CryptoStreamMode.Write);

      FileStream fsIn = new FileStream(encpath, FileMode.Open);

      int data;

      while ((data = fsIn.ReadByte()) != -1)

              cs.WriteByte((byte)data);

      MessageBox.Show("Encryption Successful!", "OK");

      fsIn.Close();

      cs.Close();

      fsCrypt.Close();

      File.Delete(encpath);

       }

      catch

       {

        MessageBox.Show("Encryption Failed!", "Error");

       }
}

Now let's explain...

int
position = 0, t = 0;

   while (encpath.IndexOf('\\', t) != -1)

    {

      position = encpath.IndexOf('\\', t);

      t = position;

    }

string outfile = encpath.Substring(position);

outfile = outfile.Replace('.', '_');

fileenc.Text = outfile + ".ibo";

The file's -that will be converted- path is taken and its new name is reset  as replacing "." Characters with "_" and sets the files extension as ".ibo" which I prefer. For example if our asset's name is "wall.x" then this will be created as "wall_x.ibo".

if
(password.Length > 8)

      password = password.Substring(0, 8);

      else if (password.Length < 8)

       {

         int add = 8 - password.Length;

         for (int i = 0; i < add; i++)

           password = password + i;

       }

We have set our password as "Sample" before so by the help of this password we will be using it as a key.

UnicodeEncoding
UE = new UnicodeEncoding();

This will help us encode the asset file as Unicode which is really hard to read.

FileStream
fsCrypt = new FileStream(cryptFile, FileMode.Create);

RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt,

                    RMCrypto.CreateEncryptor(key, key),   CryptoStreamMode.Write);

FileStream fsIn = new FileStream(encpath, FileMode.Open);

We are creating a new file and encrypting it by the help of the keys. As you can see AES is supported in .NET Framework as a part of "Cryptography"  namespace.

And here is the Decryption Code...

private
void DecryptFile(string decpath)

 {

   int position = 0, t = 0;

   while (decpath.IndexOf('\\', t) != -1)

    {

     position = decpath.IndexOf('\\', t);

     t = position;

    }

   string outfile = decpath.Substring(0, decpath.Length - 4);

   outfile = outfile.Substring(position);

   filede.Text = outfile.Replace('_', '.');

   try

    {

     if (password.Length > 8)

         password = password.Substring(0, 8);

     else if (password.Length < 8)

      {

       int add = 8 - password.Length;

       for (int i = 0; i < add; i++)

       password = password + i;

      }

    UnicodeEncoding UE = new UnicodeEncoding();

    byte[] key = UE.GetBytes(password);

    FileStream fsCrypt = new FileStream(decpath, FileMode.Open);

    RijndaelManaged RMCrypto = new RijndaelManaged();

    CryptoStream cs = new CryptoStream(fsCrypt,

    RMCrypto.CreateDecryptor(key, key),

    CryptoStreamMode.Read);

    FileStream fsOut = new FileStream(filede.Text, FileMode.Create);

    int data;

    while ((data = cs.ReadByte()) != -1)

            fsOut.WriteByte((byte)data);

    fsOut.Close();

    cs.Close();               

    fsCrypt.Close();

    MessageBox.Show("Decryption Successful!", "OK");

    File.Delete(decyol);

   }

catch

   {

    MessageBox.Show("Decryption Failed", "Error");

   }           


I don't need to re-tell you what Decryption does as I earlier described Encryption function and here it does the reverse of it...

Now let's Encrypt our Assets!

Update "encryptToolStripMenuItem_Click" function as seen below:

protected
void encryptToolStripMenuItem_Click(Object sender, EventArgs e)

 {

   EncryptFile("Textures/Davinci.jpg");

   EncryptFile("Shaders/Post_glow.cgfx");

   EncryptFile("Shaders/Post_glow.fx");

   EncryptFile("Models/house.fbx");

   EncryptFile("Models/fish.x");

   EncryptFile("Scripts/Flag.lua");

   EncryptFile("Sounds/Sample.mp3");

   EncryptFile("Videos/Wildlife.wmv");

 }

Let's run it! See all the assets have been encrypted and can't be opened outside...

14.gif

Ok. That's a nice show! What about Decrypting?

Update your "decryptToolStripMenuItem_Click" Function as seen below:

protected void decryptToolStripMenuItem_Click(object sender, EventArgs e)

 {

   DecryptFile("Textures/Davinci_jpg.ibo");

   DecryptFile("Shaders/Post_glow_cgfx.ibo");

   DecryptFile("Shaders/Post_glow_fx.ibo");

   DecryptFile("Models/house_fbx.ibo");

   DecryptFile("Models/fish_x.ibo");

   DecryptFile("Scripts/Flag_lua.ibo");

   DecryptFile("Sounds/Sample_mp3.ibo");

   DecryptFile("Videos/Wildlife_wmv.ibo");

 }

Yap as you have seen, the encrypted files have been decrypted after running the Decode...

In this kind of application you need to at least call "EncryptFile" for once and in your Game constructor function call "DecryptFile" for reading the assets inside a game, after by calling EncryptFile in UnloadContent() function your assets will be unreadable again...With this way you will have a chance to secure your assets.

XNB SECURITY

What's XNB File?

XNB file is the encrypted version of all the assets being used inside your XNA Game.

Actually you don't need to worry about XNB files and their security. But I have seen an application that does "Reverse Engineering" on XNB files. I don't know whether it still exists though.

This is very critical for all the XNA Developers I believe. Because you build a game in a very long time and someone comes and gets("steal" in a rude way) all your assets that has been used in your game. But don't worry I'm here to help and solve the problem: "We will be encrypting our Assets"

This application will be a simple one. Which loads assets in runtime and encrypts the assets after closed...

We will be loading Model, Texture and a Video

Because of we will be working with Content Importers let me mention this: if you import a Video for using Content Importers there will be 2 more operations we need to do. When built the game, Video Content Importer creates one XNB file which stores the path of the video and the "Real" video. So there will be 2 videos we need to encrypt and decrypt.

Actually we have done everything in our previous example using Rijndael Algorithm. So I won't be discussing them.

All right Let's Start:

Systematically we will be using similar Application Structure we have used before...

15.gif

We are creating 3 folders inside "Content" folder which will help us convert the assets to .xnb files using Content Importers.

Let me show you all the source code for the application then step by step explain everything  we've done in this project

using
System;

using System.Collections.Generic;

using System.Linq;

using Microsoft.Xna.Framework;

using Microsoft.Xna.Framework.Audio;

using Microsoft.Xna.Framework.Content;

using Microsoft.Xna.Framework.GamerServices;

using Microsoft.Xna.Framework.Graphics;

using Microsoft.Xna.Framework.Input;

using Microsoft.Xna.Framework.Media;

using Microsoft.Xna.Framework.Net;

using Microsoft.Xna.Framework.Storage;

using System.Windows.Forms;

using System.Drawing;

using System.IO;

using System.Security.Cryptography;

using System.Text;

namespace XNBSecurity

{

    public class Game1 : Microsoft.Xna.Framework.Game

    {

        GraphicsDeviceManager graphics;

        SpriteBatch spriteBatch;

        private string password = "Sample";

        MenuStrip menuStrip1;

        ToolStripMenuItem showToolStripMenuItem;

        ToolStripMenuItem modelToolStripMenuItem;

        ToolStripMenuItem textureToolStripMenuItem;

        ToolStripMenuItem videoToolStripMenuItem;

        bool vply;

        bool model_loaded,texture_loaded,video_loaded;

              

        Label fileenc = new Label();

        Label filede = new Label();

      

        //Model Loading Variables

        Model myModel;

        float aspectRatio;

        Vector3 modelPosition = Vector3.Zero;

        float modelRotation = 0.0f;

        Vector3 cameraPosition = new Vector3(100.0f, 500.0f, 500.0f);

        //Texture Loading Variables

        Texture2D myTexture;

        Vector2 spritePosition = Vector2.Zero;

        //Video Loading Variables

        Video myVideoFile;

        VideoPlayer videoPlayer=new VideoPlayer();

         public Game1()

        {

            graphics = new GraphicsDeviceManager(this);

            Content.RootDirectory = "Content";

            DecryptFile("Content/Models/torus_xnb.ibo");

            DecryptFile("Content/Textures/armor_xnb.ibo");

            DecryptFile("Content/Videos/Wildlife_xnb.ibo");

            DecryptFile("Content/Videos/Wildlife_wmv.ibo");

        }

        protected override void Initialize()

        {

            this.menuStrip1 = new System.Windows.Forms.MenuStrip();

            this.showToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();

            this.modelToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();

            this.textureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();

            this.videoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();

                                

            this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {

            this.showToolStripMenuItem});

            this.menuStrip1.BackColor = System.Drawing.Color.CornflowerBlue;

            this.menuStrip1.Location = new System.Drawing.Point(0, 0);

            this.menuStrip1.Name = "menuStrip1";

            this.menuStrip1.Size = new System.Drawing.Size(707, 24);

            this.menuStrip1.TabIndex = 0;

            this.menuStrip1.Text = "menuStrip1";

            // showToolStripMenuItem

            this.showToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {

            this.modelToolStripMenuItem,

            this.textureToolStripMenuItem,

            this.videoToolStripMenuItem});

            this.showToolStripMenuItem.Name = "showToolStripMenuItem";

            this.showToolStripMenuItem.Size = new System.Drawing.Size(53, 20);

            this.showToolStripMenuItem.Text = "Show";

            // modelToolStripMenuItem

            this.modelToolStripMenuItem.Name = "modelToolStripMenuItem";

            this.modelToolStripMenuItem.Size = new System.Drawing.Size(152, 22);

            this.modelToolStripMenuItem.Text = "Model";

            modelToolStripMenuItem.Click+=new EventHandler(Model_Load);

            // textureToolStripMenuItem

            this.textureToolStripMenuItem.Name = "textureToolStripMenuItem";

            this.textureToolStripMenuItem.Size = new System.Drawing.Size(152, 22);

            this.textureToolStripMenuItem.Text = "Texture";

            textureToolStripMenuItem.Click+=new EventHandler(Texture_Load);

            // videoToolStripMenuItem

            this.videoToolStripMenuItem.Name = "videoToolStripMenuItem";

            this.videoToolStripMenuItem.Size = new System.Drawing.Size(152, 22);

            this.videoToolStripMenuItem.Text = "Video";

            videoToolStripMenuItem.Click+=new EventHandler(Video_Load);

                                              

            Control.FromHandle(Window.Handle).Controls.Add(menuStrip1);

            base.Initialize();

        }

        protected override void LoadContent()

        {

            spriteBatch = new SpriteBatch(GraphicsDevice);

            myModel = Content.Load<Model>("Models\\torus");

            aspectRatio = graphics.GraphicsDevice.Viewport.AspectRatio;

            myTexture = Content.Load<Texture2D>("Textures\\Armor");

            myVideoFile = Content.Load<Video>("Videos\\Wildlife");

        } 

        protected void Model_Load(Object sender, EventArgs e)

        {

            model_loaded = true;

            texture_loaded = false;

            video_loaded = false;

        } 

        protected void Texture_Load(Object sender, EventArgs e)

        {

            texture_loaded = true;

            video_loaded = false;

            model_loaded = false;

        } 

        protected void Video_Load(Object sender, EventArgs e)

        {

            video_loaded = true;

            texture_loaded = false;

            model_loaded = false;

        } 

        protected override void UnloadContent()

        {

            videoPlayer.Stop();

            videoPlayer.Dispose();

            EncryptFile("Content/Models/torus.xnb");

            EncryptFile("Content/Textures/Armor.xnb");

            EncryptFile("Content/Videos/Wildlife.wmv");

            EncryptFile("Content/Videos/Wildlife.xnb");

        } 

        protected override void Update(GameTime gameTime)

        {

            if (model_loaded == true)

            {

                modelRotation += (float)gameTime.ElapsedGameTime.TotalMilliseconds *

       MathHelper.ToRadians(0.1f);

            }

            if (video_loaded == true)

            {

                KeyboardState stat = Keyboard.GetState();

                if (stat.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Space))

                {

                    vply = true;

                    videoPlayer.Play(myVideoFile);

                }

                else if (stat.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape))

                {

                    vply = false;

                    videoPlayer.Stop();

                }

            }          

            base.Update(gameTime);

        } 

        protected override void Draw(GameTime gameTime)

        {

            GraphicsDevice.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue);

            if (model_loaded == true)

            {

                Matrix[] transforms = new Matrix[myModel.Bones.Count];

                myModel.CopyAbsoluteBoneTransformsTo(transforms);

                foreach (ModelMesh mesh in myModel.Meshes)

                {

                    foreach (BasicEffect effect in mesh.Effects)

                    {

                        effect.EnableDefaultLighting();

                        effect.World = transforms[mesh.ParentBone.Index] *

                            Matrix.CreateRotationY(modelRotation)

                            * Matrix.CreateTranslation(modelPosition);

                        effect.View = Matrix.CreateLookAt(cameraPosition,

                            Vector3.Zero, Vector3.Up);

                        effect.Projection = Matrix.CreatePerspectiveFieldOfView(

                            MathHelper.ToRadians(90.0f), aspectRatio,

                            1.0f, 10000.0f);

                    }

                    mesh.Draw();

                }

            } 

            if (texture_loaded == true)

            {

                spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

                spriteBatch.Draw(myTexture, spritePosition, Microsoft.Xna.Framework.Graphics.Color.White);

                spriteBatch.End();

            } 

            if (vply == true)

            {

                spriteBatch.Begin();

                spriteBatch.Draw(videoPlayer.GetTexture(), new Microsoft.Xna.Framework.Rectangle(0, 0, myVideoFile.Width, myVideoFile.Height), Microsoft.Xna.Framework.Graphics.Color.White);

                spriteBatch.End();               

            }                     

            base.Draw(gameTime);

        } 

        private void EncryptFile(string encpath)

        {

            int position = 0, t = 0;

            while (encpath.IndexOf('\\', t) != -1)

            {

                position = encpath.IndexOf('\\', t);

                t = position;

            } 

            string outfile = encpath.Substring(position);

            outfile = outfile.Replace('.', '_');

            fileenc.Text = outfile + ".ibo"

            try

            {

                if (password.Length > 8)

                    password = password.Substring(0, 8);

                else if (password.Length < 8)

                {

                    int add = 8 - password.Length;

                    for (int i = 0; i < add; i++)

                        password = password + i;

                }

                UnicodeEncoding UE = new UnicodeEncoding();

                byte[] key = UE.GetBytes(password); 

                string cryptFile = fileenc.Text;

                FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create); 

                RijndaelManaged RMCrypto = new RijndaelManaged(); 

                CryptoStream cs = new CryptoStream(fsCrypt,

                    RMCrypto.CreateEncryptor(key, key),

                    CryptoStreamMode.Write); 

                FileStream fsIn = new FileStream(encpath, FileMode.Open);

                int data;

                while ((data = fsIn.ReadByte()) != -1)

                    cs.WriteByte((byte)data);             

                fsIn.Close();

                cs.Close();

                fsCrypt.Close();

                File.Delete(encpath);

            }

            catch

            {

                MessageBox.Show("Encryption Failed!", "Error");

            }           

        } 

        private void DecryptFile(string decpath)

        {

            int position = 0, t = 0;

            while (decpath.IndexOf('\\', t) != -1)

            {

                position = decpath.IndexOf('\\', t);

                t = position;

            }

            string outfile = decpath.Substring(0, decpath.Length - 4);

            outfile = outfile.Substring(position);

            filede.Text = outfile.Replace('_', '.'); 

            try

            {

                if (password.Length > 8)

                    password = password.Substring(0, 8);

                else if (password.Length < 8)

                {

                    int add = 8 - password.Length;

                    for (int i = 0; i < add; i++)

                        password = password + i;

                } 

                UnicodeEncoding UE = new UnicodeEncoding();

                byte[] key = UE.GetBytes(password); 

                FileStream fsCrypt = new FileStream(decpath, FileMode.Open); 

                RijndaelManaged RMCrypto = new RijndaelManaged(); 

                CryptoStream cs = new CryptoStream(fsCrypt,

                    RMCrypto.CreateDecryptor(key, key),

                    CryptoStreamMode.Read); 

                FileStream fsOut = new FileStream(filede.Text, FileMode.Create); 

                int data;

                while ((data = cs.ReadByte()) != -1)

                    fsOut.WriteByte((byte)data);

                 fsOut.Close();

                cs.Close();

                fsCrypt.Close();

                File.Delete(decpath);

            }

            catch

            {

                MessageBox.Show("Decryption Failed!", "Error");

            }

        }

    }

}

MenuStrip menuStrip1;

ToolStripMenuItem showToolStripMenuItem;

ToolStripMenuItem modelToolStripMenuItem;

ToolStripMenuItem textureToolStripMenuItem;

ToolStripMenuItem videoToolStripMenuItem;
Here we are declaring our 1 MainMenu(Show) and 3 Sub-menus(Model,Texture,Video).

bool
vply;
bool model_loaded,texture_loaded,video_loaded; 

"vply" is a variable which controls whether video should play or stop.

"model_loaded","texture_loaded","video_loaded":

Controls whether "model" or "texture" or "video" are loaded or not..

//Model Loading Variables
 Model myModel;
 float aspectRatio;
 Vector3 modelPosition = Vector3.Zero;
 float modelRotation = 0.0f;
 Vector3 cameraPosition = new Vector3(100.0f, 500.0f, 500.0f);

These declarations are "Model Loading Variables" and will be used for showing models on xna window.

 
//Texture Loading Variables
Texture2D myTexture;
Vector2 spritePosition = Vector2.Zero;

We are declaring our Texture's variables.

//Video Loading Variables
Video myVideoFile;
VideoPlayer videoPlayer=new VideoPlayer();

And this one is the Video Loading Variables, which will help us display video that has been supported by XNA 3.1,a new feature indeed.

public Game1()

{

  graphics = new GraphicsDeviceManager(this);

  Content.RootDirectory = "Content";

  DecryptFile("Content/Models/torus_xnb.ibo");

  DecryptFile("Content/Textures/armor_xnb.ibo");

  DecryptFile("Content/Videos/Wildlife_xnb.ibo");

  DecryptFile("Content/Videos/Wildlife_wmv.ibo");
}

When first run, our DecryptFile function will decrypt assets and load the contents. By doing so the assets will be readable.

protected
override void UnloadContent()

{

  videoPlayer.Stop();

  videoPlayer.Dispose();

  EncryptFile("Content/Models/torus.xnb");

  EncryptFile("Content/Textures/Armor.xnb");

  EncryptFile("Content/Videos/Wildlife.wmv");

  EncryptFile("Content/Videos/Wildlife.xnb");

}

And That's the Encryption. When closed the application the assets will again be unreadable. Let's go on...

protected
override void Initialize()

        {

            this.menuStrip1 = new System.Windows.Forms.MenuStrip();

            this.showToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();

            this.modelToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();

            this.textureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();

            this.videoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();

                                

            this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {

            this.showToolStripMenuItem});

            this.menuStrip1.BackColor = System.Drawing.Color.CornflowerBlue;

            this.menuStrip1.Location = new System.Drawing.Point(0, 0);

            this.menuStrip1.Name = "menuStrip1";

            this.menuStrip1.Size = new System.Drawing.Size(707, 24);

            this.menuStrip1.TabIndex = 0;

            this.menuStrip1.Text = "menuStrip1";

            //

            // showToolStripMenuItem

            //

            this.showToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {

            this.modelToolStripMenuItem,

            this.textureToolStripMenuItem,

            this.videoToolStripMenuItem});

            this.showToolStripMenuItem.Name = "showToolStripMenuItem";

            this.showToolStripMenuItem.Size = new System.Drawing.Size(53, 20);

            this.showToolStripMenuItem.Text = "Show";

            //

            // modelToolStripMenuItem

            //

            this.modelToolStripMenuItem.Name = "modelToolStripMenuItem";

            this.modelToolStripMenuItem.Size = new System.Drawing.Size(152, 22);

            this.modelToolStripMenuItem.Text = "Model";

            modelToolStripMenuItem.Click+=new EventHandler(Model_Load);

            //

            // textureToolStripMenuItem

            //

            this.textureToolStripMenuItem.Name = "textureToolStripMenuItem";

            this.textureToolStripMenuItem.Size = new System.Drawing.Size(152, 22);

            this.textureToolStripMenuItem.Text = "Texture";

            textureToolStripMenuItem.Click+=new EventHandler(Texture_Load);

            //

            // videoToolStripMenuItem

            //

            this.videoToolStripMenuItem.Name = "videoToolStripMenuItem";

            this.videoToolStripMenuItem.Size = new System.Drawing.Size(152, 22);

            this.videoToolStripMenuItem.Text = "Video";

            videoToolStripMenuItem.Click+=new EventHandler(Video_Load);

                                             

            Control.FromHandle(Window.Handle).Controls.Add(menuStrip1);

            base.Initialize();

        }

We are building our menu in Initialize() function...This is our Menu...

protected
override void LoadContent()

{

  spriteBatch = new SpriteBatch(GraphicsDevice);

  myModel = Content.Load<Model>("Models\\torus");

  aspectRatio = graphics.GraphicsDevice.Viewport.AspectRatio;

  myTexture = Content.Load<Texture2D>("Textures\\Armor");

  myVideoFile = Content.Load<Video>("Videos\\Wildlife");

}

LoadContent() function loads all the contents and ensures us to be able to call them where we need.

 

protected void Model_Load(Object sender, EventArgs e)

{

  model_loaded = true;

  texture_loaded = false;

  video_loaded = false;

}

protected void Texture_Load(Object sender, EventArgs e)

{

  texture_loaded = true;

  video_loaded = false;

  model_loaded = false;

}

protected void Video_Load(Object sender, EventArgs e)

{

  video_loaded = true;

  texture_loaded = false;

  model_loaded = false;

}

I
n this functions we control whether they are called or not. As you can understand, there can only one call each time.

protected
override void Update(GameTime gameTime)

        {

            if (model_loaded == true)

            {

                modelRotation += (float)gameTime.ElapsedGameTime.TotalMilliseconds *

       MathHelper.ToRadians(0.1f);

            }

            if (video_loaded == true)

            {

                KeyboardState stat = Keyboard.GetState();

                if (stat.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Space))

                {

                    vply = true;

                    videoPlayer.Play(myVideoFile);

                }

                else if (stat.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape))

                {

                    vply = false;

                    videoPlayer.Stop();

                }

            }          

            base.Update(gameTime);

        }

If model_loaded is true then we are rotating the model with this code besides;

If video_loaded is true and if we press Space key then we will be playing Video object, else pressing Escape key will stop the video.

protected
override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue); 

            if (model_loaded == true)

            {

                Matrix[] transforms = new Matrix[myModel.Bones.Count];

                myModel.CopyAbsoluteBoneTransformsTo(transforms);

                foreach (ModelMesh mesh in myModel.Meshes)

                {

                    foreach (BasicEffect effect in mesh.Effects)

                    {

                        effect.EnableDefaultLighting();

                        effect.World = transforms[mesh.ParentBone.Index] *

                            Matrix.CreateRotationY(modelRotation)

                            * Matrix.CreateTranslation(modelPosition);

                        effect.View = Matrix.CreateLookAt(cameraPosition,

                            Vector3.Zero, Vector3.Up);

                        effect.Projection = Matrix.CreatePerspectiveFieldOfView(

                            MathHelper.ToRadians(90.0f), aspectRatio,

                            1.0f, 10000.0f);

                    }

                    mesh.Draw();

                }

            }

            if (texture_loaded == true)

            {

                spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

                spriteBatch.Draw(myTexture, spritePosition, Microsoft.Xna.Framework.Graphics.Color.White);

                spriteBatch.End();

            }          

            if (vply == true)

            {

                spriteBatch.Begin();

                spriteBatch.Draw(videoPlayer.GetTexture(), new Microsoft.Xna.Framework.Rectangle(0, 0, myVideoFile.Width, myVideoFile.Height), Microsoft.Xna.Framework.Graphics.Color.White);

                spriteBatch.End();               

            }                     

            base.Draw(gameTime);

        }

If "model_loaded" is true then we are loading the model with this code besides;
If "texture_loaded" is true then we are drawing the texture on the xna window;
If "video_loaded" & "vply" is true then we are playing the video, if "vply" is false then we are stopping the video with this code...

All right then. Let's run our application and see what will happen?

16.gif

First Appearance of our applications...

17.gif

When Clicking Show->Model you see a Torus model rotating on the screen...

18.gif

When Clicking Show->Texture you see our texture...

19.gif

When Clicking Show->Video & pressing Space key you see our Video playing, and if you press ESC key it will stop...

Here this article ends. From now on you don't need to worry about your assets' security, because by using these algorithms I have explained in this article will help you protect them.

I hope this article may be very useful for you.

Because of "Digital Rights Management(DRM)" I'm unable to publish assets being used in this project. For that I'm really sorry..But you can set your own assets the same way I have done in this article and in the source codes published with it..

I wish you happy XNA Coding!

Cheers,

Reference:

I have used Nenad Djodievic's -from CsharpCorner- File Encryption Codes for the project @
http://www.c-sharpcorner.com/UploadFile/ndjodievic/FileEncryption11282005062339AM/FileEncryption.aspx

Ibrahim Ersoy

Ibrahim Ersoy lives in Turkey, Istanbul. He helps running and managing C# Corner. He is a Software Consultant (Sharepoint) and MindCracker MVP. He writes about cutting-edge Microsoft Technologies including XNA,... Read more

Personal Blog: http://www.iersoy.com
COMMENT USING

Trending up