Decrypting AES in SQL Anywhere 16 encrypted in C# and vise versa - c#

I have this peace of code that encrypts stuff using AES, to be more precise Rijndael algorithm to mimic (http://dcx.sybase.com/index.html#sa160/en/dbreference/encrypt-function.html) the behaviour of SQL Anywhere 16, for sake of examples simplicity keys are fake:
var Key = Encoding.ASCII.GetBytes("1234567812345678");
var IV = Encoding.ASCII.GetBytes("1234567812345678");
var text = "stuff";
string encrypted;
var aes = new RijndaelManaged { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7, BlockSize = 128, KeySize = 128, Key = Key, IV = IV };
using (var encryptor = aes.CreateEncryptor())
{
var tmp = Encoding.ASCII.GetBytes(text);
encrypted = Convert.ToBase64String(encryptor.TransformFinalBlock(tmp, 0, tmp.Length));
}
Console.WriteLine("Encrypted text: " + encrypted);
And the result I get: do3BgGEeCWS5+mruUU1Czg== nXnrIX9m4zCxupbPsw3zsg==
Decrypting it in SQL Anywhere 16:
select cast(decrypt(base64_decode('do3BgGEeCWS5+mruUU1Czg=='), '1234567812345678', 'AES(format=RAW;padding=PKCS5)', '1234567812345678') as varchar)
I get this result: s t u f f stuff
So it almost works, comparing in hex it is 0x73007400750066006600 instead of 0x7374756666. Furthermore, if I decrypt same text in C# (decryptor source can be found bellow), I also get same spaces, what am I doing wrong?
Also I tried it other way around, encrypted in SQL Anywhere:
select base64_encode(encrypt('stuff', '1234567812345678', 'AES(format=RAW;padding=PKCS5)', '1234567812345678'))
Got this line: nXnrIX9m4zCxupbPsw3zsg==
Trying to decrypt in C# using same procedure:
string decrypted;
using (var decryptor = aes.CreateDecryptor())
{
var tmp = System.Convert.FromBase64String(encrypted);
decrypted = Encoding.ASCII.GetString(decryptor.TransformFinalBlock(tmp, 0, tmp.Length));
};
Console.WriteLine("Decrypted text: " + decrypted);
I get the correct result: stuff with no unnecessary spaces in it.
So it works with a mirror drawback, any ideas where from are those the extra spaces?
Update: Error was in var tmp = Encoding.Unicode.GetBytes(text); line, changed Unicode to ASCII.

If you are using SQL Anywhere version 16.0 or later, you can do this using the 'format=raw' option of the decrypt function. If you are using a version earlier than that, the decrypt function will not be able to decrypt data encrypted outside the database server.
Update: Since you updated your question, I'll address that too. I ran through the decryption in the SQL Anywhere server, and the data that comes out has the embedded NULLs in it, which means that the data that's encrypted already contains the embedded NULLs. I'm not a C# guy so I can't tell you for sure, but I suspect that var text = "stuff"; stores the data in UTF-16.
Full disclosure: I work for SAP in SQL Anywhere engineering.

Related

C# AES decrypted output is not the same as input

I wanted to do a simple message encrypter to dip my toes into the matter but I can't make it to work. The problem is that whatever input I start with, sometimes it encrypts it but when I try to decrypt it, it just doesn't return the original string. It would be really helpful if you could tell me what I'm doing wrong or guide in the right direction.
Complete code
This are the sections in charge of encrypting and decrypting.
void Decrypt()
{
using var crypt = Aes.Create();
string[] input = ClipboardService.GetText()?.Split(SEPARATOR) ?? Array.Empty<string>();
byte[] key = input[0].ToBytes();
byte[] IV = input[^1].ToBytes();
byte[] value = string.Join(string.Empty, input[1..^1]).ToBytes();
crypt.IV = IV;
crypt.Key = key;
var decryptedValue = crypt.DecryptCbc(value, IV, PaddingMode.Zeros);
string decryptedValueInText = decryptedValue.ToUnicodeString();
ClipboardService.SetText(decryptedValueInText);
LogInfoMessage($"{decryptedValueInText}: {decryptedValue.Length}");
crypt.Clear();
}
void Encrypt()
{
using var crypt = Aes.Create();
crypt.GenerateKey();
string value = ClipboardService.GetText() ?? string.Empty;
var encryptedValue = crypt.EncryptCbc(value.ToBytes(), crypt.IV, PaddingMode.Zeros);
string encryptedValueInText = $"{crypt.Key.ToUnicodeString()}{SEPARATOR}{encryptedValue.ToUnicodeString()}{SEPARATOR}{crypt.IV.ToUnicodeString()}";
ClipboardService.SetText(encryptedValueInText);
LogInfoMessage($"{encryptedValueInText}: {encryptedValue.Length}");
crypt.Clear();
}
There are two extension methods:
public static string ToUnicodeString(this byte[] bytes) => Encoding.Unicode.GetString(bytes);
public static byte[] ToBytes(this string str) => Encoding.Unicode.GetBytes(str);
Example
The input links were:
https://www.youtube.com/
https://www.youtube.com/watch?v=bSA91XTzeuA
I don't think it matters because the key and IV are autogenerated everytime anyways but still.
Per our discussion...
Using the clipboard to store binary data as Unicode text will fail due to invalid UTF-16 codepoints. UTF-16 uses some multi-word encoding for certain Unicode characters, using 32 bits in surrogate pairs to encode Unicode code points from the supplementary planes. There are plenty of primers on the UTF-16 encoding, but basically you have a pair of 16-bit values where the first is in the range 0xD800-0xDBFF and the second must be in the range 0xDC00-0xDFFF. Odds on your encrypted data will break this rule.
As noted, if your encrypted binary data must be sent through a text-only transport you should encode the bytes in the encrypted block using Base64 or similar.
I'd also like to stress that writing methods that can be called with parameters rather than directly accessing the clipboard for I/O makes it much simpler to do testing, including round-trip tests on the various parts of the problem. Proving that the codec is working without reference to the clipboard is a good test and separation of concerns helps to more readily identify the source of problems in the future.

Blowfish ECB encryption in C# implementation

I have a client who needs us to use Blowfish ECB encryption with cipherMode 0, output 1. I have tried to solve for this, but I'm getting stuck. How do I fix my code?
Here are the full client instructions:
Algorithm: Blowfish
・ Mode: ECB
・ Padding: PKCS5Padding
*Initial vector is unnecessary because we use ECB mode.
Example
・Encrypt Key: 2fs5uhnjcnpxcpg9
 → Plain Text : 3280:99:20120201123050
 → Cipher Text : daa745f1901364c0bd42b9658db3db96336758cd34b2a576
* Please keep Cipher Text with 16 hexadecimal characters .
* Please generate Cipher Text without “salt”.
I need to write this in C#. Here's what I did, but it doesn't seem to be working:
string member_id = "3280";
string panelType = "99";
string RandomString = "20120201123050";
string encryptionKey = "2fs5uhnjcnpxcpg9";
string cryptstr = member_id + ":" + panelType + ":" + RandomString;
string plainText = cryptstr;
BlowFish b = new BlowFish(encryptionKey);
string cipherText = b.Encrypt_ECB("3280:99:20120201123050");
The result is not daa745f1901364c0bd42b9658db3db96336758cd34b2a576. Where did I go wrong?
Encrypt_ECB() so I assume its Schneier's class.
The ctor expects a hexadecimal string if one is passed, you need the overload for a byte array:
BlowFish b = new BlowFish(Encoding.UTF8.GetBytes(encryptionKey));
The output is still not correct, lets see what it really should be by decrypting their example:
string clear = b.Decrypt_ECB("daa745f1901364c0bd42b9658db3db96336758cd34b2a576");
gives us:
"3280:99:20120201123050\u0002\u0002"
Which is good but there are 2 0x2 bytes on the end, the N x 0xN is due to the PKCS padding. To get the match you need to pad the input:
// input to bytes
List<byte> clearBytes = new List<byte>(Encoding.UTF8.GetBytes("3280:99:20120201123050"));
// how many padding bytes?
int needPaddingBytes = 8 - (clearBytes.Count % 8);
// add them
clearBytes.AddRange(Enumerable.Repeat((byte)needPaddingBytes, needPaddingBytes));
// encrypt
byte[] cipherText = b.Encrypt_ECB(clearBytes.ToArray());
// to hex
string cipherTextHex = BitConverter.ToString(cipherText).Replace("-", "").ToLowerInvariant();

C# Triple DES in ECB Decrypt Issues

I have this PHP script which we need to convert to C# to decode a result from an API post request, but I am experiencing issues with my C# implementation.
We get the result fine while running the PHP script which is XXXX-XXXX-XXXX but get different errors.
Values are:
encodedText = "U8Q+m2zpFMLa/3gYILHx5w=="
key = "examplesecret"
keyHash = "6315046b2c085bbeeab87c65"
Php Script:
<?php
$secret = 'examplesecret';
$encrypted_code = 'U8Q+m2zpFMLa/3gYILHx5w==';
// Base64
// Decode
$encrypted_code = base64_decode( $encrypted_code );
// Create decryption module
$cipher = mcrypt_module_open( 'tripledes', '', 'ecb', '' );
$keysize = mcrypt_enc_get_key_size( $cipher ); // = 24
// Generate key
$hash = md5( $secret );
$key = substr( $hash, 0, $keysize );
// Initialise decrypter
$iv = mcrypt_create_iv( mcrypt_enc_get_iv_size( $cipher ),
MCRYPT_DEV_RANDOM );
mcrypt_generic_init( $cipher, $key, $iv );
// Decrypt code
$decrypted = mdecrypt_generic( $cipher, $encrypted_code );
// Output decrypted code
echo $decrypted;
?>
C# Script
public static string Decrypt(string encodedText, string key)
{
TripleDESCryptoServiceProvider desCryptoProvider = new TripleDESCryptoServiceProvider();
MD5CryptoServiceProvider hashMD5Provider = new MD5CryptoServiceProvider();
byte[] byteHash;
byte[] byteBuff;
byteHash = hashMD5Provider.ComputeHash(Encoding.UTF8.GetBytes(key));
desCryptoProvider.Key = byteHash;
desCryptoProvider.Padding = PaddingMode.None;
desCryptoProvider.Mode = CipherMode.ECB; //CBC, CFB
byteBuff = Convert.FromBase64String(encodedText);
var byteHex = BitConverter.ToString(byteBuff).Replace("-", " ");
string plaintext = Encoding.UTF8.GetString(desCryptoProvider.CreateDecryptor().TransformFinalBlock(byteHex, 0, byteHex.Length));
return plaintext;
}
A couple of things I see:
You derive $key via a substr in PHP; but just take the output of MD5 in C#, are they the same? (e.g. if PHP's value was a hex string, then the 16 bytes of MD5 are 32 characters, which gets trunctated to 24 characters, being a 12-byte (96-bit) value; in C# it's 16 bytes)
Speaking of the keys, if you're using PHP before 5.6, a deficient key was 0-padded (per the documentation of mcrypt_encrypt; C# won't do that for you. So that's another thing to check.
You have an IV in the PHP code, but ECB doesn't use an IV. That won't be causing your problem here, but it's less than ideal.
Of course, I should at this point warn that ECB is "too stable" and doesn't hide patterns in encrypted data, and doesn't hide that you're sending the same message again.
TransformFinalBlock takes bytes, but you seem to be giving it a string here. Since C# is a strongly typed language, this can't really be the code you're running.
You should avoid talking about the specific implementations of algorithms. TripleDES.Create() is preferred to new TripleDESCryptoServiceProvider(), MD5.Create() over new MD5CryptoServiceProvider(); and type the variables as just TripleDES and MD5.
The TripleDES object, the MD5 object, and the output of CreateDecryptor() are all IDisposable, so you should really wrap them in a using statement to be more efficient with your native resources.
The most likely culprit for your problem, though, is that you're not generating the same key in both languages.

SHA1 encryption trouble

I have a problem with the password encryption.
I would like to have the password encrypted like those not highlighted in the picture.
I wrote the following c# code:
SHA1CryptoServiceProvider x = new SHA1CryptoServiceProvider();
//byte[] bs = System.Text.Encoding.Unicode.GetBytes(password);
//byte[] bs = System.Text.Encoding.UTF32.GetBytes(password);
byte[] bs = System.Text.Encoding.UTF8.GetBytes(password);
bs = x.ComputeHash(bs);
var s = new StringBuilder();
foreach (byte b in bs)
{
s.Append(b.ToString("x2").ToLower());
}
new UserService().ChangeUserPassword(username, s.ToString());
to encrypt the password in the correct way I using the following SQL code that I want remove:
CAST(hashbytes('SHA1',#newuserpassword) as nvarchar)
this is the result:
Looking at the docs for CONVERT, I suspect you just want:
CONVERT(nvarchar, hashbytes('SHA1',#newuserpassword), 2)
Where 2 is the style which converts to hex without a leading 0x. I suggest you specify the length of the nvarchar though, which should be 40 (20 bytes, 2 characters per byte).
I strongly advise you to abandon storing binary data in characters. Still, if you need to keep it for legacy reasons, here is the translation of the SQL statement:
byte[] bytes = ...; //your binary data here
var nastilyBrokenChars =
Enumerable.Range(0, bytes.Length / 2)
.Select(i => (char)BitConverter.GetInt16(bytes, i * 2))
.ToArray();
string nastilyBrokenString = new string(nastilyBrokenChars);
As you can, I'm stuffing two bytes into each char. This by itself is a lossless conversion. But I wouldn't trust that storing this data into SQL Server (and comparing it later) is loss-less.

Hashing password algorith issue

I am new in C# wpf programming but I am trying to connect to MySQL database and to hash my password. Unfortunately while I was implementing the algorith I get error in this code:
byte[] plainTextWithSaltBytes = new byte[plainTextBytes.Length + saltBytes.Length];
salt.CopyTo(plainTextWithSaltBytes, 0);
plainText.CopyTo(plainTextWithSaltBytes, salt.Length);
The error is:
Error: no overload for method 'Copy To' takes 2 arguments Exceptions: System.ArgumentNullException System.ArgumentOutOfRangeException
enter code here
By any chance do you know what is causing this errors and how to fix it?
You need to copy plainTextBytes, not plainText:
byte[] plainTextWithSaltBytes = new byte[plainTextBytes.Length + saltBytes.Length];
salt.CopyTo(plainTextWithSaltBytes, 0);
plainTextBytes.CopyTo(plainTextWithSaltBytes, salt.Length);
If you need to do simple hash, this bit of code may encrypt your password:
String GetEncryptedPassword (String prmUser, String prmPassword)
{
// Concatenating the password and user name.
String encryptedPassword = prmUserName + prmPassword;
// Converting into the stream of bytes.
Byte[] passInBytes = Encoding.UTF8.GetBytes(encryptedPassword);
// Encrypting using SHA1 encryption algorithm.
passInBytes = SHA1.Create().ComputeHash(passInBytes);
// Formatting every byte into %03d to make a fixed length 60 string.
return passInBytes.Aggregate(String.Empty, (pass, iter) => pass += String.Format("{0:000}", iter));
}
This code will give you a nice encrypted hash of 60 characters. But remember that you can't regenerate your original username and password from the hash, because this is a one way algorithm. There are few more encryption algorithms in System.Security.Cryptography that you can use.

Categories

Resources