Comparing hashed password with salt always fails - c#

I saw multiple questions about password hashing and salt but they all seem to fail for me. I use this function to hash/salt it and then put it to database:
public string HashPassword(string password)
{
byte[] salt;
new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]);
var pbkdf2 = new Rfc2898DeriveBytes(PasswordTextbox.Password, salt, 10000);
byte[] hash = pbkdf2.GetBytes(20);
byte[] hashBytes = new byte[36];
Array.Copy(salt, 0, hashBytes, 0, 16);
Array.Copy(hash, 0, hashBytes, 16, 20);
string savedPasswordHash = Convert.ToBase64String(hashBytes);
return savedPasswordHash;
}
and then I try to compare it with user input using this function:
public static void UnhashPassword(string hashedPassword, string hashedPasswordFromDatabase)
{
byte[] hashBytes = Convert.FromBase64String(hashedPasswordFromDatabase);
byte[] salt = new byte[16];
Array.Copy(hashBytes, 0, salt, 0, 16);
var pbkdf2 = new Rfc2898DeriveBytes(hashedPassword, salt, 10000);
byte[] hash = pbkdf2.GetBytes(20);
for (int i = 0; i < 20; i++)
if (hashBytes[i + 16] != hash[i])
throw new UnauthorizedAccessException();
}
the second function always throws exception. Not sure what is the reason since this answer seemed to work for everybody in other question.

Without a good Minimal, Complete, and Verifiable code example that reliably reproduces the problem, it's impossible to say for sure what is wrong. However, the following code example does work exactly as expected (i.e. the value of result is true after it's been initialized by the call to ValidatePassword():
static void Main(string[] args)
{
string password = "password";
string hashedPassword = HashPassword(password);
bool result = ValidatePassword(password, hashedPassword);
}
static string HashPassword(string password)
{
byte[] salt;
new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]);
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000);
byte[] hash = pbkdf2.GetBytes(20);
byte[] hashBytes = new byte[36];
Array.Copy(salt, 0, hashBytes, 0, 16);
Array.Copy(hash, 0, hashBytes, 16, 20);
string savedPasswordHash = Convert.ToBase64String(hashBytes);
return savedPasswordHash;
}
static bool ValidatePassword(string password, string hashedPasswordFromDatabase)
{
byte[] hashBytes = Convert.FromBase64String(hashedPasswordFromDatabase);
byte[] salt = new byte[16];
Array.Copy(hashBytes, 0, salt, 0, 16);
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000);
byte[] hash = pbkdf2.GetBytes(20);
for (int i = 0; i < 20; i++)
{
if (hashBytes[i + 16] != hash[i])
{
return false;
}
}
return true;
}
The only material change between the above and your original code is that the HashPassword() method uses the password value passed in, rather than PasswordTextbox.Password.
Based on that observation, I can only surmise that in your own scenario, you are not really hashing the same password as you are validating later. Whether this is because PasswordTextbox.Password never did have the right password, or you are passing a different password later, I can't say.
If the above code example does not adequately point you in the right direction so that you can get your code working, please improve your question so that it includes a good MCVE.

Related

Generating Hashed Passwords in C#

I don't know if this is the correct place to ask, but I am having an issue hashing passwords for MySql Backend. I am running mosquitto 1.4.3 broker and I have the mosquitto-auth-plugin working on the same server. But I want to move the auth-plugin to a new server. So I created a admin program in C# to add users and access controls however I cant seem to the get the correct hash code for the password.
Has anyone implemented this or is there some resoucres available to create the correct hash?
I have tried this Hash It Right
private const int SaltByteLength = 12;
private const int DerivedKeyLength = 24;
public string CreatePasswordHash(string password)
{
var salt = GenerateRandomSalt();
var iterationCount = GetIterationCount();
var hashValue = GenerateHashValue(password, salt, iterationCount);
var iterationCountBtyeArr = BitConverter.GetBytes(iterationCount);
var valueToSave = new byte[SaltByteLength + DerivedKeyLength + iterationCountBtyeArr.Length];
Buffer.BlockCopy(salt, 0, valueToSave, 0, SaltByteLength);
Buffer.BlockCopy(hashValue, 0, valueToSave, SaltByteLength, DerivedKeyLength);
Buffer.BlockCopy(iterationCountBtyeArr, 0, valueToSave, salt.Length + hashValue.Length, iterationCountBtyeArr.Length);
return Convert.ToBase64String(valueToSave);
}
private int GetIterationCount()
{
return 901;
}
private static byte[] GenerateRandomSalt()
{
var csprng = new RNGCryptoServiceProvider();
var salt = new byte[SaltByteLength];
csprng.GetBytes(salt);
return salt;
}
private static byte[] GenerateHashValue(string password, byte[] salt, int iterationCount)
{
byte[] hashValue;
var valueToHash = string.IsNullOrEmpty(password) ? string.Empty : password;
using (var pbkdf2 = new Rfc2898DeriveBytes(valueToHash, salt, iterationCount))
{
hashValue = pbkdf2.GetBytes(DerivedKeyLength);
}
return hashValue;
}
will this make it easier for you?
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
class Class1
{
static void Main(string[] args)
{
byte[] HashValue;
string MessageString = "This is the original message!";
//Create a new instance of the UnicodeEncoding class to
//convert the string into an array of Unicode bytes.
UnicodeEncoding UE = new UnicodeEncoding();
//Convert the string into an array of bytes.
byte[] MessageBytes = UE.GetBytes(MessageString);
//Create a new instance of the SHA1Managed class to create
//the hash value.
SHA1Managed SHhash = new SHA1Managed();
//Create the hash value from the array of bytes.
HashValue = SHhash.ComputeHash(MessageBytes);
//Display the hash value to the console.
foreach(byte b in HashValue)
{
Console.Write("{0} ", b);
}
}

Rfc2898DeriveBytes password matches with string and not with byte[]

I am trying to create an authentication server with login and password credentials and I used this tutorial that gave me the following PasswordHash class. This works great when I use the Verify method with a string but I do not want to send the password unencrypted through UDP for security reasons but when I test the hashes directly by feeding it the byte array it returns false (as shown below the class).
public sealed class PasswordHash
{
const int SaltSize = 16, HashSize = 20, HashIter = 10000;
readonly byte[] _salt, _hash;
private PasswordHash() { }
public PasswordHash(string password)
{
new RNGCryptoServiceProvider().GetBytes(_salt = new byte[SaltSize]);
_hash = new Rfc2898DeriveBytes(password, _salt, HashIter).GetBytes(HashSize);
}
public PasswordHash(byte[] hashBytes)
{
Array.Copy(hashBytes, 0, _salt = new byte[SaltSize], 0, SaltSize);
Array.Copy(hashBytes, SaltSize, _hash = new byte[HashSize], 0, HashSize);
}
public PasswordHash(byte[] salt, byte[] hash)
{
Array.Copy(salt, 0, _salt = new byte[SaltSize], 0, SaltSize);
Array.Copy(hash, 0, _hash = new byte[HashSize], 0, HashSize);
}
public byte[] ToArray()
{
byte[] hashBytes = new byte[SaltSize + HashSize];
Array.Copy(_salt, 0, hashBytes, 0, SaltSize);
Array.Copy(_hash, 0, hashBytes, SaltSize, HashSize);
return hashBytes;
}
public byte[] Salt { get { return (byte[])_salt.Clone(); } }
public byte[] Hash { get { return (byte[])_hash.Clone(); } }
public bool Verify(string password)
{
byte[] test = new Rfc2898DeriveBytes(password, _salt, HashIter).GetBytes(HashSize);
for (int i = 0; i < HashSize; i++)
if (test[i] != _hash[i])
return false;
return true;
}
public bool Verify(byte[] passwordHash)
{
for (int i = 0; i < HashSize; i++)
if (passwordHash[i] != _hash[i])
return false;
return true;
}
}
I tested it by using:
bool test = new PasswordHash("test123").Verify("test123"); //TRUE
bool test2 = new PasswordHash("test123").Verify(newPasswordHash("test123").ToArray()); //FALSE
So as Nissim pointed out, they don't match because the salt is randomly generated every time I call new PasswordHash(). Is there anyway to bypass the salt that's added to the byte[]?
Each time you call the PasswordHash's constructor it creates a new salt, hence in the following example:
byte[] pass1 = new HashPassword("abc").ToArray();
byte[] pass2 = new HashPassword("abc").ToArray();
pass1 is different than pass2

encryption result in java and .net are not same

I have a method in my .net project to encrypt a password
public string Encrypt(string plainText)
{
string PassPhrase = "#$^&*!#!$";
string SaltValue = "R#j#}{BAe";
int PasswordIterations = Convert.ToInt32(textBox5.Text); //amend to match java encryption iteration
string InitVector = "#1B2c3D4e5F6g7H8";
int KeySize = 256; //amend to match java encryption key size
byte[] initVectorBytes = Encoding.ASCII.GetBytes(InitVector);
byte[] saltValueBytes = Encoding.ASCII.GetBytes(SaltValue);
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
PasswordDeriveBytes password= new PasswordDeriveBytes(
PassPhrase,
saltValueBytes,
"MD5",
PasswordIterations);
byte[] keyBytes = password.GetBytes(KeySize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
keyBytes,
initVectorBytes);
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream,
encryptor,
CryptoStreamMode.Write);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
string cipherText = Convert.ToBase64String(cipherTextBytes);
return cipherText;
}
I have been tasked to convert this method to java but in java I don't get the same result as the .Net version
My java code is
package com.andc.billing.pdc.security;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.management.openmbean.InvalidKeyException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class PasswordCrypto {
private static final String password = "#$^&*!#!$";
private static String initializationVector = "#1B2c3D4e5F6g7H8";
private static String salt = "R#j#}{BAe";
private static int pswdIterations = 2;
private static int keySize = 128;
private static final Log log = LogFactory.getLog(PasswordCrypto.class);
public static String encrypt(String plainText) throws
NoSuchAlgorithmException,
InvalidKeySpecException,
NoSuchPaddingException,
InvalidParameterSpecException,
IllegalBlockSizeException,
BadPaddingException,
UnsupportedEncodingException,
InvalidKeyException,
InvalidAlgorithmParameterException, java.security.InvalidKeyException, NoSuchProviderException
{
byte[] saltBytes = salt.getBytes("ASCII");//"UTF-8");
byte[] ivBytes = initializationVector.getBytes("ASCII");//"UTF-8");
// Derive the key, given password and salt.
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");//PBEWithMD5AndDES");
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(),
saltBytes,
pswdIterations,
keySize
);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //Cipher.getInstance("AES/CBC/PKCSPadding"
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(ivBytes));
byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("ASCII"));//UTF-8"));
String str=new org.apache.commons.codec.binary.Base64().encodeAsString(encryptedTextBytes);
log.info(str);
return str;
}
}
.net result of encryption of "1" is :
7mPh3/E/olBGbFpoA18oqw==
while java is
7RPk77AIKAhOttNLW4e5yQ==
Would you please help me solve this problem ?
First thing i've noticed is that the algorithms you are using are different, in .Net it's an extension of PBKDF1 and in java it's PBKDF2, PBKDF2 replaced PBKDF1.
In .net you are using the PasswordDeriveBytes class which "derives a key from a password using an extension of the PBKDF1 algorithm."
I also notice that the password iterations is hard-coded to 2 in Java and comes from a text box in .Net... ensure they are the same.
Correct that and let us know the outcome.
Update: For PBKDF2 in .net use the Rfc2898DeriveBytes class.
For some very good relevant information have a read of this page
EDIT: This link should be helpful and if you can use the Chilkat library
It's a complicated difference between 1 and 2, 1 is only supposed to do upto 20 bytes, MS has built an extension which allows more than that and the following code should reporduce the .net output more accurately. Taken from here.
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Hex;
public class PKCS5Test
{
/**
* #param args
*/
public static void main(String[] args) throws Exception
{
byte[] password = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
byte[] salt = PKCS5S1ParametersGenerator.PKCS5PasswordToBytes("MyTesting".toCharArray());
PKCS5S1ParametersGenerator generator = new PasswordDeriveBytes(new SHA1Digest());
generator.init(password, salt, 100);
byte[] key = ((KeyParameter)generator.generateDerivedParameters(512)).getKey();
System.out.println( "64 " + new String(Hex.encode(key)).toUpperCase() );
}
static class PasswordDeriveBytes extends PKCS5S1ParametersGenerator
{
private final Digest d;
private byte[] output = null;
public PasswordDeriveBytes(Digest d)
{
super(d);
this.d = d;
}
public CipherParameters generateDerivedParameters(int keySize)
{
keySize = keySize / 8;
byte[] result = new byte[keySize];
int done = 0;
int count = 0;
byte[] b = null;
while (done < result.length)
{
if (b == null)
{
b = generateInitialKey();
}
else if (++count < 1000)
{
b = generateExtendedKey(++count);
}
else
{
throw new RuntimeException("Exceeded limit");
}
int use = Math.min(b.length, result.length - done);
System.arraycopy(b, 0, result, done, use);
done += use;
}
return new KeyParameter(result);
}
private byte[] generateOutput()
{
byte[] digestBytes = new byte[d.getDigestSize()];
d.update(password, 0, password.length);
d.update(salt, 0, salt.length);
d.doFinal(digestBytes, 0);
for (int i = 1; i < (iterationCount - 1); i++)
{
d.update(digestBytes, 0, digestBytes.length);
d.doFinal(digestBytes, 0);
}
return digestBytes;
}
private byte[] generateInitialKey()
{
output = generateOutput();
d.update(output, 0, output.length);
byte[] digestBytes = new byte[d.getDigestSize()];
d.doFinal(digestBytes, 0);
return digestBytes;
}
private byte[] generateExtendedKey(int count)
{
byte[] prefix = Integer.toString(count).getBytes();
d.update(prefix, 0, prefix.length);
d.update(output, 0, output.length);
byte[] digestBytes = new byte[d.getDigestSize()];
d.doFinal(digestBytes, 0);
//System.err.println( "X: " + new String(Hex.encode(digestBytes)).toUpperCase() );
return digestBytes;
}
}
}
Thank you very much for the provided solution - it works very well but with a small correction (according to initial post mentioned below):
Please use:
b = generateExtendedKey(count);
instead of:
b = generateExtendedKey(++count);
It'll work even for 256 key size:
Here is a small code which decrypts C# Rijndael encoded data using 256 bits keys:
public static String decrypt(final String cipherText, final String passPhrase, final String saltValue, final int passwordIterations, final String initVector, final int keySize)
throws Exception {
final byte[] initVectorBytes = initVector.getBytes("ASCII");
final byte[] saltValueBytes = saltValue.getBytes("ASCII");
final byte[] cipherTextBytes = Base64.decode(cipherText);
final PKCS5S1ParametersGenerator generator = new PasswordDeriveBytes(new SHA1Digest());
generator.init(passPhrase.getBytes("ASCII"), saltValueBytes, passwordIterations);
final byte[] key = ((KeyParameter) generator.generateDerivedParameters(keySize)).getKey();
final SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);
final Cipher cipher = Cipher.getInstance(TRANSFORMATION);
final IvParameterSpec iv = new IvParameterSpec(initVectorBytes);
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
final byte[] decryptedVal = cipher.doFinal(cipherTextBytes);
return new String(decryptedVal);
}
Addon:
In case you care about key size limitation, you may use this solution which works just fine (tested under Ubuntu 12, Java 1.7 64 bits (java version "1.7.0_25"
Java(TM) SE Runtime Environment (build 1.7.0_25-b15)
Java HotSpot(TM) 64-Bit Server VM (build 23.25-b01, mixed mode))

Is it safe to reuse an AES object when encrypting strings?

I'm having some performance problems in an app that encrypts lots of strings. Most of the CPU use happens when I call the private method getAes() from a public method called Encrypt():
public static class CryptKeeper
{
const int HASH_SIZE = 32; //SHA256
/// <summary>
/// Encrypts a string message. Includes integrity checking.
/// </summary>
public static string Encrypt(string messageToEncrypt, string sharedSecret, string salt)
{
// Prepare message with hash
var messageBytes = Encoding.UTF8.GetBytes(messageToEncrypt);
var hashedMessageBytes = new byte[HASH_SIZE + messageBytes.Length];
var hash = Utilities.GenerateSha256Hash(messageBytes, 0, messageBytes.Length);
Buffer.BlockCopy(hash, 0, hashedMessageBytes, 0, HASH_SIZE);
Buffer.BlockCopy(messageBytes, 0, hashedMessageBytes, HASH_SIZE, messageBytes.Length);
// Encrypt message
using (var aes = getAes(sharedSecret, Encoding.UTF8.GetBytes(salt)))
{
aes.GenerateIV();
using (var encryptor = aes.CreateEncryptor())
{
var encryptedBytes = encryptor.TransformFinalBlock(hashedMessageBytes, 0, hashedMessageBytes.Length);
// Add the initialization vector
var result = new byte[aes.IV.Length + encryptedBytes.Length];
Buffer.BlockCopy(aes.IV, 0, result, 0, aes.IV.Length);
Buffer.BlockCopy(encryptedBytes, 0, result, aes.IV.Length, encryptedBytes.Length);
return Convert.ToBase64String(result);
}
}
}
public static string Decrypt(string encryptedMessage, string sharedSecret, string salt)
{
if (encryptedMessage == null) return null;
using (var aes = getAes(sharedSecret, Encoding.UTF8.GetBytes(salt)))
{
var iv = new byte[aes.IV.Length];
Buffer.BlockCopy(Convert.FromBase64String(encryptedMessage), 0, iv, 0, iv.Length);
aes.IV = iv;
using (var decryptor = aes.CreateDecryptor())
{
var decryptedBytes = decryptor.TransformFinalBlock(Convert.FromBase64String(encryptedMessage), iv.Length, Convert.FromBase64String(encryptedMessage).Length - iv.Length);
// Check hash
var hash = Utilities.GenerateSha256Hash(decryptedBytes, HASH_SIZE, decryptedBytes.Length - HASH_SIZE);
var existingHash = new byte[HASH_SIZE];
Buffer.BlockCopy(decryptedBytes, 0, existingHash, 0, HASH_SIZE);
if (!existingHash.compareBytesTo(hash))
{
throw new CryptographicException("Message hash invalid.");
}
// Hash is valid, we're done
var res = new byte[decryptedBytes.Length - HASH_SIZE];
Buffer.BlockCopy(decryptedBytes, HASH_SIZE, res, 0, res.Length);
return Encoding.UTF8.GetString(res);
}
}
}
private static Aes getAes(string sharedSecret, byte[] salt)
{
var aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.Key = new Rfc2898DeriveBytes(sharedSecret, salt, 129).GetBytes(aes.KeySize / 8);
return aes;
}
}
I tried to improve performance by caching the AES object, but I am getting into unfamiliar territory:
public static class CryptKeeper
{
const int HASH_SIZE = 32; //SHA256
private static Aes aes;
/// <summary>
/// Encrypts a string message. Includes integrity checking.
/// </summary>
public static string Encrypt(string messageToEncrypt, string sharedSecret, string salt)
{
// unchanged
}
public static string Decrypt(string encryptedMessage, string sharedSecret, string salt)
{
// unchanged
}
private static Aes getAes(string sharedSecret, byte[] salt)
{
if (aes != null) return aes;
var aesNew = Aes.Create();
aesNew.Mode = CipherMode.CBC;
aesNew.Key = new Rfc2898DeriveBytes(sharedSecret, salt, 129).GetBytes(aesNew.KeySize / 8);
return aes = aesNew;
}
}
I get this error:
Safe handle has been closed
at System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean& success) at
System.StubHelpers.StubHelpers.SafeHandleAddRef(SafeHandle pHandle, Boolean& success) at
System.Security.Cryptography.CapiNative.UnsafeNativeMethods.CryptGenRandom(SafeCspHandle hProv, Int32 dwLen, Byte[] pbBuffer) at
System.Security.Cryptography.AesCryptoServiceProvider.GenerateIV() at
Obr.Lib.CryptKeeper.Encrypt(String messageToEncrypt, String sharedSecret, String salt) in ...CryptKeeper.cs:line 28 at
Obr.Lib.HtmlRenderer.renderLawCitation(RenderContext renderContext, XElement xElement) in ...HtmlRenderer.cs:line 1472
I understand that the using() statement in Encrypt() is going to dispose of AES and that is causing it to break. I don't want to troubleshoot further unless I know it's safe to reuse. If it is safe to reuse, what is the best way to do this?
Update: I have solved the performance problem by keeping the AES object around longer. I've removed the static keywords, and made the class disposable. Here's how it looks now:
public class CryptKeeper : IDisposable
{
const int HASH_SIZE = 32; //SHA256
private readonly Aes aes;
public CryptKeeper(string sharedSecret, string salt)
{
aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.Key = new Rfc2898DeriveBytes(sharedSecret, Encoding.UTF8.GetBytes(salt), 129).GetBytes(aes.KeySize / 8);
}
/// <summary>
/// Encrypts a string message. Includes integrity checking.
/// </summary>
public string Encrypt(string messageToEncrypt)
{
// Prepare message with hash
var messageBytes = Encoding.UTF8.GetBytes(messageToEncrypt);
var hashedMessageBytes = new byte[HASH_SIZE + messageBytes.Length];
var hash = Utilities.GenerateSha256Hash(messageBytes, 0, messageBytes.Length);
Buffer.BlockCopy(hash, 0, hashedMessageBytes, 0, HASH_SIZE);
Buffer.BlockCopy(messageBytes, 0, hashedMessageBytes, HASH_SIZE, messageBytes.Length);
// Encrypt message
aes.GenerateIV();
using (var encryptor = aes.CreateEncryptor())
{
var encryptedBytes = encryptor.TransformFinalBlock(hashedMessageBytes, 0, hashedMessageBytes.Length);
// Add the initialization vector
var result = new byte[aes.IV.Length + encryptedBytes.Length];
Buffer.BlockCopy(aes.IV, 0, result, 0, aes.IV.Length);
Buffer.BlockCopy(encryptedBytes, 0, result, aes.IV.Length, encryptedBytes.Length);
return Convert.ToBase64String(result);
}
}
public string Decrypt(string encryptedMessage)
{
if (encryptedMessage == null) return null;
var iv = new byte[aes.IV.Length];
Buffer.BlockCopy(Convert.FromBase64String(encryptedMessage), 0, iv, 0, iv.Length);
aes.IV = iv;
using (var decryptor = aes.CreateDecryptor())
{
var decryptedBytes = decryptor.TransformFinalBlock(Convert.FromBase64String(encryptedMessage), iv.Length, Convert.FromBase64String(encryptedMessage).Length - iv.Length);
// Check hash
var hash = Utilities.GenerateSha256Hash(decryptedBytes, HASH_SIZE, decryptedBytes.Length - HASH_SIZE);
var existingHash = new byte[HASH_SIZE];
Buffer.BlockCopy(decryptedBytes, 0, existingHash, 0, HASH_SIZE);
if (!existingHash.compareBytesTo(hash))
{
throw new CryptographicException("Message hash invalid.");
}
// Hash is valid, we're done
var res = new byte[decryptedBytes.Length - HASH_SIZE];
Buffer.BlockCopy(decryptedBytes, HASH_SIZE, res, 0, res.Length);
return Encoding.UTF8.GetString(res);
}
}
bool disposed;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
aes.Dispose();
}
}
disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
I invoke it like this:
using (cryptKeeper = new CryptKeeper(Repository.AppSettings["SharedSecret"], Repository.AppSettings["Salt"]))
{
renderingReport.Rendering = renderSegmentNav(currentUser.UserOwnsProduct(productId), book, renderingReport, currentSegment);
}
This has improved performance tremendously. A previous call to an MVC controller that needed to build many encrypted links in its result took 2.7 seconds total. With the new code where AES is reused, it takes 0.3 seconds total.
I can confirm that the code works and is much, much faster. I just want to confirm that reuse of AES in this manner is not a BAD IDEA for security reasons. According to a little google searching, the fact that I am calling GenerateIV() each time is good, and I can't find anything saying I should not re-use the AES for as long as I like.
In general you can reuse objects that implement cryptographic algorithms in both Java and C#. You should however be sure that you always leave the encryptors and decryptors in the correct state. You should not use these classes for multi-threading purposes unless specifically specified.
Note that the reason that you are experiencing a slowdown is because of the PBKDF2 function within Rfc2898DeriveBytes. This method is deliberately slow. You may reuse the key that you get from Rfc2898DeriveBytes but you should be sure that you do not reuse any IV, the IV should be random. It does of course not make sense to call Rfc2898DeriveBytes derive bytes multiple times.
Finally, it could be somewhat beneficial to cache objects holding an AES key locally. First of all, you don't want any additional key objects around if you don't have to, and second, AES first calculates the subkeys from the given key which takes a small amount of time (although nowhere near the time it takes to execute Rfc2898DeriveBytes).
Then again, don't do this if it unnecessarily complicates your design. The advantages are not big enough for that.

Manually Converting Password and Salt to Compare with ASPNETDB Password

I'm trying to manually compare the aspnetdb password field with a manually-hashed password on my end to check for validity (I can't use the default Membership implementation as I'm using a custom one). Anyway, I'm taking the Base64 password salt from the record, and using the following algorithm to get the salted hash:
static byte[] GenerateSaltedHash(byte[] plainText, byte[] salt)
{
HashAlgorithm algorithm = new SHA256Managed();
byte[] plainTextWithSaltBytes =
new byte[plainText.Length + salt.Length];
for (int i = 0; i < plainText.Length; i++)
{
plainTextWithSaltBytes[i] = plainText[i];
}
for (int i = 0; i < salt.Length; i++)
{
plainTextWithSaltBytes[plainText.Length + i] = salt[i];
}
byte[] hash = algorithm.ComputeHash(plainTextWithSaltBytes);
return hash;
}
I then take those bytes and compare with a Convert.GetBase64Bytes(MembershipUser.Password). There's a disconnect somewhere though as the bytes I get from the Convert method and my computed hash are never the same even when I know the passwords are the same.
Any insight on where I'm going wrong?
In looking at the source for SqlMembershipProvider, it appears that they copy the salt before the password:
static byte[] GenerateSaltedHash(byte[] plainText, byte[] salt)
{
HashAlgorithm algorithm = new SHA256Managed();
byte[] plainTextWithSaltBytes = new byte[plainText.Length + salt.Length];
salt.CopyTo(plainTextWithSaltBytes, 0);
plainText.CopyTo(plainTextWithSaltBytes, salt.Length);
byte[] hash = algorithm.ComputeHash(plainTextWithSaltBytes);
return hash;
}

Categories