Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Wednesday, May 26, 2010

Experiment Extracting Windows Thumbnails (XP and Windows 2003 only)

Windows thumbnails have existed for eons. But in all that time I never really used the windows explorer thumbnail view. Yes, pictures are nice. But invariably I found myself needing the detail view with its listing of file types, sizes and dates.

Well, this week I came across an old stackoverflow thread which mentioned a C# tool for reading and extracting images from the windows thumbnail cache (ie thumbs.db). At least on older XP and Windows 2003 systems. (Vista and later use a slightly different format.) While there are tools galore in this category, the idea of a small DLL that could be called from CF was appealing. So I decided to give it a quick whirl with CF8, under XP.

After compiling the source with Visual C# Express, I changed my explorer settings so I actually had a thumbnails file to test. Next, I created an instance of the ThumbDB class and initialized it by passing in the path my thumbnails database. Once initialized, I used the GetThumbFiles() method to grab an array of all file names within that database.

<cfset util = createObject(".net", "ThumbDBLib.ThumbDB", "c:/test/ThumbDBLib.dll")>
<cfset util.init( "C:/docs/thumbs.db" )>
<cfset fileNames = util.GetThumbFiles()>
<cfdump var="#fileNames#" label="Top 25 Files in Thumbs.db" top="25">

Next, I selected one of the file names and used the GetThumbData() method to retrieve the image bytes. I was hoping to create a CF image object from the bytes and display it in my browser. But every time I called ImageNew(), ColdFusion kept complaining about my argument type.

<!--- this does NOT work --->
<!---  grabbing an arbitrary file from the listing for testing ...--->
<cfset thumbnailName = fileNames[1] />
<cfset bytes = util.GetThumbData( thumbnailName )>
<cfset img = ImageNew(bytes) />

That is when it hit me. A byte in C# is not the same as byte in Java. Unlike Java, C# has signed and unsigned types. So where C# has two data types, sbyte (-128 to 127) and byte (0 to 255), java has only one, byte (range -128 to 127). So according to the data type conversion matrix, the C# byte array was being converted to a short array.

Thanks to a tip from BlackWasp.com, I added a new method called GetJavaThumbData(), which converts the C# byte array into one that is compatible with Java. Using the new method, I was then able to display the thumbnail perfectly.

C# Code:
public sbyte[] GetJavaThumbData(string fileName)
    {
        byte[] data = GetThumbData(fileName);
        if (data != null)
        {
        sbyte[] jvData  = Array.ConvertAll(data, delegate(byte b) { return unchecked((sbyte)b); });
        return jvData;
        }
        return null;
    }

ColdFusion Code:
<!---  grabbing an arbitrary file from the listing for testing ...--->
<cfset thumbnailName = fileNames[1] />
<cfset bytes = util.GetJavaThumbData( thumbnailName )>
<cfset img = ImageNew( bytes )>
<cfimage action="writeToBrowser" source="#img#">

It is worth noting the ThumbDB class also has a method named GetThumbnailImage(), which returns a C# image object. You can use its properties to display things like height, width, resolution. The class also has several methods for saving the image to a file. Out of curiosity, I tried several of the overloaded save() methods. But I only had success with the simplest form: Image.save(filepath). I am not sure why. Though with CF's own image functions, they are not really needed anyway.

<cfset img = util.GetThumbnailImage( thumbnailName ) />
<cfoutput>
    <strong>Thumbnail: #thumbnailName#</strong> <hr />
    Height      = #img.Get_Height()#
    Width       = #img.Get_Width()# 
    PixelFormat = #img.Get_PixelFormat()#
    Resolution  = #img.Get_HorizontalResolution()# /
                  #img.Get_VerticalResolution()# 
</cfoutput>

Supposedly the Windows API Code pack can extract thumbnails on later systems like Windows 7. If anyone has used it, or something similar, on Windows 7, let me know. I would be interested in hearing your experiences with it.

...Read More

Thursday, December 3, 2009

ColdFusion: Encryption Interoperability Issues (Beginner)

Recently, I have seen a few questions about encryption interoperability. The libraries used by ColdFusion (Sun JCE) are pretty standard. So for the most part, compatibility should not be issue. But it got me to thinking about some of the common pitfalls when trading encrypted data with external tools. While they may seem obvious to some of you, many of them were not at all obvious to me.


While I am certainly no expert on the subject, most of the issues I have encountered, or seen in various forums, tend to involve two things: cipher settings and encoding. Now I know that seems like a blatantly obvious statement. But it is the source of more problems than you might think.

Unfortunately, the ColdFusion documentation on the encrypt/decrypt functions is a bit sketchy in places. Now to a degree, the minimal documentation is understandable. Realistically it would require whole volumes to provide a comprehensive explanation of encryption. But there are a few key aspects of the encrypt/decrypt functions that I feel could really use some illumination. If only to help developers avoid some of the more common interoperability problems.

A prime example is the algorithm argument. Most examples you will see use simple names like AES or DESEDE. What is not immediately obvious is that those simple names are short-hand for several settings: the algorithm, cipher mode and padding scheme. When you use the short-hand name, ColdFusion applies the default cipher mode and padding scheme automatically. The defaults may vary depending on which algorithm you select. But in the case of AES the defaults are ECB and PKCS5Padding. (At least from what I can tell). So in other words, the algorithm values AES and AES/ECB/PKCS5Padding are equivalent. To specify a different mode or padding just change the algorithm value.

Now chances are the external tool you are working with probably does not use exactly the same defaults as ColdFusion. But once you are aware of the additional settings for the various algorithms, it is much easier to figure out how to align the results. You just need to ensure the settings on both ends match up.

Take C# for example. The defaults for the RijndaelManaged class are a bit different than ColdFusion's. For example, the default mode is CBC rather than ECB and the default key size is 256 bit. So exchanging values between the two may not work right off the bat.

                    ColdFusion            C# (RijndaelManaged)
Mode: ECB CBC
Padding: PKCS5Padding PKCS7
Key Size: 128 bit 256 bit
Block Size: 128 bit 128 bit
Allowed Key Lengths 128, *192, *256 bit 128, 192, 256 bit

* Note: Unlimited encryption is required in CF for keys larger than 128 bit

Once you have aligned the cipher settings, double check the encoding used on the various values. In ColdFusion the string to be encrypted/decrypted "is always interpreted as a UTF-8 string". From what I observed, the key value seems to be interpreted as base64. So using this collective information you can now easily adjust the settings of ColdFusion and C# to produce the same results.
<!---
Encrypt/Decrypt
---->
<h3>ColdFusion (AES/CBC/PKCS5Padding) + iv</h3>

<cfset thePlainData = "Nothing to see here folks" />

<cfset theKey = generateSecretKey("AES", 128) />
<cfset theAlgorithm = "AES/CBC/PKCS5Padding" />
<cfset theEncoding = "base64" />
<cfset theIV = BinaryDecode("7fe8585328e9ac7b7fe8585328e9ac7b", "hex") />

<cfset encryptedString = encrypt(thePlainData, theKey, theAlgorithm, theEncoding, theIV) />
<cfset decryptedString = decrypt(encryptedString, theKey, theAlgorithm, theEncoding, theIV) />


<!---
Display results
--->
<cfset keyLengthInBits = arrayLen(BinaryDecode(theKey, "base64")) * 8 />
<cfset ivLengthInBits = arrayLen(theIV) * 8 />
<cfdump var="#variables#" label="AES/CBC/PKCS5Padding Results" />



using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;

namespace AESTest
{
public class AESCBC
{
public static void Main()
{
try
{
// Just hard coded values for testing ...
// MUST change them to match the values used in the CF code
String thePlainData = "Nothing to see here folks";
String theKey = "oRJUjgbx9SGGR6v3T8JGJg==";
String theIV = "f+hYUyjprHt/6FhTKOmsew==";
String encryptedText = EncryptText(thePlainData, theKey, theIV);
String decryptedText = DecryptText(encryptedText, theKey, theIV);

Console.WriteLine("Encrypted String: {0}", encryptedText);
Console.WriteLine("Decrypted String: {0}", decryptedText);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}

Console.ReadLine();
}


public static String EncryptText(String Data, String Key, String IV)
{
// Extract the bytes of each of the values
byte[] input = Encoding.UTF8.GetBytes(Data);
byte[] key = Convert.FromBase64String(Key);
byte[] iv = Convert.FromBase64String(IV);


// Create a new instance of the algorithm with the desired settings
RijndaelManaged algorithm = new RijndaelManaged();
algorithm.Mode = CipherMode.CBC;
algorithm.Padding = PaddingMode.PKCS7;
algorithm.BlockSize = 128;
algorithm.KeySize = 128;
algorithm.Key = key;
algorithm.IV = iv;

// Create a new encryptor and encrypt the given value
ICryptoTransform cipher = algorithm.CreateEncryptor();
byte[] output = cipher.TransformFinalBlock(input, 0, input.Length);

// Finally, return the encrypted value in base64 format
String encrypted = Convert.ToBase64String(output);

return encrypted;
}

public static String DecryptText(String Data, String Key, String IV)
{
// Extract the bytes of each of the values
byte[] input = Convert.FromBase64String(Data);
byte[] key = Convert.FromBase64String(Key);
byte[] iv = Convert.FromBase64String(IV);


// Create a new instance of the algorithm with the desired settings
RijndaelManaged algorithm = new RijndaelManaged();
algorithm.Mode = CipherMode.CBC;
algorithm.Padding = PaddingMode.PKCS7;
algorithm.BlockSize = 128;
algorithm.KeySize = 128;
algorithm.Key = key;
algorithm.IV = iv;

//FromBase64String
// Create a new encryptor and encrypt the given value
ICryptoTransform cipher = algorithm.CreateDecryptor();
byte[] output = cipher.TransformFinalBlock(input, 0, input.Length);

// Finally, convert the decrypted value to UTF8 format
String decrypted = Encoding.UTF8.GetString(output);

return decrypted;
}
}
}

Hopefully these small tips will help someone else or at least keep them from pulling out their hair trying to coerce ColdFusion and some other tool into producing the same results.

...Read More

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep