Please note that the issue I am having here is with the key size. At first, based on the comments included in the below code, I figured that my key needed to be 24 bytes (192 bits). This didn't work so I gave 16, 32, and 8 byte keys a shot - nothing seems to be working. By "not working" I mean that after my text has been encrypted and decrypted it does not hold the same value as my original text.
Example:
Original Text: 'Example test this should work '
Encrypted Text: ¸¹pÕô6
Decrypted Text: 'Example '
Here are the two functions I am using (Encrypt / Decrypt functions). Also I will include how I am calling each function.
// 168-bit (three-key) 3DES (Triple-DES) encrypt a single 8-byte block (ECB mode)
// plain-text should be 8-bytes, key should be 24 bytes.
public byte[] TripleDesEncryptOneBlock(byte[] plainText, byte[] key)
{
// Create a new 3DES key.
TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
// Set the KeySize = 192 for 168-bit DES encryption.
// The msb of each byte is a parity bit, so the key length is actually 168 bits.
des.KeySize = 192;
des.Key = key;
des.Mode = CipherMode.ECB;
des.Padding = PaddingMode.None;
ICryptoTransform ic = des.CreateEncryptor();
byte[] enc = ic.TransformFinalBlock(plainText, 0, 8);
return enc;
}
public byte[] TripleDesDecryptBlock(byte[] plainText, byte[] key)
{
// Create a new 3DES key.
TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
// Set the KeySize = 192 for 168-bit DES encryption.
// The msb of each byte is a parity bit, so the key length is actually 168 bits.
des.KeySize = 192;
des.Key = key;
des.Mode = CipherMode.ECB;
des.Padding = PaddingMode.None;
ICryptoTransform ic = des.CreateDecryptor();
byte[] dec = ic.TransformFinalBlock(plainText, 0, 8);
return dec;
}
// Encrypt Text
textBox5.Text = ByteToString(TripleDesEncryptOneBlock(StringToByte(textBox5.Text), StringToByte("1 2 3 4 5 6 7 8 9 1 1 2 ")));
// Decrypt Text
textBox5.Text = ByteToString(TripleDesDecryptBlock(StringToByte(textBox5.Text), StringToByte("1 2 3 4 5 6 7 8 9 1 1 2 ")));
Thank you for any help,
Evan
The clue is in the name of the function you're using: TripleDesEncryptOneBlock
This method only encrypts one block of the input string (8 bytes or 64 bits). To encrypt the entire string you need to chain multiple calls to this method.
Use this:
byte[] enc = ic.TransformFinalBlock(plainText, 0, plainText.Length);
I hope it will encrypt/decrypt your whole string. Also you will need not to call this method multiple times
Your problem is here:
byte[] dec = ic.TransformFinalBlock(plainText, 0, 8);
^
you only encode the first 8 characters of your array So when you decode, there are only those 8 characters to decode, which results in 'Example '.
if you want to encode all your text, you have to increase that value. But be carefull, if you use PaddingMode.None it will fail if the lenght of the array-to-be-encoded is not a multiple of 8.
I added some padding to my text like this:
int length = plainText.Length / 8;
if(plainText.Length%8 > 0)
{
length++;
}
byte[] paddedText = new byte[length * 8];
plainText.CopyTo(paddedText, 0);
byte[] enc = ic.TransformFinalBlock(paddedText, 0, length * 8);
Related
I spent a whole day investigating this and search all related questions on Stack Overflow for this question so please don't mention about possible duplicates.
The code below gives me a System.Security.Cryptography.CryptographicException: 'Specified padding mode is not valid for this algorithm.'
While using the very same parameters on this website : http://aes.online-domain-tools.com it decrypts perfectly into "Hello world" then filled with five 'x05' bytes for padding (PKCS#7 padding).
However the code below will always yield an exception when calling the TransformFinalBlock()
Context:
Console application running on Win8.1 with .NET Core 2.0 / Algorithm is AES / CBC / padding PKCS#7
I also tried the proposed solution here: Specified padding mode is not valid for this algorithm - c# - System.Security.Cryptography but no success (I also don't understand why if IV is already set in the SymmetricAlgorithm instance, it should be used later on when deciphering?
static void Main(string[] args)
{
string encryptedStr = "e469acd421dd71ade4937736c06fdc9d";
string passphraseStr = "1e089e3c5323ad80a90767bdd5907297b4138163f027097fd3bdbeab528d2d68";
string ivStr = "07dfd3f0b90e25e83fd05ba338d0be68";
// Convert hex strings to their ASCII representation
ivStr = HexStringToString(ivStr);
passphraseStr = HexStringToString(passphraseStr);
encryptedStr = HexStringToString(encryptedStr);
// Convert our ASCII strings to byte arrays
byte[] encryptedBytes = Encoding.ASCII.GetBytes(encryptedStr);
byte[] key = Encoding.ASCII.GetBytes(passphraseStr);
byte[] iv = Encoding.ASCII.GetBytes(ivStr);
// Configure our AES decryptor
SymmetricAlgorithm algorithm = Aes.Create();
algorithm.Mode = CipherMode.CBC;
algorithm.Padding = PaddingMode.PKCS7;
algorithm.KeySize = 256;
//algorithm.BlockSize = 128;
algorithm.Key = key;
algorithm.IV = iv;
Console.WriteLine("IV length " + iv.Length); // 16
Console.WriteLine("Key length " + key.Length); // 32
ICryptoTransform transform = algorithm.CreateDecryptor(algorithm.Key, algorithm.IV);
// Perform decryption
byte[] outputBuffer = transform.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
// Convert it back to a string
string result = Encoding.ASCII.GetString(outputBuffer);
Console.WriteLine(result);
Console.ReadLine();
}
public static string HexStringToString(string hexString)
{
var sb = new StringBuilder();
for (var i = 0; i < hexString.Length; i += 2)
{
var hexChar = hexString.Substring(i, 2);
sb.Append((char)Convert.ToByte(hexChar, 16));
}
return sb.ToString();
}
The problem is in the way how you convert hex string to byte array. Try to debug your code and check the value of array encryptedBytes. You'll see the following array:
{ 0x3f, 0x69, 0x3f, 0x3f, 0x21, 0x3f, 0x71, 0x3f, 0x3f, 0x3f, 0x77, 0x36, 0x3f, 0x6f, 0x3f, 0x3f }
which is far from input e469acd421dd71ade4937736c06fdc9d.
You shouldn't use System.String object as just a holder of binary char codes because .Net strings are UTF16-encoded.
Now when root cause is clear, the fix is pretty straighforward. Change your HexStringToString method so that it converts hex string to bytes array directly:
public static byte[] HexStringToByteArray(string hexString)
{
if (hexString.Length % 2 != 0)
{
throw new InvalidOperationException($"Inalid hex string '{hexString}'");
}
byte[] bytes = new byte[hexString.Length / 2];
for (var i = 0; i < hexString.Length; i += 2)
{
var hexChar = hexString.Substring(i, 2);
bytes[i / 2] = Convert.ToByte(hexChar, 16);
}
return bytes;
}
Then adjust the code in Main():
byte[] encryptedBytes = HexStringToByteArray(encryptedStr);
byte[] key = HexStringToByteArray(passphraseStr);
byte[] iv = HexStringToByteArray(ivStr);
This will give you desired Hello world in result variable.
I'm trying to port following code to java, but wondering how this can work in C#.
Provider is being initialized with keySize = 192, but key is only 16 bytes long.
When doing this in java I got error about incorrect key size.
Can someone explain what is going on here?
public static byte[] TripleDesEncryptOneBlock(byte[] plainText)
{
//This is key of length 16 bytes (128 bits)
byte[] key = Encoding.ASCII.GetBytes("0123456789abcdef");
byte[] result;
try
{
int count = (plainText.Length > 8) ? 8 : plainText.Length;
byte[] array = new byte[8];
Buffer.BlockCopy(plainText, 0, array, 0, count);
ICryptoTransform cryptoTransform = new TripleDESCryptoServiceProvider
{
KeySize = 192,
Key = key,
Mode = CipherMode.ECB,
Padding = PaddingMode.None
}.CreateEncryptor();
byte[] array2 = cryptoTransform.TransformFinalBlock(array, 0, 8);
result = array2;
}
catch (Exception)
{
result = null;
}
return result;
}
It's copying the first 8 bytes of the key to the end of the key. The TDEA standard calls this "keying option 2", and it only provides 80 bits of effective security (that is, it's a weak cipher).
Many Java providers won't do this automatically; by forcing the application to do it explicitly, unsuspecting fallback to a weaker scheme is less likely.
I have to recreate things from c# to php.
I dont really know what to do cuz I never really had to deal with things like encryption or something.
In c# I got this:
public static byte[] decrypt(byte[] data, byte[] key)
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
des.Key = key;
des.Mode = CipherMode.ECB;
des.Padding = PaddingMode.None;
return des.CreateDecryptor().TransformFinalBlock(data, 0, data.Length);
}
public static byte[] encrypt(byte[] data, byte[] key)
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
des.Key = key;
des.Mode = CipherMode.ECB;
des.Padding = PaddingMode.None;
return des.CreateEncryptor().TransformFinalBlock(data, 0, data.Length);
}
public static byte[] get8byte(string input)
{
byte[] ByteArray = new byte[8];
string tmp = string.Empty;
int j = 0;
for (int i = 0; i < 16; i++)
{
tmp = "" + input[i];
tmp = tmp + input[i + 1];
ByteArray[j] = byte.Parse(tmp, System.Globalization.NumberStyles.HexNumber);
j++;
i++;
}
return ByteArray;
}
and the key i have to use is encrypted like the following:
var Buffer = new char[16];
var cMasterKey = new byte[8];
byte[] Key = {
(byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5',
(byte) '6', (byte) '7', (byte) '8'
};
cMasterKey = DESUtils.get8byte(new string(Buffer));
MasterKey = DESUtils.decrypt(cMasterKey, Key);
The "Buffer" comes from an USB Drive which has a File on it which contains a Masterkey of 16 Chars.
I really don't know how to realize it in PHP. I tried a lot of things like pack('C*', $var) and things like that but I didnt get the same result.
Is there anyone here who knows how to handle it? I dont know if I'm on the right way but I tried things like this:
$key = pack('C*', 1, 2, 3, 4, 5, 6, 7, 8);
$masterbyte = pack('C*', $buffer);
$decmasterkey = mcrypt_decrypt(MCRYPT_DES, $key, $masterbyte, MCRYPT_MODE_ECB);
'1' in C# is a character literal. Characters can be directly cast to a byte under the default ASCII assumption. So '1' is actually a 0x31 byte and not a 0x01 byte like you have in PHP.
You "want":
$key = "12345678";
Whether the decoding of $buffer is correct depends on its content and how you read it.
Some notes:
Don't use DES nowadays. It's really insecure. AES is a better alternative.
Never use ECB mode. It's deterministic and therefore not semantically secure. You should at the very least use a randomized mode like CBC or CTR. It is better to authenticate your ciphertexts so that attacks like a padding oracle attack are not possible. This can be done with authenticated modes like GCM or EAX, or with an encrypt-then-MAC scheme.
I tried implementing 3DES in ECB mode in c#.
The problem is code below gives me different ciphertext each time I run it, even though I pass it same parameters as you can see - and I use ECB mode.
Can someone help what is wrong? The output must be same each time I run program below isn't it?
public static byte[] SingleBlock3DES_ECB_Encrypt(byte [] plain, byte [] key)
{
if(plain.Length != 8)
throw new Exception("Plain text length for single block should be 8 bytes");
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
// set the secret key for the tripleDES algorithm
tdes.Key = key;
// mode of operation. there are other 4 modes.
// We choose ECB(Electronic code Book)
tdes.Mode = CipherMode.ECB;
// padding mode(if any extra byte added)
tdes.Padding = PaddingMode.None;
// Set key size
tdes.KeySize = 192;
ICryptoTransform cTransform = tdes.CreateEncryptor();
// transform the specified region of bytes array to resultArray
byte[] resultArray = cTransform.TransformFinalBlock(plain, 0, plain.Length);
// Release resources held by TripleDes Encryptor
tdes.Clear();
return resultArray;
}
static void Main(string[] args)
{
byte[] plain = new byte[8];
byte[] key = new byte[24];
for (int i = 0; i < 8; i++)
plain[i] = 1;
for (int i = 0; i < 24; i++)
key[i] = (byte) i;
byte[] res = SingleBlock3DES_ECB_Encrypt(plain, key);
string hex = BitConverter.ToString(res);
Console.WriteLine(hex);
}
So in simple words if I run this program multiple times I get different output each time. Clearly there must be some problem somewhere?
As you have written, if you remove tdes.KeySize = 192 the code works. But what happens in truth is that when you
tdes.KeySize = 192;
the key is reset to a random value.
So you could move the
tdes.KeySize = 192;
BEFORE the
tdes.Key = key;
or simply remove it, because for 3DES the KeySize is fixed to 192.
I solved the problem. By some miracle, when I remove this
tdes.KeySize = 192;
from code, it works.
I'm trying to do the following test to return results that should return a specific cipher. They provide the Key, IV and Plaintext string as seen below.
But I am getting "Specified initialization vector (IV) does not match the block size for this algorithm."
I been stuck on this for a while and can't find a good simple example and tried a combination of things.
Below is my C# code. I tried to keep it very simple.
string AesPlainText = "1654001d3e1e9bbd036a2f26d9a77b7f";
string AesKey = "3ccb6039c354c9de72adc9ffe9f719c2c8257446c1eb4b86f2a5b981713cf998";
string AesIV = "ce7d4f9679dfc3930bc79aab81e11723";
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.KeySize = 256;
aes.IV = HexToByteArray(AesIV);
aes.Key = HexToByteArray(AesKey);
aes.Mode = CipherMode.CBC;
// Convert string to byte array
byte[] src = Encoding.Unicode.GetBytes(AesPlainText);
// encryption
using (ICryptoTransform encrypt = aes.CreateEncryptor())
{
byte[] dest = encrypt.TransformFinalBlock(src, 0, src.Length);
// Convert byte array to Base64 strings
Console.WriteLine(Convert.ToBase64String(dest));
}
UPDATED PER ANSWER:
Thanks, great observation. I changed Encoding.UTF8.GetBytes to use HexToByteArray in the above example and it works now.
public static byte[] HexToByteArray(String hex)
{
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
Your plaintext, key and IV seem to be specified in hexadecimals, so you need to decode the hexadecimals to get to the underlying bytes instead of performing UTF8 encoding.
You can get a byte array from hex here. Note that the name of the method should have something with hex in in, don't call it StringToByteArray or atoi or something stupid like that.