This is a work in progress, and for my furthering my notes. Your comments are welcome.
I decide to code my personal Captcha Image. My Captcha idea is new, which probably can be found somewhere on the internet, however I tweaked the idea into a .NET Class (CaptchaDisplay class within my Captcha namespace)..
This Captcha class is instantiated from two areas. It is instantiated within an User control and a Generic Handler.
The User control’s code behind handles generating the Captcha characters, while the Generic Handler handles creating the Image from characters passed it through a query string. The characters are generated from the User Control.
I added an external appSettings page to the Web.config:
<appSettings file="appSettings.config"/>
Within the appSettings.config file:
<?xml version="1.0"?>
<appSettings>
<add key="Tumblers" value="j(mW*)UYRvxQ$4^54hT" />
</appSettings>
The User Control is shown below:
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="CaptchaControl.ascx.cs"
Inherits="CaptchaHandler.CaptchaControl" %><asp:HiddenField ID="captchaKey" ClientIDMode="Static" runat="server" />
<asp:Panel ID="Captcha" ClientIDMode="Static" runat="server">
</asp:Panel>
and its Code Behind page:
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.UI;using System.Web.UI.WebControls;using deDogs.Captcha;namespace CaptchaHandler{public partial class CaptchaControl : System.Web.UI.UserControl
{private int _width;
public int Width
{set
{ _width = value;}
}
protected void Page_Load(object sender, EventArgs e)
{using (CaptchaDisplay captcha = new CaptchaDisplay(_width))
{captcha.GetCharacters();
string[] c = captcha.characters;string key =new string(captcha.SaltKey(c));
string encodeKey = HttpUtility.UrlEncode(key);captchaKey.Value = encodeKey;
Image img = new Image();img.ImageUrl = "Captcha.ashx?key=" + encodeKey + "&w=" + _width;
img.AlternateText = "Captcha Image";Captcha.Controls.Add(img);
}
}
}
}
using System;using System.Collections.Generic;using System.Linq;using System.Web;using deDogs.Captcha;using System.Drawing.Imaging;namespace CaptchaHandler{ /// <summary> /// Display Captcha Characters Image /// </summary>public class CaptchaHandler : IHttpHandler
{public void ProcessRequest(HttpContext context)
{ HttpContext.Current.Response.ContentType = "image/jpeg";HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Response.BufferOutput = false; try { Int32 width = Convert.ToInt32(HttpContext.Current.Request["w"]); if (width > 0) {using (CaptchaDisplay captcha = new CaptchaDisplay(width))
{ try {string key = HttpUtility.UrlDecode(HttpContext.Current.Request["key"].ToString());
if (key != "")
{ //Decrypt key char[] unSalted = captcha.SaltKey(key.ToCharArray());captcha.characters = unSalted.SelectMany(x => new string[] { x.ToString() }).ToArray();
captcha.CaptchaImage();
captcha._bitMap.Save(HttpContext.Current.Response.OutputStream, ImageFormat.Jpeg);
}
}
catch (Exception) { throw;}
}
}
}
catch (Exception) { throw;}
}
public bool IsReusable
{get
{return false;
}
}
}
}
Finally the Captch class shown below:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.IO;using System.Drawing;using System.Drawing.Imaging;using System.Web;using deDogs.Validation;using System.Configuration;using System.Web.SessionState;namespace deDogs{ namespace Captcha {public class CaptchaDisplay : IDisposable
{public CaptchaDisplay(int width)
{this.fonts = new string[] { "Times New Roman", "Georgia", "Arial", "Comic Sans MS" };
this.sizes = new int[] { 36, 48, 60, 72 };
this.characterSet = @"ABDEFGHJKLMNQRTYZabdefghkmnqryz23456789*#@%$";
this.colr = new Brush[] { Brushes.Black };
this.backgroundColor = Brushes.White;this.salt = new char[] { 'g', '-', '$', 'k', '5', '?', 'O' };
this._bitMap = new Bitmap(width, (int)(width * .32F));
}
public CaptchaDisplay() { }protected class CaptchaChars
{ public CaptchaChars() { }public CaptchaChars(string character, CaptchaRegion region)
{ this.character = character; this.region = region;this.memoryStream = new MemoryStream();
}
public MemoryStream memoryStream { get; set; }public string character;
public CaptchaRegion region;public int angle { get; set; }
}
protected class CaptchaRegion
{ public CaptchaRegion() { }public CaptchaRegion(float x, float y, float width, float height)
{ this.x = x; this.y = y; this.width = width; this.height = height;}
public float x { get; set; }
public float y { get; set; }
public float width { get; set; }
public float height { get; set; }
}
public Bitmap _bitMap;Brush color(Random rnd)
{ return colr[rnd.Next(0, colr.Length)];}
PinTumblerLock pinTumblerLock;
public char[] salt { get; private set; }
public string[] characters;
public string characterSet;
public string[] fonts { get; set; }
public int[] sizes { get; set; }
public Brush[] colr; public Brush backgroundColor; public PinTumblerLock LockKey {get
{ return pinTumblerLock;}
set
{ pinTumblerLock = value;}
}
public void GetCharacters()
{ Random rnd = new Random(); int numChars = rnd.Next(4, 7);this.characters = new string[numChars];
for (var i = 0; i < numChars; i++) {this.characters[i] = this.characterSet.Substring(rnd.Next(0, this.characterSet.Length), 1);
}
}
public void CaptchaImage()
{ Random rnd = new Random();CaptchaChars captchaChar;
List<CaptchaChars> captchaChars = new List<CaptchaChars>(); CharacterRange[] cR = { new CharacterRange(0, 1) };StringFormatFlags flags = StringFormatFlags.NoWrap | StringFormatFlags.NoClip;
StringFormat sF = new StringFormat(flags);sF.SetMeasurableCharacterRanges(cR);
RectangleF displayRectangleF = new RectangleF(0, 0, this._bitMap.Width, this._bitMap.Height);
Font largeFont;
int width = 80, height = 80; for (var c = 0; c < characters.Length; c++) {HttpContext.Current.Trace.Write(characters[c]);
int angle = rnd.Next(0, 30);angle = (angle % 2 == 0) ? -angle : angle;
captchaChar = new CaptchaChars(characters[c], new CaptchaRegion(0, 0, width, height));
captchaChar.angle = angle;
using (Bitmap bitMap = new Bitmap(width, height, PixelFormat.Format32bppArgb))
{ using (Graphics g = Graphics.FromImage(bitMap)) {Rectangle rect = new Rectangle(new Point(0, 0), new Size(bitMap.Width, bitMap.Height));
g.FillRectangle(this.backgroundColor, rect);largeFont = new Font(this.fonts[rnd.Next(0, this.fonts.Length)], this.sizes[rnd.Next(0, this.sizes.Length)], GraphicsUnit.Pixel);
Region[] charRegion = g.MeasureCharacterRanges(characters[c], largeFont, displayRectangleF, sF);
g.DrawString(characters[c], largeFont, colr[rnd.Next(0, colr.Length)], displayRectangleF);
bitMap.Save(captchaChar.memoryStream, ImageFormat.Jpeg);
captchaChars.Add(captchaChar);
}
}
}
this.Display(captchaChars);}
void Display(List<CaptchaChars> c) {using (Graphics mainGraphic = Graphics.FromImage(this._bitMap))
{ StringBuilder charSet = new StringBuilder();Rectangle rect = new Rectangle(new Point(0, 0), new Size(this._bitMap.Width, this._bitMap.Height));
mainGraphic.FillRectangle(this.backgroundColor, rect); int newX = 0; float bwidth, bheight = 0;foreach (CaptchaChars m in c)
{bwidth = m.region.width;
bheight = m.region.height;
Bitmap bitMap = new Bitmap(m.memoryStream); using (Graphics g = Graphics.FromImage(bitMap)) {Rotate(m, g);
g.DrawImage(bitMap, 0, 0);
mainGraphic.DrawImage(bitMap, newX, 0);
newX += (int)m.region.width;charSet.Append(m.character);
}
}
//Crop image bounding selected charactersthis._bitMap = this._bitMap.Clone(new Rectangle(0, 0, newX, (int)bheight), PixelFormat.Format32bppArgb);
Lockup(charSet.ToString());
Distort();
Decorate();
}
}
private void Lockup(string key)
{PinTumblerLock lockup = new PinTumblerLock(key, (ConfigurationManager.AppSettings["Tumblers"]));
}
public char[] SaltKey(char[] key)
{ int len = key.Length;for (int i = 0; i < len; i++)
{ key[i] ^= this.salt[i];}
return key;}
public char[] SaltKey(string[] key)
{char[] c = string.Join(string.Empty, key).ToCharArray();
return this.SaltKey(c);
}
void Rotate(CaptchaChars c, Graphics g) {g.TranslateTransform(c.region.width / 2, c.region.height / 2);
g.RotateTransform(c.angle);
g.TranslateTransform(-c.region.width / 2, -c.region.height / 2);
}
protected void Distort()
{ Random rnd = new Random(); double distort = rnd.Next(5, 20) * (rnd.Next(10) == 1 ? 1 : -1);int width = this._bitMap.Width;
int height = this._bitMap.Height;
using (Bitmap copy = (Bitmap)this._bitMap.Clone())
{for (int y = 0; y < height; y++)
{for (int x = 0; x < width; x++)
{ // Adds a simple waveint newX = (int)(x + (distort * Math.Sin(Math.PI * y / 84.0)));
int newY = (int)(y + (distort * Math.Cos(Math.PI * x / 44.0)));
if (newX < 0 || newX >= width) newX = 0; if (newY < 0 || newY >= height) newY = 0; this._bitMap.SetPixel(x, y, copy.GetPixel(newX, newY));}
}
}
}
protected void Decorate()
{using (Graphics g = Graphics.FromImage(this._bitMap))
{ Random rnd = new Random();int width = this._bitMap.Width;
int height = this._bitMap.Height;
// Draw lines between original points to screen. g.DrawCurve(new Pen(color(rnd), 3.0F), Points(5, rnd).ToArray());foreach (Point pt in Points(5, rnd))
{g.DrawRectangle(new Pen(color(rnd), 3.0F), new Rectangle(pt, new Size(rnd.Next(5, 20), rnd.Next(5, 20))));
}
}
}
protected List<Point> Points(int pts, Random rnd)
{ List<Point> points = new List<Point>();int width = this._bitMap.Width - 20;
int height = this._bitMap.Height - 20;
for (int i = 0; i < pts; i++)
{ points.Add(new Point(rnd.Next(0, width), rnd.Next(0, height)));}
return points;}
public void Dispose()
{ this._bitMap.Dispose();this._bitMap = null;
}
}
}
}