Restricting rijndaelmanaged algorithm key size? - c#

I am using rijndaelmanaged algoritham for password encryption
Is there a way to restrict the size of the encrypted text key?
eg:1, ABC - Encrypted key size 10
2, ABCDHGF - Encrypted key size 10
Means Fixed size !!

If you do not need to have password back from encrypted data, you can use hash algorithms. First compute the hash value for the password and then encrypt this hash value. Since hash values have fixed length your encrypted data will have a fixed length. When you need to check a password, decrypt the encrypted value and recalculate hash value from entered password then check if they match.
For example on a sign up page
var encryptedPwd = Encrypt(ComputeHash(txtPassword.Text));
Save(txtUsername.Text, encryptedPwd);
And on a login page
var encryptedPwd = SelectPwd(txtUsername.Text);
var pwdHash1 = Decrypt(encryptedPwd);
var pwdHash2 = ComputeHash(txtPassword.Text);
if (AreEqual(pwdHash1, pwdHash2))
// Login OK!
else
// Login fail
Another option could be making a custom padding. Say your passwords will have max length of 16 characters. Then you can pad every password to 16 chars with some fixed char. Then encrypt this padded password. It would be easier for validation but using hash is a bit more secure.
Sign up
var encryptedPwd = Encrypt(txtPassword.Text.PadRight(16, 'X'));
Save(txtUsername.Text, encryptedPwd);
Login
var encryptedPwd = SelectPwd(txtUsername.Text);
var pwd1 = Decrypt(encryptedPwd);
var pwd2 = txtPassword.Text.PadRight(16, 'X');
if (AreEqual(pwd1, pwd2))
// Login OK!
else
// Login fail

Instead of using a simple hash, it's suggested to use a password-strengthening algorithm, like the one specified in Rfc2898
string password = "P#$$w0rd";
byte[] salt = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }; // this is fixed... It would be better you used something different for each user
// You can raise 1000 to greater numbers... more cycles = more security. Try
// balancing speed with security.
Rfc2898DeriveBytes pwdGen = new Rfc2898DeriveBytes(password, salt, 1000);
// generate key and iv
byte[] key = pwdGen.GetBytes(16);
byte[] iv = pwdGen.GetBytes(16);
byte[] encrypted;
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
rijndaelCipher.Key = key;
rijndaelCipher.IV = iv;
// Or your data
byte[] data = System.Text.Encoding.UTF8.GetBytes("hello world");
var encryptor = rijndaelCipher.CreateEncryptor();
encrypted = encryptor.TransformFinalBlock(data, 0, data.Length);
}
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
rijndaelCipher.Key = key;
rijndaelCipher.IV = iv;
var decryptor = rijndaelCipher.CreateDecryptor();
byte[] decrypted = decryptor.TransformFinalBlock(encrypted, 0, encrypted.Length);
// this if you are encrypting text, otherwise decrypted is already your data
string text = System.Text.Encoding.UTF8.GetString(decrypted);
}

Related

Blazor.SubtleCrypto Decrypting on .net 6

As per https://www.nuget.org/packages/Blazor.SubtleCrypto
The package uses SubtleCrypto encrypt/decrypt methods and AES-GCM algorithm and returned in ciphertext.
I am under the impression that using a shared key used during encryption, I should be able to decrypt the ciphertext in the backend using System.Security.Cryptography.
But I encounter an exception:
System.Security.Cryptography.CryptographicException : The computed authentication tag did not match the input authentication tag.
private static string Decrypt(string cypherText)
{
// convert from base64 to raw bytes spans
var encryptedData = Convert.FromBase64String(cypherText).AsSpan();
var key = Convert.FromBase64String("V4iOXGCoPpX3UncQ5W9bsfUCqIlWvewbMsBNT4IHZRQ=").AsSpan();
// 128 bit encryption / 8 bit = 16 bytes
var tagSizeBytes = 16;
var ivSizeBytes = 12; // 12 bytes iv
// cipher text size is whole data - iv - tag
var cipherSize = encryptedData.Length - tagSizeBytes - ivSizeBytes;
// extract iv (nonce) 12 bytes prefix
var iv = encryptedData.Slice(0, ivSizeBytes);
// followed by the real cipher text
var cipherBytes = encryptedData.Slice(ivSizeBytes, cipherSize);
// followed by the tag (trailer)
var tagStart = ivSizeBytes + cipherSize;
var tag = encryptedData.Slice(tagStart);
// now that we have all the parts, the decryption
Span<byte> plainBytes = cipherSize < 1024
? stackalloc byte[cipherSize]
: new byte[cipherSize];
using var aes = new AesGcm(key);
aes.Decrypt(iv, cipherBytes, tag, plainBytes);
return Encoding.UTF8.GetString(plainBytes);
}

Need help creating IsValidPassword for PBKDF2 in C#

Having the hardest time trying to create a PBKDF2 valid password checker. The PBKDF2 code comes from a SharpHash project; https://github.com/ron4fun/SharpHash. The Class is: SharpHash/SharpHash.Tests/KDF/PBKDF2_HMACTests.cs
The example shows how to implement it but does not have any examples on how to verify the hash afterwards. I managed to tried several different "IsValidPassword" is one of the methods, but none of them seem to work. Each and every one of them the result is false no matter what values I add to the PBKDF2 or IsValidPassword methods. I also tried changing to a hex and also base64 but got the same results; it failed.
I even replaced Rfc2898DeriveBytes.
Does anyone have any experience with PBKDF2 password verification. This would be application based, not website based. IDE environment Visual Studios 2019 - C#.
Thank you.
public void TestOne()
{
IPBKDF2_HMAC PBKDF2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(hash, Password, Salt, 100000);
byte[] Key = PBKDF2.GetBytes(64);
PBKDF2.Clear();
string ActualString = Converters.ConvertBytesToHexString(Key, false);
Assert.AreEqual(ExpectedString, ActualString);
}
public bool IsValidPassword(string password, string hashPass)
{
bool result = false;
// Extract the bytes
byte[] hashBytes = Encoding.ASCII.GetBytes(hashPass);
// Get the salt
byte[] salt = new byte[20]; // Doesn't matter what values and here; same issue… False
Array.Copy(hashBytes, 0, salt, 0, 20);// Doesn't matter what values and here; same issue… False
// Compute the hash on the password the user entered
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100000);
byte[] hash = pbkdf2.GetBytes(64);
// compare the results
for (int i = 0; i < 20; i++) // If I go to 64 I get an error
{
if (hashBytes[i + 20] != hash[i])
{
return false;
}
}
return true;
}
// Replaced Rfc2898DeriveBytes
public bool IsValidPassword(string password, string hashPass)
{
bool result = false;
IHash hash1 = HashFactory.Crypto.CreateSHA1();
// Extract the bytes
byte[] hashBytes = Encoding.ASCII.GetBytes(hashPass);
byte[] Password = Encoding.ASCII.GetBytes(password);
// Get the salt
byte[] salt = new byte[20]; // Doesn't matter what values and here; same issue… False
Array.Copy(hashBytes, 0, salt, 0, 20); // Doesn't matter what values and here; same issue… False
// Compute the hash on the password the user entered
var pbkdf2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(hash1, Password, salt, 100000); // Replaced Rfc2898DeriveBytes
byte[] Key = pbkdf2.GetBytes(64);
pbkdf2.Clear();
string test = Converters.ConvertBytesToHexString(Key, false); // Taking a peek
string test2 = Encoding.ASCII.GetString(hashBytes); // Taking a peek
// compare the results
for (int i = 0; i < 20; i++)
{
if (hashBytes[i + 20] != Key[i])
{
return false;
}
}
return true;
}
taking a quick look at this, the issue you are encountering is simply a misrepresentation of the data contents involved.
password is a base256 based data while hashPass is a base16 based data.
You have to use the appropriate conversion routine when converting hashPass to byte[].
To do that, simply use this method found in the Converters class too.
byte[] hashBytes = Converters.ConvertHexStringToBytes(hashPass);
Do note that I assumed that your password is in base256 (since you didn't specify this in the question) so you can leave it as it is.
The only change you need to make is the one I described above.
Well, I suppose this will help someone else down the road apiece. I had to do some serious searching and banging my head up against the wall because this was my first attempt at PBKDF2 to come up with a functional verification method that isn't included with the SharpHash project. Just so that anyone reading this would understand what my problem was. The code generated the password with no issues whatsoever. However, there was no function in the code project that actually verified the password using the salt, generated password and iteration.
The code provided is the simple version because I have also added overloads to the methods that I don't need to post. This method has default settings whereas one of the overloads allows for complete customization of the hash algorithm, salt and iteration. Each of these I had tested and they worked as expected.
Hope this helps someone. :-)
private static Int32 Salt256bit { get; } = 256 / 8; // 256 bits = 32 Bytes
public static string GetHashPBKDF2Password(string password)
{
// Notes:
// Create a 32-byte secure salt and the same size of the key. This 32-byte produces 256 bits key output.
// Add the same 32-byte size to the pbkdf2.GetBytes.
// KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC hashes these values using the crypto SHA presented.
// Double the hashBytes bytes then add the salt and pbkdf2.GetBytes value.
// Copy each of the 32-bytes to the hashBytes bytes from the hashed salt and hashed value.
//
byte[] Password = Encoding.Unicode.GetBytes(password);
string hashPass = string.Empty;
// Create the salt value with a cryptographic PRNG
byte[] salt;
new RNGCryptoServiceProvider().GetBytes(salt = new byte[ByteSize]); // 32 Bytes = 256 bits.
// Create the KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC and get the hash value using the SHA presented.
// I know SHA1 is not that secured at all anymore. Just using it to test with. :-)
IHash sha = HashFactory.Crypto.CreateSHA1();
IPBKDF2_HMAC pbkdf2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(sha, Password, salt, Pbkdf2Iterations);
byte[] hash = pbkdf2.GetBytes(ByteSize); // 32 Bytes = 256 bits.
// Double the size of the byte array to include the "pbkdf2.GetBytes" and salt.
Int32 g = hash.Length + salt.Length;
byte[] hashBytes = new byte[g];
// Combine the salt and password bytes.
Array.Copy(salt, 0, hashBytes, 0, ByteSize);
Array.Copy(hash, 0, hashBytes, ByteSize, ByteSize);
// Turn the combined salt+hash into a string for storage
hashPass = Convert.ToBase64String(hashBytes);
return hashPass;
}
public static bool ValidatePBKDF2Password(string password, string hashPass)
{
try
{
byte[] Password = Encoding.Unicode.GetBytes(password);
bool result = true;
// Extract the bytes
byte[] hashBytes = Convert.FromBase64String(hashPass);
// Get the salt
byte[] salt = new byte[32];
Array.Copy(hashBytes, 0, salt, 0, 32);
// Compute the hash on the password that user entered.
// I know SHA1 is not that secured at all anymore. Just using it to test with. :-)
IHash hash1 = HashFactory.Crypto.CreateSHA1();
IPBKDF2_HMAC pbkdf2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(hash1, Password, salt, Pbkdf2Iterations);
byte[] hash = pbkdf2.GetBytes(32);
// compare the results
for (int i = 0; i < 32; i++)
{
if (hashBytes[i + 32] != hash[i])
{
result = false;
}
}
return result;
}
catch (Exception)
{
return false;
}
}
How to use: string GeneratedHash = PBKDF2Helper.GetHashPBKDF2Password("password");
Results: hv6t8N4rrVSKYFm80cCoVUEiUk2o11xLBc6lJb5kBXKTcwcKwl9dZwSdce01X0bi8BBhJY/QGGnNVAcR7ZhSvQ==
Verify Paword: Boolean tester = PBKDF2Helper.ValidatePBKDF2Password("password", GeneratedHash);
txtVerificationResults.Text = tester.ToString();

When merging two arrays, the first array gets shorter by 1

I have a password which I hash with SHA256. Then I have a salt which looks like that:
AAAAAAAAAAAAAAAAAAAAAA==
At the end of the process both of them are byte-arrays which I then merge into a new byte-array.
My PROBLEM is that while merging the password with the salt, my hashed password gets shorter by one character at the end.
Expected output:
uIxnpgdBQpSPJrqwYucIOeyOyqyCv7HbBfd74ovoxjI=AAAAAAAAAAAAAAAAAAAAAAAAA==
Output:
uIxnpgdBQpSPJrqwYucIOeyOyqyCv7HbBfd74ovoxjIAAAAAAAAAAAAAAAAAAAAAAAAA==
As you can see there is a = missing after the l.
My Method:
public static byte[] HashPassword(byte[] passwordToHash)
{
byte[] hInput;
byte[] hSalt = GetSalt();
using(SHA256 sh = SHA256.Create())
{
hInput = sh.ComputeHash(passwordToHash);
}
byte[] SaltedPw = new byte[(hInput.Length+ 1 ) + (hSalt.Length + 3)];
Array.Copy(hInput,0, SaltedPw, 0,hInput.Length);
Array.Copy(hSalt, 0, SaltedPw, hInput.Length, hSalt.Length);
return SaltedPw;
}
public static byte[] GetSalt()
{
byte[] salt = new byte[16];
return salt;
}
How can I prevent the shortening of my password?
You are doing it wrong. You must not add the salt to the hashed password. You must add the salt to the plain password and then hash. The point is to make the hash of a current or short password unrecognizable.
The base 64 encoding is only applied to the final result to allow storing the password hash as string. Therefore, you will never have to merge base 64 strings. Base 64 strings are padded with = at the end to get a length which is a multiple of 4. Therefore you will never see a = in the middle.
public static string GetHashedPassword(string plainPassword, byte[] salt)
{
byte[] passwordBytes = GetBytes(plainPassword);
// Merge the password bytes and the salt bytes
var mergedBytes = new byte[passwordBytes.Length + salt.Length];
Array.Copy(passwordBytes, mergedBytes, passwordBytes.Length);
Array.Copy(salt, 0, mergedBytes, passwordBytes.Length, salt.Length);
// Now hash password + salt
byte[] hash;
using (var sha = SHA256.Create()) {
hash = sha.ComputeHash(mergedBytes);
}
return Base64Encode(hash);
}
You will also need this:
public static string Base64Encode(byte[] bytes)
{
return System.Convert.ToBase64String(bytes);
}
static byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
Create random salt bytes for each password and store the salt as a separate piece of information together with the hashed password. Like this, every password gets a different salt. This makes Pre-computed dictionary attack/Rainbow table attack infeasible. The salt does not need to be encrypted. You probably will want to store it as base 64 string as well. To get the salt bytes again you will need Convert.FromBase64String().

AES 256 Encryption is not matching with result

I have a document that is saying to Encrypted string using AES256. According to my document with two value 10002:1486703720424 AND HashKey: hpIw4SgN)TxJdoQj=GKo)p83$uHePgoF it will generate the result 1ltQFLRGNif73uCNzi0YEvBqLKiRgx6fWsk5e/GcTQc= but when i try to generate the result it is generating 6SKbqJAxbBrg4eU7r/B8gJoJEPg+KjMvGL5L7bfykUU= from my code. Can you please tell what i am doing mistakes. This is the first time when i doing encryption so i am little bit confuse to find the my mistakes.
string getHashKey1 = EncryptText("10002:1486703720424", "hpIw4SgN)TxJdoQj=GKo)p83$uHePgoF");
public string EncryptText(string input, string password)
{
string result = "";
try
{
// Get the bytes of the string
byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
// Hash the password with SHA256
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
result = Convert.ToBase64String(bytesEncrypted);
}
catch (Exception ex)
{
ErrorLog errLog = new ErrorLog();
errLog.LogsWrite(ex, Path.GetDirectoryName(Application.ExecutablePath));
}
return result;
}
public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
try
{
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = new byte[] { 1, 2, 23, 234, 37, 48, 134, 63, 248, 4 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
}
catch (Exception ex)
{
ErrorLog errLog = new ErrorLog();
errLog.LogsWrite(ex, Path.GetDirectoryName(Application.ExecutablePath));
}
return encryptedBytes;
}
Found... They are using ECB as the cypher mode, so no IV. I won't comment on the "security" of this. The padding seems to be PKCS7 (the default of AES). The password is used "as is", simply encoded in UTF8 (or perhaps even ASCII) (so it must be 32 bytes long).
public static string EncryptText(string input, string password)
{
// Get the bytes of the string
byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
string result = Convert.ToBase64String(bytesEncrypted);
return result;
}
public static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
using (MemoryStream ms = new MemoryStream())
{
using (Aes aes = Aes.Create())
{
aes.Key = passwordBytes;
aes.Mode = CipherMode.ECB;
// "zero" IV
aes.IV = new byte[16];
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
}
byte[] encryptedBytes = ms.ToArray();
return encryptedBytes;
}
}
#LukePark correctly made a tirade about this answer that is useful for the OP but useless in the greater world. I'll bold the correctly word. For this reason I'll explain what is "wrong" on the specification given to A. Goutam and what a "correct" specification must always contain.
A specification for encryption should always contain: the algorithm used (AES for example), the key size (if you say AES256 then clearly it is 256 bits), the block mode (CBC, ECB etc). Many block modes (CBC for example) require an IV vector. ECB has a lower security than other block modes (see for example https://crypto.stackexchange.com/questions/225/should-i-use-ecb-or-cbc-encryption-mode-for-my-block-cipher). If the IV is necessary, then the specification must contain it (or explain how it should be generated). The specification must contain the padding that should be used. The Padding.None should be used only if the data to be encrypted can be exactly subdivided in encryption blocks (so for example, with AES, PaddingMode.None is good only if the data is 16, 32, 64, 96, ... bytes). PaddingMode.Zeros is good only for text (and I wouldn't use it, because it will add '\0' at the end of the text). The other padding modes are good.
Often the key isn't used "as is", because for example it is a string. The specification should contain how the encryption key must be derived from the string key. SHA256 on the key is a weak solution. Normally a good solution is using a strong key derivation function, like Rfc2898DeriveBytes. If this function is used, the specification must contain the number of iteration and other informations about using Rfc2898DeriveBytes or similar functions. Clearly what encoding should be used for the key (and for the data to be encrypted, if it is a text) must be included (UTF8 is always a good idea).
I'll add that a good specification should contain some test cases. At least one test case should be of length smaller than the encryption block and at least one test case must be of length greater than the encryption block but smaller than two encryption blocks (or > 2 and < 3... some complete blocks and one non-complete block). In this way you are testing both the PaddingMode and the CipherMode (note the should/must: by testing something bigger than an encryption block plus an incomplete block you are already testing everything)

How to get fixed size key from user input

Just want to know, If I use TripleDES encryption (keysize (168)192bits) and ask the key from the user and the user gives a key which might be more or less in size compare to actual size of the cipher then what should I do? Should I just add 0s or 1s using String.PadLeft or String.PadRight to the key? Is there any built in methods/idea/library/tricky codes to add random strings to the key? If so, then is it possible to reverse the process?
I was hoping to use hashing algorithm but .Net doesn't support any hashing algorithm with output size 192bits. So is there any solution to this?
This simple code is to make it more clear: (encryption part only)
static void Main(string[] args)
{
Console.WriteLine("Enter Plain Text: ");
string original = Console.ReadLine();
Console.WriteLine("Enter The Key: ");
string keyString = Console.ReadLine();
byte[] data = Encoding.UTF8.GetBytes(original);
byte[] key = Encoding.UTF8.GetBytes(keyString);
TripleDESCryptoServiceProvider myTripleDES = new TripleDESCryptoServiceProvider();
byte[] encrypted = EncryptString(data, key, myTripleDES.IV);
string encrypt = Convert.ToBase64String(encrypted);
string decrypted = DecryptStringFromBytes(encrypted, key, myTripleDES.IV);
Console.WriteLine("encryted: " + encrypt);
Console.WriteLine("decrypted: " + decrypted);
Console.ReadLine();
}
static byte[] EncryptString(byte[] data, byte[] Key, byte[] IV)
{
TripleDESCryptoServiceProvider tdsAlg = new TripleDESCryptoServiceProvider();
tdsAlg.BlockSize = 64;
tdsAlg.KeySize = 192;
tdsAlg.Padding = PaddingMode.PKCS7;
tdsAlg.Mode = CipherMode.CBC;
tdsAlg.Key = Key;
tdsAlg.IV = IV;
ICryptoTransform encryptor = tdsAlg.CreateEncryptor(tdsAlg.Key, tdsAlg.IV);
byte[] encrypted = encryptor.TransformFinalBlock(data, 0, data.Length);
Console.WriteLine("The Cipher length: " + encrypted.Length);
return encrypted;
}
This code will not work until I give (valid) fixed size key input. And my question is what is the best way to achieve this?

Categories

Resources