AES Encryption In .Net And Decryption In Node JS - c#

I have a simple node js code with crypto js library through which I am decrypting a json object which I will receive from a .net based desktop application, hence the whole process is cross-platform. For testing I can easily encrypt using crypto js library and decrypt the cipher text generated by it. But my doubt is if this method is going to be compatible with encryption in .net. As I'm new to this, so far what I've learned is that the crypto js library generates a random Integration vector (IV) value. If an IV is generated by the .net code as well, do I need to specify it during decryption in node js as well? Currently I'm just using the simple example given in the documentation and no IV or padding is specified and I'm not sure if any default specifications are used by this library. In short I just need to make sure I'm doing this correctly and the method used for decryption won't cause any issues for the encryption part.
var CryptoJS = require("crypto-js");
var aeskey = "bQeThWmZq4t7w!z%C*F-JaNcRfUjXn2r";
var cloudcreds = {
accessKeyId: "abcdef",
accessKeySecret: "zxywvt",
};
//Encryption - Tbd with .net
var encryptedData = CryptoJS.AES.encrypt(
JSON.stringify(cloudcreds),
aeskey
).toString();
console.log("Encrypted Cloud Creds =>", encryptedData);
//Decryption - With node js
var bytes = CryptoJS.AES.decrypt(encryptedData, aeskey);
var decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
console.log("Decrypted Cloud Creds => ", decryptedData);

With the crypto module I'm now using my own IV and so far it seems like this could work.
var crypto = require("crypto");
var algorithm = "aes-256-cbc"; //algorithm to use
const key = "3zTvzr3p67VC61jmV54rIYu1545x4TlY"; //create key
var data = {
name: "Catalin",
surname: "Munteanu",
address: "Romania",
};
data = JSON.stringify(data);
const iv = "0000000000000000"; // generate different ciphertext everytime
const cipher = crypto.createCipheriv(algorithm, key, iv);
var encrypted = cipher.update(data, "utf8", "base64") + cipher.final("base64"); // encrypted text
console.log(encrypted);
const decipher = crypto.createDecipheriv(algorithm, key, iv);
var decrypted = decipher.update(encrypted, "base64", "utf8") + decipher.final("utf8"); //deciphered text
console.log(JSON.parse(decrypted));

Related

RSA Decryption exception: The length of the data to decrypt is not valid for the size of this key

I have got an Angular + Net Core application with an (RSA + AES) encrypted connection.
All requests from client are coming via POST. (You will be given an example below.
The script provided below works quite well but throws in 5% cases an exception:
The length of the data to decrypt is not valid for the size of this key
in the line:
var decryptedAesKey = Encoding.UTF8.GetString(rsaCng.Decrypt(Convert.FromBase64String(request.k), RSAEncryptionPadding.Pkcs1));
Encryption part (Front-end)
encrypt(requestObj:any):any {
var rsaEncrypt = new JsEncryptModule.JSEncrypt();
var key = this.generateAesKey(32); //secret key
var iv = this.generateAesKey(16); //16 digit
var stringifiedRequest = CryptoJS.enc.Utf8.parse(JSON.stringify(requestObj));
var aesEncryptedRequest = CryptoJS.AES.encrypt(stringifiedRequest,
CryptoJS.enc.Utf8.parse(key),
{
keySize: 128 / 8,
iv: CryptoJS.enc.Utf8.parse(iv),
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.CBC
});
rsaEncrypt.setPrivateKey(this.publicPemKey);
var encryptedKey = rsaEncrypt.encrypt(key);
var encryptedIV = rsaEncrypt.encrypt(iv);
var encryptedRequestObj = {
k: encryptedKey,
v: encryptedIV,
r: aesEncryptedRequest.toString()
};
return encryptedRequestObj;
}
Decryption part (C# Back-end)
var decryptedAesKey = Encoding.UTF8.GetString(rsaCng.Decrypt(Convert.FromBase64String(request.k),
RSAEncryptionPadding.Pkcs1));
var decryptedAesIV = Encoding.UTF8.GetString(rsaCng.Decrypt(Convert.FromBase64String(request.v), RSAEncryptionPadding.Pkcs1));
byte[] encryptedBytes = request.r;
AesCryptoServiceProvider aes = new AesCryptoServiceProvider()
{
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7,
Key = Encoding.UTF8.GetBytes(decryptedAesKey),
IV = Encoding.UTF8.GetBytes(decryptedAesIV)
};
ICryptoTransform crypto = aes.CreateDecryptor(aes.Key, aes.IV);
byte[] secret = crypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
crypto.Dispose();
requestJson = Encoding.UTF8.GetString(secret);
Example, a user wants to open a page by id.
Front-end:
1) encrypts request Id using AES
2) encrypts AES' key & iv using RSA
3) sends to Back-end
Back-end:
1) decrypts AES' key & value using RSA <--- BREAKS HERE
2) decrypts request Id using AES' key & iv
3) decrypts and get id as if there was no encryption
This logic works quite well but breaks sometimes...
EXAMPLE OF FAILING request:
{ "k":"L+ikMb/JGvFJmhBpADMGTVLFlkHOe69dZUVSQ5r7yHCvWSwY2x6KMR274ByflF0lDMYdCmywo+Nfq6JUybRctDqmAp8UFHXnhwBAv49d99mF5x2yGbJr/j0cn6EZyhweNK4p97i5yMM6MQtluZTIErpsUa22Cajtj8F+xl0jJPUMXIf8cs2X+ooFr5VP/p/vlbPmnEY3K/hMCRZRdXMkEqaCWoA5EnYMTQABtRXPZWgLSQwJpr4dqEAhGCBtga1AGsKF3dQCsKO92NYyst0ngkBiKwFNfy1QDwbk4SzKAKeBckaY17SHt526NMvpEv08BGV6btBxcM+ypsmpB4o0",
"v":"LIndJOjUgKHDlXqwpg7uSmDuut3oi5z9L/GKm2KgU7P2EXmf/JIpXM0JgpTXPJL7wUTndq3F9UMlMdU70JBOV56x/4uIBRbHbyvaG2JZYxbBZblwyYgdo1ZcK1OSE4k5oesQmMEGNEk9RVu+EZO4xAme6+mlyd2/Y/709jaC90PuiOG/k/4JMTTI/2q4s7tk6IgSxLBT8ZiOtgJVGdasSaAksEBMRHyUkzAIr5tSUw1VXedwJFPfwQT2nOD5dU2cxiNJKOwtO9uAYXly0U0FDoa/nkWskca8zaU+4EiPikJ6Km7phViH9JvwZFgHhBj+8FM6Jof+AdrY3q1dcMLFlg==",
"r":"OJnA3wFoKKG+iu4FciXyJg=="
}
EXAMPLE OF CORRECT REQUEST:
{ "k":"uW8d7vIzlgkEkKTkDnHbBZeqKwdgoG+1BVZ/NUiC0pZ/LqZM9aUasQSx+qDg+X50ur30uRnEyAyIZXruYeHQb8cacx5mvr9LWLud+wueJXsOlEEdocD/4A1DfE9TDFdnTaVcMSIwhSVlLPUjO7ubJdANY9yK4S+vb0IyPbsrYpAT7ho01mDkvsH1rZsId/TmzQadmsGhThowu+mrQlz78rrdlN8nI5LnUQHXRNWMUgBvuteTpVBmyrfnIELIKoo/jI6Nj4rGPQBf7+2OOoZPs0Y1GtjXxUCTAt7madNLKSOdaPjdWjaOfGSwnymDNeEFyJQOmAwHZoOGYNd2B/UhQQ==",
"v":"IimiJFcKv5ZHWHljJixX0LUgV4I2GWAWPbk7dWHVhwmHEhTHA/hCdih/E1wiWFS+0KaL05ZobiZInyK7gCwYPHaz0aRCSQtVeBPiFg4f7L0gwfvk1GHwJ1wZjqNJZaYf0elXJzc2l5BwN+aXNWaNJDPA7M6kfK6UPkq84IV3ohCQcTuC8zPM7aMJHxpz9IudcrMmYIkeqrj9Do88CkTLv8yg5hk3EASPk9HqsUieuQixggv/8ZlHnp00iftc62LJlIuCkGn4WR3FkMdFdqpKXf6Ebj8PU1HOmokEtKtYJiOZ5JxieZO5Pnd+ez6sO7khIbdRFDhAQ20chsxKUypezw==",
"r":"2mbUgU44JFFDlWu8As2RIw=="
}
In the case of the failed request, the Base64 decoded encrypted AES key has a length of 255 bytes. For a 2048 bit RSA key it should actually be 256 bytes, as it is for the remaining data.
For the RSA-encryption JSEncrypt is used, which has a known bug that sporadically causes too short ciphertexts and which is probably responsible for your issue, see here. This bug was opened in July 2019 and is not fixed yet.
Within JSEncrypt the too short ciphertexts are processed correctly, so that no error occurs. Cross platform however, this is often not the case, because the too short ciphertexts are strictly speaking invalid and therefore some programming languages identify them as invalid, e.g. Python, apparently C# is another one.
If the too short ciphertext is manually padded from the left to the length of the modulus with 0x00, the ciphertext should also be decryptable in the C# code.
Update:
I have successfully tested the suggested fix using your code. The ciphertext can be fixed in the JavaScript or C# code. A possible implementation for the JavaScript side is e.g. for the key:
encryptedKey = btoa(atob(encryptedKey).padStart(256, "\0"));
where encryptedKey is the Base64 encoded ciphertext as returned by JSEncrypt#encrypt. To ensure that this correction isn't applied to ciphertexts that already have the correct length, a length check is useful: A Base64 encoded ciphertext of length 4 * Math.ceil(256 / 3) doesn't need to be fixed because it corresponds to a ciphertext of the correct length of 256 bytes, see here.
You apply the method setPrivateKey in the JSEncrypt part when setting the public key for the encryption, correct would be setPublicKey, see here. However, JSEncrypt seems to fix this internally, because it works as well. Nevertheless it should be changed, because it's misleading.
As already mentioned in the comments by #kelalaka, the IV is no secret and doesn't need to be encrypted.

RC2CryptoServiceProvider .NET algorithm differences with node js rc2 algorithm

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!

Importing an Objective-C RSA public key into c# RSACryptoServiceProvider

Here is the Objective-C we are using to generate the RSA object using the following lib: https://github.com/kuapay/iOS-Certificate--Key--and-Trust-Sample-Project
BDRSACryptor *rsa = [[BDRSACryptor alloc] init];
BDRSACryptorKeyPair *RSAKeyPair = [rsa generateKeyPairWithKeyIdentifier:nil error:error];
We then pass RSAKeyPair.publicKey to our c#, where using the BouncyCastles library:
using (TextReader sr = new StringReader(pempublic))
{
var pemReader = new PemReader(sr);
var temp = (RsaKeyParameters)pemReader.ReadObject();
var RSAKeyInfo = new RSAParameters
{
Modulus = temp.Modulus.ToByteArray(),
Exponent = temp.Exponent.ToByteArray()
};
var rsaEncryptor = new RSACryptoServiceProvider();
rsaEncryptor.ImportParameters(RSAKeyInfo);
}
There are no errors, but the encryption is different. The same string encrypted in c# and obj-c are different, and we are unable to encrypt on one end and decrypt on the other.
Help!
Edit: Willing to consider any methodology of exchanging public keys between c# and obj-c. This is just the closest we have come so far.
Edit2: Contents of pempublic
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/ugxekK+lY0VLeD8qA5nEhIn7IzBkgcrpiEM109chFxHobtvWEZbu8TqTIBtIgtISNp4idcEvahPniEyUawjmRSWB7uYmcHJ3pWaIo5/wBthmGrqS/XjedVXT6RuzaoPf9t0YXyW6YiH1kQZn4gjZF51O6iIk2+VnfkYVqeKBtQIDAQAB-----END PUBLIC KEY-----
Edit3: Regarding padding: C# and obj-c are both using OEAP padding.
Edit4: How the text is being encrypted: c#
byte[] testBytes = Encoding.UTF8.GetBytes("1234567890");
byte[] encryptedBytes = rsaEncryptor.Encrypt(testBytes, true);
string base64 = Convert.ToBase64String(encryptedBytes);
obj-c
NSString *encrypted = [rsa encrypt:#"1234567890" key:RSAKeyPair.publicKey error:error];
Final Edit:
Solved by using the Chilkat encryption library on the .NET server. We are now able to load an RSA encryptor from a public key in both XML and PEM format generated from a .NET, Java, or Objective-C Client. If anyone could explain why the .NET RSACryptoServiceProvider wouldn't work, we are all quite curious.
please check my answer to my own question
RSA C# encryption with public key to use with PHP openssl_private_decrypt(): Chilkat, BouncyCastle, RSACryptoServiceProvider
i think it may be helpful
to make it short, try using temp.Modulus.ToByteArrayUnsigned()
I wrote RSA and AES implementation using CommonCrypto, implementation is done in order to be interoperable with .NET
Check it out
https://github.com/ozgurshn/EncryptionForiOS
I used base64 encoding
.NET side could be
public string RsaDecryption(byte[] cipherText, string privateKey)
{
var cspDecryption = new RSACryptoServiceProvider();
cspDecryption.FromXmlString(privateKey);
var bytesPlainTextData = cspDecryption.Decrypt(cipherText, false);
return Encoding.UTF8.GetString(bytesPlainTextData);
}
public byte[] RsaEncryption(string plainText, string publicKey)
{
var cspEncryption = new RSACryptoServiceProvider();
cspEncryption.FromXmlString(publicKey);
var bytesPlainTextData = Encoding.UTF8.GetBytes(plainText);
var bytesCypherText = cspEncryption.Encrypt(bytesPlainTextData, false);
return bytesCypherText;
}

How to decrypt AesCryptoServiceProvider using JavaScript?

I am using EncryptStringToBytes_Aes method from MSDN to encrypt some data using custom passphrase like this:
string original = "some data to encrypt";
byte[] encrypted;
using (AesManaged aes = new AesManaged())
{
// Prepare new Key and IV.
string passphrase = "somepassphrase";
byte[] saltArray = Encoding.ASCII.GetBytes("somesalt");
Rfc2898DeriveBytes rfcKey = new Rfc2898DeriveBytes(passphrase, saltArray);
aes.Key = rfcKey.GetBytes(aes.KeySize / 8);
aes.IV = rfcKey.GetBytes(aes.BlockSize / 8);
// Encrypt the string to an array of bytes.
encrypted = EncryptStringToBytes_Aes(original, aes.Key, aes.IV);
// Decrypt the bytes to a string.
string roundtrip = DecryptStringFromBytes_Aes(encrypted, aes.Key, aes.IV);
return Convert.ToBase64String(encrypted);
}
and it works (DecryptStringFromBytes_Aes returns the original string).
My question is how do I decrypt encrypted using JavaScript if I have the same passphrase on the client-side as well? I tried using CryptoJS to decrypt it but had no success. The data gets encrypted in a webservice and I tried passing it to JS as a byte array, string, tried encoding it with various encodings but no matter what I did, I couldn't get the original string. What am I doing wrong here and how can I make this work? Is it even doable like this? Could the saltArray encoding or even the usage of the custom passphrase be the cause of my problems?
Here is for example one of my JS tries (using base64 encoding):
var decoded = CryptoJS.enc.Base64.parse(encrypted);
var decrypted = CryptoJS.AES.decrypt(decoded, "somepassphrase");
(edit: I meant to implement random salt later, once I got everything else wokring since it is easier to track what is going on that way)
try using Stanford Javascript Crypto Library.
Link:http://crypto.stanford.edu/sjcl/

Generate authenticated CMSEnvelopedData Messages with bouncycastle

I am trying to encrypt data with a password and store it inside a ASN.1 encoded CMS message (using C# and BouncyCastle 1.4)
The code I have seems to have two problems:
the data does not seem to be signed with a HMAC, so when I tamper with the encodedData (by enabling the commented out line), the decryption still succeeds.
when I decrypt the data I have tampered with, I get beck corrupted plain text. However only a two blocks of plaintext data are corrupted. This seems to suggest that the encryption does not actually use CBC mode.
(edit: disregard the second point, this is exactly how CBC is supposed to work)
This is what I am testing with:
public void TestMethod1()
{
byte[] data = new byte[1024]; // plaintext: a list of zeroes
CmsEnvelopedDataGenerator generator = new CmsEnvelopedDataGenerator();
CmsPbeKey encryptionKey = new Pkcs5Scheme2PbeKey("foo", new byte[] { 1, 2, 3 }, 2048);
generator.AddPasswordRecipient(encryptionKey, CmsEnvelopedDataGenerator.Aes256Cbc);
CmsProcessableByteArray cmsByteArray = new CmsProcessableByteArray(data);
CmsEnvelopedData envelopeData = generator.Generate(cmsByteArray, CmsEnvelopedDataGenerator.Aes256Cbc);
byte[] encodedData = envelopeData.GetEncoded();
// encodedData[500] = 10; // tamper with the data
RecipientID recipientID = new RecipientID();
CmsEnvelopedData decodedEnvelopeData = new CmsEnvelopedData(encodedData);
RecipientInformation recipient = decodedEnvelopeData.GetRecipientInfos().GetFirstRecipient(recipientID);
byte[] data2 = recipient.GetContent(encryptionKey);
CollectionAssert.AreEqual(data, data2);
}
What am I doing wrong? What would be the correct way to write this?
To add an HMAC to a CMS message, you would have to use a AuthenticatedData-structure.
I am not especially familiar with Bouncy Castle, but from a cursory look at the API, I would say that it does not support AuthenticatedData. In fact, it looks like it only supports SignedData for authentication.
So your options seems to be:
Use another library (or write your own code) to handle the AuthenticatedData-structure.
Calculate the HMAC and provide it in a non-standard way (in a proprietary Attribute or out-of-band).
Use SignedData with an RSA key pair instead.

Categories