diff --git a/jituan/services/alipay_pay.py b/jituan/services/alipay_pay.py index ef5ae4d..f865a99 100644 --- a/jituan/services/alipay_pay.py +++ b/jituan/services/alipay_pay.py @@ -48,31 +48,80 @@ def get_alipay_config(club_id=None): # ==================== 密钥加载 ==================== def _load_private_key(key_str): - """加载 RSA 私钥,兼容 PEM 格式与裸 Base64(DER)格式。""" + """加载 RSA 私钥,兼容 PEM、PKCS#8 DER、PKCS#1、PKCS#8 多种格式。 + + 沙箱私钥常为 PKCS#1 格式(裸 base64 前缀 MIIEowIBAAKCAQEA), + 生产私钥常为 PKCS#8 格式(裸 base64 前缀 MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcw), + 此处按顺序尝试多种加载方式以兼容两者。 + """ if isinstance(key_str, bytes): key_str = key_str.decode('utf-8') + key_str = key_str.strip() + + # 1. 标准 PEM(含 -----BEGIN 头) if '-----BEGIN' in key_str: return serialization.load_pem_private_key( key_str.encode('utf-8'), password=None, ) - der_bytes = base64.b64decode(key_str) - return serialization.load_der_private_key(der_bytes, password=None) + raw = key_str.replace('\n', '').replace('\r', '').replace(' ', '') + + # 2. PKCS#8 DER(裸 base64) + try: + der_bytes = base64.b64decode(raw) + return serialization.load_der_private_key(der_bytes, password=None) + except Exception: + pass + + # 3. PKCS#1 PEM 包装(-----BEGIN RSA PRIVATE KEY-----) + pem_pkcs1 = _wrap_pem(raw, 'RSA PRIVATE KEY') + try: + return serialization.load_pem_private_key(pem_pkcs1.encode('utf-8'), password=None) + except Exception: + pass + + # 4. PKCS#8 PEM 包装(-----BEGIN PRIVATE KEY-----) + pem_pkcs8 = _wrap_pem(raw, 'PRIVATE KEY') + return serialization.load_pem_private_key(pem_pkcs8.encode('utf-8'), password=None) def _load_public_key(key_str): - """加载支付宝公钥,兼容 PEM 格式与裸 Base64(DER)格式。""" + """加载支付宝公钥,兼容 PEM、X.509 DER 及 PEM 包装格式。 + + 支付宝公钥标准为 X.509 SubjectPublicKeyInfo,裸 base64 前缀通常为 + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A,load_der_public_key 原生支持; + 为稳妥起见追加 PEM 包装兜底。 + """ if not key_str: return None if isinstance(key_str, bytes): key_str = key_str.decode('utf-8') + key_str = key_str.strip() + + # 1. 标准 PEM(含 -----BEGIN 头) if '-----BEGIN' in key_str: return serialization.load_pem_public_key(key_str.encode('utf-8')) - der_bytes = base64.b64decode(key_str) - return serialization.load_der_public_key(der_bytes) + raw = key_str.replace('\n', '').replace('\r', '').replace(' ', '') + + # 2. X.509 DER(裸 base64) + try: + der_bytes = base64.b64decode(raw) + return serialization.load_der_public_key(der_bytes) + except Exception: + pass + + # 3. PEM 包装兜底(-----BEGIN PUBLIC KEY-----) + pem_pub = _wrap_pem(raw, 'PUBLIC KEY') + return serialization.load_pem_public_key(pem_pub.encode('utf-8')) + + +def _wrap_pem(raw_b64, label): + """将裸 base64 字符串包装为标准 PEM 格式(每行 64 字符)。""" + lines = [raw_b64[i:i + 64] for i in range(0, len(raw_b64), 64)] + return f'-----BEGIN {label}-----\n' + '\n'.join(lines) + f'\n-----END {label}-----' # ==================== 签名 / 验签 ====================