在Python中为Redsys/Sermepa实现新的SHA256签名



我有一个基于django的应用程序,它使用Redsys(早期的Sermepa)作为信用卡支付平台。最近,我们收到一条消息,敦促我们从旧的SHA1签名迁移到SHA256,以便继续安全地使用该平台。

平台提供了几个示例,说明如何使新请求生成新签名,但仅使用PHP和Java。现在,我正试图将提供的解决方案移植到Python,但没有成功。

这是一个PHP中的示例工作代码(带有伪密钥/数据):

<?php
    // Init form with default data
    $order = "ABCDEFGHI";
    $merchant_parameters = array( 
        "DS_MERCHANT_AMOUNT" => "1000",
        "DS_MERCHANT_ORDER" => strval($order) ,
        "DS_MERCHANT_MERCHANTCODE" => "012345678",
        "DS_MERCHANT_CURRENCY" => "978",
        "DS_MERCHANT_TRANSACTIONTYPE" => "0",
        "DS_MERCHANT_TERMINAL" => "1",
    ); 
    // Key definition, decoding, and encrypt using $order
    $key = 'Bg8HYhVT79PDvOxbI/Newcm31QY/9999';
    echo 'key --> ' , $key , '<br/><br/>';
    $key_decoded = base64_decode($key);
    echo 'key-decoded --> ' , $key_decoded , '<br/><br/>';
    $ciphertext = mcrypt_encrypt(MCRYPT_3DES, $key_decoded, $order, MCRYPT_MODE_CBC,  implode(array_map("chr", array(0,0,0,0,0,0,0,0))) );
    echo 'ciphertext --> ' , $ciphertext , '<br/><br/>';
    // Transform array in encoded json
    $parameters = json_encode($merchant_parameters);
    echo 'parameters --> ' , $parameters , '<br/><br/>';
    $parameters_encoded = base64_encode($parameters);
    echo 'parameters_encoded --> ' , $parameters_encoded , '<br/><br/>';
    // Calculate MAC256 of encoded array
    $mac_256 = hash_hmac('sha256', $parameters_encoded, $ciphertext, true);
    echo 'mac_256 --> ' , $mac_256 , '<br/><br/>';
    // Encode MAC256 in base64 to get the signature
    $mac_256_encoded = base64_encode($mac_256);
    echo 'mac_256_encoded --> ' , $mac_256_encoded , '<br/><br/>';
?>

这是我在Python中的等效代码,使用相同的伪数据:

# -*- encoding: utf-8 -*-
from pyDes import triple_des, CBC, PAD_PKCS5
import hashlib, json, base64, hmac
from json import JSONEncoder

# Init form with default data
order = "ABCDEFGHI"
merchant_parameters = {}
merchant_parameters['DS_MERCHANT_AMOUNT'] = "1000"
merchant_parameters['DS_MERCHANT_ORDER'] = order
merchant_parameters['DS_MERCHANT_MERCHANTCODE'] = "012345678"
merchant_parameters['DS_MERCHANT_CURRENCY'] = "978"
merchant_parameters['DS_MERCHANT_TRANSACTIONTYPE'] = "0"
merchant_parameters['DS_MERCHANT_TERMINAL'] = "1"

# Key definition, decoding, and encrypt using order
key = 'Bg8HYhVT79PDvOxbI/Newcm31QY/9999'
print 'nnkey --> %s' % key
key_decoded  = base64.b64decode(key)
print 'nnkey_decoded --> %s' % key_decoded
k = triple_des(key_decoded, CBC, "", pad=None,  padmode=PAD_PKCS5)
ciphertext  = k.encrypt(order)
print 'nnciphertext --> %s' % ciphertext

#  Transform array in encoded json (with no lines
parameters = (json.dumps(merchant_parameters)).encode()
print 'nnparameters --> %s' % parameters
parameters_encoded = ''.join(unicode(base64.encodestring(parameters), 'utf-8').splitlines())
print 'nnparameters_encoded --> %s' % parameters_encoded

# Calculate MAC256 of encoded array
mac_256 = hmac.new(ciphertext, parameters_encoded, digestmod=hashlib.sha256).hexdigest()
print 'nnmac_256 --> %s' % mac_256

# Encode MAC256 in base64 to get the signature
mac_256_encoded = base64.b64encode(mac_256)
print 'nnmac_256_encoded --> %s' % mac_256_encoded

可以看出,如果运行代码,生成的签名是不等价的,因此支付平台拒绝了我的Python请求。

有人能注意到我在签名生成方面哪里错了吗?

提前感谢,阿尔瓦罗。

遇到了同样的问题。

实际上,在JSON编码之后,我不得不清理键/值对之间的空白。但我得到了不同的密文签名。这导致最终签名与示例不同。

注意,在示例代码中,他们在3DES加密中为订单号做了一个尾随填充。

iv = b''
k = DES3.new(key, DES3.MODE_CBC, iv)
ceros = b''*(len(DS_MERCHANT_ORDER)%8)
clave = k.encrypt(DS_MERCHANT_ORDER+ceros.encode('UTF-8'))

感谢

编辑:

最后我修复了它:

要获得特定的每个操作密钥:

    iv = b''
    k = DES3.new(key, DES3.MODE_CBC, iv)
    ceros = b''*(len(DS_MERCHANT_ORDER)%8)
    claveOp = k.encrypt(DS_MERCHANT_ORDER+ceros.encode('UTF-8'))

获取hmac.sha256:

    from Crypto.Hash import HMAC, SHA256
    # Realizo la codificacion SHA256
    dig = HMAC.new(claveOp , msg=DS_MERCHANT_PARAMETERS,    digestmod=SHA256).digest()
    signatureEnc = base64.b64encode(dig)

希望它能帮助你

Hie,最后,我在pypi中发布了关于redsys客户端的新版本。https://pypi.python.org/pypi/redsys/

感谢阿尔瓦罗即将做sha256签名。

最新更新