Finding encrypt for a decrypt - c#

I have legacy C# code that has an encrypted password in code, decrypts it and uses it. I now have to change the actual source value, i.e. the base64 string needs to change.
Decryption code:
var des = new TripleDESCryptoServiceProvider();
var md5Hash = new MD5CryptoServiceProvider();
des.Key = md5Hash.ComputeHash(Encoding.Unicode.GetBytes(...key...));
des.Mode = CipherMode.ECB;
var desDecrypt = des.CreateDecryptor();
var buffer = Convert.FromBase64String(...value...);
var result = Encoding.Unicode.GetString(desDecrypt.TransformFinalBlock(buffer, 0, buffer.Length));
return result;
I am trying to do an ad-hoc re-encrypt of the changed value like this:
var des = new TripleDESCryptoServiceProvider();
var md5Hash = new MD5CryptoServiceProvider();
des.Key = md5Hash.ComputeHash(Encoding.Unicode.GetBytes(...key...));
des.Mode = CipherMode.ECB;
var desEncrypt = des.CreateEncryptor();
var s = "...new value...";
var b = Encoding.Unicode.GetBytes(s);
desEncrypt.TransformFinalBlock(b, 0, b.Length);
var x = Convert.ToBase64String(b);
This gives me a nice base64 value to replace my old constant with. However, when the code tries to decrypt this new value, I get a
System.Security.Cryptography.CryptographicException: Length of the
data to decrypt is invalid.
The source values are of the same length. The base64 values are of the same length. I know this is no way to run a railroad. I'm just hoping that I am missing something obvious, easy and/or silly.

It looks like you're not actually using the output of the 3DES encryption:
var b = Encoding.Unicode.GetBytes(s);
// This returns the encrypted data, but you're not storing it anywhere
desEncrypt.TransformFinalBlock(b, 0, b.Length);
// b is still the original input, not the encrypted data
var x = Convert.ToBase64String(b);
Try this:
var b = Encoding.Unicode.GetBytes(s);
// Store encrypted data in "e"
var e = desEncrypt.TransformFinalBlock(b, 0, b.Length);
// Convert the encrypted data to base64
var x = Convert.ToBase64String(e);

Related

Hash values different when comparing .NET to nodejs (crypto)

I'm getting different values when I attempt to hash the same password/salt combination in node and .NET. (Yes, I know SHA1 is dangerous, I'm trying to change that too).
C#
byte[] unencodedBytes = Encoding.Unicode.GetBytes(password);
byte[] saltBytes = Convert.FromBase64String(salt);
byte[] buffer = new byte[unencodedBytes.Length + saltBytes.Length];
Buffer.BlockCopy(unencodedBytes, 0, buffer, 0, unencodedBytes.Length);
Buffer.BlockCopy(saltBytes, 0, buffer, unencodedBytes.Length - 1, saltBytes.Length);
byte[] hash = HashAlgorithm.Create("SHA1").ComputeHash(buffer);
//This is what I need
string hashedString = Convert.ToBase64String(hash);
Here's my JS
var buffer = [];
var unicodePassword = new Buffer(password, 'utf16le');
for (var i = 0; i < unicodePassword.length; ++i) {
buffer.push(unicodePassword[i]);
}
var salt = new Buffer(userEntry.PasswordSalt, 'base64');
for (var i = 0; i < salt.length; i++) {
buffer.push(salt[i]);
}
var bufferString = new Buffer(buffer);
//This is what I need
var hashedString = crypto.createHash('sha1').update(bufferString).digest('base64');
I know that I'm getting the exact same byte array in both implementations when I send it off to be hashed. It looks like this code is doing the exact same thing but the value of hashedString is not the same. Any ideas what's going on?
the default Encoding.Unicode is emitting byteOrderMask
What if you create Encoding without BOM in C# code?
new UnicodeEncoding(false, false)

Restricting rijndaelmanaged algorithm key size?

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);
}

Playing around with C# encryption

Been trying to venture out and learn some C# and powershell, giving myself little projects to try and learn. Recently I have been trying to convert some code from powershell to C# and I believe I got it working but am coming across some errors creating the IV for RijndaelManaged.
This is the powershell code that works fine, pulled from the internet
function Decrypt-String($Encrypted, $Passphrase, $salt, $init="Yet another key")
{
if($Encrypted -is [string]){
$Encrypted = [Convert]::FromBase64String($Encrypted)
}
$r = new-Object System.Security.Cryptography.RijndaelManaged
$pass = [System.Text.Encoding]::UTF8.GetBytes($Passphrase)
$salt = [System.Text.Encoding]::UTF8.GetBytes($salt)
$r.Key = (new-Object Security.Cryptography.PasswordDeriveBytes $pass, $salt, "SHA1", 5).GetBytes(32) #256/8
$r.IV = (new-Object Security.Cryptography.SHA1Managed).ComputeHash( [Text.Encoding]::UTF8.GetBytes($init) )[0..15]
$d = $r.CreateDecryptor()
$ms = new-Object IO.MemoryStream #(,$Encrypted)
$cs = new-Object Security.Cryptography.CryptoStream $ms,$d,"Read"
$sr = new-Object IO.StreamReader $cs
Write-Output $sr.ReadToEnd()
$sr.Close()
$cs.Close()
$ms.Close()
$r.Clear()
}
And this is the C# code i moved it over to
public static string Decrypt_String(string cipherText, string passPhrase, string Salt)
{
string hashAlgorithm = "SHA1";
int passwordIterations = 5;
initName = "Yet another key";
using (RijndaelManaged r = new RijndaelManaged())
{
byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
byte[] PassPhraseBytes = Encoding.UTF8.GetBytes(passPhrase);
byte[] SaltBytes = Encoding.UTF8.GetBytes(Salt);
byte[] initVectorBytes = Encoding.UTF8.GetBytes(initName);
PasswordDeriveBytes password = new PasswordDeriveBytes(PassPhraseBytes,SaltBytes,hashAlgorithm,passwordIterations);
byte[] keyBytes = password.GetBytes(32); //(256 / 32)
r.Key = keyBytes;
SHA1Managed cHash = new SHA1Managed();
r.IV = cHash.ComputeHash(Encoding.UTF8.GetBytes(initName),0,16);
ICryptoTransform decryptor = r.CreateDecryptor();
MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
CryptoStream cryptoStream = new CryptoStream(memoryStream,
decryptor,
CryptoStreamMode.Read);
StreamReader streamReader = new StreamReader(cryptoStream);
string output = streamReader.ReadToEnd();
return output;
}
}
Currently the ComputeHash is spitting back an error telling me the value is invalid.
here are the values I am using from the working encrypt function
cipherText = "s6ZqNpJq05jsMh2+1BxZzJQDDiJGRQPqIYzBjYQHsgw="
saltValue = "}=[BJ8%)vjJDnQfmvC))))3Q"
passphrase = "S#lt3d"
Any ideas on why the IV wont set properly?
EDIT:
Sorry the exception is
Line 38: r.IV = cHash.ComputeHash(initVectorBytes, 0, 16);
Exception Details: System.ArgumentException: Value was invalid.
Kind of generic
#Nate is correct, you are using a different overload of the ComputeHash method, and you are not quite handling it properly:
Encoding.UTF8.GetBytes(initName)
This will return a byte array the same length as your string - 15. But by passing 0 and 16, you are asking ComputeHash to use the first 16 elements of the array.
cHash.ComputeHash(Encoding.UTF8.GetBytes(initName),0,16);
So this first fix is to either pass 0 and 15 (or maybe 0 and initName.Length), or better yet, go back to the overload you are using in your powershell script, which figures out the array length automatically:
cHash.ComputeHash(Encoding.UTF8.GetBytes(initName));
But you will need to shorten the resulting array (it comes back length 20, but you only want 16):
using System.Linq;
...
cHash.ComputeHash(Encoding.UTF8.GetBytes(initName)).Take(16).ToArray();

C# DES ECB Encryption

I am having difficulty encrypting something in C#.
I have 3 variables.
First one is a 16 digit hex,lets call it X value I.E 0072701351979990
Second one is also a 16 digit hex value, lets call it Y I.E 3008168011FFFFFF
These two values have to be XOR 'ed to get the key for the DES-ECB encryption.
Thus resulting in 307a66934068666f . Now thus is my keyblock for the encryption.
Then i have this as my datablock,which is 64 bits for encryption 0E329232EA6D0D73
Now i have the following code for encryption this.
The result of the encryption should be XOR'ed with the datablock again and
result in a 64bit result. This is not the case.
This is my code for the encryption
$ public static string DESEncrypt(string keyBlock,string dataBlock){
DES desEncrypt = new DESCryptoServiceProvider();
byte[] keyBlockBytes = BitConverter.GetBytes(Convert.ToInt64(keyBlock, 16));
byte[] dataBlockBytes = BitConverter.GetBytes(Convert.ToInt64(dataBlock, 16));
desEncrypt.Mode = CipherMode.ECB;
desEncrypt.Key = keyBlockBytes;
ICryptoTransform transForm = desEncrypt.CreateEncryptor();
MemoryStream enecryptedStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(enecryptedStream, transForm, CryptoStreamMode.Write);
cryptoStream.Write(dataBlockBytes, 0, dataBlockBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] encryptedData = new byte[enecryptedStream.Length];
enecryptedStream.Position = 0;
enecryptedStream.Read(encryptedData, 0, encryptedData.Length);
string enCryptedHex = BitConverter.ToString(encryptedData);
return enCryptedHex.Replace("-","");
}
What am i doing wrong?
UPDATED QUESTION
I have tested the above solution from CodeInChaos.
It does give me back a 64 bit result. But still there is something wrong.
Here is my updated code.
The keyblock value is abababababababab
and the data block value is 215135734068666F.
The resultant 64 bit result should be XOR'ed with the data block again.
The final answer is suppose to be 414945DD33C97C47 but I get
288a08c01a57ed3d.
Why does it not come out right?
Here is the specifications in suppliers documentation for the encryption.
Encryption is DEA in accordance with FIPS 46-3, single DES in ECB mode, using a single 64-
bit DES Key with odd parity.
$ public static string DESEncrypt(string keyBlock,string dataBlock){
DES desEncrypt = new DESCryptoServiceProvider();
byte[] keyBlockBytes = BitConverter.GetBytes(Convert.ToInt64(keyBlock, 16));
byte[] dataBlockBytes = BitConverter.GetBytes(Convert.ToInt64(dataBlock, 16));
desEncrypt.Mode = CipherMode.ECB;
desEncrypt.Key = keyBlockBytes;
desEncrypt.Padding = PaddingMode.None;
ICryptoTransform transForm = desEncrypt.CreateEncryptor();
MemoryStream enecryptedStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(enecryptedStream, transForm, CryptoStreamMode.Write);
cryptoStream.Write(dataBlockBytes, 0, dataBlockBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] encryptedData = enecryptedStream.ToArray();
string enCryptedHex = BitConverter.ToString(encryptedData);
enCryptedHex = enCryptedHex.Replace("-", "");
long iDeaEncrypt = Convert.ToInt64(enCryptedHex, 16);
long iDataBlock = Convert.ToInt64(dataBlock, 16);
long decoderKey = iDeaEncrypt ^ iDataBlock;
string decKeyHex = Convert.ToString(decoderKey, 16);
return decKeyHex;
}
I think you need to set the padding to PaddingMode.None:
desEncrypt.Padding = PaddingMode.None;
But you should really think hard, if DES and ECB is really what you want.
b.t.w.
byte[] encryptedData = new byte[enecryptedStream.Length];
encryptedStream.Position = 0;
encryptedStream.Read(encryptedData, 0, encryptedData.Length);
can be replaced by:
encryptedData = encryptedStream.ToArray();
Perhaps it is necessary to set DES Provider to use the FIPS 46-3 Standard so that the DEA uses the permutation tables etc. specified in FIPS 46-3. Unfortunately I’m also struggling with this same issue.

Bouncy Castle Not Decrypting DoFinal from Java in C#

I am trying to decrypt data that is encrypted using the Bouncy Castle library in Java with a C#. The data that is encrypted in Java (again with the Bouncy Castle Libraries) can be decrypted with Java. I am using the same keys and parameters but when I reach DoFinal I get the error "pad block corrupted".
Here is the Java:
KeyParameter keyParam = new KeyParameter(key);
CipherParameters param = new ParametersWithIV(keyParam, initVector);
BlockCipherPadding padding = new PKCS7Padding();
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), padding);
cipher.reset();
cipher.init(true, param);
byte[] fileBytes = Base64.decodeBase64(decryptedStringData);
byte[] encrypted = new byte[cipher.getOutputSize(fileBytes.length)];
int l = cipher.processBytes(fileBytes, 0, fileBytes.length, encrypted, 0);
l += cipher.doFinal(encrypted, l);
return (Base64.encodeBase64String(encrypted));
Here is the C#:
KeyParameter keyParam = new KeyParameter(key);
ICipherParameters param = new ParametersWithIV(keyParam, initVector);
IBlockCipherPadding padding = new Pkcs7Padding();
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(new AesEngine()), padding);
cipher.Reset();
cipher.Init(false, param);
byte[] fileBytes = Convert.FromBase64String(encryptedDataString);
byte[] decrypted = new byte[cipher.GetOutputSize(fileBytes.Length)];
int l = cipher.ProcessBytes(fileBytes, 0, fileBytes.Length, decrypted, 0);
l += cipher.DoFinal(decrypted, l);
return(Convert.ToBase64String(decrypted));
I am generating a 32 byte PBK for the key based on a hash that has been buffered... however, we checked the key generated between Java and C# and they are the same.
It turns out that the data provided for encryption and decryption were encoded in different formats. I was pulling in UTF-8 encoded strings to decrypt and they needed to be made into Base64 strings first. Thanks for all of the help provided.

Categories

Resources