using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Security.Cryptography; public class Turing : System.Web.UI.Imaging.ImageGenerator { #region Randomized Data private static string[] fontNames = new string[] { "Arial", "Comic Sans MS", "Courier New", "Lucida Console", "Tahoma", "Times New Roman", "Trebuchet MS", "Verdana", }; private static Color[] backColors = new Color[] { Color.Beige, Color.AliceBlue, Color.AntiqueWhite, Color.WhiteSmoke, Color.White, Color.FloralWhite, Color.Linen, Color.OldLace, Color.LightYellow, Color.MistyRose, Color.MintCream, Color.LightCyan, Color.Azure }; private static Color[] foreColors = new Color[] { Color.RoyalBlue, Color.DarkMagenta, Color.Teal, Color.ForestGreen, Color.Goldenrod, Color.Sienna, Color.Firebrick }; private Color randomForeColor() { return foreColors[random.Next(0, foreColors.Length)]; } private Color randomBackColor() { return backColors[random.Next(0, backColors.Length)]; } private Brush randomSolidBrush() { return new SolidBrush(randomForeColor()); } private Pen randomPen() { return new Pen(randomSolidBrush()); } private string randomFontName() { return fontNames[random.Next(0, fontNames.Length)]; } #endregion private static Rijndael rijndael; static Turing() { rijndael = Rijndael.Create(); rijndael.GenerateKey(); rijndael.GenerateIV(); } /// /// Sets the image height. /// protected override int DefaultHeight { get { return 30; } } /// /// Sets the image width. /// /// protected override int DefaultWidth { get { return 120; } } #region Painting Code private Random random; // Use a single random instance so we don't get the same values on fast processors protected override void RenderImage(Graphics g) { // Get the string to render byte[] nonce = Convert.FromBase64String(Request.QueryString["nonce"]); string str = decrypt(nonce); this.random = new Random(generateSeed(nonce)); // Set pretty quality g.SmoothingMode = SmoothingMode.AntiAlias; g.CompositingQuality = CompositingQuality.HighQuality; drawBackground(g); // Changing these around allow the lines to be drawn under the letters drawLines(g); drawLetters(g, str); } private int generateSeed(byte[] nonce) { byte[] seedData = Convert.FromBase64String(encrypt(nonce)); return BitConverter.ToInt32(seedData, 0); } /// /// Creates a psuedo-random background. /// private void drawBackground(Graphics g) { g.Clear(Color.White); // Some code to play with to get coloured backgrounds. // Also check out using a HatchBrush for added fun. /*Color backColor1 = randomForeColor(); backColor1 = Color.FromArgb(96, backColor1.R, backColor1.G, backColor1.B); Color backColor2 = randomForeColor(); backColor2 = Color.FromArgb(96, backColor2.R, backColor2.G, backColor2.B); LinearGradientBrush b = new LinearGradientBrush( new Point(0, 0), new Point(DefaultWidth, DefaultHeight), backColor1, backColor2); b.RotateTransform(90); b.ScaleTransform(.3f, .1f); g.FillRectangle(b, 0, 0, DefaultWidth, DefaultHeight);*/ } /// /// Draws the string's letters in a psuedo-random looking way. /// private void drawLetters(Graphics g, string str) { int currentX = random.Next(5, 15); for (int i = 0; i < str.Length; i++) { string currentChar = str[i].ToString(); // new randomized font Font font = new Font( randomFontName(), random.Next(16, 20), GraphicsUnit.Pixel); g.DrawString(currentChar, font, randomSolidBrush(), currentX, random.Next(0, 10)); // increase starting point currentX += (int)g.MeasureString(currentChar, font).Width; currentX += random.Next(0, 5); } } /// /// Draws some random lines on top of the output. /// private void drawLines(Graphics g) { int numLines = random.Next(3, 5); for (int i = 0; i < numLines; i++) { Pen p = randomPen(); p.Width = (float)((random.NextDouble() + 0.1) * 2.5); g.DrawLine(p, random.Next(0, DefaultWidth), random.Next(0, DefaultHeight), random.Next(0, DefaultWidth), random.Next(0, DefaultHeight)); } } #endregion /// /// Creates a /// /// /// private static string encrypt(byte[] input) { byte[] enc = rijndael.CreateEncryptor().TransformFinalBlock(input, 0, input.Length); return Convert.ToBase64String(enc); } private static string decrypt(byte[] nonce) { byte[] dec = rijndael.CreateDecryptor().TransformFinalBlock(nonce, 0, nonce.Length); return System.Text.Encoding.ASCII.GetString(dec); } public static string GenerateNewNonce() { // First come up with 5 capital ASCII letters byte[] input = new byte[5]; Random r = new Random(); for (int i = 0; i < input.Length; i++) { input[i] = (byte)r.Next(0x41, 0x5A); } // Encrypt them return encrypt(input); } public static bool Verify(string nonce, string input) { byte[] inData = System.Text.Encoding.ASCII.GetBytes(input); return encrypt(inData) == nonce; } }