I want to create a Postgres user with the CREATE USER command and an already hashed digest for the password. After much searching, I thought it was only possible with MD5 until I found this link. I've verified that works like so:
CREATE USER test_user WITH LOGIN PASSWORD 'SCRAM-SHA-256$4096:H45+UIZiJUcEXrB9SHlv5Q==$I0mc87UotsrnezRKv9Ijqn/zjWMGPVdy1zHPARAGfVs=:nSjwT9LGDmAsMo+GqbmC2X/9LMgowTQBjUQsl45gZzA=';
I can then log into that user with the password, which the article doesn't necessarily say but it's "postgres". Now that I know it's possible, how using .NET 5 can I generate a scram-sha-256 digest that Postgres 13 will accept? I've seen other Postgres articles using the outdated MD5 hash where the username is concatenated with the password before hashing. Does that need to happen with the new scram-sha-256 as well? I couldn't find much information on this topic anywhere.
Someone built a Go tool to do this:
https://github.com/supercaracal/scram-sha-256
Here's a python 3 port based on that Go project:
from base64 import standard_b64encode
from hashlib import pbkdf2_hmac, sha256
from os import urandom
import hmac
import sys
salt_size = 16
digest_len = 32
iterations = 4096
def b64enc(b: bytes) -> str:
return standard_b64encode(b).decode('utf8')
def pg_scram_sha256(passwd: str) -> str:
salt = urandom(salt_size)
digest_key = pbkdf2_hmac('sha256', passwd.encode('utf8'), salt, iterations,
digest_len)
client_key = hmac.digest(digest_key, 'Client Key'.encode('utf8'), 'sha256')
stored_key = sha256(client_key).digest()
server_key = hmac.digest(digest_key, 'Server Key'.encode('utf8'), 'sha256')
return (
f'SCRAM-SHA-256${iterations}:{b64enc(salt)}'
f'${b64enc(stored_key)}:{b64enc(server_key)}'
)
def print_usage():
print("Usage: provide single password argument to encrypt")
sys.exit(1)
def main():
args = sys.argv[1:]
if args and len(args) > 1:
print_usage()
if args:
passwd = args[0]
else:
passwd = sys.stdin.read().strip()
if not passwd:
print_usage()
print(pg_scram_sha256(passwd))
if __name__ == "__main__":
main()
NOTE: Passing passwords directly on the command line can leak secrets into shell history, make secrets visible in process output to other users on the system, etc. If you are using this tool to generate real password hashes, take care to protect it from such leakage (e.g. write the plain text password to a text file with secure permissions and redirect it to the script's input using python scram256.py < secret-password.txt). Also take care to use non-reused randomly generated passwords of sufficient length and other best practices that are beyond the scope of this question/answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With