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:

  1. You're using binary output from sha1() in PHP, but hex output in python. Use digest(), not hexdigest(). This is why your output is longer in python.

  2. Your timestamp format is incorrect.

    The PHP format DATE_ATOM is "Y-m-d\TH:i:sP", where P outputs the UTC offset in the format +00:00. Unfortunately Python's strftime() doesn't seem to have an equivalent, but it's all in UTC anyway and your python code simply specifies the static string Z. 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')

  3. You're using SECRETPASSWORD in PHP and MYSECRETPASSWORD 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=