Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Password Manager (.NET)

0.00/5 (No votes)
20 Oct 2008 1  
Password manager application in .NET

Introduction

I wanted for some time to write a very simple password keeper for personal use. I'm posting it in case someone else finds it useful. I think it has some interesting ideas.

Once the username/password is inputted, it's kept encrypted. The way to use it: either by drag and drop (this is useful for HTML based forms) or SendMessage(WM_SETTEXT,...) buttons. You can look at the tool tips for each button.

So the password is never seen in the program itself in plain text unless you use the password or import previous passwords. No copy/paste to/from clipboard.

The app minimizes to a system tray. It has a hotkey (CTRL+F12). During startup, it asks for a password. When used for the first time, it will use this password to encrypt the data. Once you add your passwords, it's a good idea to backup the file to a safe location.

Notes

  • In most cases, the sensitive information is cleared right away (doesn't mean it's a 100% sure way) after it's not needed anymore through Array.Clear. But when dragging/sending the info to another window or when importing from another file, user and password is decrypted into a String and not cleared because String is immutable. So it's left to the GC.
  • Set the appropriate NTFS permissions on the application's directory, that's where the password file will be.
  • It's a good idea to modify the code that derives the encryption keys (add different iteration counts for hash, etc.) so it will be different from the version included with this article.
  • Use some .NET obfuscator. For example, at least the basic version that comes with Visual Studio 2003 (Dotfuscator community edition).

Code Snippets

The hotkey is registered and used through P/Invoke:

[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, Int32 id, 
                                   UInt32 fsModifiers, UInt32 vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, Int32 id);

Then actual WM_HOTKEY message is handled in WndProc:

protected override void WndProc(ref Message m)
{
  if (m.Msg == 0x0312) //WM_HOTKEY
  {
    Visible = true;
    WindowState = FormWindowState.Normal;
    Activate();
  }
  base.WndProc(ref m);
}

The username and password use different encryption algorithms from whole file encryption. The keys and ivs are derived for two algorithms through PasswordDeriveBytes:

pdb = PasswordDeriveBytes(txtPass.Text, salt, "SHA512", 1000)

keyRij = pdb.GetBytes(32);
key3des = pdb.GetBytes(24);

ivRij = pdb.GetBytes(16);
iv3des = pdb.GetBytes(8);

In most cases, the sensitive information is cleared right away after it's not needed anymore through Array.Clear. But when dragging/sending the info to another window, it's decrypted into a String and not cleared because String is immutable.

string val =//user or pass.
IntPtr ptr = Marshal.StringToCoTaskMemAuto(val);
SendMessage(hWnd, 0x000C, 0, ptr.ToInt32());
Marshal.FreeCoTaskMem(ptr);
//
DoDragDrop(val, DragDropEffects.Copy);

For SendMessage case in mouse button down I've set Capture = true. Then in mouse button up event the Capture is set to false. And actual target window is found through P/Invoke.

[DllImport("user32.dll")]
private static extern bool ScreenToClient(IntPtr hWnd, ref SeqPoint pt);
[DllImport("user32.dll")]
private static extern IntPtr GetParent(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern IntPtr WindowFromPoint(SeqPoint pt);        
[DllImport("user32.dll")]
private static extern IntPtr 
   RealChildWindowFromPoint(IntPtr hWndParent, SeqPoint Point);

The actual code to find the target window:

private IntPtr FindTargetWindow(Point pt)
{
  SeqPoint seqpt = new SeqPoint();
  seqpt.x = pt.X;
  seqpt.y = pt.Y;
  IntPtr hWnd = WindowFromPoint(seqpt);

  //it's our window!
  if(Bounds.Contains(pt))
    return this.Handle;

  if (hWnd.ToInt64() != 0)
  {
    IntPtr hParent = GetParent(hWnd);
    if(hParent.ToInt64() != 0 && 
        ScreenToClient(hParent, ref seqpt))
    {
      hParent = RealChildWindowFromPoint(hParent, seqpt);
      if(hParent.ToInt64() != 0)
        hWnd = hParent;
    }
  }
  return hWnd;
}

When selecting the target window, there is a selection frame drawn for the target window. This is accomplished as follows:

[DllImport("gdi32.dll")]
private static extern int GetBkColor(IntPtr hdc);
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, out SeqRect rect);

private void FrameTargetWnd()
{
  Point pt = Cursor.Position;
  IntPtr hWnd = FindTargetWindow(pt);
  if(hWnd.ToInt64() != 0)
  {
    SeqRect rc;
    if(GetWindowRect(hWnd, out rc))
    {
      Rectangle rect = new Rectangle(rc.left, 
         rc.top, rc.right - rc.left, rc.bottom - rc.top);
      if(rect != previousRect)
      {
        Graphics g = Graphics.FromHwnd(hWnd);
        IntPtr hDC = g.GetHdc();
        if(hDC.ToInt64() != 0)
        {
          try
          {
            if(previousRect.Height > 0 
                  || previousRect.Width > 0)
              ControlPaint.DrawReversibleFrame(previousRect, 
                  drawFrameBkColorPrev, FrameStyle.Dashed);
            //prevent our window from being the target
            if(hWnd != this.Handle)
            {
              //get color for the target background window
              drawFrameBkColor = Color.FromArgb(GetBkColor(hDC));
              ControlPaint.DrawReversibleFrame(rect, 
                   drawFrameBkColor, FrameStyle.Dashed);
              //save current frame rect and bk color, 
              //so it can be reset with next call 
              //or in mouse up event
              previousRect = rect;
              drawFrameBkColorPrev = drawFrameBkColor;
            }
            else
            {
              //if it's our window just reset 
              //previous rect's size to 0
              //so it won't be drawn next time
              previousRect.Size = new Size(0,0);
            }
          }
          finally
          {
            g.ReleaseHdc(hDC);
            g.Dispose();
          }
        }
      }
    }
  }
}

Everything else is just standard .NET stuff.

Disclaimer

THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

History

  • 17th August, 2003: Initial post
  • 18th October, 2008: Download file updated

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here