3131from urllib3 .exceptions import InsecureRequestWarning
3232import uvicorn
3333from Framework .Built_In_Automation .Web .Selenium .utils import ChromeExtensionDownloader
34+ from cryptography .hazmat .primitives .asymmetric import rsa
35+ from cryptography .hazmat .primitives import serialization
36+
37+ from settings import ZEUZ_NODE_PRIVATE_RSA_KEYS_DIR
3438
3539
3640print (
@@ -645,7 +649,6 @@ def update_machine(dependency, should_print=True):
645649 if data ["registered" ]:
646650 if should_print :
647651 rich_print = console .print
648- # rich_print(":green_circle: Zeuz Node is online: ", end="")
649652 rich_print (":green_circle: " + data ["name" ], style = "bold cyan" , end = "" )
650653 print (" is online\n " )
651654 CommonUtil .ExecLog (
@@ -728,6 +731,151 @@ def get_folder_creation_time(folder_path):
728731 return dt .fromtimestamp (creation_time ).date ()
729732
730733
734+ def generate_rsa_key ():
735+ """Generate a new RSA private key and save it to the rsa_private_keys folder."""
736+ console = Console ()
737+
738+ key_folder = ZEUZ_NODE_PRIVATE_RSA_KEYS_DIR
739+ key_folder .mkdir (parents = True , exist_ok = True )
740+
741+ private_key = rsa .generate_private_key (
742+ public_exponent = 65537 ,
743+ key_size = 2048 ,
744+ )
745+
746+ timestamp = dt .now ().strftime ("%Y%m%d_%H%M%S" )
747+ key_filename = f"private_key_{ timestamp } .pem"
748+ key_path = key_folder / key_filename
749+
750+ # Save private key
751+ with open (key_path , 'wb' ) as f :
752+ f .write (private_key .private_bytes (
753+ encoding = serialization .Encoding .PEM ,
754+ format = serialization .PrivateFormat .PKCS8 ,
755+ encryption_algorithm = serialization .NoEncryption ()
756+ ))
757+
758+ # Generate public key
759+ public_key = private_key .public_key ()
760+ public_key_pem = public_key .public_bytes (
761+ encoding = serialization .Encoding .PEM ,
762+ format = serialization .PublicFormat .SubjectPublicKeyInfo
763+ ).decode ('utf-8' )
764+
765+ console .print (f"\n [green]✓[/green] RSA private key generated successfully!" )
766+ console .print (f"[cyan]Location:[/cyan] { key_path } " )
767+ console .print (f"\n [cyan]Public Key:[/cyan]" )
768+ console .print (public_key_pem )
769+
770+ return key_path
771+
772+
773+ def add_existing_rsa_key (key_content : str ):
774+ """Copy an existing RSA private key to the rsa_private_keys folder."""
775+ console = Console ()
776+
777+ key_folder = ZEUZ_NODE_PRIVATE_RSA_KEYS_DIR
778+ key_folder .mkdir (parents = True , exist_ok = True )
779+
780+ try :
781+ private_key = serialization .load_pem_private_key (
782+ key_content .encode ('utf-8' ),
783+ password = None ,
784+ )
785+ except Exception as e :
786+ console .print (f"[red]Error:[/red] Invalid PEM private key. { e } " )
787+ return False
788+
789+ new_public_key = private_key .public_key ()
790+ new_public_key_pem = new_public_key .public_bytes (
791+ encoding = serialization .Encoding .PEM ,
792+ format = serialization .PublicFormat .SubjectPublicKeyInfo
793+ ).decode ('utf-8' )
794+
795+ pem_files = list (key_folder .glob ("*.pem" ))
796+ for pem_file in pem_files :
797+ try :
798+ with open (pem_file , 'rb' ) as f :
799+ existing_private_key = serialization .load_pem_private_key (f .read (), password = None )
800+ existing_public_key = existing_private_key .public_key ()
801+ existing_public_key_pem = existing_public_key .public_bytes (
802+ encoding = serialization .Encoding .PEM ,
803+ format = serialization .PublicFormat .SubjectPublicKeyInfo
804+ ).decode ('utf-8' )
805+ if existing_public_key_pem == new_public_key_pem :
806+ console .print (f"[yellow]Warning:[/yellow] This private key already exists as { pem_file .name } . Not adding duplicate." )
807+ return False
808+ except Exception as e :
809+ console .print (f"[red]Error checking existing key { pem_file .name } :[/red] { e } " )
810+ continue
811+
812+ # Copy the key with a timestamp
813+ timestamp = dt .now ().strftime ("%Y%m%d_%H%M%S" )
814+ new_filename = f"imported_key_{ timestamp } .pem"
815+ new_key_path = key_folder / new_filename
816+
817+ with open (new_key_path , 'wb' ) as f :
818+ f .write (private_key .private_bytes (
819+ encoding = serialization .Encoding .PEM ,
820+ format = serialization .PrivateFormat .PKCS8 ,
821+ encryption_algorithm = serialization .NoEncryption ()
822+ ))
823+
824+ console .print (f"\n [green]✓[/green] Private key imported successfully!" )
825+ console .print (f"[cyan]To:[/cyan] { new_key_path } " )
826+
827+ # Show the public key
828+ console .print (f"\n [cyan]Public Key:[/cyan]" )
829+ console .print (new_public_key_pem )
830+
831+ return True
832+
833+
834+ def show_existing_rsa_keys ():
835+ """List all existing RSA private keys and show their public keys."""
836+ console = Console ()
837+
838+ key_folder = ZEUZ_NODE_PRIVATE_RSA_KEYS_DIR
839+
840+ if not key_folder .exists ():
841+ console .print (f"[yellow]![/yellow] No private keys folder found at: { key_folder } " )
842+ console .print ("[yellow]![/yellow] Use --generate-key to create a new key." )
843+ return
844+
845+ pem_files = list (key_folder .glob ("*.pem" ))
846+
847+ if not pem_files :
848+ console .print (f"[yellow]![/yellow] No private keys found in: { key_folder } " )
849+ console .print ("[yellow]![/yellow] Use --generate-key to create a new key." )
850+ return
851+
852+ console .print (f"\n [cyan]Found { len (pem_files )} private key(s) in:[/cyan] { key_folder } \n " )
853+
854+ for idx , pem_file in enumerate (pem_files , 1 ):
855+ console .print (f"[bold cyan]Key #{ idx } :[/bold cyan] { pem_file .name } " )
856+ console .print (f"[dim]Path:[/dim] { pem_file } " )
857+
858+ try :
859+ with open (pem_file , 'rb' ) as f :
860+ private_key = serialization .load_pem_private_key (f .read (), password = None )
861+
862+ # Generate and show public key
863+ public_key = private_key .public_key ()
864+ public_key_pem = public_key .public_bytes (
865+ encoding = serialization .Encoding .PEM ,
866+ format = serialization .PublicFormat .SubjectPublicKeyInfo
867+ ).decode ('utf-8' )
868+
869+ console .print (f"[dim]Public Key:[/dim]" )
870+ console .print (public_key_pem )
871+
872+ except Exception as e :
873+ console .print (f"[red]Error loading key:[/red] { e } " )
874+
875+ if idx < len (pem_files ):
876+ console .print ("---" )
877+
878+
731879def command_line_args () -> Path | None :
732880 """
733881 This function handles command line arguments for configuring and running Zeuz Node.
@@ -762,6 +910,15 @@ def command_line_args() -> Path | None:
762910 Example 9 - Advanced options:
763911 python node_cli.py -spu -sbl -slg
764912
913+ Example 10 - Generate RSA private key:
914+ python node_cli.py -gpk
915+
916+ Example 11 - Add existing RSA private key:
917+ python node_cli.py -apk /path/to/private_key.pem
918+
919+ Example 12 - Show existing RSA keys and their public keys:
920+ python node_cli.py -spk
921+
765922 Use -h or --help to see full documentation of all available arguments.
766923 """
767924 # try:
@@ -848,6 +1005,27 @@ def command_line_args() -> Path | None:
8481005 metavar = "" ,
8491006 )
8501007
1008+ # RSA key management arguments
1009+ parser_object .add_argument (
1010+ "-gpk" ,
1011+ "--generate-private-key" ,
1012+ action = "store_true" ,
1013+ help = "Generate a new RSA private key for encrypting secrets" ,
1014+ )
1015+ parser_object .add_argument (
1016+ "-apk" ,
1017+ "--add-private-key" ,
1018+ action = "store" ,
1019+ help = "Add an existing RSA private key (provide content of the .pem file)" ,
1020+ metavar = "" ,
1021+ )
1022+ parser_object .add_argument (
1023+ "-spk" ,
1024+ "--show-private-keys" ,
1025+ action = "store_true" ,
1026+ help = "Show all existing RSA private keys and their public keys" ,
1027+ )
1028+
8511029 all_arguments = parser_object .parse_args ()
8521030
8531031 server = all_arguments .server
@@ -864,6 +1042,26 @@ def command_line_args() -> Path | None:
8641042 chrome_fetch = all_arguments .chrome_fetch
8651043 chrome_cleanup = all_arguments .chrome_cleanup
8661044
1045+ # RSA key management options
1046+ generate_key = all_arguments .generate_private_key
1047+ add_key = all_arguments .add_private_key
1048+ show_keys = all_arguments .show_private_keys
1049+
1050+ # Handle RSA key management commands
1051+ if generate_key :
1052+ generate_rsa_key ()
1053+ sys .exit (0 )
1054+
1055+ if add_key :
1056+ if add_existing_rsa_key (add_key ):
1057+ sys .exit (0 )
1058+ else :
1059+ sys .exit (1 )
1060+
1061+ if show_keys :
1062+ show_existing_rsa_keys ()
1063+ sys .exit (0 )
1064+
8671065 # Update chrome extension download settings if specified
8681066 if chrome_fetch is not None :
8691067 os .environ ["CHROME_DAYS_BEFORE_FETCH" ] = str (chrome_fetch )
0 commit comments