I use AES encryption. It's ok when I encrypt and then decrypt whole file. I want to add multiple files to one encrypted. That's where the problem is. Encryption is fine, but decryption causes CryptographicException - bad data length. Is it even possible to decrypt part of file or is it encrypted as whole ? I used one cryptostream and passed there all files I want to encrypt to single file. I am trying to do opposite:
AesManaged aes = AES.InitAes(key, salt);
ICryptoTransform transform = aes.CreateDecryptor(aes.Key, aes.IV);
int defChunkSize = 1024 * 1024 * 50;
using (FileStream source = new FileStream(header.data.filename, FileMode.Open))
{
foreach (CryptHeader.fileStruct file in header.data.files)
{
preparePath(file.filename);
using (FileStream target = new FileStream(file.filename, FileMode.Create))
{
using (CryptoStream cryptoStream = new CryptoStream(target, transform, CryptoStreamMode.Write))
{
long padding = source.Length - header.data.files.Sum(x => x.length);//Just test
int chunkSize = (defChunkSize > (int)file.length) ? (int)file.length : defChunkSize;
byte[] chunkData = new byte[chunkSize];
int bytesRead = 0;
int totalRead = 0;
while (totalRead < file.length)
{
bytesRead = source.Read(chunkData, 0, chunkSize);
if (bytesRead <= 0) break;
totalRead += bytesRead;
cryptoStream.Write(chunkData, 0, bytesRead);
}
chunkData = null;
}
}
}
}
I've done the same few years ago without any problem. The logic I used is the following:
Encryption
define number of files
define array for keeping encrypted sizes
open output stream
seek (forced) to (number of files * 4) + 4 (assuming lengths are integers)
loop for encryption (encrypt- write encrypted data -assigned encrypted size)
seek to 0 (begin)
write number of files
write encrypted size array
close output stream
Decryption
open input stream
read number of files
define-read-fill array with encrypted sizes
loop for decryption (read using known sizes)
close output stream
I hope that this helps.
Short form: "you can't get there from here", it's impossible.
If you look at the description of how AES works you'll see two things.
1 AES uses a block size of 128 bits, so if your files aren't multiples of 8 bytes in length the blocks in the appended part won't line up,
2: AES use uses different keys for each block according to the rijndael key schedule, this is a likely going to be a deal breaker.
If you need to be able to concatenate encrypted files either wrap them so that the joins are visible and the fragments can be individually decrypted (gzip does this when compressing) or use a fixed substitution cypher like rot13
It can be done and it's working. I make table during encryption containing lengths of encrypted files. Than I decrypt exact parts (with padding).
Related
I was working a while ago with AHK (Autohotkey) and I found the crypt.ahk is handy in encrypting/decrypting files. I also decided to use an AES256 with SHA512 and a "Password".
Here you can see a short code from the crypto.ahk file:
if !dllCall("Advapi32\CryptAcquireContextW"
,"Ptr*",hCryptProv ; pointer to a handle of a CSP
,"Uint",0 ; key container name
,"Uint",0 ; 0 = default provider
,"Uint",c.PROV_RSA_AES ; AES type of provider to acquire => 24
,"UInt",c.CRYPT_VERIFYCONTEXT) ; 0xF0000000 <= This option is intended for applications that are using ephemeral keys, or applications that do not require access to persisted private keys, such as applications that perform only hashing, encryption, and digital signature verification.
if !dllCall("Advapi32\CryptCreateHash"
,"Ptr",hCryptProv ; handle to a CSP created by a call to CryptAcquireContext
,"Uint",CUR_PWD_HASH_ALG ; HASH algorithm to use ALG_ID: HashAlg==6 || HashAlg = "SHA512" ?c.CALG_SHA_512
; CALG_SHA_512 := (CryptConst.ALG_CLASS_HASH | CryptConst.ALG_TYPE_ANY | CryptConst.ALG_SID_SHA_512)
; 4<<13 = hex 8000 | 0 | 14 dec => 0x800E = 512 bit sha hashing algorithm
,"Uint",0 ; MAC / HMAC --> For nonkeyed algorithms, this parameter must be set to zero.
,"Uint",0 ; this flag is not used
,"Ptr*",hHash ) ; The address to which the function copies a handle to the new hash object.
;hashing password
passLen := StrPutVar(password, passBuf,0,this.PassEncoding)
if !dllCall("Advapi32\CryptHashData"
,"Ptr",hHash ; Handle of the hash object.
,"Ptr",&passBuf ; "thisIsMyPassword" in Unicode - pointer to a buffer that contains the data to be added to the hash object.
,"Uint",passLen ; 34 - Number of bytes of data to be added.
,"Uint",0 ) ; no flags are used
;getting encryption key from password
if !dllCall("Advapi32\CryptDeriveKey"
,"Ptr",hCryptProv ; A HCRYPTPROV handle of a CSP created by a call to CryptAcquireContext.
,"Uint",CUR_ENC_ALG ; An ALG_ID structure that identifies the symmetric encryption algorithm for which the key is to be generated
,"Ptr",hHash ; A handle to a hash object that has been fed the exact base data.
,"Uint",KEY_LENGHT ; 256 keylength - Specifies the type of key generated. lower 16 bit == 0 -> no flags set upper 16 bit => 256
,"Ptr*",hKey ) ; A pointer to a HCRYPTKEY variable to receive the address of the handle of the newly generated key.
if !dllCall("Advapi32\CryptGetKeyParam"
,"Ptr",hKey ; The handle of the key being queried.
,"Uint",c.KP_BLOCKLEN ; Specifies the type of query being made. For all key types, this parameter can contain one of the following values. If a session key is specified by the hKey parameter, retrieve the block length of the key cipher. The pbData parameter is a pointer to a DWORD value that receives the block length, in bits. For stream ciphers, this value is always zero.
,"Uint*",BlockLen ; A pointer to a buffer that receives the data. The form of this data depends on the value of dwParam.
,"Uint*",dwCount := 4 ; A pointer to a DWORD value that, on entry, contains the size, in bytes, of the buffer pointed to by the pbData parameter. When the function returns, the DWORD value contains the number of bytes stored in the buffer.
,"Uint",0) ; nothing
{foo := "CryptGetKeyParam", err := GetLastError(), err2 := ErrorLevel
GoTO FINITA_LA_COMEDIA
}
if !dllCall(CryptEnc
,"Ptr",hKey ;key ; A handle to the encryption key.
,"Ptr",0 ;hash ; If no hash is to be done, this parameter must be NULL.???
,"Uint",isFinal ;final ; A Boolean value that specifies whether this is the last section in a series being encrypted. Final is set to TRUE for the last or only block
,"Uint",0 ;dwFlags ; no flags
,"Ptr",&ReadBuf ;pbdata ; A pointer to a buffer that contains the plaintext to be encrypted. The plaintext in this buffer is overwritten with the ciphertext created by this function.
,"Uint*",BytesRead ;dwsize ; A pointer to a DWORD value that , on entry, contains the length, in bytes, of the plaintext in the pbData buffer. On exit, this DWORD contains the length, in bytes, of the ciphertext written to the pbData buffer.
,"Uint",ReadBufSize+BlockLen ) ;dwbuf ; Specifies the total size, in bytes, of the input pbData buffer. Note that, depending on the algorithm used, the encrypted text can be larger than the original plaintext. In this case, the pbData buffer needs to be large enough to contain the encrypted text and any padding.
I worked along with many Microsoft Docs and I could say, I have a little understanding. At least, the self-written comments (after the ;) seem to be okay.
create a handle for a CSP with AES
hash object gets the CALG_SHA_512
hash the Password "thisIsMyPassword"
put the hashing and aes together
crypt the file
Now I want to write a C# using System.Security.Cryptography; Programm.
But I can't manage to decrypt the file with AES or RijndaelManaged, because I can't get the SHA512 into my AES256. I always get an error stating that the Size is too big.
I tried it with .Net5.0 and .Net4.8 but no luck.
public static void FileDecrypt(string inputFile, string outputFile, string password)
{
// https://foxlearn.com/windows-forms/how-to-encrypt-and-decrypt-files-using-aes-encryption-algorithm-in-csharp-396.html
using (FileStream fsCrypt = new FileStream(inputFile, FileMode.Open))
{
byte[] passwordBytes = System.Text.Encoding.Unicode.GetBytes(password);
byte[] aesKey = SHA512.Create().ComputeHash(passwordBytes);
byte[] aesIV = { 0 }; // iV is zero
Aes trueAES = Aes.Create();
trueAES.BlockSize = 256;
trueAES.KeySize = 512;
trueAES.Mode = CipherMode.CBC;
trueAES.Padding = PaddingMode.None;
using (CryptoStream cryptoStream = new CryptoStream(fsCrypt, trueAES.CreateDecryptor(), CryptoStreamMode.Read))
{
using (FileStream fsOut = new FileStream(outputFile, FileMode.Create))
{
int read;
byte[] buffer = new byte[10240]; // [1048576];
while ((read = cryptoStream.Read(buffer, 0, buffer.Length)) > 0)
{
fsOut.Write(buffer, 0, read);
}
}
}
I already have found a solution on how to translate the advapi32.dll to C#, but the crypto stuff seems to be deprecated. See MS Site:
This API is deprecated. New and existing software should start using
Cryptography Next Generation APIs. Microsoft may remove this API in
future releases.
Could someone help me out with my code?
Update and solution - 05/09/22
Thanks to Richard I was able to decrypt in c# the previous encrypted advapi32.dll file. My code is now:
public static void FileDecrypt(string inputFile, string outputFile, string password)
{
// https://foxlearn.com/windows-forms/how-to-encrypt-and-decrypt-files-using-aes-encryption-algorithm-in-csharp-396.html
using (FileStream fsCrypt = new FileStream(inputFile, FileMode.Open))
{
byte[] passwordBytes = System.Text.Encoding.Unicode.GetBytes(password);
byte[] aesKey = SHA512.Create().ComputeHash(passwordBytes);
// shorten the 512 bit (64 byte) to 256bit (32 byte)
// just take the first 32 byte of the sha512 key
byte[] shortingAesKey = aesKey.Take(32).ToArray();
trueAES.Key = shortingAesKey;
byte[] aesIV = { 0 }; // iV is zero
Aes trueAES = Aes.Create();
trueAES.BlockSize = 256;
trueAES.KeySize = 512;
trueAES.Mode = CipherMode.CBC;
trueAES.Padding = PaddingMode.None;
using (CryptoStream cryptoStream = new CryptoStream(fsCrypt, trueAES.CreateDecryptor(), CryptoStreamMode.Read))
{
using (FileStream fsOut = new FileStream(outputFile, FileMode.Create))
{
int read;
byte[] buffer = new byte[10240]; // [1048576];
while ((read = cryptoStream.Read(buffer, 0, buffer.Length)) > 0)
{
fsOut.Write(buffer, 0, read);
}
}
}
Hi i am using c# code and trying to encrypt and decrypt a CSV file
which is having lacks of record and 24 columns.
Encypting it by 4 mb chunk by chunk and generated the salt by random. How do i fix this issue.
encrypting the csv files chunk by chunk and decypting it back.I was getting wrong result in the resulting csv. some junk chars were getting after each chunk.
Thanks in Advance.
You write the salt one time at the beginning of the stream, but read it for each chunk.
Change your encrpt method like this:
....
MemoryStream mstream = null;
try {
while ((bytesRead = Infs.Read(chunk, 0, chunkSizeInBytes)) > 0) {
mstream = new MemoryStream();
mstream.Write(salt, 0, salt.Length);
using (var cryptoStream = new CryptoStream(mstream, aesProvider.CreateEncryptor(), CryptoStreamMode.Write)) {
....
or remove the fsCrypt.Read(salt, 0, salt.Length); inside the while loop of the Decrypt method.
I am downloading a binary file from a server and accessing it partially while the download progresses. I'd like to encrypt the file prior to uploading, and decrypt its content as my program receives it.
The file arrives in byte chunks of random size using the code below, so I think I need a method which acts on individual bytes, or at least a fixed number of bytes, and keeping the overall file size intact.
private void DownloadFile()
{
WebClient client = new WebClient();
Stream stream = client.OpenRead(address);
byte[] readBuffer = new byte[139043]; // File size known ahead of time
int totalBytesRead = 0;
int bytesRead;
int i = 0;
while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
{
File.WriteAllBytes("file_" + i + ".ext", readBuffer); // Save partially downloaded file
totalBytesRead += bytesRead;
i++;
}
}
Solution:
I opted for the simple XOR algorithm shown in my answer below. It works on individual bytes and considering I can generate a unique key for each file, I am comfortable with the level of protection.
You would want to look into AES CTR encryption. There is a related SO question: Can I use AES in CTR mode in .NET?
There is an answer there that points to an MSDN article about implementing that encryption in your applications: https://msdn.microsoft.com/en-us/library/system.security.cryptography.aesmanaged.aesmanaged%28v=vs.90%29.aspx#2
I have written the following XOR based algorithm which I believe sufficient for my purposes.
The password and salt can be derived from miscellaneous information about the file (eg. its size or unique file name) and as a consequence, no key will be ever used more than once.
All criticism and hate is welcome.
public void Crypt(byte[] data, string filepath)
{
// Define password and salt
byte[] pwd = GetBytes("PASSWORD");
byte[] salt = GetBytes("SALT");
// Generate PasswordDeriveBytes from the password and salt.
PasswordDeriveBytes pdb = new PasswordDeriveBytes(pwd, salt);
// Generate key from PasswordDeriveBytes
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
tdes.Key = pdb.CryptDeriveKey("TripleDES", "SHA1", 192, tdes.IV);
// Encrypt/Decrypt
for(int i = 0; i < data.Length; i++)
{
data[i] = (byte)(data[i] ^ tdes.Key[i % tdes.Key.Length]);
}
// Save File
File.WriteAllBytes(filepath, data);
}
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
I know there is plenty of this questions with answers around but I have spent hours and hours googleing and have tried all suggestions that I have found.
I download a file and I want to store it encrypted in the isolated storage.
This is how I store it:
using (var fs = new IsolatedStorageFileStream(fileName, FileMode.Create, store))
{
byte[] bytesInStream = new byte[args.Result.Length];
args.Result.Read(bytesInStream, 0, bytesInStream.Length);
var aes = new AesManaged
{
Key = GetBytes("aaaaaaaa"),
IV = GetBytes("bbbbbbbb")
};
byte[] encryptedArray;
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cryptoStream.Write(bytesInStream, 0, bytesInStream.Length);
cryptoStream.FlushFinalBlock();
encryptedArray = memoryStream.ToArray();
}
}
fs.Write(encryptedArray, 0, encryptedArray.Length);
fs.Flush();
}
The following code is for reading the file from isolated storage and decrypt it:
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
if (store.FileExists(fileName))
{
var file = store.OpenFile(fileName, FileMode.Open,FileAccess.Read,FileShare.Read);
var reader = new BinaryReader(file);
var aes = new AesManaged
{
Key = GetBytes("aaaaaaaa"),
IV = GetBytes("bbbbbbbb")
};
byte[] decodedContent;
byte[] encodedContent = reader.ReadBytes(1280);
using (MemoryStream ms = new MemoryStream(encodedAudio))
{
using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read))
{
BinaryReader r= new BinaryReader(cs);
decodedContent= r.ReadBytes(encodedContent.Length);
}
}
}
When the program reaches this line: decodedContent= r.ReadBytes(encodedContent.Length); I get CryptographicException with the following message: Padding is invalid and cannot be removed.
Can anyone help me with this issue?
You can't decrypt by blocks because silverlight AesManaged always include padding and if you read only a part of encrypted block - decryption class can't find the padding that should be removed. You can decrypt whole data only. Alternatively, you can split the data manually in the encryption part of your algorithm. Hans Passant told you the same, just in a short form ;)
You can decrypt 1,280 bytes at a time, if you decrypt without removing padding until the last block. That is, you have to set the decryptor to not remove padding (i.e. "no padding") on all but the last block.
I.e. when there are N blocks:
for blocks 1 to N-1: decrypt(1280 bytes, no padding)
for block N: decrypt(however many bytes left, padded)
You can also run the entire decryption with "no padding" and strip the padding yourself. The last byte will give you the number of bytes to clip from the end, from 1 to 16.
If you cannot turn off padding-removal on decryption (implied by your comment above), you can still decrypt 1,280 bytes at a time. Just encrypt the blocks individually. They will each get padding, and make it fit 1,280. For example, encrypt 1,279 bytes at a time (each block is given a 1 byte pad.) Encrypting the full 1,280 with padding will give you 1,296 bytes (a multiple of 16 bytes of plaintext is going to get a full 16-byte pad.)
Edit, for the interested:
If you find yourself with a large ciphertext, and you want to decrypt it in blocks, and for some reason your decryption is constrained such that you are forced to use padding mode PKCS#7, you can still decrypt the data a block at a time. It's a bit more expensive -- it will cost you an extra encryption of 16 bytes per block, but at least it is possible.
Take each block of raw ciphertext, and encrypt a little tail of 16 bytes for each block which is correctly padded for that block. Then decrypt the enlarged block, and finally, remove the extra bit of data used to encrypt the padding tail.
You just take the last 16 bytes of the block as your IV, and using the same key, encrypt some small data -- for example a single byte -- padded with PKCS#7. Attach the 16 byte result of the encryption to the block and now decrypt the block + 16 bytes. The padding is removed from the tail 16 bytes, and you can remove the bit of data, and end up with the original block of plaintext. See more in my answer here