PowerShell和Python中的AES加密

迈克尔·威廉姆斯

我的目标是能够在PowerShell中AES加密字符串,将其发送到具有python的UNIX系统,然后将字符串解密回纯文本。我也想做相反的事情。我不是加密专家或PowerShell / python程序员,但是到目前为止,这是我能够使用的代码:

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"

PowerShell脚本似乎可以很好地用于加密和解密。对于python端,我可以定义相同的AES密钥值,因为它只是密钥密码短语的base64编码。但是,执行时我没有得到相同的字符串加密值(例如,PowerShell输出UXKWIhtaUgFOvN13bvA4tx4 + 2Hjkv4v6I1G3Xfl6zp0 =和Python输出BOJ3Ox4fJxR + jFZ0CBQ25Q ==)。我相信这些必须匹配才能解密,但我可能会误会。我知道设置静态IV和密钥会使它不安全,但是我愿意这样做,以便能够跨平台进行加密和解密(除非有使用AES的更好方法)。任何帮助,将不胜感激。

Python代码

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()
安迪

几个建议:

  1. 128^16 = 5.19229686e33可以输入16个字符的ASCII字符串Base64编码的16个字节产生24个字节(4*ceil(16/3))。因此,即使您使用的是192位AES密钥(理论上是6.27710174e57密钥组合),也只能使用1/1208925820422879877545683[超过一万亿兆]。实际上,您将密钥大小设置为256位,并且显然代码忽略了这一点/允许192位密钥没有错误。

    使用Rfc2898DeriveBytes导出您的AES密钥,而不是原始字符串的Base64编码转换。RFC 2898定义了PBKDF2(基于密码的密钥派生功能2),一种基于HMAC的密钥派生功能,可从密码安全地派生加密密钥,并为HMAC / SHA1提供了大量迭代功能,以缓解针对您的蛮力攻击。键。

  2. 您仅TransformFinalBlock()在PowerShell中调用加密和解密。我想如果消息长于一个块(16字节),它将无法加密或解密完整的消息。尝试使用输入消息,例如This is a plaintext message.(29个字节)。我相信您想同时使用TransformBlock()TransformFinalBlock()

  3. 您正确地认为静态IV是不安全的(违反了IV的用途,对于具有相同密钥的每次加密操作,IV的用途都是唯一且不可预测的)。AesManaged已经提供了一种GenerateIV()生成令人满意的IV的方法,您可以从IV属性中访问该方法,并在其前面添加密文。

  4. 您的Base64编码密文的PowerShell输出为44个字符(16 byte IV + 16 byte ciphered message = 32 bytes -> 44 bytes在Base64中)。您的Python Base64输出为24个字符(16 bytes -> 24 bytes在Base64中)。该输出不包括IV或消息(或其他一些不太可能的原因导致输出受限)。查看代码,您的encrypt方法不会将IV放在密码文本上。

  5. 最后,在这一点上,您的代码应该可以正常工作并且在内部是一致的并且是相互兼容的。在这里,您应该重新审视几个设计决策:

    • 零填充是非标准的,并且在您手动实现时,PKCS #5/#7更希望使用定义良好的填充方案在Python和.NET中都有丰富的实现和代码示例。

    • 您正在使用CBC分组密码操作模式尽管CBC可以保密,但它不能提供完整性。您应该使用经过身份验证的加密模式(AE / AEAD),例如GCM或EAX。如果不能,请通过使用HMAC结构(例如HMAC / SHA-256 )在密文上提供消息身份验证码(MAC),并使用与加密密钥不同的共享密钥,并尝试解密之前使用固定时间方法 验证MAC

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章