In company we move some parts of the system from .NET to python. One of these parts is logging/authorization module. I need to implement in python CryptographyManager.CompareHash Method. It takes salted and hashed password from database and compares it with user input (plaintext). To do it I have to salt and hash user input and then check it with hashed password from database.
The password in the database was hashed by Microsoft Enterprise Library.Security.Cryptography. Here more details:
Symmetric Crypto Providers:
algorithmType="System.Security.Cryptography.AesManaged name="AES"
Hash Providers:
algorithmType="System.Security.Cryptography.SHA256Managed
saltEnabled="true" name="SHA256"
I found that password is slated in that way: Base64(salt + SHA256(salt + password)). The salt is the first 16 bytes from the hashed password
What I am trying to do is get salt from hashed password in database and next hash and salt user input (plain text) to compare both.
Unfortunately, in the end there is no succes. Hashes are diffrent. Probably I am doning something wrong with de/encoding
My code looks like that:
import base64
import hashlib
hash = 'EwxBhfN0fM5Puv8/z+3/L50QvdU6BHFb4XQU9xtye/mOXJ8tBPc3tIyW7dEiZrvA'
password = 'some_plain_password'
#password = password.encode()
password = base64.b64decode(password)
#hash to byte
b_hash = base64.b64decode (hash)
print(b_hash)
#get salt, first 16 bytes
salt = b_hash[:15]
salt=base64.b64encode(salt)
print(salt)
m = hashlib.sha256()
m.update(salt)
m.update(password)
print(m.hexdigest())
It's a bit tricky to do this without an example plaintext and hash to verify the results, but your code should implement the algorithm you list in your question:
Base64(salt + SHA256(salt + password))
I think you want something like this:
import base64
import hashlib
hash = 'EwxBhfN0fM5Puv8/z+3/L50QvdU6BHFb4XQU9xtye/mOXJ8tBPc3tIyW7dEiZrvA'
salt = base64.b64decode(hash)[:16] # Use 16 here, not 15
password = 'some_plain_password'
# First let's do the SHA256(salt + password) part
m = hashlib.sha256()
m.update(salt)
m.update(password.encode('utf-8'))
# Now let's feed that into the Base64 part
new_hash = base64.encode(salt + m.digest())
My post is not 100% answer but more the end of the story.
The problem is that there is no official source how works CryptographyManager.Compare/Create Hash
I tried a lot of combinations with Base64(salt + SHA256(salt + password)) but without succes.
Finally, I decide to rewrite C# code.
If someone is looking for python code that is salting and hashing password and compare two hashes, below is my working example:
import base64
import hashlib
import os
class Hashing(object):
# base64( SHA256(password + salt) + salt)
# generate new salt (default 16 bytes_
def generate_new_salt(self, salt_ln=16):
self.new_salt = os.urandom(salt_ln)
print(f'new salt: {self.new_salt}')
return self.new_salt
# get salt from hash
def get_old_salt(self, input_hash, salt_ln=16):
self.old_salt = base64.b64decode(input_hash)[-salt_ln:]
print(f'old salt: {self.old_salt}')
return self.old_salt
# compute hash using parameters
def compute_hash(self, password, salt):
self.salt = salt
self.enc_password = password.encode()
# hashing SHA256(password + salt)
hash_object = hashlib.sha256(self.enc_password + salt)
# add salt to hash and encode to base64
hash_b64 = base64.b64encode(hash_object.digest() + salt)
print(f'new_hash: {hash_b64}')
return hash_b64
# create hash from new or old salt
def create_hash(self, password, salt_ln=16,old_salt=None):
if old_salt: #if old salt then use it
self.salt = old_salt
else: #else generate new salt
self.salt = Hashing().generate_new_salt(salt_ln)
hash = Hashing().compute_hash(password, self.salt)
return hash
# compare input hash with created using salt get from input
def compare_hashes(self, password, old_hash, salt_ln=16):
self.enc_password = password.encode()
#get salt from input hash
self.old_salt = Hashing().get_old_salt(old_hash, salt_ln)
#recreat input hash
re_hash = Hashing().create_new_hash(password,salt_ln, self.old_salt)
print(f'Compare: old_hash: {old_hash}')
print(f'Compare: new_hash: {re_hash}')
#compare
if re_hash.decode() == old_hash.decode():
return True
else:
return False
#code below is just for testing
NewSalt = Hashing().generate_new_salt()
Hash = Hashing().create_new_hash('pass')
OldSalt = Hashing().get_old_salt(Hash)
CompareHash = Hashing().compare_hashes('pass', Hash)
if CompareHash:
print('HASHES THE SAME')
else:
print('NOT THE SAME')
print(CompareHash)
Related
I am new to bcrypt and Im trying to use a specific field on my database to create a has with bcrypt.
what I did so far
import bcrypt
# example password
password = "password123"
accountName = "james"
# converting password to array of bytes
pw_bytes = password.encode('utf-8')
# generating the salt
salt = (accountName.Upper()).encode()
# Hashing the password
pw_hash = bcrypt.hashpw(pw_bytes, salt)
print(pw_hash)
I get this error message
invalid salt
I also want to add the cost factor of 4 and I am not sure how to. Some help will do some good.
As pointed out in the bcrypt Wikipedia article the salt passed to bcrypt.hashpw needs to contain a hash algorithm identifier, cost, as well as a 22 byte salt.
If you wanted to generate your own salt it might look like:
from string import printable
import secrets
import bcrypt
def gen_salt(phrase: str, rounds: int = 12, prefix: bytes = b'2a') -> bytes:
# choose a random printable byte to use as pad character
pad_byte = secrets.choice(printable[:62]).encode()
padded_phrase = phrase.upper().encode().ljust(22, pad_byte)
cost = str(rounds).encode()
return b'$' + prefix + b'$' + cost + b'$' + padded_phrase
# example password
password = "password123"
accountName = "james"
# converting password to array of bytes
pw_bytes = password.encode('utf-8')
# generating the salt
salt = gen_salt(accountName)
print("salt:", salt)
# Hashing the password
pw_hash = bcrypt.hashpw(pw_bytes, salt)
print(pw_hash)
I'm writing a password manager program, and I need to store an encrypted string in a file. (The "service" variable) When the user needs to retrieve the information, they enter the same string as before. When I encrypt the same string, I get a different output. I've checked multiple times, and the string is the same. Anyone know of a fix for this?
Code I'm using to encrypt and write to the file:
def new_account(service, username, password, filepath, key):
# Encrypting the account details #
encrypted_service = key.encrypt(bytes(service, "utf-8"))
encrypted_username = key.encrypt(bytes(username, "utf-8"))
encrypted_password = key.encrypt(bytes(password, "utf-8"))
encrypted_service = encrypted_service.decode("utf-8")
encrypted_username = encrypted_username.decode("utf-8")
encrypted_password = encrypted_password.decode("utf-8")
password_file = open(f"{filepath}\passwords.txt", "a")
password_file.close()
password_file = open(f"{filepath}\passwords.txt", "r")
password_file_content = password_file.read()
password_file.close()
password_file = open(f"{filepath}\passwords.txt", "a")
if f"{encrypted_service},{encrypted_username},{encrypted_password}" in password_file_content:
password_file.close()
return("The account already exists.")
else:
password_file.write(f"{encrypted_service},{encrypted_username},{encrypted_password}\n")
password_file.close()
return f"Account saved for {service.title()} with username {username} and password {password}."
Code I'm using to retrieve the information:
# The account retrieval function #
def get_account(service, filepath, key):
encrypted_service = key.encrypt(bytes(service, "utf-8"))
encrypted_service = encrypted_service.decode("utf-8")
password_file = open(f"{filepath}\passwords.txt")
lines = password_file.read().split("\n")
word = f"{encrypted_service}"
# Getting the line with the account details #
for i in lines:
index = lines.index(i)
myline = lines[index]
encrypted_account_content = myline.split(",")
print(encrypted_account_content)
print(f"service is: {encrypted_service}")
if encrypted_account_content.count(encrypted_service) != 0:
# Decrypting the account details #
username = encrypted_account_content[1]
decrypted_username = key.decrypt(bytes(username, "utf-8"))
decrypted_username = decrypted_username.decode("utf-8")
password = encrypted_account_content[2]
decrypted_password = key.decrypt(bytes(password, "utf-8"))
decrypted_password = decrypted_password.decode("utf-8")
return f"Service: {service.title()}\nUsername: {decrypted_username}\nPassword: {decrypted_password}"
else:
return "Account not found. Please try again."
Any proper encryption will use randomization, so that the result will always be different, and you won't be able to tell that the plaintext was the same. That's needed to achieve semantic security. In practice, initialization vectors and modes of operation like CBC or CTR are used. The cryptography library you're using does it out of the box (with CBC mode).
You will either have to store service as a plaintext, which shouldn't significantly reduce the overall security, or decrypt each service field in order to find the needed record.
i am building a software with python and qt designer that take picture and encrypt it
when reach into saving the password hash and salt (i've chosen to write them with encrypted file it self )I've faced a problem which is i am not getting the same output of hash
i dont know what i'm doing wrong
this is the encrypting function
global pic_path,getOpenFileName
password_provided = self.pass_entry1.text() # This is input in the form of a string
password = password_provided.encode() # Convert to type bytes
salt =bytes(os.urandom(16)) # recommend using a key from os.urandom(16), must be of type bytes
pass_hash= hashlib.pbkdf2_hmac(
'sha256', # The hash digest algorithm for HMAC
password_provided.encode('utf-8'), # Convert the password to bytes
salt, # Provide the salt
100000 # It is recommended to use at least 100,000 iterations of SHA-256
)
print("salt: "+str(salt))
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
backend=default_backend()
)
key = base64.urlsafe_b64encode(kdf.derive(password)) # Can only use kdf once
file = open(pic_path,'rb+')
fdata=file.read()
f = Fernet(key)
encrypted = f.encrypt(fdata) # Encrypt the bytes. The returning object is of type bytes
file.close()
try:
os.mkdir(os.getcwd()+"/encrypted")
except:
pass
pic_path=os.getcwd()+"/encrypted/"+name
fileencrypted=open(os.path.splitext(pic_path)[0]+'.cod','wb+')
fileencrypted.write(salt)
fileencrypted.write(pass_hash)
fileencrypted.write(encrypted)
fileencrypted.close()
self.error_label.setText("file encrypted scessfully")
and this is the decrypting function which am still working on
def decrypte(self):
file= open(filename,'rb')
salt= file.read(16)
password_hash= file.read(80)[16:80]
given_pass= self.pass_entry_dec.text()
key_given=hashlib.pbkdf2_hmac(
'sha256', # The hash digest algorithm for HMAC
given_pass.encode('utf-8'), # Convert the password to bytes
salt, # Provide the salt
100000 # It is recommended to use at least 100,000 iterations of SHA-256
)
print("salt: "+str(salt))
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
backend=default_backend()
)
print("this is the given password hash:"+ str(key_given)
+"\n this is the password hash from file"+str(password_hash))
this is the output I'm getting the salt is the same but the password hash isn't
salt: b'\xabS\x04\xbe7!\x192i\x1d\xc6\x85\xf4\xd3\x87\x0e'
salt: b'\xabS\x04\xbe7!\x192i\x1d\xc6\x85\xf4\xd3\x87\x0e'
this is the given password hash: +b'r\xda\xa8\xbd~\xac\xc3Cd\xef\x14\xa3L\x9f\x17\x8f%Z*n\xc4!aLc?\xebCB\xf3H\\'
this is the password hash from file b'\x8f%Z*n\xc4!aLc?\xebCB\xf3H\\gAAAAABfd20OSsKipnqDuh6USgKr3Z8KiEvIoieTDTRLYZE'
I need to port old Python 2 code to Python 3 and I think I'm messing up with string encoding.
It's a custom password hasher.
I've tried different ways, unsuccessfully, obtaining only errors or wrong results.
This is the Python 2 code which needs to work with Python 3:
from hashlib import sha256
from base64 import b64encode
# 32 characters length string
SALT = "SQ7HqXQhrOIPEALbI7QhVjZ3DHJGhK18"
PLAIN_PASSWORD = "PLAIN_PASSWORD"
SALTED_PASSWORD = "%s{%s}" % (PLAIN_PASSWORD, SALT)
digest = ""
for i in range(100):
digest = sha256(digest + SALTED_PASSWORD).digest()
print b64encode(digest)
Output:
Yb0W9H+R7xQDStPfBjKMjFbe05jDPK6OXrdhVWCDJrU=
Operate on bytes from the beginning:
SALTED_PASSWORD = ("%s{%s}" % (PLAIN_PASSWORD, SALT)).encode()
digest = b""
for i in range(100):
digest = sha256(digest + SALTED_PASSWORD).digest()
print(b64encode(digest).decode())
# Yb0W9H+R7xQDStPfBjKMjFbe05jDPK6OXrdhVWCDJrU=
from hashlib import sha256
from base64 import b64encode
# 32 characters length string
SALT = b"SQ7HqXQhrOIPEALbI7QhVjZ3DHJGhK18"
PLAIN_PASSWORD = b"PLAIN_PASSWORD"
SALTED_PASSWORD = b"%s{%s}" % (PLAIN_PASSWORD, SALT)
digest = b""
for i in range(100):
digest = sha256(digest + SALTED_PASSWORD).digest()
print(b64encode(digest))
I'm trying to migrate the following to Python 3.
def mkhash(password, salt=None):
"""
Compute SHA256 hash of password with pbkdf2 algorithm.
Call with salt=None for creating hash. To compute verification
hash, supply salt stored in the user's row in auth_user.
Args:
password :
salt=None :
Returns: tuple (hash, salt)
Raises: Nothing
"""
if salt is None:
## use a 16 char random string
randchars = [random.choice(string.ascii_lowercase) for _ in range(16)]
#salt = b''.join(randchars)# works in 2 but not 3
salt = ''.join(randchars) # works in 3 but result fails in hashlib call
# See https://docs.python.org/2/library/hashlib.html
dk = hashlib.pbkdf2_hmac('sha256', password, salt, 10000)
pwhash = binascii.hexlify(dk)
return (pwhash, salt)
Here's a traceback of the failure in Python 3.
Traceback (most recent call last):
File "auth.py", line 451, in <module>
_ = mkhash('badpassword')
File "auth.py", line 146, in mkhash
dk = hashlib.pbkdf2_hmac('sha256', password, salt, 10000)
TypeError: a bytes-like object is required, not 'str'
What's the right way, in Python 3, to generate a salt of length N that's compatible with hashlib functions?
EDIT: Working version using accepted answer:
def mkhash(password, salt=None):
"""
Compute SHA256 hash of password with pbkdf2 algorithm.
Call with salt=None for creating hash. To compute verification
hash, supply salt stored in the user's row in auth_user.
Args:
password :
salt=None :
Returns: tuple (hash, salt)
Raises: Nothing
"""
if salt is None:
salt = os.urandom(16)
elif type(salt) is not bytes:
salt = salt.encode('utf-8')
# See https://docs.python.org/3/library/hashlib.html
dk = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 10000)
pwhash = binascii.hexlify(dk)
return (pwhash, salt)
You can use .encode() to turn a string object into bytes.
salt = salt.encode('utf-8')
But you shouldn't.
The random module doesn't produce cryptographically secure random numbers. That leaves a vulnerability in your code. If you're using Python 3.6, the secrets module is better.
salt = secrets.token_bytes(16)
If not, os.urandom() is also documented to be "unpredictable enough for cryptographic applications".
salt = os.urandom(16)