Convertir encriptación en PHP 3DES en C#

Recientemente ha avisado que el sistema de pago por TPV virtual de Redsys se va a actualizar. En esta actualización se van a incorporar medidas de cifrado más potentes que la firma actual que se tiene implementado. Para ello los bancos han enviado cierta documentación (un poco tarde…) para hacer esta migración, puesto que el 23/11/2015 se supone que dejará de funcionar el sistema de encriptación actual.

Realizando la migración en mi empresa me he encontrado con un escollo que me ha costado un poco de solventar, y por lo que veo a muchos más desarrolladores de .NET les ha sucedido lo mismo. El problema está en el cifrado triple DES.

Este es el código de ejemplo que te facilitan en Redsys para realizar este cifrado.

function encrypt_3DES($message, $key){
// Se establece un IV por defecto
$bytes = array(0,0,0,0,0,0,0,0); //byte [] IV = {0, 0, 0, 0, 0, 0, 0, 0}
$iv = implode(array_map("chr", $bytes)); //PHP 4 = 4.0.2


// Se cifra
$ciphertext = mcrypt_encrypt(MCRYPT_3DES, $key, $message, MCRYPT_MODE_CBC, $iv); //PHP 4 >= 4.0.2
return $ciphertext;
}


$key = $this->decodeBase64($key);
$ciphertext = $this->encrypt_3DES($message, $key);

La variable $message sería el texto a encryptar (en el caso de Redsys el número de pedido). La variable $key es la clave de encriptación de Redsys que previamente la decodificamos en Base64.
Al final obtenemos un resultado ($ciphertext) que es un array de bytes.

Ahora viene cuando hacemos lo mismo en .NET C#.


///
/// Uso de la encriptación TripleDES con IV todo 0 -> byte [] IV = {0, 0, 0, 0, 0, 0, 0, 0}
/// Esta encriptación se usa así para los TPV's de Redsys
///


///Texto a encriptar ///Clave de la encriptación en un array de Bytes ///
public static byte[] EncriptarTripleDES_IV_0(string texto, byte[] key)
{
using (TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider())
{
byte[] iv_0 = { 0, 0, 0, 0, 0, 0, 0, 0 };  //same IV that Redsys
byte[] toEncryptArray = Encoding.ASCII.GetBytes(texto);
tdes.IV = iv_0;
//assign the secret key
tdes.Key = key;
tdes.Mode = CipherMode.CBC;
tdes.Padding = PaddingMode.Zeros;
ICryptoTransform cTransform = tdes.CreateEncryptor();
//transform the specified region of bytes array to resultArray
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0,toEncryptArray.Length);
//Clear to Best Practices
tdes.Clear();
return resultArray;
}
}

Esta función nos devuelve el mismo resultado que la orginial proporcionada por Redsys en PHP.

83 opiniones en “Convertir encriptación en PHP 3DES en C#”

  1. and for this code?

    /****** MAC Function ******/
    function mac256($ent,$key){
    $res = hash_hmac(‘sha256’, $ent, $key, true);//(PHP 5 >= 5.1.2)
    return $res;
    }

    1. Hi Roberto, it may be like this.

      public static byte[] EncriptarSHA256(string texto, byte[] privateKey)
      {
      var encoding = new System.Text.UTF8Encoding();
      byte[] key = privateKey;
      var myhmacsha256 = new HMACSHA256(key);
      byte[] hashValue = myhmacsha256.ComputeHash(encoding.GetBytes(texto));
      myhmacsha256.Clear();
      return hashValue;
      }

  2. Muy bueno el artículo. Yo tengo el mismo problema de actualizar el TPV en c# y me ha servido de mucho. Pero no me funciona …
    Con esta secuencia de generación de firma con tus funciones no me funciona, redsys me da error de firma incorrecta (SIS0042). ¿?

    // Se decodifica la clave Base64, 24 chars
    byte[] key = Convert.FromBase64String(HashReal);

    // Se diversifica la clave con el Número de Pedido
    var key2 = TripleDESEncrypt(Order, key);

    // MAC256 del parámetro Ds_MerchantParameters
    var res = EncriptarSHA256(MerchantParameters, key2);

    // Se codifican los datos Base64
    var firma = Convert.ToBase64String(res);

    1. ¿Estas usando la clave (HashReal) antigua de Redsys o la nueva? . Con estos cambios de Redsys no vale la secretkey que se usaba hasta ahora. Entrado desde la administración del TPV Virtual podrás acceder a ver la secretKey nueva y también la vieja.

      1. Check using test key reported in the example and everything is ok.
        If I used the production key provided me by sms I got the same error.
        I’m waiting for an answer for support.
        My prod key does not work even with the php code provided by RedSys.
        I think there is something wrong in the production url reported in the manual….
        I tried to call the support, but they are in trouble if I speak English…

      2. Pues estoy usando la nueva. Me la enviaron por SMS, en la administración del TPV (es del Sabadell) no está esa la opción de ver la clave .

          1. Using testing key reported in the example provided by RedSys, it works.
            Using the key provided by SMS no.

          2. Yes sure.
            I tried using the example provided by Redsys “As is”. It works.
            Then I replaced in the same example the URL, Merchant_Code, Terminal and the Key using data provided for production
            I got error. In the page all 0 and error.

            Then I used my own interface in C#. Using example data, it works. With the production data no.

    2. Hola, acabo de implentar las funciones y he comparado los resultados con el ejemplo que hay en php y los resultados son los mismos. Ahora cuando cambio los datos por los míos me da error: SIS0042 – La firma enviada no es correcta.

      ¿Sabéis si los de redsys aun están haciendo cambios o debería de funcionar ya? Lo he probado tanto en la url de pruebas como en la de producción y me da el mismo error. Los de soporte del tpv no me contestan….

      1. A mi con las claves que me facilitan para el entorno de pruebas, no funciona. En cambio con las claves de su ejemplo sí.
        Por otro lado he probado con las claves que me facilitan en el entorno de pruebas en el entorno real y si que me ha dejado.
        Otra prueba que he realizado ha sido en el entorno de pruebas antiguo. Ahí me ha funcionado correctamente.

        Esta sería la URL Pruebas:
        https://sis-t.redsys.es:25443/sis/realizarPago

        Esta sería la URL Producción:
        https://sis.redsys.es/sis/realizarPago

          1. Hola Rebeca,
            He visto que preguntabas por los códigos en ASP clásico, yo tengo el mismo problema, has conseguido algo?? me puedes ayudar??
            Gracias.

  3. Tengo una duda importante
    Cada vez que ejecuto el php de prueba me genera una clave diferente (ejemploGeneraPet.php), sin embargo mi c# con los datos de prueba siempre me genera la misma. ¿que me falta?

    1. El ejemplo tal cual está de PHP, creo recordar que siempre devolvía los mismos datos puesto que no cambian en las llamadas.
      Debería de devolverte el mismo resultado, salvo que cambies algún dato como el Número de pedido o algo similar.

  4. Hola,

    Gracias por el código, me ha funcionado genial.

    Ahora me da un error que no se por donde cogerlo.

    Entiendo que los parámetros los paso bien, el objeto es:

    Merchant_Parameters parameters = new Merchant_Parameters()
    {
    Ds_Merchant_Amount = importe (multiplicado por cien para evitar comas.),
    Ds_Merchant_MerchantCode = ConfigurationManager.AppSettings["Ds_Merchant_MerchantCode"],
    Ds_Merchant_Currency = ConfigurationManager.AppSettings["Ds_Merchant_Currency"],
    Ds_Merchant_TransactionType = "0", //autorización
    Ds_Merchant_Terminal = ConfigurationManager.AppSettings["Ds_Merchant_Terminal"],
    Ds_Merchant_Order = order,
    Ds_Merchant_MerchantURL = ConfigurationManager.AppSettings["Ds_Merchant_MerchantURL"],
    Ds_Merchant_UrlOK = String.Format("{0}://{1}/pago_ok", protocol, entorno),
    Ds_Merchant_UrlKO = String.Format("{0}://{1}/pago_ko", protocol, entorno)
    };

    Pasado a JSON.

    El error es: “Error en datos enviados. Contacte con su comercio.”

    gracias por la ayuda.

  5. Gracias por los ejemplos, me estaba volviendo un poco “loco” buscando por la red como adaptarlo a C# (aunque el sitio usa asp clasico, intentaremos cambiar la pasarela salvo que encontremos ejemplos).

    El problema viene despues de realizar todo el proceso envio el formulario de la siguiente manera :

    Realizando el pago.

    Siguiendo el esquema del PDF, pero no hay manera ni en el sandbox, ni en produccion siempre el mismo error :

    Importe 0 Sin Asignar
    Código Comercio 0
    Terminal 0
    Número pedido
    Error en datos enviados. Contacte con su comercio.

    Un saludo!

      1. Parece que el error aparece cuando el codigo de comercio no existe o algo similar. Al introducir el codigo de comercio correcto la pagina de prueba funciona

      1. //Capturo los parámetros que me manda Redsys por POST
        string ds_SignatureVersion = Request.Form[“Ds_SignatureVersion”].ToString();
        string ds_MerchantParameters = Request.Form[“Ds_MerchantParameters”].ToString();
        string ds_Signature = Request.Form[“Ds_Signature”].ToString();

        //Decodifico los parámetros para obtener el JSON
        string parametrosJSON = string.Empty;
        if(!string.IsNullOrEmpty(ds_MerchantParameters))
        {
        var base64EncodedBytes = System.Convert.FromBase64String(ds_MerchantParameters);
        parametrosJSON = System.Text.Encoding.UTF8.GetString(base64EncodedBytes);

        //RedsysRespuestaJSON es una clase que he creado con la estructura que devuelve Redsys (la verás en el ejemplo de PHP)
        RedsysRespuestaJSON oRedsys = Newtonsoft.Json.JsonConvert.DeserializeObject(parametrosJSON);

        //A partir de aquí ya tienes en el objeto oRedsys todos los datos que te ha llegado de la pasarela. Una buena práctica (creo que se hace también en el ejemplo de PHP) sería comprobar la firma.
        }

        1. De antemano mil gracias Jesus.
          he implementado eso pero me dice que falta la directiva de ensamblado para el Newtonsoft.Json
          cual es la directiva?
          using …?
          Saludos y gracias otra vez

          1. Hola,
            alguien me puede decir si hay un ejemplo que funciona en cualquier lenguaje de programación?
            El ejemplo en lenguaje php disponible en esta dirección:
            http://www.redsys.es/wps/portal/redsys/publica/areadeserviciosweb,
            proporcionada por Redsys estaba en funcionamiento hasta hace unos días.
            ahora ya no.
            pero ahora no funciona más.
            Los otros ejemplos en esta página no funcionan

            Incluyendo los códigos de producción proporcionados por el banco no funciona, hay siempre el error “Error en Datos Enviados. Contacte con el comercial”.

          2. Yo estoy probando directamente en Real. Luego me devuelvo la operación desde la administración del TPV.
            El entorno de pruebas no me funciona, les he mandado correos pero no me responden.

          3. Muchas Gracias por todo Jesús :):):), indagando había encontrado la librería y ya conseguí que retornase el OK.

  6. Por cierto, una pregunta mas “mercantil o de negocio” que cobrareis a vuestros clientes por el cambio de SHA1 a SHA256?

  7. Os habéis encontrado con un error como este?

    PAGO_TARJETA_POST – FIRMA GENERADA – rgd3uHJTL0D9wlath/+s/koThfMweEAXKzfdaxc4Ucw=
    PAGO_TARJETA_POST – SIGNATURE PARAMETERS – rgd3uHJTL0D9wlath_-s_koThfMweEAXKzfdaxc4Ucw=

    Genero la firma para validar que los datos que me devuelven, una vez hecha la operación, son correctos y pueda procesar y meter en el sistema el pago.
    Esta firma generada es la primera, la que me llega por parámetro es la segunda.

    Como podéis ver, las diferencias son con los “SIMBOLOS”, y esto no me había pasado nunca.

    ¿Me podéis ayudar?
    Gracias de antemano

    1. a mi me pasa lo mismo. Al generar la firma aparecen “símbolos” raros y en la respuesta me aparece otra firma. He podido hacer pagos hasta ahora, en todos los que no ha aparecido ningún símbolo, los hace perfectamente.

      mi script es para cobrar los pagos “recurrentes”:

      function download_page($url,$xml){
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_POST, 1);
      curl_setopt($ch, CURLOPT_POSTFIELDS, “entrada=$xml”);
      curl_setopt($ch, CURLOPT_FAILONERROR,1);
      curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1);
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
      curl_setopt($ch, CURLOPT_TIMEOUT, 15);
      $result = curl_exec($ch);
      curl_close($ch);

      return $result;
      }

      $xml = ”

      $Merchant_Amount
      $Merchant_Order
      $Merchant_Code
      $Merchant_Currency
      $Merchant_TransactionType
      $Merchant_Terminal

      HMAC_SHA256_V1
      $signature
      “;

      $sXML = download_page($url,$xml);

      $oXML = new SimpleXMLElement($sXML);

      me devuelve “error en la firma” SIS0042

      esto es lo que yo le mando -> Gf9hsF3zVb9H55+8hjCe4LhQNoq6Fa+O4ubbNcyRrtU=
      esto es lo que me responde -> Gf9hsF3zVb9H55 8hjCe4LhQNoq6Fa O4ubbNcyRrtU=

      Insisto, si al generar la firma no aparecen símbolos, me funciona correctamente.

      alguna idea?

      Gracias!!

    2. a mi me pasa lo mismo. Al generar la firma aparecen “símbolos” raros y en la respuesta me aparece otra firma. He podido hacer pagos hasta ahora, en todos los que no ha aparecido ningún símbolo, los hace perfectamente.

      mi script es para cobrar los pagos “recurrentes”:


      function download_page($url,$xml){
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_POST, 1);
      curl_setopt($ch, CURLOPT_POSTFIELDS, "entrada=$xml");
      curl_setopt($ch, CURLOPT_FAILONERROR,1);
      curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1);
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
      curl_setopt($ch, CURLOPT_TIMEOUT, 15);
      $result = curl_exec($ch);
      curl_close($ch);

      return $result;
      }

      $xml = "

      $Merchant_Amount
      $Merchant_Order
      $Merchant_Code
      $Merchant_Currency
      $Merchant_TransactionType
      $Merchant_Terminal

      HMAC_SHA256_V1
      $signature
      ";

      $sXML = download_page($url,$xml);

      $oXML = new SimpleXMLElement($sXML);

      me devuelve “error en la firma” SIS0042

      esto es lo que yo le mando -> Gf9hsF3zVb9H55+8hjCe4LhQNoq6Fa+O4ubbNcyRrtU=
      esto es lo que me responde -> Gf9hsF3zVb9H55 8hjCe4LhQNoq6Fa O4ubbNcyRrtU=

      Insisto, si al generar la firma no aparecen símbolos, me funciona correctamente.

      alguna idea?

      Gracias!!

    3. a mi me pasa lo mismo. Al generar la firma aparecen “símbolos” raros y en la respuesta me aparece otra firma. He podido hacer pagos hasta ahora, en todos los que no ha aparecido ningún símbolo, los hace perfectamente.

      mi script es para cobrar los pagos “recurrentes”:

      http://pastie.org/10663831

      me devuelve “error en la firma” SIS0042

      esto es lo que yo le mando -> Gf9hsF3zVb9H55+8hjCe4LhQNoq6Fa+O4ubbNcyRrtU=
      esto es lo que me responde -> Gf9hsF3zVb9H55 8hjCe4LhQNoq6Fa O4ubbNcyRrtU=

      Insisto, si al generar la firma no aparecen símbolos, me funciona correctamente.

      alguna idea?

      Gracias!!

  8. La verdad que yo me encontré con eso al validar la firma en la respuesta. Según investigué tiene que ver con las codificaciones elegidas. A pesar de que hice mil pruebas con distintas codificaciones siempre me aparecía algo mal.
    Una cosa que encontré navegando con gente que también ha sufrido problemas similares es reemplazar los símbolos.

    “Replace(“=”, String.Empty).Replace(‘+’, ‘-‘).Replace(‘/’, ‘_’)” // Al parecer sólo pasa con estos símbolos.

    //Esta sería la comparación de firmas para validar la operación.
    if (claveSha256Encodeada.Replace(“=”, String.Empty).Replace(‘+’, ‘-‘).Replace(‘/’, ‘_’) == ds_Signature.Replace(“=”, String.Empty).Replace(‘+’, ‘-‘).Replace(‘/’, ‘_’))