I am currently trying to implement a C# encryption program for the back ups from an SQL database. The database is running on an SQL Express Server which does not support automatic back-up encryption. I am using a free SQL back up app which creates regular back ups of the database. These are unencrypted, so I attempted to create a program to encrypt them as they come in.
I have a method which looks like this.
private static void FileEncrypt(string inputFile, string password)
{
byte[] salt = GenerateRandomSalt();
FileStream fsCrypt = new FileStream(inputFile + ".aes", FileMode.Create);
byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(password);
Aes AES = Aes.Create();
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Padding = PaddingMode.PKCS7;
var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CFB;
fsCrypt.Write(salt, 0, salt.Length);
CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write);
FileStream fsIn = new FileStream(inputFile, FileMode.Open);
byte[] buffer = new byte[1048576];
int read;
try
{
while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
{
cs.Write(buffer, 0, read);
}
// Close up
fsIn.Close();
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
finally
{
cs.Close();
fsCrypt.Close();
}
}
The method uses the password provided to create a key and the use it to encrypt the file. I also have a decryption method which also takes in a path and password and reverses the operation. This all works wonderfully if the user provides the password.
The issue arises when I try to automate the process. I can't have the user typing in the password every time a file need encrypting but I do not know how to provide the password to the method otherwise.
I obviously cannot store the plaintext password somewhere to be read. I understand that hashing is usually used to store passwords safely. A hash is created and stored from the user's password and then when the user types in their password a hash is recreated and if it matches the ones stored on the system it authenticates the user. This however, takes me back to a user typing in the password which was what I was trying to avoid.
Ideally I would want the user to provide the password once and then all the files that come in would be encrypted with that password. When the user required one of the files they would again provide the password which would decrypt the file back to its original state.
I am very new to cryptography so I may be misunderstanding some things. I have tired to look around the issue but can't seem to find a solution.
One option could be to use public-key cryptography. I.e. you would generate a public and private key-pair. The encryption program would generate a random AES key. This key would be encrypted with the public key and stored as part of the data. The rest of the data can then be encrypted using the AES key. The encryption program only need access to the public key, and would have no way for the to decrypt any data once the AES key is overwritten in memory.
The decryption program would work in reverse, using the private key to first decrypt the AES key, and then use said key to decrypt the rest of the data. An potential problem with this process would be to ensure that the private key is accessible in case of a failure. So I would highly recommend testing the restoration process.
See Cryptographic services for more information. Also consider that if an attacker can access your backup server, he likely also has access to the live data. So a backup encryption might have more value if the backups are stored less securely than your live data.
This kind of credentials usually are stored in application configuration (application secrets). Security is managed on the level of operating system - eg. by restricting access to the files for specified users. It is probably the easiest way to do this but also the least secure.
(Windows Only) Another way to store your password securely is to use CredentialManager to save / read the password.
If you host your application on eg. Azure, you can use Azure Key Vault to store the secrets in secure manner.
Hashing the passwords and storing it in secure manner are two different things in this case.
Related
I'm working on a website, where users are able to upload files. I want to encrypt these files, in case there is some kind of security breach where access is granted to them.
When the user wants to download their files, I decrypt directly to the HTTP(S) output stream.
The files are placed on disc, and a record for each is inserted in the website database with some additional data (file name, size, file path, IV and such).
I only have a basic understanding of how to use encryption and therefore have some questions.
I'm using Rfc2898DeriveBytes to generate the bytes for the encryption key. Is it okay to use this class? As far as I know it uses SHA1, which might no longer be secure?
Right now I'm using the same password and salt for each encryption, but a random IV each time. Should I also be randomizing the salt and keep it in the database along with the IV? Will this give additional security?
Should I be using a message authentication code (MAC)? The encrypted files themselves are only stored and never transferred, so I don't know if it's necessary.
I don't really know how to best store the encryption password. I don't want to include it in my website DLL, so I'll probably have it in a file on the server somewhere that isn't in my website folder. How else could I be doing this?
This is my code for encryption. Any obvious security flaws?
const int bufferSize = 1024 * 128;
Guid guid = Guid.NewGuid();
string encryptedFilePath = Path.Combine(FILE_PATH, guid.ToString());
byte[] rgbIV;
using (Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes("PASSWORD HERE", Encoding.ASCII.GetBytes("SALT HERE")))
{
byte[] rgbKey = deriveBytes.GetBytes(256 / 8);
using (FileStream decryptedFileStream = File.OpenRead(decryptedFilePath))
using (FileStream encryptedFileStream = File.OpenWrite(encryptedFilePath))
using (RijndaelManaged algorithm = new RijndaelManaged() { KeySize = 256, BlockSize = 128, Mode = CipherMode.CBC, Padding = PaddingMode.ISO10126 })
{
algorithm.GenerateIV();
rgbIV = algorithm.IV;
using (ICryptoTransform encryptor = algorithm.CreateEncryptor(rgbKey, rgbIV))
using (CryptoStream cryptoStream = new CryptoStream(encryptedFileStream, encryptor, CryptoStreamMode.Write))
{
int read;
byte[] buffer = new byte[bufferSize];
while ((read = decryptedFileStream.Read(buffer, 0, bufferSize)) > 0)
cryptoStream.Write(buffer, 0, read);
cryptoStream.FlushFinalBlock();
}
}
}
I'm using Rfc2898DeriveBytes to generate the bytes for the encryption key. Is it okay to use this class? As far as I know it uses SHA1, which might no longer be secure?
The recent efficient breakage of SHA-1 really only impacts collision resistance which is not needed for PBKDF2 (the algorithm behind Rfc2898DeriveBytes). See: Is PBKDF2-HMAC-SHA1 really broken?
Right now I'm using the same password and salt for each encryption, but a random IV each time. Should I also be randomizing the salt and keep it in the database along with the IV? Will this give additional security?
Maybe it will give additional security, but it certainly won't hurt to do this except if you add a bug. Source: Need for salt with IV
Should I be using a message authentication code (MAC)? The encrypted files themselves are only stored and never transferred, so I don't know if it's necessary.
Usually, a storage system has checks and procedures to prevent and fix data corruption. If you don't have that, then a MAC is a good way to check if the data was corrupted even if this didn't happen maliciously.
If the end user is supposed to receive the data, they can check the MAC themselves and make sure that nobody altered the ciphertext.
I don't really know how to best store the encryption password. I don't want to include it in my website DLL, so I'll probably have it in a file on the server somewhere that isn't in my website folder. How else could I be doing this?
As I understand, you actually want to hold the encryption/decryption key. Anything that you can do is really obfuscation and doesn't provide any actual security. An attacker might just use the same connection to the data storage as your usual code. At best, the attacker will be slowed down a little bit. At worst, they don't even notice that the data was encrypted, because the decryption happened transparently.
It is best to make sure that an attacker cannot get in. Go through the OWASP top 10 and try to follow the advice. Then you can do some security scanning with Nikto or hire a professional penetration tester.
This is my code for encryption. Any obvious security flaws?
Using PaddingMode.ISO10126 doesn't seem like a good idea. You should go with PKCS#7 padding. Source: Why was ISO10126 Padding Withdrawn?
Rfc2898DeriveBytes is essentially PBKDF2 which is NIST recommended.
IF you randomize the salt (a good security practice) would will have top supply it for decryption. A common way is to prefix the encrypted data with the salt and IV.
Yes, you should be using a Mac over the encrypted data and any prepended information such as above.
In order to provide suggestions on securing the encryption key more information on how the the encryption will be used.
Use PKCS#7 padding, sometimes the option is named PKCS#5 for historical reasons.
I'm working on a simple utility to encrypt files as a learning experience.
Everything seems to be working fine, but I'm wondering if I have setup the Key/IV/Salt data securely.
I have noticed that when it comes to cryptography most people seem to envision a working environment that is loaded with malware manned by a wizard remotely ready to dive through the memory of a running application/page file to get at these secure files.
Let's pretend that you're on a clean machine and you encrypt some files and turn off your computer.
What I want to know is whether or not someone can take your hard drive and retrieve the contents of the files using the code I have proposed.
The attack vector I am most concerned with is ensuring that the page files/file caches are inaccessible.
I also want to make sure that the Key/IV system used is not going to make a rainbow table/hash based attack feasible.
Entering the Password:
The password is entered using a text box with the passwordchar value set to true.
I'm not really concerned with the string being in memory as long as it is properly removed after the encryption. I read that using SecureString is kind of pointless at this point because if you have malware on your computer already, you could just as easily have a keylogger on there which renders everything else useless.
private static string salt = "02341235XadfaDADFexA8932F7Dz3J3X";
I salt the password using a hard coded 32 character string.
(The above string is just an example.)
To get at it, it will require someone to decompile/view the .exe file itself with a hex editor (something that I know if very easy to do, but an extra step nonetheless).
I have considered making this salt editable, but I'm not sure how I could securely store it. I think it's a little ridiculous to encrypt your salt because then you will have the same issue etc, so just leaving it as a hard coded string inside the exe itself seems to make the most sense to me.
The way this works is if you decide to make your password "thepassword", it is actually saved as "thepasswordfaDADFexA8932F7Dz3J3X".
The main key here is that you always have a 32 character password, regardless of what you enter.
The Key and IV:
The Key and IV are also salted as follows.
This is what I wanted to get some input on, because to be honest I'm not entirely sure what it's doing:
UnicodeEncoding UE = new UnicodeEncoding();
byte[] keysalt = UE.GetBytes("Xjafe231x42X423XadXCadfkhjeAdS"); //Another string of random characters hard coded in the exe
byte[] IVSodium = UE.GetBytes("83Xkda7l78Dkx85KdJazppoqq6SaxDs"); //Another string of random characters hard coded in the exe
byte[] key = new Rfc2898DeriveBytes(password, keysalt).GetBytes(32); //Derive the key using the password and salt
byte[] IV = new Rfc2898DeriveBytes(password, IVSodium).GetBytes(16); //Derive the IV using the password and salt
My main concern here is that the IV is based on the key. Again, I'm not sure if this will cause any issues and I was hoping you guys could let me know if there are issues, what they are.
Also, is this another scenario where hard coding the salt is a bad practice? Should this be stored in the encrypted file, and if so, does it really make it more secure? Should I make this editable as well?
The crypto streams are setup using the using keyword:
using (FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create))
{
using (RijndaelManaged RMCrypto = new RijndaelManaged())
{
using (CryptoStream cs = new CryptoStream(fsCrypt, RMCrypto.CreateEncryptor(key, IV), CryptoStreamMode.Write))
{
using (FileStream fsIn = new FileStream(inputFile, FileMode.Open))
{
byte[] buffer = new byte[4096]; //4096 is kind of arbitrary - better idea?
int data;
long bytesRead = 0;
while((data = fsIn.Read(buffer, 0, buffer.Length)) > 0)
{
bytesRead += data;
/////////////////////////////////////////
// Handle Aborts and Update Progress Bar
/////////////////////////////////////////
if (!caller.isClosing)
caller.Invoke((MethodInvoker)delegate {
caller.fileProgressBar.Value = ((int)(((double)bytesRead / totalBytes) * 100));
});
else
return false; //Encryption Aborted
/////////////////////////////////////////
cs.Write(buffer, 0, data);
fsIn.Close();
cs.Close();
fsCrypt.Close();
return true;
}
}
}
}
}
Thanks for your time and please let me know if there is a better way to setup the Key/IV/Salt.
I think that it is most likely secure enough as long as there is not a mathematical issue with the IV and Key containing similar characters. If so, should I use a hard coded IV as well? That seems weird.
Note that I'm not saving a hash of the password or anything like that. The password is not saved anywhere. It is just used to generate the Key and the IV.
Thanks for your time.
Edit: Here are the changes recommended for anyone looking in the future.
Note that this is not using a pepper - just a random salt, although it would be easy enough to add
byte[] salt = new byte[32]; //Create a 32 byte salt
rand.NextBytes(salt); //Fill it with random values (use RandomNumberGenerator rand = new RNGCryptoServiceProvider(); to be safe
byte[] IV = new byte[16]; //Create a 16 byte IV
rand.NextBytes(IV); //Fill it with random numbers
byte[] key = new Rfc2898DeriveBytes(password, salt).GetBytes(32); //Derive our Key by mixing our password with the salt
using (FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create))
{
using (RijndaelManaged RMCrypto = new RijndaelManaged())
{
using (CryptoStream cs = new CryptoStream(fsCrypt, RMCrypto.CreateEncryptor(key, IV), CryptoStreamMode.Write))
{
using (FileStream fsIn = new FileStream(inputFile, FileMode.Open))
{
fsCrypt.Write(salt, 0, salt.Length); //Write our salt to the file
fsCrypt.Write(IV, 0, IV.Length); //Write our IV to the file
fsIn.CopyTo(cs); //Encrypt and Write
}
}
}
}
The salt is used for two purposes:
to prevent rainbow table attacks (and it does if applied correctly);
to prevent identical passwords to generate the same password hash.
To do this the salt needs to be 8 to 16 bytes (not characters) of random data, stored with the password hash. Using a static hash as you do defeats both purposes of the hash.
If you need strings, use base 64 to encode the salt and password hash. If you want you can add static data (sometimes called "pepper") to the salt before calling the password hash function. This may add some security if the program data cannot be easily read by an attacker.
You should never directly mix the salt and the password yourself; the Rfc2898DeriveBytes (which is an implementation of PBKDF2) already mixes the two. You should also never store the password, nor should you have to append any data to it. PBKDF2 can handle any size of input, so it doesn't add any functionality.
Now the IV can be taken from the PBKDF2 function (using GetBytes). There is however a problem, it's likely that this will double the initial amount of iterations of PBKDF2 function, which costs CPU time and reduces the advantage over an attacker. It's probably better to just generate a random IV and prefix it to the ciphertext.
So in the end you should store salt | IV | ciphertext, then use salt | pepper as salt and calculate your key, then encrypt/decrypt using the calculated key and IV.
As far as I know,
IV/Salt does not need to be private. It can be stored in plain-text on the hard drive. In fact, salt must be in plain-text, otherwise you cannot generate the same output with it.
It is not a good idea to use your key information for generating the IV as it may leak your key information.
There is no way you can prevent attacks like rainbow. But with salt, rainbow attack becomes expensive as the talbes only work with this salt value.
There is a standard of key derivation fucntion that may useful to you (http://en.wikipedia.org/wiki/PBKDF2).
In the Microsoft page they have a tutorial how to use DES using the built in library:
private static void EncryptData(String inName, String outName, byte[] desKey, byte[] desIV)
{
//Create the file streams to handle the input and output files.
FileStream fin = new FileStream(inName, FileMode.Open, FileAccess.Read);
FileStream fout = new FileStream(outName, FileMode.OpenOrCreate, FileAccess.Write);
fout.SetLength(0);
//Create variables to help with read and write.
byte[] bin = new byte[100]; //This is intermediate storage for the encryption.
long rdlen = 0; //This is the total number of bytes written.
long totlen = fin.Length; //This is the total length of the input file.
int len; //This is the number of bytes to be written at a time.
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
CryptoStream encStream = new CryptoStream(fout, des.CreateEncryptor(desKey, desIV), CryptoStreamMode.Write);
Console.WriteLine("Encrypting...");
//Read from the input file, then encrypt and write to the output file.
while (rdlen < totlen)
{
len = fin.Read(bin, 0, 100);
encStream.Write(bin, 0, len);
rdlen = rdlen + len;
Console.WriteLine("{0} bytes processed", rdlen);
}
encStream.Close();
fout.Close();
fin.Close();
}
Well I got the most of it, but i didn't understand what is desIV and how can I call the function to test it..
Can someone help please?
It might not be a very technical explanation, but I guess a very good way to understand that is thinking about passwords.
Imagine that an attacker could download your whole database of logins and could retrieve everyone's passwords along with their e-mail accounts. But before that, he decides to create some accounts (with some passwords he knows).
If the encryption used just a key, maybe someone could try to 'decode' your key just by having some sample pairs of unencrypted passwords and encrypted ones (using the accounts he created before) and then use the key + the algorithm to decipher every password in the whole database. A worse scenario would be several users with the same password having the same resulted encryption - the attacker wouldn't need even to decipher the key to use other accounts. He would only have to be a good "known passwords" guesser.
Now let's suppose you could introduce some 'ramdom' code at the begging of your encryption/decryption. I.E.: when a user chooses password '12345' instead of encrypting this, you encrypt his e-mail also concatenated 'test#gmail.test12345' and then encrypt. Since there's no way to have two different login e-mails, even if a lot of users choose to use the same password, the resulted encryption would be different. Plus, an attacker would never know what kind of pseudorandom information you have input. This is called 'Salting'.
Salting is not very different than initialization vector, but uses different conventions - this one is array of bytes, but it would mean a string. Some engineers even generate random strings as salt and stores it along the encrypted password. As unrepeatable the the salt is, as difficult to decrypt.
I'm using AesCryptoServiceProvider and CryptoStream to encrypt some data and it seems to be working OK when I use the same key for decryption. However, If I try to decrypt it with the wrong key, I don't get an exception, just junk data. I can't find anything in the .Net documentation which says what is supposed to happen but according to this:
http://books.google.co.uk/books?id=_Y0rWd-Q2xkC&pg=PA631
and this:
Why does a bad password cause "Padding is invalid and cannot be removed"?
I should be getting a CryptographicException. Am I doing it wrong? my function is this:
public static byte[] Encrypt(byte[] data, string password, string salt, bool decrypt)
{
SymmetricAlgorithm aes = new AesCryptoServiceProvider();
Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(salt));
aes.IV = rfc2898.GetBytes(aes.BlockSize / 8);
aes.Key = rfc2898.GetBytes(256 / 8);
ICryptoTransform enc;
if (decrypt) {
enc = aes.CreateDecryptor();
} else {
enc = aes.CreateEncryptor();
}
using (enc) {
using (MemoryStream ms = new MemoryStream()) {
using (CryptoStream cs = new CryptoStream(ms, enc, CryptoStreamMode.Write)) {
cs.Write(data, 0, data.Length);
return ms.ToArray();
}
}
}
Relying on padding errors is not a good way to determine if a key is correct or not. You should really consider using Authenticated Encryption for this purpose.
I have a public domain snip-it that works in C# for this Modern Examples of Symmetric Authenticated Encryption of a string. that I try to keep up to date and reviewed.
P.S. Also it's not clear if your salt is per domain, per user, or per ciphertext from your sample, but if it's not per ciphertext in your code the IV will be predictable and the same for many ciphertexts which is not good for AES-CBC. Implementing crypto is hard.
I've also worked on a highlevel encryption library , a C# port of Google Keyczar. But that may not work very well for you, it only supports randomly generate keys and keysets, and those keysets can then be password encrypted, but only the keysets. High level encryption frameworks are the best practice for encyption.
If you have no padding set on decryption then the decryption method won't be able to recognise junk. Set padding to PKCS#7 for both encryption and encryption and the decryption method will probably be able to recognise junk.
For full assurance, you will need authentication, as jbtule says. To include authentication and encryption in the one data pass use GCM mode. For separate authentication use HMAC.
I'm going to have to put my hands up here and say False Alarm.
I have no idea what was happening on Friday but now I'm getting what I would expect - most of the time the CryptographicException happens as expected. I've no idea whether I was just hugely unlucky with my test data or whether there was a bug in my test harness which I inadvertently fixed, but it's all behaving as expected now.
Incidentally I did a quick empirical test which validates rossum's 1/256 number but that's acceptable for my purposes. In the general case I completely accept the other comments here about HMACs etc, but what I'm doing is for a test tool
A bit more background info as suggested:
I'm finsihing of an Intranet CMS web app where I have to use the products API (ASP.NET based). Because of time constraints and issues with Windows authen' I need another way to ensure staff do not need to re login everytime they visit the site to view personalised content. The way it works is that once a user logs in (username/password), a Session ID storing a new different Security context value is generated that is used to display the personalised content. The API login method called uses the username and password as parameters. The only way I can think of automatically logging in the next time the staff visits the site is by storing the password in a enrypted cookie and checking of its existing when the site is visited and then calling the API login method using the username and decrypted password cookie values.
Any other ideas as an alternative welcomed.
Mo
Hi,
I'm using some code found on the web to encrypt and decrypt a password string. It encrypts fine but when it calls the code below to decrypt the string it throws the error "Length of the data to decrypt is invalid" How can I resolve this?
Thanks in advance.
Mo
System.Text.Encoding enc = System.Text.Encoding.ASCII;
byte[] myByteArray = enc.GetBytes(_pword);
SymmetricAlgorithm sa = DES.Create();
MemoryStream msDecrypt = new MemoryStream(myByteArray);
CryptoStream csDecrypt = new CryptoStream(msDecrypt, sa.CreateDecryptor(), CryptoStreamMode.Read);
byte[] decryptedTextBytes = new Byte[myByteArray.Length];
csDecrypt.Read(decryptedTextBytes, 0, myByteArray.Length);
csDecrypt.Close();
msDecrypt.Close();
string decryptedTextString = (new UnicodeEncoding()).GetString(decryptedTextBytes);
A couple of things here...
You shouldn't encrypt passwords usually. You should hash them.
If you decide to continue down the road of encryption..
You are using the DES algorithm. This is considered insecure and flawed. I'd recommend looking at the AES algorithm.
Depending on how much data you are working with, the CryptoStream might be overkill.
Using the ASCII encoding can cause loss of data that isn't ASCII, like Cyrillic letters. The recommended fix is to use something else, like UTF8.
Here is an example:
string text = "Hello";
using (var aes = new AesManaged())
{
var bytes = System.Text.Encoding.UTF8.GetBytes(text);
byte[] encryptedBytes;
using (var encrypt = aes.CreateEncryptor())
{
encryptedBytes = encrypt.TransformFinalBlock(bytes, 0, bytes.Length);
}
byte[] decryptedBytes;
using (var decrypt = aes.CreateDecryptor())
{
decryptedBytes = decrypt.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
}
var decryptedText = System.Text.Encoding.UTF8.GetString(decryptedBytes);
Console.Out.WriteLine("decryptedText = {0}", decryptedText);
}
This will use a random key every time. It is likely that you will need to encrypt some data, then decrypt it at a later time. When you create the AesManaged object, you can store the Key and IV property. You can re-use the same Key if you'd like, but different data should always be encrypted with a different IV (Initialization Vector). Where you store that key, is up to you. That's why hashing might be a better alternative: there is no key, and no need to worry about storing the key safely.
If you want to go down the hashing route, here is a small example:
var textToHash = "hello";
using (SHA1 sha = new SHA1Managed())
{
var bytesToHash = System.Text.Encoding.UTF8.GetBytes(textToHash);
var hash = sha.ComputeHash(bytesToHash);
string base64hash = Convert.ToBase64String(hash);
}
This uses the SHA1 algorithm, which should work fine for passwords, however you may want to consider SHA256.
The concept is simple: a hash will produce a (mostly) unique output for an input, however the output cannot be converted back to the input - it's destructive. Whenever you want to check if a user should be authenticated, check hash the password they gave you, and check it against the hash of the correct password. That way you aren't storing anything sensitive.
I've actually had this error before and it took me 3 days to figure out the solution. The issue will be the fact that the machine key you need for descryption needs to be registered on your machine itself.
Read fully up on DES encryption, it works by an application key, and a machine-level key. The error you're getting is likely because of the machine key missing.
Compare the bytes used to create the _pword string (in the encryption method) to the bytes retrieved with GetBytes. Probably you will notice a change in the data there.
To store the encrypted bytes, I think you should use Convert.ToBase64String and Convert.FromBase64String turn the encrypted password to/from a string.
I also do not see the code where you set the Key and IV. So I guess you are using a different key to encrypt and decrypt the password.
If the current Key property is null,
the GenerateKey method is called to
create a new random Key. If the
current IV property is null, the
GenerateIV method is called to create
a new random IV.
DES is a block based cipher - only certain lengths of buffers are valid. If I remember correctly, the block size for DES is 64 bits, so you need to ensure that your byte array is a multiple of 8 bytes long.
(That should fix your immediate problem, but I'd reference other peoples advice here - you really ought not to be using DES for any new code, and for passwords it's usually more appropriate to hash than to encrypt).