I am trying to encrypt and decrypt a stream (a PDF document at base), but I am having issues with this. When I try to open the document after decryption and download, I get the error Failed to load the PDF document.
Do you know why this might be happening?
Here is the code for encryption:
public EncryptResult EncryptStream(Stream dataStream, bool reuseIV = false)
{
RijndaelManaged crypto = new RijndaelManaged();
crypto.Key = _key;
if (!reuseIV || _iv == null)
{
// make a copy of the current IV
_iv = crypto.IV;
}
else
{
// reuse the previous IV
crypto.IV = _iv;
}
var result = new EncryptResult() { IV = crypto.IV };
using (var encryptor = crypto.CreateEncryptor())
{
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
var byteArrayInput = new byte[dataStream.Length];
dataStream.Read(byteArrayInput, 0, byteArrayInput.Length);
csEncrypt.Write(byteArrayInput, 0, byteArrayInput.Length);
dataStream.Close();
result.Cipher = msEncrypt.ToArray();
msEncrypt.Flush();
msEncrypt.Position = 0;
return result;
}
}
}
}
and decryption:
public Stream DecryptStream(byte[] cipher, byte[] iv)
{
RijndaelManaged crypto = new RijndaelManaged();
crypto.Key = _key;
crypto.IV = iv;
crypto.Padding = PaddingMode.Zeros;
using (var decryptor = crypto.CreateDecryptor())
{
using (MemoryStream msDecrypt = new MemoryStream(cipher))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
var sOutputFilename = new MemoryStream();
var fsDecrypted = new StreamWriter(sOutputFilename);
fsDecrypted.Write(new StreamReader(csDecrypt).ReadToEnd());
sOutputFilename.Position = 0;
return sOutputFilename;
}
}
}
}
Thanks in advance.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
//var byteArrayInput = new byte[dataStream.Length];
//dataStream.Read(byteArrayInput, 0, byteArrayInput.Length);
//csEncrypt.Write(byteArrayInput, 0, byteArrayInput.Length);
dataStream.CopyTo(csEncrypt);
dataStream.Close();
//result.Cipher = msEncrypt.ToArray(); // not here - not flushed yet
//msEncrypt.Flush(); // don't need this
//msEncrypt.Position = 0;
}
result.Cipher = msEncrypt.ToArray();
return result;
}
and in the decryptor, get rid of all the StreamReader/StreamWriter stuff.
A PDF file is compressed, ie binary. But this is after the decryption so it can't be your error.
using (var decryptor = crypto.CreateDecryptor())
{
using (MemoryStream msDecrypt = new MemoryStream(cipher))
{
var outputStream = new MemoryStream();
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
csDecrypt.CopyTo(outputStream );
}
outputStream .Position = 0;
return outputStream ;
}
}
One issue is, you are likely encrypting excess bytes at the end of your stream, you need to work out how many bytes are read or use
Stream.CopyTo Method
Reads the bytes from the current stream and writes them to another
stream.
Related
I'm trying to encrypt binary data with AES in .Net 6.0. Unfortunately, the decryption does not bring back the original data:
public static byte[] Encrypt(byte[] plainBytes)
{
using (var aes = System.Security.Cryptography.Aes.Create())
{
byte[] cipherBytes;
using (MemoryStream cipherStream = new MemoryStream())
using (CryptoStream cryptoStream = new CryptoStream(cipherStream, aes.CreateEncryptor(aes.Key, aes.IV), CryptoStreamMode.Write))
{
cryptoStream.Write(plainBytes, 0, plainBytes.Length);
cryptoStream.FlushFinalBlock();
cipherStream.Seek(0, SeekOrigin.Begin);
cipherBytes = new byte[cipherStream.Length];
cipherStream.Read(cipherBytes, 0, cipherBytes.Length);
}
byte[] wrongPlainBytes;
using (MemoryStream cipherStream = new MemoryStream(cipherBytes))
using (CryptoStream cryptoStream = new CryptoStream(cipherStream, aes.CreateDecryptor(aes.Key, aes.IV), CryptoStreamMode.Read))
{
wrongPlainBytes = cipherStream.ToArray();
}
bool shouldBeTrue = plainBytes.Equals(wrongPlainBytes);
return cipherBytes;
}
}
After executing this code, shouldBeTrue should be true, but it's false. Also, plainBytes.Length is different from wrongPlainBytes.Length.
What do I wrong while encryption / decryption?
public static byte[] Encrypt( byte[] plainBytes )
{
using ( var aes = System.Security.Cryptography.Aes.Create() )
{
byte[] cipherBytes;
using ( MemoryStream cipherStream = new MemoryStream() )
using ( CryptoStream cryptoStream = new CryptoStream( cipherStream, aes.CreateEncryptor( aes.Key, aes.IV ), CryptoStreamMode.Write ) )
{
cryptoStream.Write( plainBytes, 0, plainBytes.Length );
cryptoStream.FlushFinalBlock();
cipherBytes = cipherStream.ToArray();
}
byte[] wrongPlainBytes;
using ( MemoryStream cipherStream = new MemoryStream() )
using ( CryptoStream cryptoStream = new CryptoStream( cipherStream, aes.CreateDecryptor( aes.Key, aes.IV ), CryptoStreamMode.Write ) )
{
cryptoStream.Write( cipherBytes, 0, cipherBytes.Length );
cryptoStream.FlushFinalBlock();
wrongPlainBytes = cipherStream.ToArray();
}
bool shouldBeTrue = plainBytes.SequenceEqual( wrongPlainBytes );
return cipherBytes;
}
}
Try to avoid direct Read on streams where possible - this operation is not guaranteed to read the amount of bytes you requested, so when you do use it - you need to check the return value (which you ignore) to figure out actual number of bytes that were read, and then act accordingly (do more reads if that amount is less than what you need).
There are several issues in your code, which you can avoid this way:
public static byte[] Encrypt(byte[] plainBytes)
{
using (var aes = System.Security.Cryptography.Aes.Create())
{
byte[] cipherBytes;
using (MemoryStream cipherStream = new MemoryStream()) {
// use leaveOpen parameter here, that way it will NOT dispose cipherStream when closing cryptoStream
using (CryptoStream cryptoStream = new CryptoStream(cipherStream, aes.CreateEncryptor(aes.Key, aes.IV), CryptoStreamMode.Write, true)) {
// just write
cryptoStream.Write(plainBytes, 0, plainBytes.Length);
}
// now, cryptoStream is disposed, so we can be sure all data is propertly written to the cipherStream
// no need to flush or anything
// do not manage buffer yourself, use ToArray
cipherBytes = cipherStream.ToArray();
}
byte[] wrongPlainBytes;
using (MemoryStream cipherStream = new MemoryStream(cipherBytes)) {
using (CryptoStream cryptoStream = new CryptoStream(cipherStream, aes.CreateDecryptor(aes.Key, aes.IV), CryptoStreamMode.Read)) {
using (var output = new MemoryStream()) {
// same story here, avoid direct reads, use CopyTo, it will copy all data, decrypted, to memory stream
cryptoStream.CopyTo(output);
// again, just ToArray to avoid manage buffer directly
wrongPlainBytes = output.ToArray();
}
}
}
bool shouldBeTrue = plainBytes.SequenceEqual(wrongPlainBytes);
return cipherBytes;
}
}
I have problem with reading from encrypted files. have 2 sources of files, one is Rapsberry PI (Java), the second is desktop appliaction (.Net 4.6.1).
When I'm creating a encrypted file on my device and then want to read it in my desktop app, there is no problem with decrypting and encoding to string.
In the same app I create the same file, adding next element to list in the file on each 5s. When I read the file in my app, I got Unexpected end when reading json. path error.
I have checked models and they are both the same and valid.
For encryption/decryption I'm using AES algorithm.
There is my code from my desktop app:
Writing data:
Aes myAes = Aes.Create();
myAes.Key = Encoding.ASCII.GetBytes("abcde");
byte[] roundtrip = EncryptStringToBytes_Aes(output, myAes.Key, myAes.IV);
DirectoryInfo info = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
string pathSave =info + "\\test.plkx";
using (FileStream sourceStream = new FileStream (pathSave , FileMode.Append))
{
sourceStream.Write(roundtrip, 0, roundtrip.Length);
};
The method byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
aesAlg.Mode = CipherMode.ECB;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// 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();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
There is my reader:
byte[] data = null;
data = new byte[fileStream.Length];
fileStream.Position = 0;
fileStream.Read(data, 0, (int)fileStream.Length);
Aes myAes = Aes.Create();
myAes.Key = Encoding.ASCII.GetBytes("abcde");
byte[] roundtrip = DecryptStringFromBytes_Aes(data, myAes.Key, myAes.IV);
return new MemoryStream(roundtrip);
And byte[] DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV) method:
byte[] plaintext = null;
using (Aes aesAlg = Aes.Create())
{
aesAlg.Mode = CipherMode.ECB;
aesAlg.Key = Key;
aesAlg.IV = IV;
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msDecrypt = new MemoryStream())
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write))
{
csDecrypt.Write(cipherText, 0, cipherText.Length);
}
plaintext = msDecrypt.ToArray();
var ads = ASCIIEncoding.ASCII.GetString(plaintext);
//There I got exception
var ab = DeserializeStream(new MemoryStream(plaintext));
}
}
return plaintext;
DeserializeStream method:
public RecorderValueTest DeserializeStream(MemoryStream stream)
{
RecorderValueTest r = new RecorderValueTest();
using (BsonDataReader reader = new BsonDataReader(stream))
{
JsonSerializer serializer = new JsonSerializer();
r = serializer.Deserialize<RecorderValueTest>(reader);
}
return r;
}
edit
I have tried this:
xxx = new xxx
{
data = listRecord,
};
string output = JsonConvert.SerializeObject(plkx);
try
{
using (BsonDataReader reader = new BsonDataReader(new MemoryStream(Encoding.UTF8.GetBytes(output))))
{
JsonSerializer serializer = new JsonSerializer();
var r = serializer.Deserialize<xxx>(reader);
}
} catch (Exception exc)
{ }
and still get the same error while deserialization. JsonConvert.DeserializeObject<xxx>(plkx); also didn't work.
When encrypting and decrypting text files, the code works fine.
But when encrypting binary and zip files, file size almost doubled after decryption. For example, a 2.06mb bin.exe became 3.69mb after decryption; a 4mb zip file became 7+mb after decryption.
Is this because of the PaddingMode? What PaddingMode should I set to work with all types of files? How to solve this problem?
private async Task RunEncrypt(string srcfile)
{
string data;
using (StreamReader sr = new StreamReader(srcfile))
{
data = sr.ReadToEnd();
}
byte[] enc_data = await Program.myEncrypt(data);
}
static async Task<byte[]> myEncrypt(string toEncStr)
{
byte[] encrypted;
using (Aes encaes = Aes.Create())
{
try
{
//store key to key.txt
FileStream fs = new FileStream("key.txt", FileMode.OpenOrCreate);
fs.Write(encaes.Key, 0, encaes.Key.Length);
fs.Write(encaes.IV, 0, encaes.IV.Length);
fs.Close();
}
catch ( Exception e)
{
Console.WriteLine("Recording encryption keys failed!{0}.", e.Message);
Environment.Exit(0);
}
encaes.Padding = PaddingMode.PKCS7;
ICryptoTransform encryptor = encaes.CreateEncryptor(encaes.Key, encaes.IV);
try
{
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(toEncStr);
}
}
encrypted = msEncrypt.ToArray();
}
return encrypted;
}
catch (Exception e)
{
Console.WriteLine("Encryption failed!{0}.", e.Message);
return Encoding.UTF8.GetBytes("null");
}
}
}
private async Task RunDecrypt(byte[] inbytes)
{
try
{
string write_data = await myDecrypt(inbytes);
using (StreamWriter sw = new StreamWriter("test.exe", true))
{
sw.Write(write_data);
sw.Close();
}
}
catch (Exception e)
{
}
}
async Task<string> myDecrypt(byte[] toDecBytes)
{
try
{
string decrypted;
using (Aes dec = Aes.Create())
{
byte[] Key = new byte[dec.Key.Length];
byte[] IV = new byte[dec.IV.Length];
//read key from key.txt
FileStream fsread = new FileStream("key.txt", FileMode.Open);
fsread.Read(Key, 0, Key.Length);
fsread.Read(IV, 0, IV.Length);
fsread.Close();
dec.Key = Key;
dec.IV = IV;
dec.Padding = PaddingMode.PKCS7;
ICryptoTransform decryptor = dec.CreateDecryptor(dec.Key, dec.IV);
using (MemoryStream msDecrypt = new MemoryStream(toDecBytes))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
decrypted = srDecrypt.ReadToEnd();
return decrypted;
}
}
}
}
}
catch (Exception e)
{
Console.WriteLine("Decryption failed! {0}.", e.Message);
return #"null";
}
}
By using a StreamReader and StreamWriter, you are treating the file content as text. To support binary files, read/write directly from/to the source/target stream instead.
I have a Security class that Encode and Decode string, but when I try do decode - something going wrong.
Here is my Security class:
class Security
{
public static String encrypt(String imput, String key)
{
String cipherText;
var rijndael = new RijndaelManaged()
{
Key = Encoding.Unicode.GetBytes(key),
Mode = CipherMode.ECB,
BlockSize = 128,
Padding = PaddingMode.Zeros,
};
ICryptoTransform encryptor = rijndael.CreateEncryptor(rijndael.Key, null);
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
using (var streamWriter = new StreamWriter(cryptoStream))
{
streamWriter.Write(imput);
streamWriter.Flush();
}
cipherText = Convert.ToBase64String(memoryStream.ToArray());
}
}
return cipherText;
}
public static String decrypt(String imput, String key)
{
byte[] data = Convert.FromBase64String(imput);
String decrypted;
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Encoding.UTF8.GetBytes(key);
rijAlg.Mode = CipherMode.ECB;
rijAlg.BlockSize = 128;
rijAlg.Padding = PaddingMode.Zeros;
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, null);
using (MemoryStream msDecrypt = new MemoryStream(data))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
decrypted = srDecrypt.ReadToEnd();
}
}
}
}
return decrypted;
}
}
In program.cs:
String A = Security.encrypt("text", "1234567891234567");
A = Security.decrypt(A, "1234567891234567");
Finaly it return something like that: �%����;\0�\a��f6 , but I need original string. Where I made a mistake?
Use the same encoding in both methods, either Encoding.Unicode or Encoding.UTF8
I got a problem with decrypting using CryptoStream to MemoryStream. When I decrypt a data that converted from a string to byte[] it decrypts normally, but when I pass an image data to the decryptor it returns x00 filled stream with the same length as the image data. Btw, no exception thrown.
private SymmetricAlgorithm crypto;
...
public byte[] Encrypt(byte[] Stream)
{
ICryptoTransform encryptor = crypto.CreateEncryptor();
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
int written = 0;
int length = Stream.Length;
int blockSize = crypto.BlockSize / 8;
while (written < length)
{
int toWrite = Math.Min(blockSize, length - written);
if ((length - written) > blockSize)
{
csEncrypt.Write(Stream, written, blockSize);
written += blockSize;
csEncrypt.Flush();
}
else
{
csEncrypt.Write(Stream, written, toWrite);
written += toWrite;
csEncrypt.FlushFinalBlock();
}
}
resBytes = msEncrypt.ToArray();
}
}
return resBytes;
}
...
public byte[] Decrypt(byte[] Stream)
{
ICryptoTransform decryptor = crypto.CreateDecryptor();
MemoryStream decrypted = new MemoryStream();
using (MemoryStream msDecrypt = new MemoryStream(Stream))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
csDecrypt.CopyTo(decrypted, crypto.BlockSize);
resBytes = decrypted.ToArray();
decrypted.Dispose();
resBytes = decrypted.ToArray();
return resBytes;
}
}
}