I have the following Code in js:
public static decrypt(inData: Buffer, inKey: Buffer, inIv: Buffer, inAuthenticationTag: Buffer = null): Buffer
{
const decipher: Decipher = createDecipheriv("aes-256-gcm", inKey, inIv);
decipher.setAuthTag(inAuthenticationTag);
return Buffer.concat([decipher.update(inData), decipher.final()]);
}
This code decrypts a buffer with the key and the given iv and authTag.
I tried to decrypt the same data in C# with BouncyCastle and always got the error "mac check in gcm failed"
Here is my C# Code:
public static string Decrypt(byte[] cipherText, byte[] key, byte[] iv, byte[] authTag)
{
var keyParameter = new KeyParameter(key);
var gcmParameters = new AeadParameters(
keyParameter,
128,
iv);
var gcmMode = new GcmBlockCipher(new AesFastEngine());
gcmMode.Init(false, gcmParameters);
var cipherBuffer = cipherText.Concat(authTag).ToArray();
var plainBytes = new byte[gcmMode.GetOutputSize(cipherBuffer.Length)];
var res = gcmMode.ProcessBytes(cipherBuffer, 0, cipherBuffer.Length, plainBytes, 0);
gcmMode.DoFinal(plainBytes, res); // When executing this line i get the Exception
var plain = Encoding.UTF8.GetString(plainBytes, 0, plainBytes.Length);
return plain;
}
Could anyone see the mistake I did in C#?
Related
I am trying to implement a AES 256 encryption with GCM using BouncyCastle library.
So far I have managed to make it work by passing Key and Nonce as string and Tag as byte array.
This is the encryption method.
private static byte[] EncryptWithGCM(string plaintext, string KeyString, string NonceString, byte[] tag)
{
byte[] key = Convert.FromBase64String(KeyString);
byte[] nonce = Convert.FromBase64String(NonceString);
var plaintextBytes = Encoding.UTF8.GetBytes(plaintext);
var bcCiphertext = new byte[plaintextBytes.Length + tagLenth];
var cipher = new GcmBlockCipher(new AesEngine());
var parameters = new AeadParameters(new KeyParameter(key), tagLenth * 8, nonce);
cipher.Init(true, parameters);
var offset = cipher.ProcessBytes(plaintextBytes, 0, plaintextBytes.Length, bcCiphertext, 0);
cipher.DoFinal(bcCiphertext, offset);
var ciphertext = new byte[plaintextBytes.Length];
Buffer.BlockCopy(bcCiphertext, 0, ciphertext, 0, plaintextBytes.Length);
Buffer.BlockCopy(bcCiphertext, plaintextBytes.Length, tag, 0, tagLenth);
return ciphertext;
}
and this is the decryption code.
private static string DecryptWithGCM(string EncryptedString, string KeyString, string NonceString, byte[] tag)
{
byte[] key = Convert.FromBase64String(KeyString);
byte[] nonce = Convert.FromBase64String(NonceString);
byte[] ciphertext = Convert.FromBase64String(EncryptedString);
var plaintextBytes = new byte[ciphertext.Length];
var cipher = new GcmBlockCipher(new AesEngine());
var parameters = new AeadParameters(new KeyParameter(key), tag.Length * 8, nonce);
cipher.Init(false, parameters);
var bcCiphertext = ciphertext.Concat(tag).ToArray();
var offset = cipher.ProcessBytes(bcCiphertext, 0, bcCiphertext.Length, plaintextBytes, 0);
cipher.DoFinal(plaintextBytes, offset);
return Encoding.UTF8.GetString(plaintextBytes);
}
As you can see I am passing everything as string except the Tag. because when I pass the Tag as string and convert it to byte array it does not work. It shows error "Mac check in GCM failed"
So, this code works:
var rnd = new Random();
var tag = new Byte[16]; //16 bytes
rnd.NextBytes(tag);
string TagString = Convert.ToBase64String(tag);
byte[] EncryptedText = EncryptWithGCM(PlainText, KeyString, NonceString, tag);
string EncryptedString = Convert.ToBase64String(EncryptedText);
string DecryptdText = DecryptWithGCM(EncryptedString, KeyString, NonceString, tag);
But when I pass the TagString in the encryption/decryption functions and converting it back to byte array, it throws "Mac check in GCM failed" error.
// this code does not work.
private static string DecryptWithGCM(string EncryptedString, string KeyString, string NonceString, string TagString)
{
byte[] key = Convert.FromBase64String(KeyString);
byte[] nonce = Convert.FromBase64String(NonceString);
byte[] tag = Convert.FromBase64String(TagString);
...
...
Why is this happening?
The tag is automatically created during encryption and used during decryption to authenticate the data (in both cases in DoFinal()).
Since C#/BC automatically concatenates the tag with the ciphertext, the tag does not need to be passed explicitly during either encryption or decryption:
private static string EncryptWithGCM(string plaintext, string keyString, string nonceString)
{
var tagLength = 16;
var key = Convert.FromBase64String(keyString);
var nonce = Convert.FromBase64String(nonceString);
var plaintextBytes = Encoding.UTF8.GetBytes(plaintext);
var ciphertextTagBytes = new byte[plaintextBytes.Length + tagLength];
var cipher = new GcmBlockCipher(new AesEngine());
var parameters = new AeadParameters(new KeyParameter(key), tagLength * 8, nonce);
cipher.Init(true, parameters);
var offset = cipher.ProcessBytes(plaintextBytes, 0, plaintextBytes.Length, ciphertextTagBytes, 0);
cipher.DoFinal(ciphertextTagBytes, offset); // create and append tag: ciphertext | tag
return Convert.ToBase64String(ciphertextTagBytes);
}
private static string DecryptWithGCM(string ciphertextTag, string keyString, string nonceString)
{
var tagLength = 16;
var key = Convert.FromBase64String(keyString);
var nonce = Convert.FromBase64String(nonceString);
var ciphertextTagBytes = Convert.FromBase64String(ciphertextTag);
var plaintextBytes = new byte[ciphertextTagBytes.Length - tagLength];
var cipher = new GcmBlockCipher(new AesEngine());
var parameters = new AeadParameters(new KeyParameter(key), tagLength * 8, nonce);
cipher.Init(false, parameters);
var offset = cipher.ProcessBytes(ciphertextTagBytes, 0, ciphertextTagBytes.Length, plaintextBytes, 0);
cipher.DoFinal(plaintextBytes, offset); // authenticate data via tag
return Encoding.UTF8.GetString(plaintextBytes);
}
Note that with a fixed key, a static nonce is a fatal bug for GCM (here). The (non-secret) nonce should be randomly generated and passed to the decrypting side along with the ciphertext and tag (typically concatenated in the following order: nonce | ciphertext | tag).
What is the best way to implement encrypt and decrypt file content using asp.net core web API?
It should be compatible with the swagger also. I have tried this the same way as encrypt a string, but there is a limitation of size length or incomplete file sent over API.
I require to encrypt at the client end before API call via swagger or postman and be decrypt at service end.
File content encrypts at the client end then only data at transmission will safe.
I have tried below
public static string encrypt(string PlainText, byte[] key, byte[] iv)
{
string sR = string.Empty;
byte[] plainBytes = Encoding.UTF8.GetBytes(PlainText);
GcmBlockCipher cipher = new GcmBlockCipher(new AesFastEngine());
AeadParameters parameters =
new AeadParameters(new KeyParameter(key), 128, iv, null);
cipher.Init(true, parameters);
byte[] encryptedBytes = new byte[cipher.GetOutputSize(plainBytes.Length)];
Int32 retLen = cipher.ProcessBytes
(plainBytes, 0, plainBytes.Length, encryptedBytes, 0);
cipher.DoFinal(encryptedBytes, retLen);
sR = Convert.ToBase64String(encryptedBytes, Base64FormattingOptions.None);
return sR;
}
You can take a look at this link: https://github.com/2Toad/Rijndael256/issues/13#issuecomment-637724412
It uses the Rijndael/AES cypher to encrypt the data you put in.
In the Github issue you will find the following code:
public static class CipherHelper
{
// This constant is used to determine the keysize of the encryption algorithm in bits.
// We divide this by 8 within the code below to get the equivalent number of bytes.
private const int Keysize = 256;
// This constant determines the number of iterations for the password bytes generation function.
private const int DerivationIterations = 1000;
public static string Encrypt(string plainText, string passPhrase)
{
// Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
// so that the same Salt and IV values can be used when decrypting.
var saltStringBytes = Generate256BitsOfRandomEntropy();
var ivStringBytes = Generate256BitsOfRandomEntropy();
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
var engine = new RijndaelEngine(256);
var blockCipher = new CbcBlockCipher(engine);
var cipher = new PaddedBufferedBlockCipher(blockCipher, new Pkcs7Padding());
var keyParam = new KeyParameter(keyBytes);
var keyParamWithIV = new ParametersWithIV(keyParam, ivStringBytes, 0, 32);
cipher.Init(true, keyParamWithIV);
var comparisonBytes = new byte[cipher.GetOutputSize(plainTextBytes.Length)];
var length = cipher.ProcessBytes(plainTextBytes, comparisonBytes, 0);
cipher.DoFinal(comparisonBytes, length);
// return Convert.ToBase64String(comparisonBytes);
return Convert.ToBase64String(saltStringBytes.Concat(ivStringBytes).Concat(comparisonBytes).ToArray());
}
}
public static string Decrypt(string cipherText, string passPhrase)
{
// Get the complete stream of bytes that represent:
// [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
// Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
// Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
// Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
var engine = new RijndaelEngine(256);
var blockCipher = new CbcBlockCipher(engine);
var cipher = new PaddedBufferedBlockCipher(blockCipher, new Pkcs7Padding());
var keyParam = new KeyParameter(keyBytes);
var keyParamWithIV = new ParametersWithIV(keyParam, ivStringBytes, 0, 32);
cipher.Init(false, keyParamWithIV);
var comparisonBytes = new byte[cipher.GetOutputSize(cipherTextBytes.Length)];
var length = cipher.ProcessBytes(cipherTextBytes, comparisonBytes, 0);
cipher.DoFinal(comparisonBytes, length);
//return Convert.ToBase64String(saltStringBytes.Concat(ivStringBytes).Concat(comparisonBytes).ToArray());
var nullIndex = comparisonBytes.Length - 1;
while (comparisonBytes[nullIndex] == (byte)0)
nullIndex--;
comparisonBytes = comparisonBytes.Take(nullIndex + 1).ToArray();
var result = Encoding.UTF8.GetString(comparisonBytes, 0, comparisonBytes.Length);
return result;
}
}
private static byte[] Generate256BitsOfRandomEntropy()
{
var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.
using (var rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with cryptographically secure random bytes.
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
}
To make this code work, you will need to install the BouncyCastle.NetCore NuGet package.
This code helps you to encrypt and decrypt a string by using a password.
In your case, if you somehow now the password at both sides of the request (send-side and receive-side) than this should be able to work.
I am using BouncyCastle.NetCore. At the moment of decryption, it seems the GetOutputSize call returns a larger than the actual required length for the output byte array, with the extra bytes having value '0x00'.
Before converting it back to objects or strings, I hence need to recreate the byte array with the actual length that I retrieve from the ProcessBytes and DoFinal calls.
Should I be using a different call to get the correct number of bytes beforehand or how should I go about this?
public AesEncryptor()
{
// AES - CBC - with default PKCS5/PKCS7 scheme
_encryptionCipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(new AesEngine()));
_decryptionCipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(new AesEngine()));
}
public byte[] Encrypt(byte[] iv, byte[] byteArrayToEncrypt)
{
ParametersWithIV keyParamWithIV = new ParametersWithIV(_keyParam, iv, 0, iv.Length);
byte[] encryptedBytes;
lock (_encryptionLock)
{
_encryptionCipher.Init(true, keyParamWithIV);
encryptedBytes = new byte[_encryptionCipher.GetOutputSize(byteArrayToEncrypt.Length)];
int length = _encryptionCipher.ProcessBytes(byteArrayToEncrypt, encryptedBytes, 0);
_encryptionCipher.DoFinal(encryptedBytes, length);
}
return encryptedBytes;
}
public byte[] Decrypt(byte[] iv, byte[] byteArrayToDecrypt)
{
ParametersWithIV keyParamWithIV = new ParametersWithIV(_keyParam, iv, 0, iv.Length);
byte[] decryptedBytesReworked;
lock (_decryptionLock)
{
_decryptionCipher.Init(false, keyParamWithIV);
var decryptedBytes = new byte[_decryptionCipher.GetOutputSize(byteArrayToDecrypt.Length)];
int length = _decryptionCipher.ProcessBytes(byteArrayToDecrypt, decryptedBytes, 0);
int newLength = _decryptionCipher.DoFinal(decryptedBytes, length); //Do the final block
// TODO - incorrect initial byte array length
decryptedBytesReworked = new byte[length + newLength];
Array.Copy(decryptedBytes, decryptedBytesReworked, decryptedBytesReworked.Length);
}
return decryptedBytesReworked;
}
Cheers.
I want to encrypt String with SessionKey. Below is sample code I am using, but I am not getting the correct encrypted answer.
string = test;
SessionKey = "ThisIsASecretKey";
For encryption, I am using the method below:
byte[] array = Encoding.ASCII.GetBytes("ThisIsASecretKey");
byte[] array1 = Encoding.ASCII.GetBytes("test");
byte[] reult = encryptUsingSessionKey(array, array1);
public byte[] encryptUsingSessionKey(byte[] skey,byte[] data)
{
Org.BouncyCastle.Crypto.Paddings.PaddedBufferedBlockCipher cipher = new Org.BouncyCastle.Crypto.Paddings.PaddedBufferedBlockCipher(new AesEngine(), new Pkcs7Padding());
cipher.Init(true, new Org.BouncyCastle.Crypto.Parameters.KeyParameter(skey));
int outputSize = cipher.GetOutputSize(data.Length);
byte[] tempOP = new byte[outputSize];
int processLen = cipher.ProcessBytes(data, 0, data.Length, tempOP, 0);
int outputLen = cipher.DoFinal(tempOP, processLen);
byte[] result = new byte[processLen + outputLen];
tempOP.CopyTo(result, 0);
// tempOP.CopyTo(tempOP,0,result,0,result.Length);
return result;
}
After encryption, I am getting
jZî|ðçê`u0aC
but the correct answer would be
ƒ_jZî|ðç_ê‹\`u0aC
I am getting exception of invalid length in time of decryption in AES algorithm in C#, while it's working fine in android code.
Android Code:
public byte[] decryp_decompress(byte[] raw, byte[] encrypted)
throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
GZip gzip = new GZip();
byte[] decompressData = gzip.decompresses(decrypted);
return decompressData;
}
C# Code:
public byte[] Decrypt(byte[] encryptedData, RijndaelManaged rijndaelManaged)
{
try
{
return rijndaelManaged.CreateDecryptor().TransformFinalBlock(encryptedData, 0, encryptedData.Length);
}
catch
{
throw;
}
}
And
public RijndaelManaged GetRijndaelManaged()
{
try
{
byte[] AESKey = new byte[] { **** };
var keyBytes = new byte[16];
var secretKeyBytes = AESKey;
Array.Copy(secretKeyBytes, keyBytes, Math.Min(keyBytes.Length, secretKeyBytes.Length));
return new RijndaelManaged
{
Mode = CipherMode.ECB,
Padding = PaddingMode.None,
KeySize = 128,
BlockSize = 128,
Key = keyBytes
};
}
catch
{
throw;
}
}
Getting exception in time of decryption of invalid length.
What could the issue be?
Edit:
I have made changes for decryption:
KeyParameter par = new KeyParameter(AESKey);
// SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
IBufferedCipher cipher = CipherUtilities.GetCipher("AES/ECB/NoPadding");
cipher.Init(true, par);
// Gives me "pad block corrupted" error
byte[] output = new byte[(encryptedData.Length)];
int len = cipher.ProcessBytes(encryptedData, 0, encryptedData.Length, output, 0);
cipher.DoFinal(output, encryptedData.Length);
// byte[] output = cipher.DoFinal(encryptedData);
return output;
but getting exception of data not block size aligned.
My data saved in little endian format . so any please help me where i need to update the code.