Introduction
Large data sets are best stored in databases. For small, simple, single-table datasets, a traditional database can be overkill, and it may be more convenient to store it in various simpler formats ranging from tab-delimited files to XML files. However, each one also brings its own complications, which are not addressed here.
XmlStore is intended to be a "no brainer" format. (More on this later.)
XmlStoreClient is a WinForms client with a simple user interface. It displays the contents of any XmlStore file in a DataGridView
. You can navigate to any record (row) or field (column), edit it, and save your changes. You can also encrypt and decrypt:
- an individual cell,
- an entire row,
- an entire column, or
- the entire data set (table).
The Title column is encrypted in the picture above, and shown decrypted in the picture below.
Warning
If you encrypt anything which requires you to supply a password, you will need the same password to decrypt the data. You have to remember it, as the password is not stored anywhere.
Background
Situation
A friend needed a simple utility to store usernames and passwords to some of the websites she visits often. Like a lot of people, she has a hard time remembering all the usernames and passwords she creates. She looked at solutions that could meet her needs, such as KeePass. But she was wary of getting a solution from an unfamiliar source. So, she wanted:
- an easy-to-use interface (as in "don't make me think") and
- a portable yet secure storage method, one that she could store on her memory stick and use from any of the several PCs she has access to at work, at home, on the road, etc.
Complication
As with any application being "custom-built" for someone, the requirements change. In this case, she first wanted only a simple list of usernames and passwords. Then, she wanted to add the name of the entity (e.g., bank), then she wanted to add its URL, and so on. This essentially became a third requirement -- the ability to add another field easily, without requiring a software update.
As this was a "gratis" assignment, I didn't want to have to maintain/update the utility each time she changed her list of "required fields".
I should mention, though, she did send in a nice donation to one of my favorite charities: Year Up. Indeed, if this utility or any of the code in it helps you, please consider sending a suitable donation to Year Up or to any similar organization in the world selflessly doing good work and helping people in need.
Solution
The DataGridView
-based interface works well for my friend, because she is familiar with spreadsheets. For the second goal, the portable part is accomplished using XML and the secure part by allowing her to encrypt any "value" in the XML using yet another password, but one that:
- is not stored anywhere, except in her head, and
- one she can change any time, and as often as she wants.
The picture below shows a screenshot of a disguised variation of what she was happy with.
Using the code
DISCLAIMER
I undertook this project to help a friend and, in the process, also learned a few things about Visual Studio 2005, C#, XML, DataGridView
, cryptography, etc. Caveat Emptor: Nothing has been properly or adequately tested. More importantly, there is a good chance you or someone else can do this better. So if you do use this code and cannot decrypt something, there is little I or anyone can do to help you. On the bright side, though, since you have the source code, you can make it more bullet-proof to suit your needs.
Building the utility
If you have Visual Studio 2005, then you should be able to use the project source code "out of the box" -- simply build and run. The code itself is not rocket science. It is reasonably documented. If you don't have Visual Studio 2005, you will have to ask a more experienced friend. (Don't ask me, as I don't have a clue! Don't ask Microsoft either, as no one there will respond!!!)
Code modules
Side benefits of the code for newbies (like myself): the project includes modules with reasonably documented and self-explanatory code. With luck, they may help you learn how to use a few features of the various technologies employed.
VVX_About.cs
VVX.About
is a simple class that provides a "cheap", zero maintenance, "Help | About" message box. If you don't know how to access information in an assembly, it can show you one way of extracting some information from it.
VVX_MsgBox.cs
VVX.MsgBox
is a class to simplify access to MessageBox
es of different kinds. It helped me learn how to use message boxes more efficiently. For example, the MsgBox.Confirm(...)
method allows me to do something like this:
if (VVX.MsgBox.Confirm("Save changes?"))
this.DoFileSave();
instead of this:
if(DialogResult.Yes == MessageBox.Show("Save changes?", "Confirm",
MessageBoxButtons.YesNo, MessageBoxIcon.Question))
this.DoFileSave();
VVX_FileDialog.cs
VVX.FileDialog
is a class to access OpenFileDialog
and SaveFileDialog
a little more efficiently. For example, the FileDialog.GetFilenameToOpen(...)
method allows me to do something like this:
string filename = VVX.FileDialog.GetFilenameToOpen(VVX.FileDialog.FileType.XML);
if (filename.Length > 0)
{
this.DoGridPopulate(this.ctlTableDGV);
}
The method simply takes care of setting up the filters, etc.
VVX_File.cs
VVX.File
is a class to simplify access to a few dile-related methods, such as Delete. Again, it is not rocket science, but I found it was hard to remember that you have to use the System.IO.FileInfo
class to delete a file! I can now delete a file with just this.
VVX.File.Delete(sTempFile);
VVX_CryptoLib.cs
VVX.CryptoLib
is a C# class that is a subset of and inspired by a VB-based CodeProject article: ".NET cryptography library for files and strings" by HanreG. I simply rewrote the code in C# and used only two methods:
public string EncryptString(string value , string password) {...}
public string DecryptString(string value , string password) {...}
For more information on these topics, see that article.
VVX_XmlStore.cs
VVX.XmlStore
is a C# class that helps us "access" and "manage" an XmlStore file. Currently, it is geared to connect the data with the UI (DataGridView
).
XmlStore
Consider the following "table":
The XmlStore file for the above "table":
- is essentially a simple "flat" XML file.
- it stores the "tabular" data, for one table, such as the one for Passwords shown above.
and it looks like this:
="1.0"="utf-8"
<xmlstore version="1.0">
<schema datanodename="record">
<field name="entity" title="Entity" />
<field name="username" title="Username" />
<field name="password" title="Password" />
<field name="url" title="URL" />
<field name="contact" title="Contact" />
<field name="phone" title="Phone" />
</schema>
<record entity="bank" username="victorbox" password="Sony1234"
url="www.xyz.com" contact="john doe" phone="123-456-7890" />
<record entity="city" username="victorboz" password="2MuchTax"
url="www.city.gov" contact="Jane Doe" phone="890-456-1234" />
<record entity="CodeProject" username="victorbos" password="goodguys"
url="www.CodeProject.com" contact="-" phone="-" />
</xmlstore>
As you can see, it really is a plain XML file with:
- a root node called
xmlstore
(but it can be named anything, because it is not directly referenced anywhere in the code).
and two types of "first-generation" child nodes within the root, namely,
- a single
<schema ... />
node, and - one data node for each row (or
<record ... />
) in the table.
The data node can have any base name; in the above example, it is named record.
The utility will work without a Schema node
The schema node is nice to have, but not necessary. If it is not found, VVX.XmlStore
will simply enumerate the attributes in the first data (e.g., <record ... />
) node and use the names of the attributes as the titles of the columns.
About the Schema node
The schema node serves a limited purpose. First, it provides the display title (i.e., column header) shown in the DataGridView
for each column whose values come from a corresponding attribute in the data (i.e., record) nodes.
Second, its datanodename
attribute identifies the base name, "record"
, of the data nodes; in this example, <record ... />
.
Important
It is likely very obvious to you, but just in case it is not ...
- The schema node must be
<schema ... />
; do not change its base name. - Its attribute must be named as shown:
<schema datanodename="..." >
; do not change its name. - Its child nodes must be
<field name="..." title="..." />
; do not change their base name or attribute names. - If you change the base name of the data nodes from
<record ... />
to, say, <lulu ... />
, then you must also change the value of the datanodename
attribute from "record"
to "lulu"
. - If you change the name of an attribute in the data node, you should also change its name in the corresponding
<field ... />
node. For example, if you change <record entity="bank" ... />
to <record vendor="bank" ... />
, then you should change <field name="entity" title="Entity" />
to <field name="vendor" title="Entity" />
. Note: Changing the title is entirely cosmetic.
Why bother with XmlStore?
After reading the last part, you may feel this is too much of a headache. In my opinion, for this utility, these "rules" are perhaps a little less demanding than some of the alternatives.
Actually, you can do all of this by using a System.Data.DataTable
and its ReadXml
and ReadXmlSchema
methods and then writing the necessary code to initialize the DataGridView
. The limitation of such an approach, for this utility anyway, is that my friend would have to maintain two files, one for the XML and one for the schema; for non-geeks, having to do so more than doubles the complexity of the solution. Besides, I may not have learned as much as I did!
Improvements needed
There is plenty of room for improvement: an easier way to "test" and "repair" an XmlStore file; an easier way to create a new file; an easier way to change the Crypto password; an easier way to rearrange the columns. My friend doesn't really want/need these ... but they reflect the obvious limitations of this utility.
If you need something more "powerful" and "rich" ...
If this utility is too wimpy for you, one of the following may suit you better:
Unfortunately, they are a little too complicated for my friend. (Recall one of the requirements was "don't make me think"!)
Finally, a word of thanks to you
I have learned a lot from people like you who have anonymously and freely shared your experiences with the world. Perhaps, you gave a little, but I learned a lot. If this utility helps even one person, you've made my day and given me a chance to give back to the community. So, thank you!
Other recent contributions