2017-01-10 72 views
5

Moim celem jest umożliwienie AES szyfrowania ciągu znaków w PowerShell, wysłania go do systemu UNIX z dostępnym pytonem i odszyfrowania ciągu znaków do zwykłego tekstu. Chciałbym również móc dokonać odwrotności. Nie jestem krypto facet lub PowerShell/programista python, ale to, co udało mi się zrobić z kodem dotąd:Szyfrowanie AES w PowerShell i Pythonie

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" 

skrypt PowerShell wydaje się być w porządku pracy do szyfrowania i deszyfrowania. Po stronie Pythona mogę zdefiniować tę samą wartość klucza AES, ponieważ jest ona kodowana tylko w base64 z hasłem mojego klucza. Jednak podczas wykonywania nie otrzymuję tej samej zaszyfrowanej wartości ciągu znaków (np. PowerShell wyprowadza UXKWIhtaUgFOvN13bvA4tx4 + 2Hjkv4v6I1G3Xfl6zp0 = i Python wyprowadza BOJ3Ox4fJxR + jFZ0CBQ25Q ==). Sądzę, że musieliby się dopasować, aby móc odszyfrować, ale mógłbym się mylić. Wiem, że ustawienie statycznej litery IV i klucza powoduje, że jest ona niepewna, ale jestem gotów to zrobić, aby móc szyfrować i odszyfrowywać na różnych platformach (chyba że istnieje lepsza metoda z wykorzystaniem AES). Każda pomoc będzie doceniona.

kod 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() 

Odpowiedz

4

Kilka sugestii:

  1. A 16 znaków ASCII ciąg jest 128^16 = 5.19229686e33 ewentualne główne dane wejściowe. Kodowanie 16 kodowaniem base64 daje 24 bajty (4*ceil(16/3)). Więc nawet jeśli używasz 192-bitowego klucza AES (teoretycznie 6.27710174e57 kombinacji klawiszy), możesz użyć tylko 1/1208925820422879877545683 [jednego na bilion trylionów] z nich. W rzeczywistości ustawiono wielkość klucza na 256 bitów i najwyraźniej kod ignoruje to/zezwalając na 192-bitowy klucz bez błędu.

    Użyj Rfc2898DeriveBytes do wyprowadzenia klucza AES zamiast transformacji Base64 nieprzetworzonego łańcucha. RFC 2898 definiuje PBKDF2 (funkcja wyprowadzania klucza opartego na haśle 2), funkcję wyprowadzania klucza opartą na HMAC, aby bezpiecznie uzyskiwać klucze szyfrowania z haseł i zapewnia użycie HMAC/SHA1 z dużą liczbą iteracji w celu złagodzenia ataków brute-force na klucz .

  2. Przywołujesz tylko TransformFinalBlock() szyfrowanie i odszyfrowywanie w PowerShell. Wyobrażam sobie, że nie uda się zaszyfrować lub odszyfrować kompletną wiadomość, jeśli wiadomość jest dłuższa niż jeden blok (16 bajtów). Wypróbuj to z komunikatem wejściowym, takim jak This is a plaintext message. (29 bajtów). Uważam, że chcesz używać zarówno TransformBlock(), jak i TransformFinalBlock().

  3. Masz rację, że statyczny IV jest niebezpieczny (pokonuje cel IV, który powinien być unikalny i nieprzewidywalny dla każdej operacji szyfrowania za pomocą tego samego klucza). AesManaged zapewnia już metodę GenerateIV() do generowania zadowalającego IV, do którego można uzyskać dostęp z właściwości IV i przedrostek do tekstu szyfrowania.

  4. Twoje wyjście PowerShell tekstu szyfrowania Base64 ma 44 znaki (16 byte IV + 16 byte ciphered message = 32 bytes -> 44 bytes w Base64). Twoje wyjście Pythona Base64 ma 24 znaki (16 bytes -> 24 bytes w Base64). Albo to wyjście nie zawiera IV lub komunikatu (lub jakiejś innej mniej prawdopodobnej przyczyny ograniczonej wydajności). Patrząc na kod, twoja metoda encrypt nie dodaje znaku IV do tekstu zaszyfrowanego.

  5. Wreszcie, w tym momencie Twój kod powinien działać i być wewnętrznie spójny i kompatybilny.Tutaj należy powrócić do decyzji projektowych para:

    • Zero wyściółka jest nietypowy i podczas wdrożyły go ręcznie, dobrze zdefiniowany schemat wyściółka jak PKCS #5/#7 jest bardziej pożądane. Istnieją bogate implementacje i przykłady kodu do implementacji tego w Pythonie i .NET.

    • Używasz urządzenia CBC block cipher mode of operation. Podczas gdy CBC jest w porządku dla poufności, nie zapewnia integralności. Powinieneś używać uwierzytelnionego trybu szyfrowania (AE/AEAD), takiego jak GCM lub EAX. Jeśli nie możesz, podaj kod uwierzytelniający (MAC) dla wiadomości zaszyfrowany tekst, używając konstrukcji HMAC, takiej jak HMAC/SHA-256, z sekretem wspólnym innym niż kluczem szyfrowania i sprawdź MAC ze stałym czasem metodaprzed próbuje odszyfrować.

+1

Dzięki, doceniam pomoc. Poprawiłem skrypt Pythona w oparciu o kilka twoich sugestii i usunąłem statyczny IV. Wiem, że skrypt Pythona może zdekodować dane wyjściowe powłoki i na odwrót. Będę pracował nad wdrożeniem innych sugestii. –

+0

@MichaelWilliams Myślę, że powinieneś odpowiedzieć na to pytanie: jeśli je zaakceptujesz, prawdopodobnie oznacza to, że zasługuje na głos. – xverges

+0

Przepraszamy, nowe do przepełnienia stosu. Uważam, że teraz należy go przegłosować. –