AES encryption in PowerShell and Python

My goal is to provide AES encryption of a string in PowerShell, send it to UNIX with python available, and decrypt the string back to plain text. I would also like to be able to do the reverse. I'm not a PowerShell / python programmer or programmer, but this is what I was able to do with the 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 (eg 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" 

The PowerShell script seems to work just fine for encryption and decryption. For the python side, I can define the same AES key value as it is just base64 encoded in my passphrase. However, I do not get the encrypted string value at runtime (for example, PowerShell outputs UXKWIhtaUgFOvN13bvA4tx4 + 2Hjkv4v6I1G3Xfl6zp0 = and Python outputs BOJ3Ox4fJxR + jFZ0CBQ25Q ==). I believe that they will need to comply in order to be able to decrypt, but I could be wrong. I know that installing a static IV and a key makes it unsafe, but I am ready to do this in order to be able to encrypt and decrypt different platforms (unless there is a better method using AES). Any help would be appreciated.

Python 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() 
+5
source share
1 answer

A few tips:

  • The 16-character ASCII string is 128^16 = 5.19229686e33 possible key inputs. Base64 encoding of 16 bytes gives 24 bytes ( 4*ceil(16/3) ). Therefore, even if you use the 192-bit AES key (theoretically, the keyboard shortcuts 6.27710174e57 ), you can use only 1/1208925820422879877545683 [one more than one trillion trillion] of them. In fact, you set the key size to 256 bits, and apparently the code ignores this / allows the 192-bit key without error.

    Use Rfc2898DeriveBytes to get the AES key, not Base64 conversion of the raw string. RFC 2898 defines PBKDF2 (password derivation function based on password 2), an HMAC-based key determination function for safely obtaining encryption keys from passwords, and provides HMAC / SHA1, used with a lot of iterations, to mitigate brute force attacks on your key.

  • You call TransformFinalBlock() only for encryption and decryption in PowerShell. I assume that this will not lead to encryption or decryption of the complete message if the message is longer than one block (16 bytes). Try this with an input message such as This is a plaintext message. (29 bytes). I believe you want to use TransformBlock() and TransformFinalBlock() .

  • You are right that a static IV is unsafe (target IV wins, which must be unique and unpredictable for each encryption operation with the same key). AesManaged already provides the GenerateIV() method to create a satisfactory IV, access to which you can get from property IV and add to the cipher,

  • The PowerShell output of Base64 encoded text is 44 characters ( 16 byte IV + 16 byte ciphered message = 32 bytes -> 44 bytes in Base64). Python Base64 output is 24 characters ( 16 bytes -> 24 bytes in Base64). Either this conclusion does not include an IV or message (or some other less likely reason for a limited release). Looking at the code, your encrypt method encrypt not add IV to the cipher.

  • Finally, at this point, your code should work and be internally consistent and cross-compatible. Here you should review a few design decisions:

    • Zero padding is non-standard, and although you implemented it manually, a preferred padding scheme such as PKCS #5/#7 . Python and .NET have rich implementations and code examples to implement this.

    • You are using CBC block cipher mode . While CBC maintains great confidentiality, it does not provide integrity. You must use authenticated encryption mode (AE / AEAD), such as GCM or EAX. If you cannot, enter a message authentication code (MAC) on top of the text. using an HMAC construct such as HMAC / SHA-256, with a shared secret other than your encryption key and check the MAC using the time constant up method , trying to decrypt.

+4
source

Source: https://habr.com/ru/post/1262603/


All Articles