C# with AES256 and SHA512 file encryption/decrption

1.6k Views Asked by At

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.

  1. create a handle for a CSP with AES
  2. hash object gets the CALG_SHA_512
  3. hash the Password "thisIsMyPassword"
  4. put the hashing and aes together
  5. 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);
                        }
                    }
                }
0

There are 0 best solutions below