Search code examples
c#powershellcryptography

How does Microsoft add the initialization vector to a secure string in AES cryptography for the ConvertTo-SecureString method in Powershell?


I have several encrypted files crypted with a Powershell script using ConvertTo-SecureString using AES-256 like this :

$secretKey = "thisIsAnExampleButNotTheRealKey2"
$cleBytes = [System.Text.Encoding]::UTF8.GetBytes($secretKey)
$aes = New-Object System.Security.Cryptography.AesCryptoServiceProvider
$aes.Key = $cleBytes
$aes.GenerateIV()
$texteBytes = [System.Text.Encoding]::UTF8.GetBytes($texteAChiffrer)
$chiffreBytes = $aes.CreateEncryptor().TransformFinalBlock($texteBytes, 0, $texteBytes.Length)
$cryptedText = [Convert]::ToBase64String($chiffreBytes)
Write-Output "encrypted Text: $cryptedText"

In my C# application, I have to uncrypt these files. I use this method:

public static string UncryptAES(string cryptedText, byte[] key, byte[] initVector)
{
  using (Aes aesAlgo = Aes.Create())
  {
    aesAlgo.Key = key;
    // how to compute the initVector instead of passing it as a parameter?
    aesAlgo.IV = initVector;
    ICryptoTransform decryptor = aesAlgo.CreateDecryptor(aesAlgo.Key, aesAlgo.IV);

    using (var msDecrypt = new MemoryStream(Convert.FromBase64String(cryptedText)))
    {
      using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
      {
        using (var srDecrypt = new StreamReader(csDecrypt))
        {
          return srDecrypt.ReadToEnd();
        }
      }
    }
  }
}

I read that the init vector is generated as a random number by Powershell when the encryption is done and it is stored with the crypted data. How do I compute the init vector ?


Solution

  • An amusing fact about the IV is that it is not secret. Think about it: if you encrypt several blocks of data, each block is the IV of the next block. You just need to bootstrap the process with a first block that is the IV.

    To get the IV across, the sender and receiver must either:

    1. Agree on a give value (often just 0), but it leads to IV reuse (not good)
    2. Send the IV in a different channel (nightmare)
    3. Send the IV along with the cipher text (what is usually done)

    Powershell is not making any assumption on the method you will use, even if by doing the right thing and generating a random IV for each encryption, choice #1 above is ruled out.

    So to answer your question, just edit your code to add the IV in front of the cipher text, and read it back before decryption. This sample Powershell code does this with a dot . between the two parts.

    $texteAChiffrer = "Bonjour"
    $secretKey = "thisIsAnExampleButNotTheRealKey2"
    $cleBytes = [System.Text.Encoding]::UTF8.GetBytes($secretKey)
    $aes = New-Object System.Security.Cryptography.AesCryptoServiceProvider
    $aes.Key = $cleBytes
    $aes.GenerateIV()
    $texteBytes = [System.Text.Encoding]::UTF8.GetBytes($texteAChiffrer)
    $chiffreBytes = $aes.CreateEncryptor().TransformFinalBlock($texteBytes, 0, $texteBytes.Length)
    $cryptedText = [Convert]::ToBase64String($aes.IV) + "." + [Convert]::ToBase64String($chiffreBytes)
    Write-Output "IV with encrypted Text: $cryptedText"
    

    Ouput:

    IV with encrypted Text: SN+W+25FCSj2Xl2Ys/nOaQ==.418V8h+95aqVHCBs2PxdFA==