83 lines
3.1 KiB
Python
83 lines
3.1 KiB
Python
import base64
|
|
from urllib.parse import quote
|
|
|
|
from cryptography.hazmat.backends import default_backend
|
|
from cryptography.hazmat.primitives import hashes, serialization
|
|
from cryptography.hazmat.primitives.asymmetric import padding
|
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
|
|
|
|
|
class AlipayAESCrypto:
|
|
def __init__(self, aes_key: str):
|
|
raw = base64.b64decode(aes_key + "=" * (4 - len(aes_key) % 4))
|
|
self._key = raw[:16]
|
|
|
|
def decrypt(self, encrypted: str | bytes) -> str:
|
|
from cryptography.hazmat.primitives import padding as sym_padding
|
|
|
|
data = base64.b64decode(encrypted)
|
|
iv = self._key
|
|
cipher = Cipher(algorithms.AES(self._key), modes.CBC(iv))
|
|
decryptor = cipher.decryptor()
|
|
padded = decryptor.update(data) + decryptor.finalize()
|
|
unpadder = sym_padding.PKCS7(128).unpadder()
|
|
plaintext = unpadder.update(padded) + unpadder.finalize()
|
|
return plaintext.decode("utf-8")
|
|
|
|
def encrypt(self, plaintext: str) -> str:
|
|
from cryptography.hazmat.primitives import padding as sym_padding
|
|
|
|
iv = self._key
|
|
padder = sym_padding.PKCS7(128).padder()
|
|
padded = padder.update(plaintext.encode("utf-8")) + padder.finalize()
|
|
cipher = Cipher(algorithms.AES(self._key), modes.CBC(iv))
|
|
encryptor = cipher.encryptor()
|
|
encrypted = encryptor.update(padded) + encryptor.finalize()
|
|
return base64.b64encode(encrypted).decode("utf-8")
|
|
|
|
|
|
class AlipayCrypto:
|
|
def __init__(self, app_private_key_pem: str, alipay_public_key_pem: str):
|
|
self._private_key = serialization.load_pem_private_key(
|
|
app_private_key_pem.encode("utf-8"),
|
|
password=None,
|
|
backend=default_backend(),
|
|
)
|
|
self._public_key = serialization.load_pem_public_key(
|
|
alipay_public_key_pem.encode("utf-8"),
|
|
backend=default_backend(),
|
|
)
|
|
|
|
def sign(self, params: dict) -> str:
|
|
content = self._build_sign_string(params)
|
|
signature = self._private_key.sign(
|
|
content.encode("utf-8"),
|
|
padding.PKCS1v15(),
|
|
hashes.SHA256(),
|
|
)
|
|
return base64.b64encode(signature).decode("utf-8")
|
|
|
|
def verify(self, params: dict, signature: str) -> bool:
|
|
content = self._build_sign_string(params)
|
|
try:
|
|
self._public_key.verify(
|
|
base64.b64decode(signature),
|
|
content.encode("utf-8"),
|
|
padding.PKCS1v15(),
|
|
hashes.SHA256(),
|
|
)
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
@staticmethod
|
|
def _build_sign_string(params: dict) -> str:
|
|
filtered = {k: v for k, v in params.items() if k not in ("sign", "sign_type") and v is not None}
|
|
sorted_items = sorted(filtered.items(), key=lambda x: x[0])
|
|
parts = []
|
|
for k, v in sorted_items:
|
|
encoded_key = quote(str(k), safe="~")
|
|
encoded_value = quote(str(v), safe="~")
|
|
parts.append(f"{encoded_key}={encoded_value}")
|
|
return "&".join(parts)
|