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.
Related
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 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#?
I have an iOS app that sends encrypted data that is later decrypted in C#. I have checked that the hex key and data received is same, but I still get Bad PKCS7 padding. Invalid length 0.
my Objective-C call is
+(NSData*) encryptData: (NSData*) data
key: (NSString*) key
{
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [data length];
//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[data bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer); //free the buffer;
return nil;
}
my keysize is 256, blocksize is 128, padding is pkcs7, IV is null, mode is CBC (default).
My C# code to decrypt is
using (MemoryStream memoryStream = new MemoryStream(outputBytes))
{
AesManaged algo = GetCryptoAlgorithm(GetRawBrokerKey());
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, algo.CreateDecryptor(), CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(cryptoStream))
{
plaintext = srDecrypt.ReadToEnd();
}
}
}
private static AesManaged GetCryptoAlgorithm()
{
return GetCryptoAlgorithm(null);
}
private static AesManaged GetCryptoAlgorithm(byte[] key)
{
AesManaged algorithm = new AesManaged();
//set the mode, padding and block size
algorithm.Padding = PaddingMode.PKCS7;
algorithm.Mode = CipherMode.CBC;
algorithm.KeySize = 256;
algorithm.BlockSize = 128;
if (key != null)
{
algorithm.Key = key;
}
algorithm.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
return algorithm;
}
I tried encryption using C# and see a different hex output using the same key.
c# encryption - 42AC7494606333309287768F47DFB35B
static byte[] EncryptStringToBytes_Aes(string plainText, byte[] key)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
byte[] encrypted;
AesManaged algorithm = new AesManaged();
//set the mode, padding and block size
algorithm.Padding = PaddingMode.PKCS7;
algorithm.Mode = CipherMode.CBC;
algorithm.KeySize = 256;
algorithm.BlockSize = 128;
if (key != null)
{
algorithm.Key = key;
}
algorithm.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = algorithm.CreateEncryptor();
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
string hex = BitConverter.ToString(encrypted);
Console.WriteLine("c# encryption - " + hex.Replace("-", ""));
// Return the encrypted bytes from the memory stream.
return encrypted;
}
Any ideas what could be going wrong? I seem to be following all the online advice around defaults for mode and IV, I think.
in GetCryptoAlgorithm, you need to pass the private key to GetCryptoAlgorithm.
I have a customer encrypting a string in PHP with the following code:
$password = 'Ty63rs4aVqcnh2vUqRJTbNT26caRZJ';
$method = 'AES-256-CBC';
texteACrypter = 'Whether you think you can, or you think you can\'t--you\'re right. - Henry Ford';
$encrypted = openssl_encrypt($texteACrypter, $method, $password);
which results in this encrypted output: MzVWX4tH4yZWc/w75zUagUMEsP34ywSYISsIIS9fj0W3Q/lR0hBrHmdvMOt106PlKhN/1zXFBPbyKmI6nWC5BN54GuGFSjkxfuansJkfoi0=
When I try to decrypt that string in C# it gives me a bunch of junk like so: Z�o�}'*2��I4y�J6S��
��xz���{9^�ED�fF
�}��گs�)�Q���i��$)�
I have tried changing the padding, using AesManaged instead of RijndaelManaged, changing the keysize, using a different key, etc. All result in either different junk strings or various exceptions. I must be missing something really basic here but I'm not sure what else to try at this point.
Here is my decryption code (that I shamelessly copied from another stackoverflow question: openssl using only .NET classes)
class Program
{
//https://stackoverflow.com/questions/5452422/openssl-using-only-net-classes
static void Main(string[] args)
{
var secret = "Ty63rs4aVqcnh2vUqRJTbNT26caRZJ";
var encrypted = "MzVWX4tH4yZWc/w75zUagUMEsP34ywSYISsIIS9fj0W3Q/lR0hBrHmdvMOt106PlKhN/1zXFBPbyKmI6nWC5BN54GuGFSjkxfuansJkfoi0=";
var yeah = OpenSSLDecrypt(encrypted, secret);
Console.WriteLine(yeah);
Console.ReadKey();
}
public static string OpenSSLDecrypt(string encrypted, string passphrase)
{
// base 64 decode
byte[] encryptedBytesWithSalt = Convert.FromBase64String(encrypted);
// extract salt (first 8 bytes of encrypted)
byte[] salt = new byte[8];
byte[] encryptedBytes = new byte[encryptedBytesWithSalt.Length - salt.Length - 8];
Buffer.BlockCopy(encryptedBytesWithSalt, 8, salt, 0, salt.Length);
Buffer.BlockCopy(encryptedBytesWithSalt, salt.Length + 8, encryptedBytes, 0, encryptedBytes.Length);
// get key and iv
byte[] key, iv;
DeriveKeyAndIV(passphrase, salt, out key, out iv);
return DecryptStringFromBytesAes(encryptedBytes, key, iv);
}
private static void DeriveKeyAndIV(string passphrase, byte[] salt, out byte[] key, out byte[] iv)
{
// generate key and iv
List<byte> concatenatedHashes = new List<byte>(48);
byte[] password = Encoding.UTF8.GetBytes(passphrase);
byte[] currentHash = new byte[0];
MD5 md5 = MD5.Create();
bool enoughBytesForKey = false;
// See http://www.openssl.org/docs/crypto/EVP_BytesToKey.html#KEY_DERIVATION_ALGORITHM
while (!enoughBytesForKey)
{
int preHashLength = currentHash.Length + password.Length + salt.Length;
byte[] preHash = new byte[preHashLength];
Buffer.BlockCopy(currentHash, 0, preHash, 0, currentHash.Length);
Buffer.BlockCopy(password, 0, preHash, currentHash.Length, password.Length);
Buffer.BlockCopy(salt, 0, preHash, currentHash.Length + password.Length, salt.Length);
currentHash = md5.ComputeHash(preHash);
concatenatedHashes.AddRange(currentHash);
if (concatenatedHashes.Count >= 48)
enoughBytesForKey = true;
}
key = new byte[32];
iv = new byte[16];
concatenatedHashes.CopyTo(0, key, 0, 32);
concatenatedHashes.CopyTo(32, iv, 0, 16);
md5.Clear();
}
static string DecryptStringFromBytesAes(byte[] cipherText, byte[] key, byte[] iv)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (key == null || key.Length <= 0)
throw new ArgumentNullException("key");
if (iv == null || iv.Length <= 0)
throw new ArgumentNullException("iv");
// Declare the RijndaelManaged object
// used to decrypt the data.
RijndaelManaged aesAlg = null;
// Declare the string used to hold
// the decrypted text.
string plaintext;
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged { Mode = CipherMode.CBC, Padding = PaddingMode.None, KeySize = 256, BlockSize = 128, Key = key, IV = iv };
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
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();
srDecrypt.Close();
}
}
}
return plaintext;
}
}
Well this was fun to work out and required jumping into the PHP source code with some interesting results. Firstly PHP doesn't even use a key derivation algorithm it just takes the bytes of the passphrase and pads it out with zero's to the required length. That means the entire DeriveKeyAndIV method isn't necessary.
Because of the above that means the IV that is being used is a 16 length byte array containing zeros.
Finally the only other thing wrong with your code is that the source you copied it from used a salt in their implementation of encrypt which then had to be removed, PHP nor you are doing this so removing the salt bytes is incorrect.
So the all of this put together means you need to change the OpenSSLDecrypt method to this.
public static string OpenSSLDecrypt(string encrypted, string passphrase)
{
//get the key bytes (not sure if UTF8 or ASCII should be used here doesn't matter if no extended chars in passphrase)
var key = Encoding.UTF8.GetBytes(passphrase);
//pad key out to 32 bytes (256bits) if its too short
if (key.Length < 32)
{
var paddedkey = new byte[32];
Buffer.BlockCopy(key, 0, paddedkey, 0, key.Length);
key = paddedkey;
}
//setup an empty iv
var iv = new byte[16];
//get the encrypted data and decrypt
byte[] encryptedBytes = Convert.FromBase64String(encrypted);
return DecryptStringFromBytesAes(encryptedBytes, key, iv);
}
And very finally the resulting string has some extra chars at the end namely a set of 3 of the ETX char but these should be easy enough to filter out. I actually can't figure out where these are coming from.
Thanks to #neubert for pointing out the padding is a part of the standard PKCS padding if you want the framework to remove this just specify that as the padding mode when instantiating the RijndaelManaged object.
new RijndaelManaged { Padding = PaddingMode.PKCS7 };
Swiftly following on from my last TripleDES-related question, here's my TripleDES wrapper code, which is used to encrypt and decrypt integers:
public static class Crypto {
private static Byte[] _fixedIv = new Byte[] { /* 8 random bytes, const */ };
private static TripleDES _tripleDes;
private static Byte[] _key;
static Crypto() {
_tripleDes = TripleDES.Create();
_tripleDes.Mode = CipherMode.CFB;
String key = ConfigurationManager.AppSettings["cryptoKeyId"];
_key = Convert.FromBase64String( key );
}
/// <summary>Encrypts the specified integer using the configuration-stored key.</summary>
public static String EncryptID(Int32 id) {
Byte[] input = new Byte[8]; // 64-bit block size
Byte[] inputLo = BitConverter.GetBytes( id );
for(int i=0;i<inputLo.Length;i++) input[i] = inputLo[i];
ICryptoTransform tr = _tripleDes.CreateEncryptor( _key, _fixedIv );
Byte[] output = new Byte[8];
tr.TransformBlock( input, 0, input.Length, output, 0 );
return Convert.ToBase64String( output );
}
/// <summary>Decrypts the specified string (storing an integer) using the configuration-stored key.</summary>
public static Int32 DecryptID(String s) {
Byte[] ciphertext = Convert.FromBase64String(s);
ICryptoTransform tr = _tripleDes.CreateDecryptor( _key, _fixedIv );
Byte[] output = new Byte[8];
tr.TransformBlock( ciphertext, 0, ciphertext.Length, output, 0 );
Byte[] outputLo = new Byte[4] { output[0], output[1], output[2], output[3] };
return BitConverter.ToInt32( outputLo, 0 );
}
}
When I run it, I get deterministic results for each input to EncryptID, but every call to DecryptID returns zero. I stepped into the code and say that the contents of the 'output' array are all zero (and tr.TransformBlock returns zero). Does anyone know what I'm doing wrong?
I tried calling tr.TransformFinalBlock but I got an exception:
'tr.TransformFinalBlock( ciphertext, 0, ciphertext.Length )' threw an exception of type System.Security.Cryptography.CryptographicException' base {System.SystemException}: {"Bad Data.\r\n"}
It turns out that for single blocks I should be using TransformFinalBlock for both encryption and decryption.
I also had to set algo.Padding to None to ensure that 8 bytes of clear is transformed into 8 bytes of ciphertext.