@ -21,14 +21,18 @@ Dependencies:
hashlib : For SHA - 256 hashing
hashlib : For SHA - 256 hashing
base64 : For JWT encoding
base64 : For JWT encoding
argparse : For command - line argument parsing
argparse : For command - line argument parsing
sqlite3 : For database operations
pathlib : For path handling
pathlib : For path handling
Usage :
Usage :
python did_generator . py < admin_did >
python did_generator . py [ options ]
Example :
Options :
python did_generator . py did : ethr : 0x1234 . . .5678
- - admin - did < did > Override default admin DID
- - api - url < url > Override default API endpoint
Environment :
Default Admin DID : did : ethr : 0x0000694B58C2cC69658993A90D3840C560f2F51F
Default API URL : https : / / test - api . endorser . ch / api / v2 / claim
"""
"""
from eth_account import Account
from eth_account import Account
@ -41,34 +45,39 @@ import requests
import argparse
import argparse
import secrets
import secrets
import hashlib
import hashlib
import sqlite3
from pathlib import Path
from pathlib import Path
class DIDRegistration :
class DIDRegistration :
"""
"""
Handles the creation and registration of DIDs with admin authorization .
Handles the creation and registration of DIDs with admin authorization .
This class manages the complete lifecycle of DID creation :
This class manages the complete lifecycle of DID creation and registration :
1. Generating secure Ethereum keypairs
1. Generating secure Ethereum keypairs with compressed public keys
2. Creating DIDs from public keys
2. Creating DIDs from public keys in did : ethr format
3. Signing registration claims
3. Signing registration claims with admin credentials
4. Submitting registration to the endorser . ch API
4. Submitting registration to the endorser . ch API
The registration process uses two keypairs :
1. Admin keypair : Used to sign and authorize the registration
2. New DID keypair : The identity being registered
Attributes :
Attributes :
api_url ( str ) : Endpoint for DID registration
api_url ( str ) : Endpoint for DID registration
admin_did ( str ) : Administrator DID for authorization
admin_keypair ( dict ) : Admin ' s credentials containing:
- did : Admin ' s DID in did:ethr format
- private_key : Admin ' s private key for signing
"""
"""
def __init__ ( self , admin_did : str ) :
def __init__ ( self , admin_keypair : dict , api_url : str = None ) :
"""
"""
Initialize DID registration with admin credentials .
Initialize DID registration with admin credentials .
Args :
Args :
admin_did ( str ) : Administrator DID for authorizing registrations
admin_keypair ( dict ) : Admin ' s DID and private key for signing
Format : did : ethr : 0 x . . .
api_url ( str , optional ) : Override default API URL
"""
"""
self . api_url = " https://api.endorser.ch/api/v2/claim "
self . api_url = api_url or " https://test- api.endorser.ch/api/v2/claim "
self . admin_did = admin_did
self . admin_keypair = admin_keypair # Store full admin keypair
Account . enable_unaudited_hdwallet_features ( )
Account . enable_unaudited_hdwallet_features ( )
def create_keypair ( self ) - > dict :
def create_keypair ( self ) - > dict :
@ -76,7 +85,7 @@ class DIDRegistration:
Generate a new Ethereum keypair and associated DID .
Generate a new Ethereum keypair and associated DID .
Creates a secure random keypair and formats it for use with the
Creates a secure random keypair and formats it for use with the
endorser . ch API , including compressed public key format matching
endorser . ch API . Uses compressed public key format to match
ethers . js implementation .
ethers . js implementation .
Returns :
Returns :
@ -154,41 +163,30 @@ class DIDRegistration:
) . decode ( ) . rstrip ( ' = ' )
) . decode ( ) . rstrip ( ' = ' )
message = f " { header_b64 } . { payload_b64 } "
message = f " { header_b64 } . { payload_b64 } "
# Hash the message with sha256 first (like did-jwt)
# Hash the message with sha256
message_hash = hashlib . sha256 ( message . encode ( ) ) . digest ( )
message_hash = hashlib . sha256 ( message . encode ( ) ) . digest ( )
# Sign the hash directly (not as an Ethereum message)
# Sign using eth_keys directly
private_key_bytes = bytes . fromhex ( private_key )
private_key_bytes = bytes . fromhex ( private_key )
account = Account . from_k ey( private_key_bytes )
private_key_obj = keys . PrivateK ey( private_key_bytes )
signed = accoun t . sign_message ( message_hash )
signatur e = priv ate_key_obj . sign_msg_hash ( message_hash )
# Get r and s from signature
# Get r and s from signature
r = signed . r . to_bytes ( 32 , ' big ' )
r = signatur e . r . to_bytes ( 32 , ' big ' )
s = signed . s . to_bytes ( 32 , ' big ' )
s = signatur e . s . to_bytes ( 32 , ' big ' )
signature_bytes = r + s
signature_bytes = r + s
signature = base64 . urlsafe_b64encode ( signature_bytes ) . decode ( ) . rstrip ( ' = ' )
# Format signature
signature_b64 = base64 . urlsafe_b64encode ( signature_bytes ) . decode ( ) . rstrip ( ' = ' )
return f " { message } . { signature } "
return f " { message } . { signature_b64 } "
def create_jwt ( self , keypair : dict ) - > str :
def create_jwt ( self , new_did : str ) - > str :
"""
"""
Create a signed JWT for DID registration .
Create a signed JWT for DID registration
Creates a registration claim that includes :
1. Admin DID as agent
2. New DID as participant
3. Timestamp and expiration
4. Required credential context
Args :
Args :
keypair ( dict ) : Generated keypair information
new_did ( str ) : The DID being registered
Returns :
str : Signed JWT containing registration claim
Note :
Matches TypeScript implementation exactly for compatibility
"""
"""
now = int ( time . time ( ) )
now = int ( time . time ( ) )
@ -196,12 +194,11 @@ class DIDRegistration:
register_claim = {
register_claim = {
" @context " : " https://schema.org " ,
" @context " : " https://schema.org " ,
" @type " : " RegisterAction " ,
" @type " : " RegisterAction " ,
" agent " : { " did " : self . admin_did } ,
" agent " : { " did " : self . admin_keypair [ ' did ' ] } ,
" participant " : { " did " : keypair [ ' did ' ] } ,
" participant " : { " did " : new_did } ,
" object " : " endorser.ch "
" object " : " endorser.ch "
}
}
# Match the TypeScript vcPayload exactly - no iss field
payload = {
payload = {
" iat " : now ,
" iat " : now ,
" exp " : now + 300 ,
" exp " : now + 300 ,
@ -215,8 +212,12 @@ class DIDRegistration:
print ( f " \n Debug - JWT payload: { json . dumps ( payload , indent = 2 ) } " )
print ( f " \n Debug - JWT payload: { json . dumps ( payload , indent = 2 ) } " )
# Sign with new DID's private key
# Sign with admin's private key
return self . sign_jwt ( payload , keypair [ ' private_key ' ] , keypair [ ' did ' ] )
return self . sign_jwt (
payload ,
self . admin_keypair [ ' private_key ' ] ,
self . admin_keypair [ ' did ' ]
)
def register_did ( self , jwt_token : str ) - > dict :
def register_did ( self , jwt_token : str ) - > dict :
"""
"""
@ -281,30 +282,6 @@ class DIDRegistration:
' error ' : f " Request failed: { str ( e ) } "
' error ' : f " Request failed: { str ( e ) } "
}
}
def get_root_did_from_db ( db_path : str = None ) - > str :
""" Get admin DID from the most recent registration. """
try :
db_path = db_path or str ( Path . home ( ) / ' .endorser ' / ' accounts.db ' )
conn = sqlite3 . connect ( db_path )
cursor = conn . cursor ( )
cursor . execute ( """
SELECT issuer as did
FROM registration
WHERE type = ' RegisterAction '
ORDER BY issuanceDate DESC
LIMIT 1
""" )
result = cursor . fetchone ( )
if not result :
raise ValueError ( ' No admin DID found in registration table ' )
return result [ 0 ]
finally :
if ' conn ' in locals ( ) :
conn . close ( )
def main ( ) :
def main ( ) :
"""
"""
Main entry point for DID generation script .
Main entry point for DID generation script .
@ -315,7 +292,7 @@ def main():
3. Result output and error display
3. Result output and error display
Usage :
Usage :
python did_generator . py < admin_did >
python did_generator . py [ options ]
"""
"""
parser = argparse . ArgumentParser (
parser = argparse . ArgumentParser (
description = ' Generate a DID with admin authorization '
description = ' Generate a DID with admin authorization '
@ -326,38 +303,39 @@ def main():
required = False
required = False
)
)
parser . add_argument (
parser . add_argument (
' --db-path ' ,
' --api-url ' ,
help = ' Path to SQLite database containing root DID ' ,
help = ' Override API URL ' ,
required = False
default = " https://test-api.endorser.ch/api/v2/claim "
)
)
args = parser . parse_args ( )
args = parser . parse_args ( )
admin_keypair = {
' did ' : ' did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F ' ,
' private_key ' : ' 2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b '
}
admin_did = args . admin_did
admin_did = args . admin_did
if not admin_did :
if not admin_did :
try :
admin_did = admin_keypair [ ' did ' ]
admin_did = get_root_did_from_db ( args . db_path )
print ( f " Found root DID in database: { admin_did } " )
print ( f " Admin DID: { admin_did } " )
except ( FileNotFoundError , ValueError , sqlite3 . Error ) as e :
print ( f " API URL: { args . api_url } " )
print ( f " Error: { str ( e ) } " )
print ( " Please provide --admin-did argument " )
return
print ( ' Starting DID Generation... \n ' )
print ( ' Starting DID Generation... \n ' )
registrar = DIDRegistration ( admin_did )
registrar = DIDRegistration ( admin_keypair , args . api_url )
print ( " Generating new keypair... " )
print ( " Generating new keypair... " )
keypair = registrar . create_keypair ( )
new_ keypair = registrar . create_keypair ( )
print ( " \n Generated DID Details: " )
print ( " \n Generated DID Details: " )
print ( " ---------------------- " )
print ( " ---------------------- " )
print ( f " DID: { keypair [ ' did ' ] } " )
print ( f " DID: { new_ keypair[ ' did ' ] } " )
print ( f " Admin DID: { admin_did } " )
print ( f " Admin DID: { admin_did } " )
print ( f " Address: { keypair [ ' address ' ] } " )
print ( f " Address: { new_ keypair[ ' address ' ] } " )
print ( f " Private Key: { keypair [ ' private_key ' ] } " )
print ( f " Private Key: { new_ keypair[ ' private_key ' ] } " )
print ( f " Public Key: { keypair [ ' public_key ' ] } \n " )
print ( f " Public Key: { new_ keypair[ ' public_key ' ] } \n " )
print ( " Creating JWT... " )
print ( " Creating JWT... " )
jwt_token = registrar . create_jwt ( keypair )
jwt_token = registrar . create_jwt ( new_ keypair[ ' did ' ] )
print ( ' \n Successfully generated DID with admin authorization! ' )
print ( ' \n Successfully generated DID with admin authorization! ' )
print ( f ' Registration JWT: { jwt_token [ : 50 ] } ... ' )
print ( f ' Registration JWT: { jwt_token [ : 50 ] } ... ' )