problem converting php code to python generating password digest for soap requests
<?php
$password = "SECRETPASSWORD";
$nonce = random_bytes(32); # requires PHP 7
date_default_timezone_set("UTC");
$timestamp = date(DATE_ATOM);
$encodedNonce = base64_encode($nonce);
$passSHA = base64_encode(sha1($nonce . $timestamp . sha1($password, true), true));
?>
it generates the below result with a 28 characters password digest, which I am using in soap requests, and it works fine
password_digest = '/pBYmwwc2cM87CUr8oB4Wkmyc0Q='
nonce = '���>�!��g��q�[�`�R��=J�o�'
nonce_base_64_encode = 'uqbkProhsR3JZxjC93HWW8BghQFSgqo9Sv9vGgUa4hs='
timestamp = '2022-01-13T18:28:52+00:00'
I need to do this same in python, but python is somehow generating longer password_digest and the soap request fails. I don't know if I am not generating the random_bytes correctly in python or some other issue. Below is python code:
import secrets
import hashlib
import datetime
clearPassword = 'MYSECRETPASSWORD'
created_at_timestamp_utc = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
def getSha1String(string):
stringInBytes = string.encode('ascii')
hash_object = hashlib.sha1(stringInBytes)
hex_dig = hash_object.hexdigest()
return hex_dig
def getBase64NonceString(nonce):
nonce_bytes = nonce.encode('ascii')
base64_bytes = base64.b64encode(nonce_bytes)
base64_nonce = base64_bytes.decode('ascii')
return base64_nonce
def getBase64String(string):
string_bytes = string.encode('ascii')
base64_bytes = base64.b64encode(string_bytes)
base64_string = base64_bytes.decode('ascii')
return base64_string
nonce = secrets.token_bytes(32)
base64_nonce = getBase64Nonce(nonce)
sha1_password = getSha1String(clearPassword)
password_digest = getBase64String(getSha1String(str(nonce) + created_at_timestamp_utc + sha1_password))
Your python code has 3 problems:
-
You're using binary output from
sha1()
in PHP, but hex output in python. Usedigest()
, nothexdigest()
. This is why your output is longer in python. -
Your timestamp format is incorrect.
The PHP format
DATE_ATOM
is"Y-m-d\TH:i:sP"
, whereP
outputs the UTC offset in the format+00:00
. Unfortunately Python'sstrftime()
doesn't seem to have an equivalent, but it's all in UTC anyway and your python code simply specifies the static stringZ
. So change that to+00:00
, otherwise your tokens won't match.Eg:
timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S+00:00')
-
You're using
SECRETPASSWORD
in PHP andMYSECRETPASSWORD
in python, and I am emabarrased at how long I bashed my head against that one without noticing.
Succint working code:
import hashlib, datetime, base64
password = 'SECRETPASSWORD'
timestamp = '2022-01-13T18:28:52+00:00'
nonce = base64.b64decode('uqbkProhsR3JZxjC93HWW8BghQFSgqo9Sv9vGgUa4hs=')
def quickSHA(input):
return hashlib.sha1(input).digest()
def makeToken(password, timestamp, nonce):
return base64.b64encode(
quickSHA( nonce + timestamp + quickSHA(password) )
)
print makeToken(password, timestamp, nonce)
Output: /pBYmwwc2cM87CUr8oB4Wkmyc0Q=