From 659730eff2c6043191fd5bdc328158712051ac72 Mon Sep 17 00:00:00 2001 From: absorprofess Date: Mon, 17 Mar 2025 14:45:32 +0800 Subject: [PATCH 1/7] add requestV3Convert --- .../cosigner/co_signer_converter.py | 22 +++++++++++++++++++ safeheron_api_sdk_python/tools.py | 11 ++++++++++ 2 files changed, 33 insertions(+) diff --git a/safeheron_api_sdk_python/cosigner/co_signer_converter.py b/safeheron_api_sdk_python/cosigner/co_signer_converter.py index 68dd459..172ef55 100644 --- a/safeheron_api_sdk_python/cosigner/co_signer_converter.py +++ b/safeheron_api_sdk_python/cosigner/co_signer_converter.py @@ -65,6 +65,28 @@ def request_convert(self, co_signer_call_back): return json.loads(r.decode()) + def request_convert_v3(self, co_signer_call_back): + platform_rsa_pk = get_rsa_key(PEM_PUBLIC_HEAD + self.api_pub_key + PEM_PUBLIC_END) + required_keys = { + 'version', + 'sig', + 'bizContent', + 'timestamp', + } + + missing_keys = required_keys.difference(co_signer_call_back.keys()) + if missing_keys: + raise Exception(co_signer_call_back) + + sig = co_signer_call_back.pop('sig') + + need_sign_message = sort_request(co_signer_call_back) + v = rsa_pass_verify(platform_rsa_pk, need_sign_message, sig) + if not v: + raise Exception("rsa verify: false") + return json.loads(b64decode(co_signer_call_back['bizContent']).decode()) + + # It has been Deprecated,Please use convertCoSignerResponseWithNewCryptoType def response_converter(self, co_signer_response: CoSignerResponse): platform_rsa_pk = get_rsa_key(PEM_PUBLIC_HEAD + self.api_pub_key + PEM_PUBLIC_END) diff --git a/safeheron_api_sdk_python/tools.py b/safeheron_api_sdk_python/tools.py index 9c85e1a..4660d8e 100644 --- a/safeheron_api_sdk_python/tools.py +++ b/safeheron_api_sdk_python/tools.py @@ -8,6 +8,7 @@ from Cryptodome.Hash import SHA256, SHA1 from Cryptodome import Random from Crypto.Cipher import PKCS1_OAEP +from Cryptodome.Signature import pss import json import time import requests @@ -157,6 +158,16 @@ def rsa_verify(public_key, message_hash: bytes, signature): except Exception as e: raise Exception("rsa sign error: %s" % e) +def rsa_pass_verify(public_key, message_hash: bytes, signature): + try: + verifier = pss.new(public_key) + digest = SHA256.new() + digest.update(message_hash) + verifier.verify(digest, b64decode(signature)) + return True + except Exception as e: + raise Exception("rsa sign error: %s" % e) + def sort_request(r: dict): sortData = json.dumps(r, sort_keys=True).replace(' ', '') From 39c19fe256eb44373c067f0bcb2e7c633bc298c5 Mon Sep 17 00:00:00 2001 From: absorprofess Date: Fri, 21 Mar 2025 14:14:11 +0800 Subject: [PATCH 2/7] add response_converter_v3 --- .../cosigner/co_signer_converter.py | 26 +++++++++++++++++++ safeheron_api_sdk_python/tools.py | 10 +++++++ 2 files changed, 36 insertions(+) diff --git a/safeheron_api_sdk_python/cosigner/co_signer_converter.py b/safeheron_api_sdk_python/cosigner/co_signer_converter.py index 172ef55..e6bed9a 100644 --- a/safeheron_api_sdk_python/cosigner/co_signer_converter.py +++ b/safeheron_api_sdk_python/cosigner/co_signer_converter.py @@ -9,6 +9,14 @@ def __init__(self): self.txKey = None +class CoSignerResponseV3: + def __init__(self): + # approve + self.action = None + # txKey + self.approvalId = None + + class CoSignerConverter: def __init__(self, config): @@ -150,3 +158,21 @@ def response_converter_with_new_crypto_type(self, co_signer_response: CoSignerRe ret['rsaType'] = ECB_OAEP_TYPE ret['aesType'] = GCM_TYPE return ret + + def response_converter_v3(self, co_signer_response: CoSignerResponseV3): + api_user_rsa_sk = get_rsa_key(self.biz_privKey) + ret = dict() + response_data = json.dumps(co_signer_response.__dict__).replace('\'', '\"').replace('\n', '').encode('utf-8') + + if response_data is not None: + ret['bizContent'] = b64encode(response_data).decode() + + ret['timestamp'] = str(int(time.time() * 1000)) + ret['code'] = str('200') + ret['version'] = str('v3') + ret['message'] = str('SUCCESS') + + # 4 sign request + need_sign_message = sort_request(ret) + ret['sig'] = rsa_pss_sign(api_user_rsa_sk, need_sign_message) + return ret diff --git a/safeheron_api_sdk_python/tools.py b/safeheron_api_sdk_python/tools.py index 4660d8e..46c75f4 100644 --- a/safeheron_api_sdk_python/tools.py +++ b/safeheron_api_sdk_python/tools.py @@ -148,6 +148,16 @@ def rsa_sign(private_key, message_hash: bytes): except Exception as e: raise Exception("rsa sign error: %s" % e) +def rsa_pss_sign(private_key, message_hash: bytes): + try: + signer = pss.new(private_key) + digest = SHA256.new() + digest.update(message_hash) + signed = signer.sign(digest) + return b64encode(signed).decode() + except Exception as e: + raise Exception("rsa sign error: %s" % e) + def rsa_verify(public_key, message_hash: bytes, signature): try: From 8bd56dd78db6bf4f9bcd2e2a332ad20a91d89578 Mon Sep 17 00:00:00 2001 From: absorprofess Date: Fri, 21 Mar 2025 15:54:20 +0800 Subject: [PATCH 3/7] add response_converter_v3 --- safeheron_api_sdk_python/cosigner/co_signer_converter.py | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/safeheron_api_sdk_python/cosigner/co_signer_converter.py b/safeheron_api_sdk_python/cosigner/co_signer_converter.py index e6bed9a..24662d9 100644 --- a/safeheron_api_sdk_python/cosigner/co_signer_converter.py +++ b/safeheron_api_sdk_python/cosigner/co_signer_converter.py @@ -11,9 +11,9 @@ def __init__(self): class CoSignerResponseV3: def __init__(self): - # approve + # action self.action = None - # txKey + # approvalId self.approvalId = None diff --git a/setup.py b/setup.py index 715ec50..aacdad0 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ with open("README.rst", "r") as f: long_description = f.read() setup(name='safeheron_api_sdk_python', - version='1.1.13', + version='1.1.14', description='Python for Safeheron API', long_description=long_description, author='safeheron', From dd30a12b487a3b92e843aa7abf8d21a2eeffcbd9 Mon Sep 17 00:00:00 2001 From: absorprofess Date: Mon, 24 Mar 2025 14:41:41 +0800 Subject: [PATCH 4/7] add response_converter_v3 --- .../cosigner/co_signer_converter.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/safeheron_api_sdk_python/cosigner/co_signer_converter.py b/safeheron_api_sdk_python/cosigner/co_signer_converter.py index 24662d9..52302d9 100644 --- a/safeheron_api_sdk_python/cosigner/co_signer_converter.py +++ b/safeheron_api_sdk_python/cosigner/co_signer_converter.py @@ -20,15 +20,15 @@ def __init__(self): class CoSignerConverter: def __init__(self, config): - self.api_pub_key = config['apiPubKey'] - if config.get('bizPrivKey'): - self.biz_privKey = PEM_PRIVATE_HEAD + config['bizPrivKey'] + PEM_PRIVATE_END - if config.get('bizPrivKeyPemFile'): - self.biz_privKey = load_rsa_private_key(config['bizPrivKeyPemFile']) + self.co_signer_pub_key = config['coSignerPubKey'] + if config.get('approvalCallbackServicePrivateKey'): + self.approval_callback_service_private_key = PEM_PRIVATE_HEAD + config['approvalCallbackServicePrivateKey'] + PEM_PRIVATE_END + if config.get('approvalCallbackServicePrivateKeyPemFile'): + self.approval_callback_service_private_key = load_rsa_private_key(config['approvalCallbackServicePrivateKeyPemFile']) def request_convert(self, co_signer_call_back): - platform_rsa_pk = get_rsa_key(PEM_PUBLIC_HEAD + self.api_pub_key + PEM_PUBLIC_END) - api_user_rsa_sk = get_rsa_key(self.biz_privKey) + platform_rsa_pk = get_rsa_key(PEM_PUBLIC_HEAD + self.co_signer_pub_key + PEM_PUBLIC_END) + api_user_rsa_sk = get_rsa_key(self.approval_callback_service_private_key) required_keys = { 'key', 'sig', @@ -74,7 +74,7 @@ def request_convert(self, co_signer_call_back): return json.loads(r.decode()) def request_convert_v3(self, co_signer_call_back): - platform_rsa_pk = get_rsa_key(PEM_PUBLIC_HEAD + self.api_pub_key + PEM_PUBLIC_END) + platform_rsa_pk = get_rsa_key(PEM_PUBLIC_HEAD + self.co_signer_pub_key + PEM_PUBLIC_END) required_keys = { 'version', 'sig', @@ -97,8 +97,8 @@ def request_convert_v3(self, co_signer_call_back): # It has been Deprecated,Please use convertCoSignerResponseWithNewCryptoType def response_converter(self, co_signer_response: CoSignerResponse): - platform_rsa_pk = get_rsa_key(PEM_PUBLIC_HEAD + self.api_pub_key + PEM_PUBLIC_END) - api_user_rsa_sk = get_rsa_key(self.biz_privKey) + platform_rsa_pk = get_rsa_key(PEM_PUBLIC_HEAD + self.co_signer_pub_key + PEM_PUBLIC_END) + api_user_rsa_sk = get_rsa_key(self.approval_callback_service_private_key) ret = dict() @@ -128,8 +128,8 @@ def response_converter(self, co_signer_response: CoSignerResponse): return ret def response_converter_with_new_crypto_type(self, co_signer_response: CoSignerResponse): - platform_rsa_pk = get_rsa_key(PEM_PUBLIC_HEAD + self.api_pub_key + PEM_PUBLIC_END) - api_user_rsa_sk = get_rsa_key(self.biz_privKey) + platform_rsa_pk = get_rsa_key(PEM_PUBLIC_HEAD + self.co_signer_pub_key + PEM_PUBLIC_END) + api_user_rsa_sk = get_rsa_key(self.approval_callback_service_private_key) ret = dict() @@ -160,7 +160,7 @@ def response_converter_with_new_crypto_type(self, co_signer_response: CoSignerRe return ret def response_converter_v3(self, co_signer_response: CoSignerResponseV3): - api_user_rsa_sk = get_rsa_key(self.biz_privKey) + api_user_rsa_sk = get_rsa_key(self.approval_callback_service_private_key) ret = dict() response_data = json.dumps(co_signer_response.__dict__).replace('\'', '\"').replace('\n', '').encode('utf-8') From 4cd256a19888e250e3baf388fadbdfc5811b8ae5 Mon Sep 17 00:00:00 2001 From: absorprofess Date: Mon, 24 Mar 2025 18:25:47 +0800 Subject: [PATCH 5/7] add response_converter_v3 --- .../cosigner/co_signer_converter.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/safeheron_api_sdk_python/cosigner/co_signer_converter.py b/safeheron_api_sdk_python/cosigner/co_signer_converter.py index 52302d9..18bb35d 100644 --- a/safeheron_api_sdk_python/cosigner/co_signer_converter.py +++ b/safeheron_api_sdk_python/cosigner/co_signer_converter.py @@ -20,11 +20,18 @@ def __init__(self): class CoSignerConverter: def __init__(self, config): - self.co_signer_pub_key = config['coSignerPubKey'] - if config.get('approvalCallbackServicePrivateKey'): - self.approval_callback_service_private_key = PEM_PRIVATE_HEAD + config['approvalCallbackServicePrivateKey'] + PEM_PRIVATE_END - if config.get('approvalCallbackServicePrivateKeyPemFile'): - self.approval_callback_service_private_key = load_rsa_private_key(config['approvalCallbackServicePrivateKeyPemFile']) + # Supports both coSignerPubKey and apiPublKey + self.co_signer_pub_key = config.get('coSignerPubKey') or config.get('apiPubKey') + + # Supports both approvalCallbackServicePrivateKey and bizPrivKey + private_key = config.get('approvalCallbackServicePrivateKey') or config.get('bizPrivKey') + if private_key: + self.approval_callback_service_private_key = PEM_PRIVATE_HEAD + private_key + PEM_PRIVATE_END + + # Supports both approvalCallbackServicePrivateKeyPemFile and bizPrivKeyPemFile + pem_file = config.get('approvalCallbackServicePrivateKeyPemFile') or config.get('bizPrivKeyPemFile') + if pem_file: + self.approval_callback_service_private_key = load_rsa_private_key(pem_file) def request_convert(self, co_signer_call_back): platform_rsa_pk = get_rsa_key(PEM_PUBLIC_HEAD + self.co_signer_pub_key + PEM_PUBLIC_END) From 63e796aaba9b081e465d150e963dad8e10c41b2a Mon Sep 17 00:00:00 2001 From: absorprofess Date: Wed, 26 Mar 2025 11:18:14 +0800 Subject: [PATCH 6/7] add response_v3_converter --- safeheron_api_sdk_python/cosigner/co_signer_converter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/safeheron_api_sdk_python/cosigner/co_signer_converter.py b/safeheron_api_sdk_python/cosigner/co_signer_converter.py index 18bb35d..34dc1a2 100644 --- a/safeheron_api_sdk_python/cosigner/co_signer_converter.py +++ b/safeheron_api_sdk_python/cosigner/co_signer_converter.py @@ -80,7 +80,7 @@ def request_convert(self, co_signer_call_back): return json.loads(r.decode()) - def request_convert_v3(self, co_signer_call_back): + def request_v3_convert(self, co_signer_call_back): platform_rsa_pk = get_rsa_key(PEM_PUBLIC_HEAD + self.co_signer_pub_key + PEM_PUBLIC_END) required_keys = { 'version', @@ -92,7 +92,7 @@ def request_convert_v3(self, co_signer_call_back): missing_keys = required_keys.difference(co_signer_call_back.keys()) if missing_keys: raise Exception(co_signer_call_back) - + co_signer_call_back['version'] = 'v3' sig = co_signer_call_back.pop('sig') need_sign_message = sort_request(co_signer_call_back) @@ -166,7 +166,7 @@ def response_converter_with_new_crypto_type(self, co_signer_response: CoSignerRe ret['aesType'] = GCM_TYPE return ret - def response_converter_v3(self, co_signer_response: CoSignerResponseV3): + def response_v3_converter(self, co_signer_response: CoSignerResponseV3): api_user_rsa_sk = get_rsa_key(self.approval_callback_service_private_key) ret = dict() response_data = json.dumps(co_signer_response.__dict__).replace('\'', '\"').replace('\n', '').encode('utf-8') From feb1963fbb567c9cd16e198cf6fe29dcf4240a39 Mon Sep 17 00:00:00 2001 From: absorprofess Date: Wed, 26 Mar 2025 11:42:18 +0800 Subject: [PATCH 7/7] add response_v3_converter --- .../cosigner/co_signer_converter.py | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/safeheron_api_sdk_python/cosigner/co_signer_converter.py b/safeheron_api_sdk_python/cosigner/co_signer_converter.py index 34dc1a2..7178814 100644 --- a/safeheron_api_sdk_python/cosigner/co_signer_converter.py +++ b/safeheron_api_sdk_python/cosigner/co_signer_converter.py @@ -101,6 +101,24 @@ def request_v3_convert(self, co_signer_call_back): raise Exception("rsa verify: false") return json.loads(b64decode(co_signer_call_back['bizContent']).decode()) + def response_v3_converter(self, co_signer_response: CoSignerResponseV3): + api_user_rsa_sk = get_rsa_key(self.approval_callback_service_private_key) + ret = dict() + response_data = json.dumps(co_signer_response.__dict__).replace('\'', '\"').replace('\n', '').encode('utf-8') + + if response_data is not None: + ret['bizContent'] = b64encode(response_data).decode() + + ret['timestamp'] = str(int(time.time() * 1000)) + ret['code'] = str('200') + ret['version'] = str('v3') + ret['message'] = str('SUCCESS') + + # 4 sign request + need_sign_message = sort_request(ret) + ret['sig'] = rsa_pss_sign(api_user_rsa_sk, need_sign_message) + return ret + # It has been Deprecated,Please use convertCoSignerResponseWithNewCryptoType def response_converter(self, co_signer_response: CoSignerResponse): @@ -165,21 +183,3 @@ def response_converter_with_new_crypto_type(self, co_signer_response: CoSignerRe ret['rsaType'] = ECB_OAEP_TYPE ret['aesType'] = GCM_TYPE return ret - - def response_v3_converter(self, co_signer_response: CoSignerResponseV3): - api_user_rsa_sk = get_rsa_key(self.approval_callback_service_private_key) - ret = dict() - response_data = json.dumps(co_signer_response.__dict__).replace('\'', '\"').replace('\n', '').encode('utf-8') - - if response_data is not None: - ret['bizContent'] = b64encode(response_data).decode() - - ret['timestamp'] = str(int(time.time() * 1000)) - ret['code'] = str('200') - ret['version'] = str('v3') - ret['message'] = str('SUCCESS') - - # 4 sign request - need_sign_message = sort_request(ret) - ret['sig'] = rsa_pss_sign(api_user_rsa_sk, need_sign_message) - return ret