Does .NET's RC2CryptoServiceProvider conform to OpenSSL. I'm using RC2CryptoServiceProvider with CBC but the encrypted value for the same text (using the same key and init vector) is different from what nodejs crypto library's rc2-cbc produces. Node js crypto library conforms to OpenSSL.
Someone had already asked about this discrepancy but no answers yet - Node.JS RC2-CBC Encryption and Decryption ciphers are not matching with C#
Can someone point me to the complete source code RC2CryptoServiceProvider? Is the encrypt/decrypt code a completely managed one available in C# or does it use C++ underneath?
I'm interested in finding the differences as I'm looking for a way to decrypt a .NET application encrypted string in node js.
Below is the C# code and the corresponding node js code. For the same data (HelloWorld), key and iv, the encrypted values produced are different.
public static string Encrypt(string data, string key, string iv)
{
try
{
byte[] ivBytes = Encoding.ASCII.GetBytes(iv);
byte[] keyBytes = Encoding.ASCII.GetBytes(key);
byte[] dataBytes = Encoding.ASCII.GetBytes(data);
RC2 rc = new RC2CryptoServiceProvider();
rc.Mode = CipherMode.CBC;
rc.Key = keyBytes;
rc.IV = ivBytes;
MemoryStream stream = new MemoryStream();
CryptoStream stream2 = new CryptoStream(stream, rc.CreateEncryptor(), CryptoStreamMode.Write);
stream2.Write(dataBytes, 0, dataBytes.Length);
stream2.Close();
return Convert.ToBase64String(stream.ToArray());
}
catch
{
return string.Empty;
}
}
Below is the node js code.
algo = 'rc2-cbc'
key = '1234567890'
iv = 'someInit'
keyBuffer = new Buffer(key)
ivBuffer = new Buffer(iv)
cipher = crypto.createCipheriv(algo, keyBuffer, ivBuffer)
textBuffer = new Buffer('HelloWorld')
encrypted = cipher.update(textBuffer)
encryptedFinal = cipher.final()
encryptedText = encrypted.toString('base64') + encryptedFinal.toString('base64')
console.log encryptedText
I hit a similar situation. There is existing .NET (core) code using RC2CryptoServiceProvider to decrypt a string. I wanted to replicate this in node.
The .NET code uses keysize 128 (which also appears to be the default) so I assumed the comparable algorithm in node (openssl) would be rc2-128. But this always failed when decrypting.
After some trial and error I discovered that using using the rc2-64 algorithm in node behaves the same as the .NET code using keysize 128. Just don't ask me why!
Related
I would like to encrypt data in iOS app with a SymetricKey and the CryptoKit and decrypt on server side with C# in Net Core.
iOS code:
class Security {
static let keyStr = "d5a423f64b607ea7c65b311d855dc48f" //32
static let iv="31348c0987c7" //12
class func encode(_ text:String)->String {
let key=SymmetricKey(data: Security.keyStr.data(using: .utf8)!)
let nonce=try! AES.GCM.Nonce(data: iv.data(using: .utf8)!)
let encrypted=try! AES.GCM.seal(text.data(using: .utf8)!, using: key, nonce: nonce)
return encrypted.combined!.base64EncodedString()
}
}
I pass the result of the encryption to my backend and I would like to decrypt
C# Code:
public string decrypt(string encryptedText)
{
string keyStr = "d5a423f64b607ea7c65b311d855dc48f";
string iv = "31348c0987c7";
string plaintext = "";
Debug.WriteLine(encryptedText);
using (Aes aesAlg = Aes.Create())
{
Debug.WriteLine(AesGcm.IsSupported);
var key = System.Text.Encoding.UTF8.GetBytes(keyStr);
var iV = System.Text.Encoding.UTF8.GetBytes(iv);
aesAlg.Key = key;
aesAlg.IV = iV;
// Create a decryptor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(Convert.FromBase64String(request.pswd)))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
Debug.WriteLine(plaintext);
}
So for example word: Test gets encrypted as: MzEzNDhjMDk4N2M3CI68IDEJeBR4OFtWO3GPO3TIgos=
When I get to line:
aesAlg.IV = iV;
I get an error "Specified initialization vector (IV) does not match the block size for this algorithm."
It seems as if C# needs byte[16], but in iOS I seem to be stuck with 12.
I got stuck at this point. Any idea greately appreciated.
Thank you.
The posted Swift code applies AES in GCM mode, s. AES.GCM. The posted C# code also uses AES, however not the GCM mode, but the default CBC mode (s. Aes, Mode).
The CBC mode applies a 16 bytes IV, while the GCM mode uses a 12 bytes nonce. That is what the error message is pointing to.
For successful decryption, AES in GCM mode must also be used on the C# side. In .NET AES in GCM mode is supported with the AesGcm class (as of .NET Core 3.0).
Note also that the data given by the Swift code is the Base64 encoding of the concatenation of 12 bytes nonce, ciphertext and 16 bytes tag (in that order), which must be separated in the C# code, where the portions are processed individually.
A possible C# implementation that decrypts the ciphertext generated by the posted Swift code is:
byte[] nonceCiphertextTag = Convert.FromBase64String("MzEzNDhjMDk4N2M3CI68IDEJeBR4OFtWO3GPO3TIgos=");
byte[] key = Encoding.UTF8.GetBytes("d5a423f64b607ea7c65b311d855dc48f");
Span<byte> nonceCiphertextTagSpan = nonceCiphertextTag.AsSpan();
Span<byte> nonce = nonceCiphertextTagSpan[..12];
Span<byte> ciphertext = nonceCiphertextTagSpan[12..^16];
Span<byte> tag = nonceCiphertextTagSpan[^16..];
byte[] plaintext = new byte[ciphertext.Length];
using AesGcm aesGcm = new AesGcm(key);
aesGcm.Decrypt(nonce, ciphertext, tag, plaintext); // throws an 'CryptographicException: The computed authentication tag did not match the input authentication tag' if authentication fails
Console.WriteLine(Encoding.UTF8.GetString(plaintext)); // Test
Edit: An alternative to the native .NET class AesGcm is C#/BouncyCastle. Maybe this is supported in your environment:
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
...
byte[] nonceCiphertextTag = Convert.FromBase64String("MzEzNDhjMDk4N2M3CI68IDEJeBR4OFtWO3GPO3TIgos=");
byte[] key = Encoding.UTF8.GetBytes("d5a423f64b607ea7c65b311d855dc48f");
Span<byte> nonceCiphertextTagSpan = nonceCiphertextTag.AsSpan();
byte[] nonce = nonceCiphertextTagSpan[..12].ToArray();
byte[] ciphertextTag = nonceCiphertextTagSpan[12..].ToArray();
GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine());
AeadParameters aeadParameters = new AeadParameters(new KeyParameter(key), 128, nonce);
gcmBlockCipher.Init(false, aeadParameters);
byte[] plaintext = new byte[gcmBlockCipher.GetOutputSize(ciphertextTag.Length)];
int length = gcmBlockCipher.ProcessBytes(ciphertextTag, 0, ciphertextTag.Length, plaintext, 0);
gcmBlockCipher.DoFinal(plaintext, length); // throws an 'InvalidCipherTextException: mac check in GCM failed' if authentication fails
Console.WriteLine(Encoding.UTF8.GetString(plaintext)); // Test
Note that unlike the native AesGcm class, C#/BouncyCastle requires the concatenation of ciphertext and tag, so only the nonce needs to be separated.
I am searching for C# Code to reproduce the following openssl command.
openssl enc -d -aes-256-cbc -in my_encrypted_file.csv.enc -out my_decrypted_file.csv -pass file:key.bin
Additional information:
The encrypted file in present as byte[]
The key.bin is a byte[] with length of 256 (the key is obtained by a more simple decryption of yet another file, which i managed to realize in C#).
I have been trying out various examples found by searching the web.
The problem is, that all of these examples require an IV (initialization vector). Unfortunately, I don't have an IV and no one on the team knows what this is or how it could be defined.
The openssl command does not seem to need one, so I am a bit confused about this.
Currently, the code, I am trying with, looks as follows:
public static string DecryptAesCbc(byte[] cipheredData, byte[] key)
{
string decrypted;
System.Security.Cryptography.Aes aes = System.Security.Cryptography.Aes.Create();
aes.KeySize = 256;
aes.Key = key;
byte[] iv = new byte[aes.BlockSize / 8];
aes.IV = iv;
aes.Mode = CipherMode.CBC;
ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream ms = new MemoryStream(cipheredData))
{
using (CryptoStream cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
decrypted = sr.ReadToEnd();
}
}
return decrypted;
}
}
The code fails saying that my byte[256] key has the wrong length for this kind of algorithm.
Thanks for any help with this!
Cheers, Mike
The posted OpenSSL statement uses the -pass file: option and thus a passphrase (which is read from a file), see openssl enc. This causes the encryption process to first generate a random 8 bytes salt and then, together with the passphrase, derive a 32 bytes key and 16 bytes IV using the (not very secure) proprietary OpenSSL function EVP_BytesToKey. This function uses several parameters, e.g. a digest and an iteration count. The default digest for key derivation is MD5 and the iteration count is 1. Note that OpenSSL version 1.1.0 and later uses SHA256 as default digest, i.e. depending on the OpenSSL version used to generate the ciphertext, the appropriate digest must be used for decryption. Preceding the ciphertext is a block whose first 8 bytes is the ASCII encoding of Salted__, followed by the 8 bytes salt.
Therefore, the decryption must first determine the salt. Based on the salt, together with the passphrase, key and IV must be derived and then the rest of the encrypted data can be decrypted. Thus, first of all an implementation of EVP_BytesToKey in C# is required, e.g. here. Then a possible implementation could be (using MD5 as digest):
public static string DecryptAesCbc(byte[] cipheredData, string passphrase)
{
string decrypted = null;
using (MemoryStream ms = new MemoryStream(cipheredData))
{
// Get salt
byte[] salt = new byte[8];
ms.Seek(8, SeekOrigin.Begin);
ms.Read(salt, 0, 8);
// Derive key and IV
OpenSslCompat.OpenSslCompatDeriveBytes db = new OpenSslCompat.OpenSslCompatDeriveBytes(passphrase, salt, "MD5", 1);
byte[] key = db.GetBytes(32);
byte[] iv = db.GetBytes(16);
using (Aes aes = Aes.Create())
{
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
aes.Key = key;
aes.IV = iv;
// Decrypt
ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV);
using (CryptoStream cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs, Encoding.UTF8))
{
decrypted = sr.ReadToEnd();
}
}
}
}
return decrypted;
}
Note that the 2nd parameter of DecryptAesCbc is the passphrase (as string) and not the key (as byte[]). Also note that StreamReader uses an encoding (UTF-8 by default), which requires compatible data (i.e. text data, but this should be met for csv files). Otherwise (i.e. for binary data as opposed to text data) StreamReader must not be used.
I have searched online but have not been able to find any solutions to my problem.
I am using previously written methods to encrypt and ecrypt text using the Rijndael class.
I use these functions to encrypt and decrypt usernames and emails for a web application I have been working on.
The encryption/decryption works perfectly, but every once in a while I get this error:
System.Security.Cryptography.CryptographicException: Length of the data to decrypt is invalid.
Currently, I am getting this error with a specific email address and I can't reproduce the error even if I replace some of the letters in the email.
Here are the encryption/decrytpion functions. The IV and Key are defined as read only strings.
static public string Encrypting(string Source)
{
byte[] bytIn = System.Text.ASCIIEncoding.ASCII.GetBytes(Source);
// create a MemoryStream so that the process can be done without I/O files
System.IO.MemoryStream ms = new System.IO.MemoryStream();
byte[] IVBytes = Encoding.ASCII.GetBytes(IV);
byte[] KEYBytes = Encoding.ASCII.GetBytes(KEY);
Rijndael rijndael = Rijndael.Create();
rijndael.IV = IVBytes;
rijndael.Key = KEYBytes;
// create Crypto Stream that transforms a stream using the encryption
CryptoStream cs = new CryptoStream(ms, rijndael.CreateEncryptor(), CryptoStreamMode.Write);
// write out encrypted content into MemoryStream
cs.Write(bytIn, 0, bytIn.Length);
cs.FlushFinalBlock();
// get the output and trim the '\0' bytes
byte[] bytOut = ms.GetBuffer();
int i = 0;
for (i = 0; i < bytOut.Length; i++)
if (bytOut[i] == 0)
break;
// convert into Base64 so that the result can be used in xml
return System.Convert.ToBase64String(bytOut, 0, i);
}
static public string Decrypting(string Source)
{
// convert from Base64 to binary
byte[] bytIn = System.Convert.FromBase64String(Source);
// create a MemoryStream with the input
System.IO.MemoryStream ms = new System.IO.MemoryStream(bytIn, 0, bytIn.Length);
byte[] IVBytes = Encoding.ASCII.GetBytes(IV);
byte[] KEYBytes = Encoding.ASCII.GetBytes(KEY);
Rijndael rijndael = Rijndael.Create();
rijndael.IV = IVBytes;
rijndael.Key = KEYBytes;
// create Crypto Stream that transforms a stream using the decryption
CryptoStream cs = new CryptoStream(ms, rijndael.CreateDecryptor(), CryptoStreamMode.Read);
// read out the result from the Crypto Stream
System.IO.StreamReader sr = new System.IO.StreamReader(cs);
return sr.ReadToEnd();
}
FYI - I am very new to cryptography and security.
Can these functions be fixed to avoid special cases that cause the error, or should I scrap these and use the RijndaelManaged class?
Sites I found that use RijndaelManaged:
SeeSharp
TekEye
The issue is almost certainly nothing to do with Rijndael vs. RijndaelManaged (or any other such implementation), but instead because the encrypted data contains a 0x00, and you are incorrectly assuming that the the ciphertext ends at the first 0x00 byte. Since the ciphertext can legitimately contain any byte value you should instead use the stream's Length property to determine the length of the ciphertext.
Eliminate the section you've commented: "get the output and trim the '\0' bytes" and replace the return ... statement with:
return System.Convert.ToBase64String(ms.GetBuffer(), 0, ms.Length);
It should be noted that there are many other issues with your use of cryptography here, e.g. the use of a key generated directly from the ASCII encoding of a string, and the fact you're using a fixed IV both negatively impact security.
The norm for the error is a padding issue. What version of .NET are you using? It is more common to use the AES classes (AES, or Advanced Encryption Standard, which is Rijndael). There are plenty of AES implementations you can find as samples.
If you need some proof AES is Rijndael: http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
Hi I'm just trying to encrypt a string but i want to reverse the decryption method to create exactly encrypted key
decryption was
public string newSample(string s)
{
byte[] buffer = Convert.FromBase64String(s);
Encoding utF8 = Encoding.UTF8;
byte[] bytes1 = utF8.GetBytes("key1");
byte[] bytes2 = utF8.GetBytes("key2");
RijndaelManaged rijndaelManaged1 = new RijndaelManaged();
rijndaelManaged1.Mode = CipherMode.CBC;
rijndaelManaged1.Padding = PaddingMode.Zeros;
rijndaelManaged1.BlockSize = 128;
rijndaelManaged1.KeySize = 128;
RijndaelManaged rijndaelManaged2 = rijndaelManaged1;
ICryptoTransform transform = (ICryptoTransform)null;
transform = rijndaelManaged2.CreateDecryptor(bytes2, bytes1);
byte[] bytes3 = (byte[])null;
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, transform, CryptoStreamMode.Write))
{
cryptoStream.Write(buffer, 0, buffer.GetLength(0));
cryptoStream.FlushFinalBlock();
}
rijndaelManaged2.Clear();
bytes3 = memoryStream.ToArray();
}
return new string(Encoding.UTF8.GetChars(bytes3));
}
is it possible to reverse the code and create encryption key ? if so
how could be the encryption should look lik for this decryption method ??
thanks
This is the problem - or at least the initial problem:
return new string(Encoding.UTF8.GetChars(bytes3));
The result of encryption is not a UTF-8-encoded byte array... it's arbitrary bytes. By assuming it's valid UTF-8-encoded text, you're losing information.
Instead, you should use a hex or base64 approach, both of which are designed to convert arbitrary binary data to text in a lossless fashion. For example:
return Convert.ToBase64String(bytes3);
Now, your decryption code should start with:
byte[] encryptedData = Convert.FromBase64String(base64EncryptedText);
(Where base64EncryptedText is the value returned from your encryption method.)
From there, it should be a matter of just reversing each step, and there are numerous examples around. You may well find that you've got a problem due to the padding mode, however - you may need to separately record the length of the original data.
As an aside, it's not clear why your method takes a string in the first place. It's odd for an encryption method to take a base64-encoded piece of data. It's more common for it to take either a normal plain text string which is converted into bytes using something like Encoding.UTF8, or for it to take a byte[] to start with.
Mysql :
SELECT AES_ENCRYPT('Test','pass')
AES_ENCRYPT() and AES_DECRYPT() enable encryption and decryption of data using the official AES (Advanced Encryption Standard) algorithm, previously known as “Rijndael.” Encoding with a 128-bit key length is used, but you can extend it up to 256 bits by modifying the source. We chose 128 bits because it is much faster and it is secure enough for most purposes.
http://dev.mysql.com/doc/refman/5.5/en/encryption-functions.html#function_aes-encrypt
I was trying to convert that Encrypted string into Decryped Strig in C#.net but i don't get the results as i expect.
http://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndael.aspx#Y0
C#
static string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
In this method I pass ciphertext,Key value which i usedfrom Mysql and
Rijndael.Create().IV for byte[] IV
I use the code but i don't get expected result.
Review the code and comment Idk where made a mistake
What you are doing is following a road of pain. Either decrypt/encrypt on MySQL and use an encrypted connection to the database (if that matters) or encrypt/decrypt on your .NET application, storing the encrypted data in a suitable column.
Mixing AES implementations is prone to mistakes and things can break more easily if you change versions of .NET or MySQL.
Now, to know what exactly is wrong we need to know if the IV is compatible between MySQL and .NET, or else find out what is MySQL's implementation IV and supply that.
And the other potential source of problems is how you have generated the byte arrays (we are not seeing that in your example). You have to consider character encoding issues in generating the arrays if the key is textual.
In the comments of this MySQL docs link there is information about the missing parameters.
After a long hours, I found a solution to this issue.
Couple of FYI's:
MySQL as a default for AES_Encrypt uses 128 bit, with ECB mode, which does not require an IV.
What padding mode they use is not specified, but they do say they pad it. For padding I use PaddingMode.Zeros.
In C#, use AesManaged, not RijndaelManaged since that is not recommended anymore.
If your Key is longer than 128 bits (16 bytes), then use a function below to create the correct key size, since the default MySQL AES algorithm uses 128 bit keys.
Make sure you play around with the correct Encoding and know exactly what type of character encoding you will receive back when translating the bytes to characters.
For more info go here: https://forums.mysql.com/read.php?38,193084,195959#msg-195959
Code:
public static string DecryptAESStringFromBytes(byte[] encryptedText, byte[] key)
{
// Check arguments.
if ((encryptedText == null || encryptedText.Length <= 0) || (key == null || key.Length <= 0))
{
throw new ArgumentNullException("Missing arguments");
}
string decryptedText = null;
// Create an AES object with the specified key and IV.
using (AesManaged aesFactory = new AesManaged())
{
aesFactory.KeySize = 128;
aesFactory.Key = AESCreateKey(key, aesFactory.KeySize / 8);
aesFactory.IV = new byte[16];
aesFactory.BlockSize = 128;
aesFactory.Mode = CipherMode.ECB;
aesFactory.Padding = PaddingMode.Zeros;
// Create a decryptor to perform the stream transform.
ICryptoTransform decryptor = aesFactory.CreateDecryptor();
// Create the streams used for decryption.
using (MemoryStream stream = new MemoryStream())
{
using (CryptoStream decryptStream = new CryptoStream(stream, decryptor, CryptoStreamMode.Write))
{
decryptStream.Write(encryptedText, 0, encryptedText.Length);
}
decryptedText = Encoding.Default.GetString(stream.ToArray());
}
}
return decryptedText.Trim();
}
public static byte[] AESCreateKey(byte[] key, int keyLength)
{
// Create the real key with the given key length.
byte[] realkey = new byte[keyLength];
// XOR each byte of the Key given with the real key until there's nothing left.
// This allows for keys longer than our Key Length and pads short keys to the required length.
for (int i = 0; i < key.Length; i++)
{
realkey[i % keyLength] ^= key[i];
}
return realkey;
}
Here is some working code for achieving the same encryption via C# as MySQL:
public byte[] AESEncrypt(byte[] plaintext, byte[] key) {
/*
* Block Length: 128bit
* Block Mode: ECB
* Data Padding: Padded by bytes which Asc() equal for number of padded bytes (done automagically)
* Key Padding: 0x00 padded to multiple of 16 bytes
* IV: None
*/
RijndaelManaged aes = new RijndaelManaged();
aes.BlockSize = 128;
aes.Mode = CipherMode.ECB;
aes.Key = key;
ICryptoTransform encryptor = aes.CreateEncryptor();
MemoryStream mem = new MemoryStream();
CryptoStream cryptStream = new CryptoStream(mem, encryptor,
CryptoStreamMode.Write);
cryptStream.Write(plaintext, 0, plaintext.Length);
cryptStream.FlushFinalBlock();
byte[] cypher = mem.ToArray();
cryptStream.Close();
cryptStream = null;
encryptor.Dispose();
aes = null;
return cypher;
}
For details see MySQL Bug # 16713
EDIT:
Since the above is relying on officially non-documented information (though it is working) I would recommend to avoid it and use one of the options described in the answer from Vinko Vrsalovic .
If you run SELECT AES_ENCRYPT('Test','pass')
your are sending the pass over the network unencrypted so any one can unencrypted the data.
The AES_ENCRYPT is used to store data so if the database gets hacked your data is safe, not to transmit data.
if you want data encryption over the net work connect to your mysql server using the ssl socket