Python Language
Säkerhet och kryptografi
Sök…
Introduktion
Python är ett av de mest populära språken inom dator- och nätverkssäkerhet och har stor potential inom säkerhet och kryptografi. Detta ämne behandlar de kryptografiska funktionerna och implementeringarna i Python från dess användningar inom dator- och nätverkssäkerhet till hash- och krypterings- / dekrypteringsalgoritmer.
Syntax
- hashlib.new (namn)
- hashlib.pbkdf2_hmac (namn, lösenord, salt, rundor, dklen = Ingen)
Anmärkningar
Många av metoderna i hashlib
kräver att du hashlib
värden som kan tolkas som buffertar av byte snarare än strängar. Detta är fallet för hashlib.new().update()
samt hashlib.pbkdf2_hmac
. Om du har en sträng kan du konvertera den till en byte-buffert genom att förbereda tecknet b
till strängens början:
"This is a string"
b"This is a buffer of bytes"
Beräkna ett meddelande Digest
hashlib
modulen tillåter att skapa meddelandegeneratorgeneratorer via den new
metoden. Dessa generatorer kommer att förvandla en godtycklig sträng till en fast längd-uppfattning:
import hashlib
h = hashlib.new('sha256')
h.update(b'Nobody expects the Spanish Inquisition.')
h.digest()
# ==> b'.\xdf\xda\xdaVR[\x12\x90\xff\x16\xfb\x17D\xcf\xb4\x82\xdd)\x14\xff\xbc\xb6Iy\x0c\x0eX\x9eF-='
Observera att du kan ringa update
ett godtyckligt antal gånger innan du ringer digest
vilket är användbart för att hash en stor filbit av bit. Du kan också få digereringen i hexadecimalt format genom att använda hexdigest
:
h.hexdigest()
# ==> '2edfdada56525b1290ff16fb1744cfb482dd2914ffbcb649790c0e589e462d3d'
Tillgängliga Hashing-algoritmer
hashlib.new
kräver namnet på en algoritm när du kallar den för att producera en generator. För att ta reda på vilka algoritmer som finns tillgängliga i den aktuella Python-tolkaren, använd hashlib.algorithms_available
:
import hashlib
hashlib.algorithms_available
# ==> {'sha256', 'DSA-SHA', 'SHA512', 'SHA224', 'dsaWithSHA', 'SHA', 'RIPEMD160', 'ecdsa-with-SHA1', 'sha1', 'SHA384', 'md5', 'SHA1', 'MD5', 'MD4', 'SHA256', 'sha384', 'md4', 'ripemd160', 'sha224', 'sha512', 'DSA', 'dsaEncryption', 'sha', 'whirlpool'}
Den returnerade listan kommer att variera beroende på plattform och tolk; se till att du kontrollerar att din algoritm är tillgänglig.
Det finns också några algoritmer som garanterat är tillgängliga på alla plattformar och tolkar, som är tillgängliga med hashlib.algorithms_guaranteed
:
hashlib.algorithms_guaranteed
# ==> {'sha256', 'sha384', 'sha1', 'sha224', 'md5', 'sha512'}
Secure Password Hashing
PBKDF2-algoritmen som exponeras av hashlib
modulen kan användas för att utföra säker lösenord-hashing. Även om denna algoritm inte kan förhindra brute-force attacker för att återställa det ursprungliga lösenordet från den lagrade hash, gör den sådana attacker mycket dyra.
import hashlib
import os
salt = os.urandom(16)
hash = hashlib.pbkdf2_hmac('sha256', b'password', salt, 100000)
PBKDF2 kan fungera med vilken som helst digergalgoritm, exemplet ovan använder SHA256 som vanligtvis rekommenderas. Det slumpmässiga saltet bör lagras tillsammans med hash-lösenordet, du behöver det igen för att jämföra ett inmatat lösenord med det lagrade hashet. Det är viktigt att varje lösenord hashas med ett annat salt. När det gäller antalet omgångar rekommenderas det att ställa in det så högt som möjligt för din ansökan .
Om du vill ha resultatet i hexadecimal kan du använda binascii
modulen:
import binascii
hexhash = binascii.hexlify(hash)
Obs : Även om PBKDF2 inte är dåligt, betraktas bcrypt och särskilt scrypt som starkare mot brute-force attacker. Inte heller ingår i Pythons standardbibliotek för tillfället.
File Hashing
En hash är en funktion som konverterar en sekvens med variabel längd av byte till en sekvens med fast längd. Hashing-filer kan vara fördelaktiga av många skäl. Hashes kan användas för att kontrollera om två filer är identiska eller för att kontrollera att innehållet i en fil inte har skadats eller ändrats.
Du kan använda hashlib
att generera en hash för en fil:
import hashlib
hasher = hashlib.new('sha256')
with open('myfile', 'r') as f:
contents = f.read()
hasher.update(contents)
print hasher.hexdigest()
För större filer kan en buffert med fast längd användas:
import hashlib
SIZE = 65536
hasher = hashlib.new('sha256')
with open('myfile', 'r') as f:
buffer = f.read(SIZE)
while len(buffer) > 0:
hasher.update(buffer)
buffer = f.read(SIZE)
print(hasher.hexdigest())
Symmetrisk kryptering med pycrypto
Pythons inbyggda kryptofunktionalitet är för närvarande begränsad till hashing. Kryptering kräver en tredjepartsmodul som pycrypto . Till exempel tillhandahåller den AES-algoritmen som anses vara känd för symmetrisk kryptering. Följande kod krypterar ett givet meddelande med en lösenfras:
import hashlib
import math
import os
from Crypto.Cipher import AES
IV_SIZE = 16 # 128 bit, fixed for the AES algorithm
KEY_SIZE = 32 # 256 bit meaning AES-256, can also be 128 or 192 bits
SALT_SIZE = 16 # This size is arbitrary
cleartext = b'Lorem ipsum'
password = b'highly secure encryption password'
salt = os.urandom(SALT_SIZE)
derived = hashlib.pbkdf2_hmac('sha256', password, salt, 100000,
dklen=IV_SIZE + KEY_SIZE)
iv = derived[0:IV_SIZE]
key = derived[IV_SIZE:]
encrypted = salt + AES.new(key, AES.MODE_CFB, iv).encrypt(cleartext)
AES-algoritmen tar tre parametrar: krypteringsnyckel, initialiseringsvektor (IV) och det faktiska meddelandet som ska krypteras. Om du har en slumpmässigt genererad AES-nyckel kan du använda den direkt och bara skapa en slumpmässig initialiseringsvektor. En lösenfras har emellertid inte rätt storlek, och det vore heller inte att rekommendera att använda det direkt med tanke på att det inte är riktigt slumpmässigt och därmed har jämförelsevis liten entropi. Istället använder vi den inbyggda implementeringen av PBKDF2-algoritmen för att generera en 128-bitars initialiseringsvektor och 256-bitars krypteringsnyckel från lösenordet.
Notera det slumpmässiga saltet som är viktigt för att ha en annan initialiseringsvektor och nyckel för varje krypterat meddelande. Detta säkerställer särskilt att två lika meddelanden inte kommer att resultera i identisk krypterad text, men det förhindrar också angripare att återanvända arbete som gett en gissfras på meddelanden som är krypterade med en annan lösenfras. Detta salt måste lagras tillsammans med det krypterade meddelandet för att härleda samma initialiseringsvektor och nyckel för dekryptering.
Följande kod dekrypterar vårt meddelande igen:
salt = encrypted[0:SALT_SIZE]
derived = hashlib.pbkdf2_hmac('sha256', password, salt, 100000,
dklen=IV_SIZE + KEY_SIZE)
iv = derived[0:IV_SIZE]
key = derived[IV_SIZE:]
cleartext = AES.new(key, AES.MODE_CFB, iv).decrypt(encrypted[SALT_SIZE:])
Generera RSA-signaturer med pycrypto
RSA kan användas för att skapa en meddelandesignatur. En giltig signatur kan endast genereras med åtkomst till den privata RSA-nyckeln, validering å andra sidan är möjlig med endast motsvarande offentlig nyckel. Så så länge den andra sidan känner till din offentliga nyckel kan de verifiera meddelandet som ska undertecknas av dig och oförändrat - en metod som används till exempel för e-post. För närvarande krävs en tredjepartsmodul som pycrypto för denna funktion.
import errno
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
message = b'This message is from me, I promise.'
try:
with open('privkey.pem', 'r') as f:
key = RSA.importKey(f.read())
except IOError as e:
if e.errno != errno.ENOENT:
raise
# No private key, generate a new one. This can take a few seconds.
key = RSA.generate(4096)
with open('privkey.pem', 'wb') as f:
f.write(key.exportKey('PEM'))
with open('pubkey.pem', 'wb') as f:
f.write(key.publickey().exportKey('PEM'))
hasher = SHA256.new(message)
signer = PKCS1_v1_5.new(key)
signature = signer.sign(hasher)
Verifiering av signaturen fungerar på liknande sätt men använder den offentliga nyckeln snarare än den privata nyckeln:
with open('pubkey.pem', 'rb') as f:
key = RSA.importKey(f.read())
hasher = SHA256.new(message)
verifier = PKCS1_v1_5.new(key)
if verifier.verify(hasher, signature):
print('Nice, the signature is valid!')
else:
print('No, the message was signed with the wrong private key or modified')
Obs : Exemplen ovan använder PKCS # 1 v1.5-signeringsalgoritm som är mycket vanligt. pycrypto implementerar också den nyare PKCS # 1 PSS-algoritmen och ersätter PKCS1_v1_5
av PKCS1_PSS
i exemplen bör fungera om du vill använda den. För närvarande verkar det dock finnas liten anledning att använda det .
Asymmetrisk RSA-kryptering med hjälp av pycrypto
Asymmetrisk kryptering har fördelen att ett meddelande kan krypteras utan att byta ut en hemlig nyckel med mottagaren av meddelandet. Avsändaren behöver bara känna till mottagarens offentliga nyckel, detta tillåter att kryptera meddelandet på ett sådant sätt att endast den utsedda mottagaren (som har motsvarande privata nyckel) kan dekryptera det. För närvarande krävs en tredjepartsmodul som pycrypto för denna funktion.
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
message = b'This is a very secret message.'
with open('pubkey.pem', 'rb') as f:
key = RSA.importKey(f.read())
cipher = PKCS1_OAEP.new(key)
encrypted = cipher.encrypt(message)
Mottagaren kan dekryptera meddelandet då om de har rätt privat nyckel:
with open('privkey.pem', 'rb') as f:
key = RSA.importKey(f.read())
cipher = PKCS1_OAEP.new(key)
decrypted = cipher.decrypt(encrypted)
Obs : Ovanstående exempel använder PKCS # 1 OAEP-krypteringsschema. pycrypto implementerar också PKCS # 1 v1.5-krypteringsschema, detta rekommenderas inte för nya protokoll men på grund av kända varningar .