views:

358

answers:

5

I want to encrypt a filename but ensure that the encrypted filename doesn't contain any of the characters that are not allowed (on a windows system) in filenames.

The string also needs to be able to be decrypted back to the original filename (so you can't just filter out the invalid characters after the encryption).

To my knowledge the unusable characters are \ / : * ? " > < |. I'm aware of System.IO.Path.InvalidPathChars but I don't know of an encryption method that will avoid those characters.

+6  A: 

You can convert the filename, or the encrypted output, to MIME Base64 encoding. I don't believe that standard uses any of the prohibited characters. (Even if it does, you can .Replace() prohibited characters with nonprohibited ones.)

For example, your question base64'd is:

c2UgdGhpcyBiYXNlNjQgc2FtcGxlIGRlY29kZXIgYW5kIGVuY29kZXIgdG86DQpGcmVuZCBDemVj aCBsaW5rcw0KDQpMYXN0IG1pbnV0ZSBaYWplemR5DQpBa2NpZSBCaWJsZQ0KTGV2bmUgcG5ldQ0K RmFrdHVyYQ0KU2VhcmNoIEN6ZWNoDQpIcnksIFZ5enZhbmVuaSBuYSBtb2JpbA0KT3RoZXINCg0K ICAgICogRGVjb2RlIGJhc2U2NCBzdHJpbmdzIChiYXNlNjQgc3RyaW5nIGxvb2tzIGxpa2UgWVRN ME5ab21JekkyT1RzbUl6TTBOVHVlWVE9PSkNCiAgICAqIERlY29kZSBhIGJhc2U2NCBlbmNvZGVk IGZpbGUgKGZvciBleGFtcGxlIElDTyBmaWxlcyBvciBmaWxlcyBmcm9tIE1JTUUgbWVzc2FnZSkN CiAgICAqIENvbnZlcnQgc291cmNlIHRleHQgZGF0YSBmcm9tIHNldmVyYWwgY29kZSBwYWdlcyBh bmQgZW5jb2RlIHRoZW0gdG8gYSBiYXNlNjQgc3RyaW5nIG9yIGEgZmlsZQ0KDQoNCiAgICAgIFRo ZSBGb3JtLlNpemVMaW1pdCBpcyAxMDAwMDAwMGJ5dGVzLiBQbGVhc2UsIGRvIG5vdCBwb3N0IG1v cmUgZGF0YSB1c2luZyB0aGlzIGZvcm0uDQoNCiAgICAgVHlwZSAob3IgY29weS1wYXN0ZSkgc29t ZSB0ZXh0IHRvIGEgdGV4dGJveCBiZWxsb3cuIFRoZSB0ZXh0IGNhbiBiZSBCYXNlNjQ=

Tom the Junglist
+1 - as noted here: http://en.wikipedia.org/wiki/Base64, "MIME Base64 uses A–Z, a–z, and 0–9 for the first 62 values."
Jeremy McGee
The problem lies in those last two values. They're `+` and `/`.
bdukes
Of course, it's a simple character replacement to swap `/` with, say, `-`. And then swap it back before the decode.
Anon.
A simple two-way String.Replace to any of the many other acceptable characters (just not A-Za-z0-9) would fix that.
Tom the Junglist
+2  A: 

As @Tom notes, you can base64 encode the file name. However, .NET's base64 methods include the slash character, so you'll need to replace that.

var encryptedFilename = GetEncryptedFileName();
var encodedFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(encryptedFilename));
var encodedAndEscapedFilename = encodedFileName.Replace('/', '-');

You can then do basically the same thing in reverse to get the original name (using Convert.FromBase64String and Encoding.UTF8.GetString).

bdukes
thanks, I just encountered that problem, I appreciate the comment!
Kelvin
+3  A: 

There is no resulting 'string', all encryption transformations result in byte[] not in string. There are string representations of byte[], like the well knows hex (0xBAADF00D) or Base64. Probably base64 is the most appropiate, just use Convert.ToBase64String and Convert.FromBase64String.

That being said encrypting a file name seems like a very bad idea. File names are by definition index lookup keys and with encryption you will loose any lookup ability. You will have to scan the entire disk/folder and decrypt each file name just to find out the file you want. Note that you cannot rely on a known encrypted value (eg. if I seek 'file.txt' I encrypt 'file.txt' and then search for the encrypted name) because encryption has to use a random IV value (which is appended to the encryption result). Failure to properly randomize the IV will result in cribs. Yet succesful salting the key (good random IV) will make the result unsearchable.

Remus Rusanu
Thanks I guess. Once I encrypt the filename I can probably manage to not put the file in some random place and lose it.
Kelvin
And an attacker will be stopped from looking up the encrypted file name there by... what exactly? You can do as you wish, i don't really care, I just want to shed some light for the innocent readers stumbling on this from google two months later.
Remus Rusanu
This is nothing but an (unclever) way to replace Path.GetRandomFileName
Remus Rusanu
It's cool, relax. The file name has important info in it and the file isn't moving so it shouldn't get lost. I agree that encrypting file names all over the file system wouldn't be a great idea.
Kelvin
+1  A: 

As Remus stated above there is no resulting 'string', all encryption transformations result in byte[] not in string.

I generally take an encryption output byte array, and call BitConverter.ToString(Bytes); to convert it to hex, though this does result in a FF-D0-FF-D1 format. If a plain hex string is required, just run a .Replace("-", "");

e.g.

return BitConverter.ToString(AESEncryption.Encrypt(token, key)).Replace("-", "");

(AESEncryption is a simple custom built wrapper around RijndaelManaged)

Decryption isn't quite as neat, a bit brute force, but still simple

    private byte[] FromHexString(string encryptedToken)
    {
        List<byte> bytes = new List<byte>();

        for (int i = 0; i <= encryptedToken.Length; i+=2)
        {
            try
            {
                bytes.Add((byte)Int32.Parse(encryptedToken.Substring(i, 2), System.Globalization.NumberStyles.HexNumber));
            }
            catch //whatever exception
            {
                //handle
            }

        }

        return bytes.ToArray();
    }
johnc
+1  A: 

My answer was to convert the encrypted code to HEX. The benefit to this was compatibility between ASP, ASPX, SQL, VB, C#, and Javascript.
Another benefit was that I could use the encryption of my choice without worrying about the output.

Dave