Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Encrypting a string to a URL in C#

Tags:

c#

encryption

For many hours now I have been searching for the perfect way to encrypt a string to a URL. Here's the method I have found

Best method I found and it's on stackoverflow

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

public class SimplerAES
{
   private static byte[] key = { 123, 217, 19, 11, 24, 26, 85, 45, 114, 184, 27, 162, 37, 112, 222, 209, 241, 24, 175, 144, 173, 53, 196, 29, 24, 26, 17, 218, 131, 236, 53, 209 };
   private static byte[] vector = { 146, 64, 191, 111, 23, 3, 113, 119, 231, 121, 221, 112, 79, 32, 114, 156 };
   private ICryptoTransform encryptor, decryptor;
   private UTF8Encoding encoder;

   public SimplerAES()
   {
      RijndaelManaged rm = new RijndaelManaged();
      encryptor = rm.CreateEncryptor(key, vector);
      decryptor = rm.CreateDecryptor(key, vector);
      encoder = new UTF8Encoding();
   }

   public string Encrypt(string unencrypted)
   {
      return Convert.ToBase64String(Encrypt(encoder.GetBytes(unencrypted)));
   }

   public string Decrypt(string encrypted)
   {
      return encoder.GetString(Decrypt(Convert.FromBase64String(encrypted)));
   }

   public string EncryptToUrl(string unencrypted)
   { 
      return HttpUtility.UrlEncode(Encrypt(unencrypted));
   }

   public string DecryptFromUrl(string encrypted)
   {
      return Decrypt(HttpUtility.UrlDecode(encrypted));
   }

   public byte[] Encrypt(byte[] buffer)
   {
      MemoryStream encryptStream = new MemoryStream();
      using (CryptoStream cs = new CryptoStream(encryptStream, encryptor, CryptoStreamMode.Write))
      {
         cs.Write(buffer, 0, buffer.Length);
      }
      return encryptStream.ToArray();
   }

   public byte[] Decrypt(byte[] buffer)
   {
      MemoryStream decryptStream = new MemoryStream();
      using (CryptoStream cs = new CryptoStream(decryptStream, decryptor, CryptoStreamMode.Write))
      {
         cs.Write(buffer, 0, buffer.Length);
      }
      return decryptStream.ToArray();
   }
}

Now when I look at the encrypted strings that are produced with

    var a1 = _aes.EncryptToUrl("aaa");
    var a2 = _aes.EncryptToUrl("aaaa");
    var a3 = _aes.EncryptToUrl("aaaaa");
    var a4 = _aes.EncryptToUrl("aaaaaa");
    var a5 = _aes.EncryptToUrl("aaaaaaa");
    var a6 = _aes.EncryptToUrl("aaaaaaaa");
    var a7 = _aes.EncryptToUrl("aaaaaaaaa");
    var a8 = _aes.EncryptToUrl("aaaaaaaaaa");
    var a9 = _aes.EncryptToUrl("aaaaaaaaaaa");
    var a10 = _aes.EncryptToUrl("aaaaaaaaaaa");
    var a11 = _aes.EncryptToUrl("aaaaaaaaaaaa");
    var a12 = _aes.EncryptToUrl("aaaaaaaaaaaaa");
    var a13 = _aes.EncryptToUrl("aaaaaaaaaaaaaa");

All of the strings (var a1 to a13)end in "%3d%3d"

As saving storage space is very important for this application I am wondering if I can just remove the "%3d%3d" and add it later before decoding to get back to the original URL.

Can anyone give me some advice on this.

Thanks,

like image 333
Emily Avatar asked Jun 15 '11 16:06

Emily


1 Answers

As Andrew says, that's padding at the end of the base64. However, I disagree with his conclusion.

We know that a well-formed base64 string is always a multiple of 4 characters in length. Therefore, so long as we can trust that we've got all the data in one go (i.e. we trust that our URL isn't truncated) we can strip any trailing '=' characters from the base64 string before we encode it for the URL. However, we need to put those characters back before we later call Convert.FromBase64String. Fortunately we can do that as we know how many there will be based on the length of the string we've got. You can do this even if the original length isn't constant, even though in your case it is.

Another minor point - the "normal" base64 alphabet isn't great for URLs; you may want to use an alternative version, as described in Wikipedia:

Using standard Base64 in URL requires encoding of '+', '/' and '=' characters into special percent-encoded hexadecimal sequences ('+' = '%2B', '/' = '%2F' and '=' = '%3D'), which makes the string unnecessarily longer. For this reason, a modified Base64 for URL variant exists, where no padding '=' will be used, and the '+' and '/' characters of standard Base64 are respectively replaced by '-' and '_', so that using URL encoders/decoders are no longer necessary and have no impact on the length of the encoded value, leaving the same encoded form intact for use in relational databases, web forms, and object identifiers in general.

I don't know of anything within .NET which makes this easy to do efficiently. The simplest approach is to perform the normal base64 conversion, replace + with -, / with _, and then use string.TrimEnd to remove the = signs from the end, with the reverse operations for decoding. That's considerably less efficient than a hand-coded version, but given the size of the data you'll be working with, it may not matter.

like image 152
Jon Skeet Avatar answered Oct 27 '22 01:10

Jon Skeet