title: AES Encryption in PowerShell and Python
body: <p>My goal is to be able to AES encrypt a string in PowerShell, send it to a UNIX system with python available, and decrypt the string back to plain text. I would also like to be able to do the inverse. I am not a crypto guy or a PowerShell/python programmer, but this is what I have been able to do with the code so far:</p>
  
  <pre><code>function Create-AesManagedObject($key, $IV) {
      $aesManaged = New-Object "System.Security.Cryptography.AesManaged"
      $aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
      $aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
      $aesManaged.BlockSize = 128
      $aesManaged.KeySize = 256
      if ($IV) {
          if ($IV.getType().Name -eq "String") {
              $aesManaged.IV = [System.Convert]::FromBase64String($IV)
          }
          else {
              $aesManaged.IV = $IV
          }
      }
      if ($key) {
          if ($key.getType().Name -eq "String") {
              $aesManaged.Key = [System.Convert]::FromBase64String($key)
          }
          else {
              $aesManaged.Key = $key
          }
      }
      $aesManaged
  }
  
  function Encrypt-String($key, $unencryptedString) {
      $bytes = [System.Text.Encoding]::UTF8.GetBytes($unencryptedString)
      $aesManaged = Create-AesManagedObject $key $IV
      $encryptor = $aesManaged.CreateEncryptor()
      $encryptedData = $encryptor.TransformFinalBlock($bytes, 0, $bytes.Length);
      [byte[]] $fullData = $aesManaged.IV + $encryptedData
      $aesManaged.Dispose()
      [System.Convert]::ToBase64String($fullData)
  }
  
  function Decrypt-String($key, $encryptedStringWithIV) {
      $bytes = [System.Convert]::FromBase64String($encryptedStringWithIV)
      $IV = $bytes[0..15]
      $aesManaged = Create-AesManagedObject $key $IV
      $decryptor = $aesManaged.CreateDecryptor();
      $unencryptedData = $decryptor.TransformFinalBlock($bytes, 16, $bytes.Length - 16);
      $aesManaged.Dispose()
      [System.Text.Encoding]::UTF8.GetString($unencryptedData).Trim([char]0)
  }
  
  # key passphrase is a 16 byte string that is used to create the AES key.
  $key_passphrase = "MypassphraseKey1"
  # base64 encode the key.  The resulting key should be exactly 44 characters (43 characters with a single = of padding) (256 bits)
  $Bytes = [System.Text.Encoding]::Ascii.GetBytes($key_passphrase)
  $key =[Convert]::ToBase64String($Bytes)
  
  # init is used to create the IV
  $init = "This is an IV123"
  # converts init to a byte array (e.g. T = 84, h = 104) and then sha1 hash it
  $IV = (new-Object Security.Cryptography.SHA1Managed).ComputeHash( [Text.Encoding]::UTF8.GetBytes($init) )[0..15] 
  write-output "IV is equal to $IV"
  
  
  write-output "AES key is $key"
  $unencryptedString = "testing"
  $encryptedString = Encrypt-String $key $unencryptedString
  $backToPlainText = Decrypt-String $key $encryptedString
  
  write-output "Unencrypted string: $unencryptedString"
  write-output "Encrypted string: $encryptedString"
  write-output "Unencrytped string: $backToPlainText"
  </code></pre>
  
  <p>The PowerShell script seems to be working fine for encrypting and decrypting. For the python side, I can define the same AES key value since it is just base64 encoded of my key's passphrase. However, I do not get the same encrypted value of the string when executing (e.g. PowerShell outputs UXKWIhtaUgFOvN13bvA4tx4+2Hjkv4v6I1G3Xfl6zp0= and Python outputs BOJ3Ox4fJxR+jFZ0CBQ25Q==). I believe these would need to match in order to be able to decrypt but I could be mistaken. I know setting a static IV and key makes it insecure, but I am willing to do that in order to be able to encrypt and decrypt across platforms (unless there is a better method using AES). Any help would be appreciated.</p>
  
  <p>Python code</p>
  
  <pre><code>import base64, array
  import Crypto
  import Crypto.Random
  from Crypto.Cipher import AES
  
  def pad_data(data):
      if len(data) % 16 == 0:
          return data
      databytes = bytearray(data)
      padding_required = 15 - (len(databytes) % 16)
      databytes.extend(b'\x80')
      databytes.extend(b'\x00' * padding_required)
      return bytes(databytes)
  
  def unpad_data(data):
      if not data:
          return data
  
      data = data.rstrip(b'\x00')
      if data[-1] == 128: # b'\x80'[0]:
          return data[:-1]
      else:
          return data
  
  def encrypt(key, iv, data):
      aes = AES.new(key, AES.MODE_CBC, iv)
      data = pad_data(data)
      return aes.encrypt(data)
  
  def decrypt(key, iv, data):
      aes = AES.new(key, AES.MODE_CBC, iv)
      data = aes.decrypt(data)
      return unpad_data(data)
  
  def test_crypto ():
      key = "MypassphraseKey1"
      # found using the debugger in the PowerShell ISE to get the value byte value which was converted to hex
      iv = "\x51\x72\x96\x22\x1b\x5a\x52\x01\x4e\xbc\xdd\x77\x6e\xf0\x38\xb7"
      msg = b"testing"
  
      # hex value of IV in powershell script is 51 72 96 22 1b 5a 52 01 4e bc dd 77 6e f0 38 b7
      print("Value of IV: " + iv)
  
      # base64 encode key
      b64key = base64.b64encode(key)
      print("AES key encoded: " + b64key)
  
      code = encrypt(key, iv, msg)
      # convert encrypted string to base64
      b64encoded = base64.b64encode(code)
      print("Encrypted string: " + b64encoded)
  
      decoded = decrypt(key, iv, code)
      print("Decoded: " + decoded)
  
  if __name__ == '__main__':
      test_crypto()
  </code></pre>