The following is the correct way to do encryption. I thought it useful to post for various reasons, most particular being the necessity for our companies to have good security controls. This is very important. If anybody has any other good snippets feel free to post. The user class is a little verbose, but you should be able to clearly see the hash algorithim which is essentially non reversable by anything other than a password crack. That is a HashAlgorithim. In order to create things you can decode you should use a SymmetricAlgorithim. Passwords should always be HashAlgorithims due to the fact that the company doesn't need to have large word lists.
/*Author: Cameron Block*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Configuration;
namespace NHibernateIdentity.Models {
public class User {
/// <summary>
/// Unique identifier for group used by program and database layer.
/// </summary>
public virtual int UserId {
get; set;
}
/// <summary>
/// User's login identity.
/// </summary>
public virtual String UserName {
get; set;
}
/// <summary>
/// The user's email address.
/// </summary>
public virtual String EmailAddress {
get; set;
}
/// <summary>
/// Used to turn off user accessibility for instance when there are forensic investigations going on.
/// </summary>
public virtual bool IsActive {
get; set;
}
/// <summary>
/// A hash of the users password is stored in the database and used for logins.
/// Storing a hash is more secure than storing plaintext.
/// No Company should have a comprehensive plaintext wordlist of it's users.
/// </summary>
public virtual byte[] PasswordHash {
get; set;
}
/// <summary>
/// The groups a user is associated with.
/// </summary>
public virtual IList<Group> Groups {
get; set;
}
/// <summary>
/// The roles associated with this user.
/// </summary>
public virtual IList<Role> Roles {
get; set;
}
/// <summary>
/// A list of invalid login attempts.
/// </summary>
public virtual IList<InvalidLoginDomainEvent> InvalidLoginEvents {
get; set;
}
/// <summary>
/// A list of locked events associated with this user.
/// </summary>
public virtual IList<LockedDomainEvent> LockedEvents {
get; set;
}
/// <summary>
/// A list of banned events associated with a user.
/// </summary>
public virtual IList<BannedDomainEvent> BannedEvents {
get; set;
}
/// <summary>
/// The events that reflect the number of times a user has been unbanned.
/// </summary>
public virtual IList<UnBannedDomainEvent> UnBans {
get; set;
}
/// <summary>
/// A request to unban this user which may or may not actually be fulfilled.
/// </summary>
public virtual IList<UnBanRequest> UnBanRequests{
get; set;
}
/// <summary>
/// Unban denial attempts.
/// </summary>
public virtual IList<UnBanDeniedDomainEvent> UnBanDenials {
get; set;
}
public User() {
IsActive = true;
}
/// <summary>
/// Sets the password for the given individual.
/// </summary>
/// <param name="password"></param>
public virtual void SetPassword(String password) {
//get salt from web config
byte[] salt = Encoding.UTF8.GetBytes(System.Configuration.ConfigurationManager.AppSettings["salt"].ToString());
byte[] passBytes = Encoding.UTF8.GetBytes(password);
//perpend salt to password
byte[] catPass = salt.Concat(passBytes).ToArray();
//call all the hash algorithims here
HashAlgorithm hashAlg = GetHashAlgorithim();
this.PasswordHash = hashAlg.ComputeHash(catPass);
}//end method
/// <summary>
/// Determines whether two passwords are equal.
/// </summary>
/// <param name="password"></param>
/// <returns></returns>
public virtual bool ComparePassword(String password) {
//get salt from web config
byte[] salt = Encoding.UTF8.GetBytes(System.Configuration.ConfigurationManager.AppSettings["salt"].ToString());
byte[] passBytes = Encoding.UTF8.GetBytes(password);
//perpend salt to password
byte[] catPass = salt.Concat(passBytes).ToArray();
//call all the hash algorithims here
HashAlgorithm hashAlg = GetHashAlgorithim();
byte[] incomingHash = hashAlg.ComputeHash(catPass);
if (incomingHash.SequenceEqual(this.PasswordHash))
return true;
return false;
}//end method
/// <summary>
/// Gets the underlying hash algorithim for the object's password capability.
/// </summary>
/// <returns></returns>
public virtual HashAlgorithm GetHashAlgorithim() {
//Detect the hash algorithim that the user wants employed.
String hashMethod = System.Configuration.ConfigurationManager.AppSettings["HashMethod"];
Dictionary<String, HashAlgorithm> hashAlgorithims = new Dictionary<String, HashAlgorithm>();
hashAlgorithims.Add("MD5", MD5.Create());
hashAlgorithims.Add("RIPEMD160", RIPEMD160.Create());
hashAlgorithims.Add("SHA1", SHA1.Create());
hashAlgorithims.Add("SHA256", SHA256.Create());
hashAlgorithims.Add("SHA384", SHA256.Create());
hashAlgorithims.Add("SHA512", SHA512.Create());
HashAlgorithm currentAlg = null;
if (hashAlgorithims.ContainsKey(hashMethod))
currentAlg = hashAlgorithims[hashMethod];
else
throw new NotImplementedException("This algorithim has not been implemented. ");
return currentAlg;
}//end method
/// <summary>
/// Determine whether user account is locked from too many invalid logins.
/// </summary>
/// <returns></returns>
public virtual bool IsLocked(TimeSpan lockDuration, TimeSpan lockDecay, int lockThreshold) {
var invalidLogins = this.InvalidLoginEvents
.Where(evt => evt.LoginTime > DateTime.Now - lockDecay)
.Where(evt => evt.LoginTime + lockDuration > DateTime.Now);
if(UnBans.Count() > 0)
invalidLogins = invalidLogins.Where(evt => evt.LoginTime >
UnBans.ToList().OrderByDescending(unBan => unBan.DateUnlocked).First().DateUnlocked);
return invalidLogins.Count() > lockThreshold;
}//end method
/// <summary>
/// Determine whether a lock is already effective.
/// </summary>
/// <param name="lockDuration"></param>
/// <param name="lockDecay"></param>
/// <param name="lockThreshold"></param>
/// <returns></returns>
public virtual bool LockIsEffective(TimeSpan lockDuration, TimeSpan lockDecay, int lockThreshold) {
return this.LockedEvents
.Where(evt => evt.LockTime > DateTime.Now - lockDecay)
.Where(evt => evt.LockTime + lockDuration > DateTime.Now)
.Count() > 0;
}//end method
/// <summary>
/// Determine whether the user account is banned.
/// </summary>
/// <returns></returns>
public virtual bool IsBanned() {
var query = BannedEvents.ToList();
if (UnBans.Count() > 0) {
UnBannedDomainEvent evt = UnBans.ToList().OrderByDescending(unban => unban.DateUnlocked).First();
query = query.Where(ban => ban.BanDate > evt.DateUnlocked).ToList();
}
return query.Count() > 0;
}//end method
/// <summary>
/// Determine whether a user is in a certain role.
/// </summary>
/// <param name="role"></param>
/// <returns></returns>
public virtual bool IsInRole(String roleName) {
return Roles.Where(role => role.Authority == roleName).Count() > 0;
}
/// <summary>
/// Determine whether a user is in a certain group.
/// </summary>
/// <param name="grpName"></param>
/// <returns></returns>
public virtual bool IsInGroup(String grpName) {
return Groups.Where(grp => grp.GroupName == grpName).Count() > 0;
}
}//end class
}//end namespace
The following is an example of a symmetric algorithim.
/*Author: Cameron Block*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.IO;
using System.Net;
using System.Diagnostics;
using System.Configuration;
namespace GadsdenReporting.Models {
/// <summary>
/// FtpCredential stores passwords in the database, and uploads files to remote FTP sites.
/// </summary>
public class FtpCredential {
private const int CHARS_AVAILABLE = 32;
public FtpCredential() {
}
/// <summary>
/// The Ftp Site name.
/// </summary>
public String FtpSite {
get; set;
}
/// <summary>
/// The user name to use when logging onto ftp site.
/// </summary>
public String UserName {
get; set;
}
/// <summary>
/// The encrypted password of the ftp site.
/// </summary>
public byte[] PasswordEncrypted {
get; set;
}
/// <summary>
/// Gets the underlying symmetric algorithim for the object's password capability.
/// </summary>
/// <returns></returns>
public SymmetricAlgorithm GetSymmetricAlgorithim() {
return Rijndael.Create();
}//end method
/// <summary>
/// Gets the key from the web config, pads it with null bytes so that it is valid.
/// </summary>
/// <param name="crypt"></param>
/// <returns></returns>
public byte[] GetKey(SymmetricAlgorithm crypt) {
int keySize = crypt.LegalKeySizes[0].MinSize / 8;
return Encoding.ASCII.GetBytes(System.Configuration.ConfigurationManager.AppSettings["EncryptionKey"].PadRight(keySize, '\0').ToCharArray(), 0, keySize);
}//end method
/// <summary>
/// Gets the initialization vector from the web config, pads it with null bytes so that it is valid.
/// </summary>
/// <param name="crypt"></param>
/// <returns></returns>
public byte[] GetIV(SymmetricAlgorithm crypt) {
int ivSize = crypt.BlockSize / 8;
return Encoding.ASCII.GetBytes(System.Configuration.ConfigurationManager.AppSettings["EncryptionIV"].PadRight(ivSize, '\0').ToCharArray(), 0, ivSize);
}//end method
/// <summary>
/// Gets the decrypted password.
/// </summary>
/// <returns></returns>
public String GetPassword() {
MemoryStream ms = new MemoryStream();
SymmetricAlgorithm crypt = GetSymmetricAlgorithim();
crypt.Padding = PaddingMode.PKCS7;
crypt.Key = GetKey(crypt);
crypt.IV = GetIV(crypt);
CryptoStream cs = new CryptoStream(ms,
crypt.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(PasswordEncrypted, 0, PasswordEncrypted.Length);
cs.Close();
byte[] decryptedData = ms.ToArray();
return Encoding.ASCII.GetString(decryptedData);
}//end method
/// <summary>
/// Sets the byte array containing the encrypted password.
/// </summary>
public void SetPassword(String password) {
//we only have a specific number of bytes in the database.
if (password.Length > CHARS_AVAILABLE)
throw new ArgumentException("Password text is too big. ");
byte[] passBytes = Encoding.ASCII.GetBytes(password);
MemoryStream ms = new MemoryStream();
SymmetricAlgorithm crypt = GetSymmetricAlgorithim();
crypt.Padding = PaddingMode.PKCS7;
crypt.Key = GetKey(crypt);
crypt.IV = GetIV(crypt);
CryptoStream cs = new CryptoStream(ms,
crypt.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(passBytes, 0, passBytes.Length);
cs.Close();
byte[] encryptedData = ms.ToArray();
PasswordEncrypted = encryptedData;
}//end method
/// <summary>
/// Gets the number of bytes for the encoding used by the credential object.
/// </summary>
/// <returns></returns>
public int GetEncodingNumBytes() {
return Encoding.ASCII.GetByteCount("1");
}//end method
/// <summary>
/// Upload file to ftp site.
/// </summary>
/// <param name="stream"></param>
public void FtpUpload(Stream stream) {
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(FtpSite);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential(UserName, GetPassword());
//copy the contents of the file to the request stream.
byte[] fileContents = new byte[stream.Length];
stream.Read(fileContents, 0, (int)stream.Length);
request.ContentLength = fileContents.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(fileContents, 0, fileContents.Length);
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
response.Close();
}//end method
}//end class
}//end namespace
Your password is your password, and your initialization vector is password number two. I typically pad this with null bytes based on what is stored in my web config. Object relational mappers seriously create good code. For C# the main two are NHibernate, and Entity Framework. If you ever find yourself converting binary crypto to text, then use a base64 layer in your crypto stream. Base 64 converts bytes to numbers which can be stored in a text file.