I am pretty new to this JASYPT library and I am working on a Project that uses JASYPT StandardByteDigester for Encrypting passwords. Now I want to get the decrypted string, and can't find any function that does the same.
String password = "Password";
byte[] password_bytes = password.getBytes("UTF-8");
byte[] digest = this.byteDigester.digest(messageBytes);
What is the reverse of this ? I mean how to enter the encrypted bytes, and get the decrypted String ?
StandardByteDigester() creates the hash of the password, this process is not reversible. If you want to test the equality with a second password, this second password is also to be hashed and the hashes are to be compared.
StandardByteDigester uses by default MD5 (which is insecure), a random 8 bytes salt and 1000 iterations (nowadays generally too small). But this can be changed.
Note that to compare two passwords, the parameters used, i.e. digest, salt, and iterations, must be the same.
Related
I've generated a random 256 bit symmetric key, in a file, to use for encrypting some data using the OpenSSL command line which I need to decrypt later programmatically using the OpenSSL library. I'm not having success, and I think the problem might be in the initialization vector I'm using (or not using).
I encrypt the data using this command:
/usr/bin/openssl enc -aes-256-cbc -salt -in input_filename -out output_filename -pass file:keyfile
I'm using the following call to initialize the decrypting of the data:
EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, keyfile.data(), nullptr))
keyfile is a vector<unsigned char> that holds the 32 bytes of the key. My question is regarding that last parameter. It's supposed to be an initialization vector to the cipher algorithm. I didn't specify an IV when encrypting, so some default must have been used.
Does passing nullptr for that parameter mean "use the default"? Is the default null, and nothing is added to the first cipher block?
I should mention that I'm able to decrypt from the command line without supplying an IV.
What is the default IV when encrypting with EVP_aes_256_cbc() [sic] cipher...
Does passing nullptr for that parameter mean "use the default"? Is the default null, and nothing is added to the first cipher block?
There is none. You have to supply it. For completeness, the IV should be non-predictable.
Non-Predictable is slightly different than both Unique and Random. For example, SSLv3 used to use the last block of ciphertext for the next block's IV. It was Unique, but it was neither Random nor Non-Predictable, and it made SSLv3 vulnerable to chosen plaintext attacks.
Other libraries do clever things like provide a null vector (a string of 0's). Their attackers thank them for it. Also see Why is using a Non-Random IV with CBC Mode a vulnerability? on Stack Overflow and Is AES in CBC mode secure if a known and/or fixed IV is used? on Crypto.SE.
/usr/bin/openssl enc -aes-256-cbc...
I should mention that I'm able to decrypt from the command line without supplying an IV.
OpenSSL uses an internal mashup/key derivation function which takes the password, and derives a key and iv. Its called EVP_BytesToKey, and you can read about it in the man pages. The man pages also say:
If the total key and IV length is less than the digest length and MD5 is used then the derivation algorithm is compatible with PKCS#5 v1.5 otherwise a non standard extension is used to derive the extra data.
There are plenty of examples of EVP_BytesToKey once you know what to look for. Openssl password to key is one in C. How to decrypt file in Java encrypted with openssl command using AES in one in Java.
EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, keyfile.data(), nullptr))
I didn't specify an IV when encrypting, so some default must have been used.
Check your return values. A call should have failed somewhere along the path. Maybe not at EVP_DecryptInit_ex, but surely before EVP_DecryptFinal.
If its not failing, then please file a bug report.
EVP_DecryptInit_ex is an interface to the AES decryption primitive. That is just one piece of what you need to decrypt the OpenSSL encryption format. The OpenSSL encryption format is not well documented, but you can work it backwards from the code and some of the docs. The key and IV computation is explained in the EVP_BytesToKey documentation:
The key and IV is derived by concatenating D_1, D_2, etc until enough
data is available for the key and IV. D_i is defined as:
D_i = HASH^count(D_(i-1) || data || salt)
where || denotes concatentaion, D_0 is empty, HASH is the digest
algorithm in use, HASH^1(data) is simply HASH(data), HASH^2(data) is
HASH(HASH(data)) and so on.
The initial bytes are used for the key and the subsequent bytes for the
IV.
"HASH" here is MD5. In practice, this means you compute hashes like this:
Hash0 = ''
Hash1 = MD5(Hash0 + Password + Salt)
Hash2 = MD5(Hash1 + Password + Salt)
Hash3 = MD5(Hash2 + Password + Salt)
...
Then you pull of the bytes you need for the key, and then pull the bytes you need for the IV. For AES-128 that means Hash1 is the key and Hash2 is the IV. For AES-256, the key is Hash1+Hash2 (concatenated, not added) and Hash3 is the IV.
You need to strip off the leading Salted___ header, then use the salt to compute the key and IV. Then you'll have the pieces to feed into EVP_DecryptInit_ex.
Since you're doing this in C++, though, you can probably just dig through the enc code and reuse it (after verifying its license is compatible with your use).
Note that the OpenSSL IV is randomly generated, since it's the output of a hashing process involving a random salt. The security of the first block doesn't depend on the IV being random per se; it just requires that a particular IV+Key pair never be repeated. The OpenSSL process ensures that as long as the random salt is never repeated.
It is possible that using MD5 this way entangles the key and IV in a way that leaks information, but I've never seen an analysis that claims that. If you have to use the OpenSSL format, I wouldn't have any hesitations over its IV generation. The big problems with the OpenSSL format is that it's fast to brute force (4 rounds of MD5 is not enough stretching) and it lacks any authentication.
We are migrating a company we acquired a lot of kiosk hardware assets from that are in the field. Since the company was suffering, we are stuck with some issues in migrating the locations fingerprints, usernames and passwords without any implementation docs. Luckily, most of the passwords used are numeric 4-6 PIN or a common used word. I'm stuck with trying to figure out what format the password is hashed in and hopefully can decipher it from there using a dictionary for the majority of the passwords. I have the following format:
"password": "ce62f0002776890507c4050a3b76c064d3d24328aea52a08633b726d352532dc",
"salt": "JUQLSPOYGFURMGSDRYWIWBIWP",
The password above is "password". Hopefully this helps in finding the format.
If it is a hash, looks like a hash, possibly HMAC-SHA256 from the length, you need to run a password cracking program. You should be able to recover well over 90% but most likely not all.
On my laptop I can run a 20 byte password through SHA-512 and compare in under 1us so with just a SHA-512 hash I can try 1,000,000 passwords a second.
You can make a list to check but there are already good lists, see below.
For more information see:
Password list at SecLists.
Infosec password-cracking-tools
Arstechnica How I became a password cracker.
You can implement the old hashing method in your new code. When the password matches (i.e. the one the partner sends) you can then store it in your new format (essentually accepting both). This saves you the need to crack the existing passwords.
For this to work you do need to know how the passwords are hashed and what formatis used, lucikly this seems to be fairly easy (Java sample):
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write("password".getBytes(StandardCharsets.US_ASCII));
bos.write("JUQLSPOYGFURMGSDRYWIWBIWP".getBytes(StandardCharsets.US_ASCII));
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] out = md.digest(bos.toByteArray());
System.out.println("hex = " + new HexBinaryAdapter().marshal(out).toLowerCase());
Produces (i.e. concatenate password bytes and salt bytes, non-iteratively calculate SHA256 and convert to hex) the expected hash:
hex = ce62f0002776890507c4050a3b76c064d3d24328aea52a08633b726d352532dc
I am implementing a new password stored procedure into my companies current product. It is a ASP.NET application with MS SQL Server.
Before they used 3DES encryption off the same common seed to encrypt, and to check a users authentication they just decrypted the password using the same seed.
I am implementing a SHA256 hash, with a salt that can not be decrypted.
Firstly, I understand that every salt should be different per user, but I don't understand where this salt would be stored? If its stored in the database, doesn't this void its purpose?
My idea was creating a salt by taking the first 4 letters of the username, the first 3 letters of the first name, and the first 3 letters of the last name, and converting it into a md5 hash and then using that as the salt without storing in the database.
This sequence would be server side so that no hacker could know the sequence without the source code.
Is there any issues with what I am doing here?
Also is SHA256 acceptable or should I be looking at SHA512.
Thanks
"Is there any issues with what I am doing here?"
Yes, there is. Obscurity is not security. Just because the salt is hard to find out doesn't mean that it's very secure. Figuring out how you created the salt would be a piece of cake compared to forcing the hash.
There is no need to keep the salt secret, just create a random number for each user and store along with the password. The purpose of the salt is to elliminate the advantages of using rainbow tables to crack all the passwords in a table. The salt just have to be different for most users (preferably unique, but that's not crucial).
If you absolutely must implement this yourself (personally I'm a fan of MembershipReboot, then you should take a look at PBKDF2 for password storage.
Not only will it implement the salt properly, it also supports multiple iterations to help deter brute force attacks. You can find guidance for the number of iterations here.
Also worth noting, PBKDF2 is an acceptable NIST standard in case validation is a concern.
To answer your question about keeping the salt in the database, there's no need to keep it secret. The purpose of the salt is simply to prevent pre-computation of hashes, not obfuscate or 'encrypt' in any way.
Rainbow tables work by pre-computing passwords, and then when brute forcing, looking up the respective hash in the rainbow table. You can actually see how simple it is by googling MD5 hashes, and often in the search results you'll find the original input.
For example, if you google the string '5f4dcc3b5aa765d61d8327deb882cf99', you'll find it corresponds to 'password'.
By using a salt, the attacker must compute unique hashes for every possible password, instead of just a generalized list.
The salt is NOT supposed to be private. It is distributed along with the hash, usually prepended to it. The sole purpose of the salt is to make sure that if the same data is encrypted twice you never get the same output. By definition it has to be unique, but randomness or secrecy is not required.
You should also NOT create a random number per user. The salt needs to be different for every encryption, even for the same user. Just generate a random salt, use it to create your hash, prepend the salt to the hash, encode everything to base64, and store it. To verify the hash you decode to binary, extract the salt, use it to hash your input data and compare with the decoded hash.
Something that works well as a salt is a GUID. 128 bits, by definition unique, although not totally random, and available instantly without any additional code. Here is an example of an AES-256 encrypt and decrypt. Keep in mind in a real implementation you would want the plain text and key to be stored in SecureString objects...
Private blockSize = 128
Private keySize = 256
Private cipherMode = CipherMode.CBC
Protected Function AESEncryptStringToBase64(strPlainText As String, strKey As String) As String
Dim Algo As AesManaged = AesManaged.Create()
With Algo
.BlockSize = blockSize
.FeedbackSize = blockSize
.KeySize = keySize
.Mode = cipherMode
.IV = Guid.NewGuid().ToByteArray()
.Key = Encoding.ASCII.GetBytes(strKey)
End With
Using Encryptor As ICryptoTransform = Algo.CreateEncryptor()
Using MemStream As New MemoryStream
Using CryptStream As New CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write)
Using Writer As New StreamWriter(CryptStream)
Writer.Write(strPlainText)
End Using
AESEncryptStringToBase64 = Convert.ToBase64String(Algo.IV.Concat(MemStream.ToArray()).ToArray())
End Using
End Using
End Using
End Function
Protected Function AESDecryptBase64ToString(strCipherText As String, strKey As String) As String
Dim arrSaltAndCipherText As Byte() = Convert.FromBase64String(strCipherText)
Dim Algo As AesManaged = AesManaged.Create()
With Algo
.BlockSize = blockSize
.FeedbackSize = blockSize
.KeySize = keySize
.Mode = cipherMode
.IV = arrSaltAndCipherText.Take(16).ToArray()
.Key = Encoding.ASCII.GetBytes(strKey)
End With
Using Decryptor As ICryptoTransform = Algo.CreateDecryptor()
Using MemStream As New MemoryStream(arrSaltAndCipherText.Skip(16).ToArray())
Using CryptStream As New CryptoStream(MemStream, Decryptor, CryptoStreamMode.Read)
Using Reader As New StreamReader(CryptStream)
AESDecryptBase64ToString = Reader.ReadToEnd()
End Using
End Using
End Using
End Using
End Function
i use this code for encrypt my data to blwofish but i dont know really to convert to blowfish or other encryption.
echo crypt('ab','$2a$09$anexamplestringforsalt$')."\n";
and i'm try bottom code but it's false
echo CRYPT_BLOWFISH('ab','$2a$09$anexamplestringforsalt$')."\n";
It is the crypt parameter string, that defines which algorithm is used:
$2a : This describes the algorithm (BCrypt) but should be 2y nowadays
$09 : This is the number of rounds and is usually 10 or higher
$anexamplestringforsalt : This should be a really random salt of a given alphabet
To generate a BCrypt hash, it is much safer to use the new password_hash() function though, there exists also a compatibility pack for earlier PHP versions.
// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);
// Check if the hash of the entered login password, matches the stored hash.
// The salt and the cost factor will be extracted from $existingHashFromDb.
$isPasswordCorrect = password_verify($password, $existingHashFromDb);
I've taken a look at the StackOverflow question, "Password Encryption / Database Layer AES or App Layer AES," and I'd like to effectively and efficiently hash my passwords on registration (web app) and then be able to check they are correct on login. I'm using VB, but comfortable using C#.
I would love to use Jeff Atwood's Encryption class described in ".NET Encryption Simplified" as it's really easy to understand. It has a hashing class—but I have no idea how to "login" and compare hashes after they have been hashed. This is Jeff's demonstration of his hash methods using his Encryption class:
Sub DemoHash()
Dim d As New Encryption.Data( _
"{ts '2004-10-09 08:10:04'}The world is beautiful and needs caring by its children")
Dim hash As New Encryption.Hash(Encryption.Hash.Provider.SHA1)
Dim hash2 As New Encryption.Hash(Encryption.Hash.Provider.SHA256)
Dim hash3 As New Encryption.Hash(Encryption.Hash.Provider.SHA384)
Dim hash4 As New Encryption.Hash(Encryption.Hash.Provider.SHA512)
Dim hash5 As New Encryption.Hash(Encryption.Hash.Provider.MD5)
Dim hash6 As New Encryption.Hash(Encryption.Hash.Provider.CRC32)
hash.Calculate(d)
hash2.Calculate(d)
hash3.Calculate(d)
hash4.Calculate(d)
hash5.Calculate(d)
Console.WriteLine("SHA1: " & hash.Value.Hex)
Console.WriteLine("SHA256: " & hash2.Value.Hex)
Console.WriteLine("SHA384: " & hash3.Value.Hex)
Console.WriteLine("SHA512: " & hash4.Value.Hex)
Console.WriteLine("MD5: " & hash5.Value.Hex)
Console.WriteLine("CRC32: " & hash6.Calculate(d).Hex)
Console.WriteLine()
Dim salt As New Encryption.Data("salty!")
Console.WriteLine("Salted CRC32: " & hash6.Calculate(d, salt).Hex)
Console.WriteLine("Press ENTER to continue...")
Console.ReadLine()
End Sub
So my questions are:
I can encrypt the password (though I have no intention of storing it) and hash a string. If I were to have a user called 'barry' with a password of 'fishlegs', what is the best way to store his password and retrieve it?
In SQL Server; is binary or nvarchar the best option for the storage of the hash?
Based on 'barry' and his password what effectively is the hash storing? Is it an encryption of 'fishlegs' appended to a salt?
Cryptography is hard!
Thanks to anyone who can assist...
Hmm, I think you're just missing some basic concepts related to how hashing works. Let me try to explain briefly. I'm going to start out simple and elaborate on my answer afterwards, so please read through the whole thing, the information at the beginning will not be secure.
What you want to use to store a password is a function known as a "one-way hash". What this means is that, for any input that you feed the function, the same input will always give the same result. However, there is no mathematical process that lets you take that result string and figure out what the original input was.
Let's take MD5 as an example of a hashing function. If I run MD5 on the string "password", I will always get the result "5f4dcc3b5aa765d61d8327deb882cf99". However, if you were to simply give someone that result string ("5f4d..."), it is impossible for them to apply some mathematical process to "reverse" the function and figure out that it came from "password".
What this means is that when a user first sets up their password, you apply a hashing function to it, and store the result. So instead of storing "password", you store "5f4dcc3b5aa765d61d8327deb882cf99". Then, when that user tries to log in, you take whatever they typed into the password box on the login form, and apply the same hashing function. If you get the same result as what's stored in the database, they must have entered the same password as they originally chose, even though you have no idea what that original password actually was.
Now, even though it's impossible to "reverse" a hash function, the fact that the same input always gives the same output means that someone can simply build up a big database of input/output pairs, and use that to effectively reverse hashes. This is called a "rainbow table". There are many of these available on the internet, so it's not safe to use simple hashing, just in case your database ever gets compromised. That is, even though it is mathematically impossible to take "5f4dcc3b5aa765d61d8327deb882cf99" and figure out that it came from running MD5 on "password", it's very easy to determine that in practice. All you have to do is run every word in a dictionary through MD5 and store the results, and you can easily reverse simple passwords.
This is where "salting" comes in. If you generate a random "salt" string for every user and attach that to their password, it effectively ruins rainbow tables. For example, let's say that the same user above registers with their password as "password". We generate a random 8-character salt to attach to their password before hashing it. Let's say that it's "A4BR82QX". Now, instead of hashing "password", we hash "A4BR82QXpassword". This gives the result "87a4ba071c8bcb5efe457e6c4e6c4490", so we store that in the database, along with the salt string. Then when this user tries to log in, instead of directly hashing and comparing the password they entered in the login form, we take what they entered, put "A4BR82QX" in front of it again, and hash that. Just as before, if it matches the stored hash, we know that they entered the right password.
Effectively what you've done here is make it so that pre-generated rainbow tables are useless for trying to crack the passwords in your database. Since the salt is random, and each user has a different one (generally), the attacker will have to re-generate their rainbow tables for every individual user. This is much more difficult.
However, there's one more problem, and that's that generating MD5 hashes is fast. Even though salting like this requires them to re-generate rainbow tables, because of how fast MD5 is, some decently-complete rainbow tables can be created very quickly. So if they just want to crack a high-value account on your site, it's not really a big deal for them to spend some time generating rainbow tables to try and reverse that password. If the high-value account's original password wasn't secure enough by itself, it'll still be found very quickly, even with salting.
So the next step is to find a slow hash function, and use this instead of a fast one like MD5. Having your site take an extra couple of seconds to check a login isn't a big deal at all. But when someone is trying to generate rainbow tables to crack a password, having each entry take several seconds is an absolute killer. I've written enough here, so I'll just finish by linking to this article, which goes into plenty of detail about picking a good, slow hashing function: Enough With The Rainbow Tables: What You Need To Know About Secure Password Schemes.
That was a pretty huge answer, if any of that's unclear, please let me know in a comment and I'll edit to elaborate.
OK so a couple of things about Jeff's class first of all. SHA1 and MD5 are deprecated now. CRC32 is not suitable at all for passwords. Secondly you should be salting every hash, preferably with a different salt value. Generally you choose a cryptographically random block of data for this, but at a push you could use the user name. To salt you prefix, or suffix the salt value somewhere in the process. I tend to hash the password, hash the salt, combine the two, then hash again. But you can swap things around it doesn't really matter that much, as long as you are consistent.
So rather than confuse things further with Jeff's class lets do this the classic way.
First off random salt generation.
public static byte[] GetRandomSalt()
{
int minSaltSize = 16;
int maxSaltSize = 32;
Random random = new Random();
int saltSize = random.Next(minSaltSize, maxSaltSize);
saltBytes = new byte[saltSize];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetNonZeroBytes(saltBytes);
return saltBytes;
}
Then hashing
public static byte[] ComputeHash(string plainText)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
HashAlgorithm hash = new SHA256Managed();
return hash.ComputeHash(plainTextWithSaltBytes);
}
So this will compute a SHA256 hash and return it as a byte array.
If you were salting you'd do something like the following
byte[] passwordHash = ComputeHash(password);
byte[] salt = GetRandomSalt();
byte[] saltHash = ComputeHash(salt);
byte[] hashWithSaltBytes = new byte[hashBytes.Length + saltBytes.Length];
for (int i=0; i < hashBytes.Length; i++)
hashWithSaltBytes[i] = hashBytes[i];
for (int i=0; i < saltBytes.Length; i++)
hashWithSaltBytes[hashBytes.Length + i] = saltBytes[i];
And then, if you're bored hash it down again, or leave as is.
To turn a byte array into a string, if you don't fancy storing bytes you can use
string hashValue = Convert.ToBase64String(hashWithSaltBytes);
String comparisons are easier than byte comparisons where you have to iterate over each array, up to you. Just remember if you are using random salts you need to store them beside the password.
Take the user password "Secret!".
Generate a random salt of a few bytes "s4L75a1T".
Concat them to "s4L75a1TSecret!".
Calculate the hash "b9797a5b683804eb195b6ba2a5e368ae74394cd3" (this is SHA-1 in hex)
Store the hash and the salt (both as hex string, Base64 string, or whatever you like) in the database together with the user name and the other user information (in the example the salt is just a plain string and the hash is Base64 encoded).
FirstName LastName Salt Hash
-----------------------------------------------------------------
John Doe s4L75a1T uXl6W2g4BOsZW2uipeNornQ5TNM=
If you want to verify the user password, just take the user name, look up the salt, do the above again and see if the calculated hash and the one in the database match. There is no (known) way to (easily) recover the password.
Don't confuse encrypting passwords with hashing them; with a good cryptographic hash function, you should not be able to reverse the hashing process to retrieve the original string.
Chad's answer above is an excellent point-by-point explanation of the concepts involved.
This subject's been done to death all over the Internet; not just on Stack Overflow; seriously - a simple web search ought to find you a pretty comprehensive guide.
Thanks, Jeff, for spreading yet another bunch of misinformation. I suppose we'll see a whole slew of misguided questions about hashing, encryption, etc. over the next week, not to mention the crap that'll come up about floating-point arithmetic.
I believe the process is like this: Generate a random salt, so 'fishlegs' + 'r4nd0mS4lt' to get 'fishlegsr4nd0mS4lt'. Then you hash that, say with MD5 (though you might want to use SHA256 to be more secure) to get: 593d5518d759b4860188158ef4c71f28. Store that and the randomly generated salt. When the user logs in, append the random salt and then check if his entered password with the salt matches the hash in the database.
What you are essentially wanting to do is:
A) On account creation, you get a username and password from the user, you hash those together with your own salt and store the resultant string in your database like:
Dim sUserName = "barry"
Dim sPassWord = "fishlegs"
Dim mySalt = "A deliciously salty string! fillED WIth all KindS of Junkk(&^&*(£"
Dim d As New Encryption.Data(mySalt + sUserName + sPassWord)
Dim hash As New Encryption.Hash(Encryption.Hash.Provider.SHA256)
hash.Calculate(d)
Dim sTheSaltedHashedUnPassCombination = hash.Value.Hex;
SavenewPasswordHashToDatabase(sTheSaltedHashedUnPassCombination)
You never store sPassWord.
B) When the user logs in you perform exactly the same operation on the provided username and password then compare the resultant hash to the previously stored value in the database so you use:
Dim sThePreviouslyCreatedHashFromTheDatabase = GetHashForUserFromDatabase(usernameProvided)
Dim mySalt = "A deliciously salty string! fillED WIth all KindS of Junkk(&^&*(£"
Dim d As New Encryption.Data(mySalt + usernameProvided+ passwordProvided)
Dim hash As New Encryption.Hash(Encryption.Hash.Provider.SHA256)
hash.Calculate(d)
Dim sTheSaltedHashedUnPassCombination = hash.Value.Hex;
if (sThePreviouslyCreatedHashFromTheDatabase.Equals(sTheSaltedHashedUnPassCombination))
'UN & Password Valid!
else
'UN & PW Invalid!
end
(Pardon any errors, VB aint my language)
To answer your given questions:
1) See above. Never store the password directly, store the hashed value
2) use a char(X), the number of characters returned from the hash is constant so you can give it a known storage size.
3) What you are effectively storing is the password for the user salted with their username and also salted with the constant on your server. This will be a fixed length string and you cannot change this string back into the separate username and password.
The whole point of hashing is that you store the hashes rather than the plaintext passwords and they can't be recovered (at least only with great difficulty). Hash functions are designed to be one-way, so that retrieval is impossible - this is their very essence.
nchar[n] is probably the best option, since common hash algorithms produce results of constant length (SHA1 outputs a 160-bit hash). Common practice is to convert the hash to Base64 so that it can be stored as ASCII text.
A salt is just a method that adds random bits onto a hash in order to complicate the process of cracking. Each extra bit of salt means that a brute-force cracker must take twice as long (and use twice as much memory).
ASP.NET includes a SQL based membership provider that allows you to create, store and verify encryped or hashed passwords, without you having to do anything - as Eric Lippert says:
let me give you all my standard caution about rolling your own cryptographic algorithms and security systems: don't.
You Want Salt With That? Part One: Security vs Obscurity
You Want Salt With That? Part Two: Security vs Obscurity
You Want Salt With That? Part Three: We Need A Hash
You Want Salt With That? Part Four: Challenge-Response