So I'm a bit confused regarding the use of passlib. I have the following code to hash X amount of passwords with N amount of algorithms.
#!/usr/bin/python
from passlib.hash import *
from passlib.context import CryptContext
from passlib.utils import Base64Engine, h64
from base64 import b64decode
from binascii import hexlify
def hash_password(passwd):
schemes = ["pbkdf2_sha1",
"md5_crypt",
"sha1_crypt",
"sha256_crypt",
"nthash",
"lmhash"]
print "Hashing \"%s\"" % passwd
print "-----------------------------------------------"
# Create the hashing context
pwd_context = CryptContext(schemes)
for i in range(len(schemes)):
pw_hash = pwd_context.encrypt(passwd, schemes[i])
print "%s - %s" % (schemes[i], pw_hash)
print "-----------------------------------------------"
def main():
passwords = [line.rstrip('\n') for line in open("passwords.txt")]
for i in range(len(passwords)):
hash_password(passwords[i])
if __name__ == "__main__":
main()
I get the following output:
Hashing "Hello"
-----------------------------------------------
pbkdf2_sha1 - $pbkdf2$60000$ojQGACCEcM7ZW0vpnVMqRQ$M53EY/FaE4bn.5Peaagi51cza54
pbkdf2_sha256 - $pbkdf2- sha256$20000$4JxzLsWYk1KKUSoFwPifsw$6LCJ.389zCs4gaQuYTMTKUis7t/4yv.c9/mH/XFKEAo
md5_crypt - $1$uDGM/Vuu$jiygPs0AgWJQjdVkc5BbE0
sha1_crypt - $sha1$64000$ge8006iW$Ccn.2CfTJFHUNHKINw5uOaMP1U3v
sha256_crypt - $5$rounds=110000$BaeA4z3k.JSjarb5$veeo24NDYkEzMqhQqqUkjEst0FTqh9fWxxGPp/pO4xA
nthash - 916a8e7b1540ec179f196f8ddb603d85
lmhash - fda95fbeca288d44aad3b435b51404ee
-----------------------------------------------
Hashing "Password"
-----------------------------------------------
pbkdf2_sha1 - $pbkdf2$60000$fO.91/o/x/g/p3Qu5XxvLQ$HPcZm9FqiN59c7VurmE3gKPUlFk
pbkdf2_sha256 - $pbkdf2-sha256$20000$mrNWKkVISSll7D3nPGds7Q$AoEg6PiE7.YFQDxtmvKVlRk/i6niC1RVflFFZiZ1g.Y
md5_crypt - $1$0qgwC1qt$bpoGT56iTN2cHHXN3v0Hk/
sha1_crypt - $sha1$64000$D3aHiFZ3$8StKt6Kt0MVl.flOvuoulBJNPVG/
sha256_crypt - $5$rounds=110000$Zxm1u65zMawD1.2F$pqywgWCi556iuBTxemWIccy68IBQxqAKZgHYc0MAFY4
nthash - a4f49c406510bdcab6824ee7c30fd852
lmhash - e52cac67419a9a224a3b108f3fa6cb6d
-----------------------------------------------
Hashing "NewPassword"
-----------------------------------------------
pbkdf2_sha1 - $pbkdf2$60000$Uao1RgjhPEcI4dzbG8OYkw$TWkaN5uJ5HMh3tV75wEnMkYjZVI
pbkdf2_sha256 - $pbkdf2-sha256$20000$55zT2tub8/6f0zqHUOrdOw$voDY5VObZQieFDCqG2of1NO0NVMc4AVsznwqv9GzAhA
md5_crypt - $1$CkQuGd5z$1CcBnpwL4cFPOYiw958pZ1
sha1_crypt - $sha1$64000$hIbbDQIi$J.1kLykb3tzFUPrHZZ23TcSj.zAR
sha256_crypt - $5$rounds=110000$g7J4/8teBFGyKmI2$OakX25d3mePIAj9V1UYDWWBayaMfngWZ6fZOdgqg9l0
nthash - 91f172926b123808d76f4d40b1db18e5
lmhash - 09eeab5aa415d6e4d408e6b105741864
I have two questions:
Repeated runs of this script give me different outputs of the hash, even though I'm not using a salt and the same algorithm is performed.
I have searched all over, but have not been able to find how to actually extract the legitimate hash from the result of .encrypt(). Is this even possible? What type of conversion do I have to do on the encoding since nt/lmhash seem fine?
Thanks in advance.
passlib automatically salts the password when you call encrypt for most hash types:
classmethod PasswordHash.encrypt(secret, **kwds)
Digest password using format-specific algorithm, returning resulting hash string.
For most hashes supported by Passlib, the returned string will contain: an algorithm identifier, a cost parameter, the salt string, and finally the password digest itself.
That explains the output you see for most of the hashes. nthash and lmhash are both documented as not supporting salts, which explains why they have a simpler output.
For your second question, I would say that in general you shouldn't pull the actual hash out, because passlib is designed to use the entire hash string returned by encrypt to verify a given password. But if you want to, you can just split the returned pw_hash on $, and take the last item:
digest = pw_hash.split('$')[-1]
This works even for hashes that aren't salted like nthash and lmhash.
Related
I am using RabbitMQ with Docker. I would like to update the configurations directly in the definitions.json file. The users should have their password stored there with rabbit_password_hashing_sha256 hashing algorithm. I have found a useful Python script for hashing the password but I was not able to reproduce it's logic in NodeJS with Crypto library.
Python script:
#!/usr/bin/env python3
# RabbitMQ password hashing algorith as laid out in:
# http://lists.rabbitmq.com/pipermail/rabbitmq-discuss/2011-May/012765.html
from __future__ import print_function
import base64
import os
import hashlib
import struct
import sys
# The plain password to encode
password = sys.argv[1]
# Generate a random 32 bit salt
salt = os.urandom(4)
# Concatenate with the UTF-8 representation of plaintext password
tmp0 = salt + password.encode('utf-8')
# Take the SHA256 hash and get the bytes back
tmp1 = hashlib.sha256(tmp0).digest()
# Concatenate the salt again
salted_hash = salt + tmp1
# Convert to base64 encoding
pass_hash = base64.b64encode(salted_hash)
# Print to the console (stdout)
print(pass_hash.decode("utf-8"))
Output: python hash-password.py test >> t7+JG/ovWbTd9lfrYrPXdFhNZLcO+y56x4z0d8S2OutE6XTE
First implementation failure:
const crypto = require('crypto');
this.password = process.argv[2];
this.salt = crypto.randomBytes(16).toString('hex');
this.password_hash = crypto.pbkdf2Sync(this.password.trim(), this.salt, 1000, 24, `sha256`).toString(`hex`);
console.log(this.password_hash);
Output: node password.js test >> 7611058fb147f5e7a0faab8a806f56f047c1a091d8355544
I was not able to reproduce it in NodeJS, so I collected the stdout result of the executed child process, which is not too elegant.
Second implementation failure:
const crypto = require('crypto');
const utf8 = require('utf8');
this.password = process.argv[2];
this.salt = crypto.randomBytes(4);
this.tmp0 = this.salt + utf8.encode(this.password);
this.tmp1 = crypto.createHash(`sha256`).digest();
this.salted_hash = this.salt + this.tmp1;
this.pass_hash = Buffer.from(this.salted_hash).toString('base64');
console.log(utf8.decode(this.pass_hash));
Output: node password.js test >> Mu+/ve+/vWnvv73vv71C77+977+9HBTvv73vv73vv73ImW/vv70kJ++/vUHvv71k77+977+9TO+/ve+/ve+/vRt4Uu+/vVU=
Can anyone help with the right implementation?
You can do the port to NodeJS more or less 1:1:
var crypto = require('crypto')
// The plain password to encode
var password = Buffer.from('my passphrase', 'utf8') // sample password
// Generate a random 32 bit salt
var salt = crypto.randomBytes(4);
//var salt = Buffer.from('1234', 'utf8'); // for testing, gives pass_hash = MTIzNNcAIpZVAOz2It9VMePU/k4wequLpsQVl+aYDdJa6y9r
// Concatenate with the UTF-8 representation of plaintext password
var tmp0 = Buffer.concat([salt, password])
// Take the SHA256 hash and get the bytes back
var tmp1 = crypto.createHash('sha256').update(tmp0).digest()
// Concatenate the salt again
var salted_hash = Buffer.concat([salt, tmp1])
// Convert to base64 encoding
pass_hash = salted_hash.toString('base64')
// Print to the console (stdout)
console.log(pass_hash)
The code above uses as example password my passphrase. You need to replace the password with yours.
Note that even if the passwords are identical, you cannot directly compare the results of Python and NodeJS code because of the random salt.
Therefore, the commented out line with the UTF-8 encoded salt 1234 can be used to produce a result for comparison with the Python code: MTIzNNcAIpZVAOz2It9VMePU/k4wequLpsQVl+aYDdJa6y9r
The issue in your first implementation is, among other things, the use of PBKDF2, which is not applied in the Python code.
The second implementation is closer to the actual solution. One problem is that the hashing does not take into account the data to be hashed.
Another defect is the use of strings, where some operations implicitly apply UTF-8 encoding, which corrupts the (arbitrary binary) data. To prevent this, binary must be used as encoding instead of UTF-8. Just because of the possible encoding issues, the implementation with strings is less robust here than with buffers.
I have the following in python and want to transfer this to ruby. but i don't get it working. The original source is from here: https://github.com/HurricaneLabs/splunksecrets/blob/177d350a9164791c66139d11b4c63f5db83710b6/splunksecrets.py#L112
My "secret" is : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Python
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=b"disk-encryption",
iterations=1,
backend=default_backend()
)
key = kdf.derive(secret[:254])
print("KDF: %s" % key)
Ruby
require 'openssl'
kdf = OpenSSL::KDF.pbkdf2_hmac(
secret,
salt: 'disk-encryption',
iterations: 1,
length: 32,
hash: 'SHA256'
)
puts "KDF: #{kdf.unpack1('H*')}"
Start and end are somehow the same, but the middle is not matching at all. I removed the \x from the python string to better read it.
org py KDF: b'\x0e\x04\xc2^Z\x04t\xddCh\x97E\xf7\xc9%o\xff\xb8o\x96\x0f\x8aA\xablg\x06\x85\\\xc0\xa3\xde'
Python KDF: b'0e04c2 ^Z04tddCh97Ef7c9%offb8o960f8aAablg0685\\ c0a3de'
Ruby KDF: 0e04c2 5e5a0474dd43689745f7c9256fffb86f960f8a41ab6c6706855c c0a3de
as Topaco said in the comments: everything is okay, its only the printout format which was misleading.
The interpretation of Ruby unpack1('H*') in Python is .hex().
So it should be:
print("KDF: %s" % key.hex())
So the KDF implementation in Ruby was ok and working all the time. Only got confused by printing out the values in the wrong format.
I have been trying for a few days to validate some message signed with a private key in python. Note that the message has been signed using Ruby.
When I sign the same message in python I can verify it no problem. Note that I have already validated that the hash are the same.
Python code:
string_to_encrypt = b"aaaaabbbbbaaaaabbbbbaaaaabbbbbCC"
sha1 = SHA.new()
sha1.update(string_to_encrypt)
# load private key
pkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, open('./license.pem', 'rb').read())
sign_ssl = OpenSSL.crypto.sign(pkey, sha1.digest(), 'RSA-SHA1')
b64_ssl = base64.b64encode(sign_ssl)
Ruby:
string_to_encrypt = "aaaaabbbbbaaaaabbbbbaaaaabbbbbCC"
sha1 = Digest::SHA1.digest(string_to_encrypt)
#sign it
private_key_file = File.join(File.dirname(__FILE__), 'license.pem')
rsa = OpenSSL::PKey::RSA.new(File.read(private_key_file))
signed_key = rsa.private_encrypt(sha1)
#update the license string with it
x = Base64.strict_encode64(signed_key)
I would expect b64_ssl and x to contain the same value and they don't. Could someone explain to me what I missing there?
Neither of these code snippets is actually producing the correct signature.
In the Ruby OpenSSL library you want to be using the sign method, not the private_encrypt method, which is a low level operation that doesn’t do everything required to produce a valid signature.
In both libraries the sign operation performs the hashing for you, you don’t need to do this beforehand. In fact your Python code is actually hashing the data twice.
Try the following Python code:
import OpenSSL
import base64
string_to_encrypt = b"aaaaabbbbbaaaaabbbbbaaaaabbbbbCC"
# load private key
pkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, open('./license.pem', 'rb').read())
sign_ssl = OpenSSL.crypto.sign(pkey, string_to_encrypt, 'SHA1')
b64_ssl = base64.b64encode(sign_ssl)
print(b64_ssl.decode())
which produces the same output as this Ruby code:
require 'openssl'
require 'base64'
string_to_encrypt = "aaaaabbbbbaaaaabbbbbaaaaabbbbbCC"
#sign it
private_key_file = File.join(File.dirname(__FILE__), 'license.pem')
rsa = OpenSSL::PKey::RSA.new(File.read(private_key_file))
signed_key = rsa.sign('sha1', string_to_encrypt)
#update the license string with it
x = Base64.strict_encode64(signed_key)
puts x
I'm rewriting some existing code from Python to Ruby, and I've across a strange error that I can't seem to figure out. Here we have the Python code (which works):
import sha, hmac
data = 'sampledata'
data = data.encode('ascii')
des_key = hmac.new(data + "\0", "SUPERSECRET", sha).digest()[0:8]
Output: 0x64F461D377D9930C
And the Ruby (which I'm new to) code:
require 'openssl'
digest = OpenSSL::Digest::SHA.new
data = 'sampledata'
data.encode!('ascii')
puts OpenSSL::HMAC.hexdigest(digest, "SUPERSECRET", data + "\0")[0, 16]
Output: 0x563FDAF11E63277C
What could be causing this difference?
You made two mistakes:
Python's hmac.new takes key, method, digest - so you should write
hmac.new("SUPERSECRET",data + "\0", sha)
The default digest method for OpenSSL::Digest in Ruby isn't SHA1 (I'm not sure what it is). You should just use:
OpenSSL::HMAC.hexdigest('sha1',"SUPERSECRET",data+"\0")[0,16]
Both methods (first in Python, second in Ruby) return the same output.
In addition to Guy Adini's answer - in Ruby SHA is different from python sha which is sha1 (in sha.py: from hashlib import sha1 as sha):
from hashlib import *
import hmac
data = 'sampledata'
data = data.encode('ascii')
algo = [sha1, sha224, sha256, sha512]
for al in algo:
print al().name, hmac.new("SUPERSECRET", data + "\0", al).hexdigest()[0:16]
produces:
sha1 50c61ea49195f03c
sha224 fd6a418ee0ae21c8
sha256 79deab13bd7b041a
sha512 31561f9c9df69ab2
and in Ruby:
require 'openssl'
data = 'sampledata'
data.encode!('ascii')
%w(sha sha1 sha224 sha256 sha512).each do |al|
puts "#{al}: #{OpenSSL::HMAC::hexdigest(al, "SUPERSECRET", "#{data}\0")[0,16]}"
end
produces:
sha: 563fdaf11e63277c
sha1: 50c61ea49195f03c
sha224: fd6a418ee0ae21c8
sha256: 79deab13bd7b041a
sha512: 31561f9c9df69ab2
I'm looking for a python library that will help me to create an authentication method for a desktop app I'm writing.
I have found several method in web framework such as django or turbogears.
I just want a kind of username-password association stored into a local file.
I can write it by myself, but I'm really it already exists and will be a better solution (I'm not very fluent with encryption).
dbr said:
def hash_password(password):
"""Returns the hashed version of a string
"""
return hasher.new( str(password) ).hexdigest()
This is a really insecure way to hash passwords. You don't want to do this. If you want to know why read the Bycrypt Paper by the guys who did the password hashing system for OpenBSD. Additionally if want a good discussion on how passwords are broken check out this interview with the author of Jack the Ripper (the popular unix password cracker).
Now B-Crypt is great but I have to admit I don't use this system because I didn't have the EKS-Blowfish algorithm available and did not want to implement it my self. I use a slightly updated version of the FreeBSD system which I will post below. The gist is this. Don't just hash the password. Salt the password then hash the password and repeat 10,000 or so times.
If that didn't make sense here is the code:
#note I am using the Python Cryptography Toolkit
from Crypto.Hash import SHA256
HASH_REPS = 50000
def __saltedhash(string, salt):
sha256 = SHA256.new()
sha256.update(string)
sha256.update(salt)
for x in xrange(HASH_REPS):
sha256.update(sha256.digest())
if x % 10: sha256.update(salt)
return sha256
def saltedhash_bin(string, salt):
"""returns the hash in binary format"""
return __saltedhash(string, salt).digest()
def saltedhash_hex(string, salt):
"""returns the hash in hex format"""
return __saltedhash(string, salt).hexdigest()
For deploying a system like this the key thing to consider is the HASH_REPS constant. This is the scalable cost factor in this system. You will need to do testing to determine what is the exceptable amount of time you want to wait for each hash to be computed versus the risk of an offline dictionary based attack on your password file.
Security is hard, and the method I present is not the best way to do this, but it is significantly better than a simple hash. Additionally it is dead simple to implement. So even you don't choose a more complex solution this isn't the worst out there.
hope this helps,
Tim
I think you should make your own authentication method as you can make it fit your application best but use a library for encryption, such as pycrypto or some other more lightweight library.
btw, if you need windows binaries for pycrypto you can get them here
Treat the following as pseudo-code..
try:
from hashlib import sha as hasher
except ImportError:
# You could probably exclude the try/except bit,
# but older Python distros dont have hashlib.
try:
import sha as hasher
except ImportError:
import md5 as hasher
def hash_password(password):
"""Returns the hashed version of a string
"""
return hasher.new( str(password) ).hexdigest()
def load_auth_file(path):
"""Loads a comma-seperated file.
Important: make sure the username
doesn't contain any commas!
"""
# Open the file, or return an empty auth list.
try:
f = open(path)
except IOError:
print "Warning: auth file not found"
return {}
ret = {}
for line in f.readlines():
split_line = line.split(",")
if len(split_line) > 2:
print "Warning: Malformed line:"
print split_line
continue # skip it..
else:
username, password = split_line
ret[username] = password
#end if
#end for
return ret
def main():
auth_file = "/home/blah/.myauth.txt"
u = raw_input("Username:")
p = raw_input("Password:") # getpass is probably better..
if auth_file.has_key(u.strip()):
if auth_file[u] == hash_password(p):
# The hash matches the stored one
print "Welcome, sir!"
Instead of using a comma-separated file, I would recommend using SQLite3 (which could be used for other settings and such.
Also, remember that this isn't very secure - if the application is local, evil users could probably just replace the ~/.myauth.txt file.. Local application auth is difficult to do well. You'll have to encrypt any data it reads using the users password, and generally be very careful.
If you want simple, then use a dictionary where the keys are the usernames and the values are the passwords (encrypted with something like SHA256). Pickle it to/from disk (as this is a desktop application, I'm assuming the overhead of keeping it in memory will be negligible).
For example:
import pickle
import hashlib
# Load from disk
pwd_file = "mypasswords"
if os.path.exists(pwd_file):
pwds = pickle.load(open(pwd_file, "rb"))
else:
pwds = {}
# Save to disk
pickle.dump(pwds, open(pwd_file, "wb"))
# Add password
pwds[username] = hashlib.sha256(password).hexdigest()
# Check password
if pwds[username] = hashlib.sha256(password).hexdigest():
print "Good"
else:
print "No match"
Note that this stores the passwords as a hash - so they are essentially unrecoverable. If you lose your password, you'd get allocated a new one, not get the old one back.
import hashlib
import random
def gen_salt():
salt_seed = str(random.getrandbits(128))
salt = hashlib.sha256(salt_seed).hexdigest()
return salt
def hash_password(password, salt):
h = hashlib.sha256()
h.update(salt)
h.update(password)
return h.hexdigest()
#in datastore
password_stored_hash = "41e2282a9c18a6c051a0636d369ad2d4727f8c70f7ddeebd11e6f49d9e6ba13c"
salt_stored = "fcc64c0c2bc30156f79c9bdcabfadcd71030775823cb993f11a4e6b01f9632c3"
password_supplied = 'password'
password_supplied_hash = hash_password(password_supplied, salt_stored)
authenticated = (password_supplied_hash == password_stored_hash)
print authenticated #True
see also gae-authenticate-to-a-3rd-party-site
Use " md5 " it's much better than base64
>>> import md5
>>> hh = md5.new()
>>> hh.update('anoop')
>>> hh.digest
<built-in method digest of _hashlib.HASH object at 0x01FE1E40>