PHP and C# HMAC SHA256

前端 未结 3 917
既然无缘
既然无缘 2020-12-20 04:30

I need to convert the following php code in C#:

$res = mac256($ent, $key);
$result = encodeBase64($res);

where

function enc         


        
相关标签:
3条回答
  • 2020-12-20 04:52

    Redsys provides libraries for php and java.

    Starting from the java library, I've translated the ApiMacSha256 class to C#

    public class ApiMacSha256 {
        //////////////////////////////////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////////////////////////////
        ////////////                    FUNCIONES AUXILIARES:                              ///////////
        //////////////////////////////////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////////////////////////////
    
        /** 3DES Function */
        private byte[] encrypt_3DES(byte[] key, string data) {
            //http://www.mywebexperiences.com/2012/12/11/crypting-data-using-3des-c/
            //http://stackoverflow.com/a/33479952/2938518
            using (var tdes = new TripleDESCryptoServiceProvider()) {
                tdes.IV = new byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 };
                tdes.Key = key;
                tdes.Padding = PaddingMode.Zeros;
                tdes.Mode = CipherMode.CBC;
    
                var toEncrypt = Encoding.ASCII.GetBytes(data);
                var result = tdes.CreateEncryptor().TransformFinalBlock(toEncrypt, 0, toEncrypt.Length);
    
                return result;
            }
        }
    
        /** MAC Function */
        private byte[] mac256(string dsMerchantParameters, byte[] secretKo) {
            //http://stackoverflow.com/a/17315619/2938518
            byte[] hash;
            using (var hmac = new HMACSHA256(secretKo)) {
                hash = hmac.ComputeHash(Encoding.ASCII.GetBytes(dsMerchantParameters));
            }
    
            return hash;
        }
    
        /** Base64 Functions */
        private string encodeB64String(byte[] data) {
            return Convert.ToBase64String(data, Base64FormattingOptions.None);
        }
    
        //////////////////////////////////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////////////////////////////
        ////////////        FUNCIONES PARA LA GENERACIÓN DEL FORMULARIO DE PAGO:          ////////////
        //////////////////////////////////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////////////////////////////
        public String createMerchantSignature(string merchantParamsB64, string claveComercio, string OrderId) {
            byte[] clave = Convert.FromBase64String(claveComercio);
            byte[] secretKo = encrypt_3DES(clave, OrderId);
    
            // Se hace el MAC con la clave de la operación "Ko" y se codifica en BASE64
            byte[] hash = mac256(merchantParamsB64, secretKo);
            String res = encodeB64String(hash);
            return res;
        }
    }
    

    The main method 'createMerchantSignature', requires a string encoded in base64 of the merchant params embeded in a json structure, the secret key of the merchant and the OrderId.

    0 讨论(0)
  • 2020-12-20 05:00

    I'm pretty sure that you're dealing with the new RedSys SHA256 signature implementation. Also I saw that you have some issue with the 3DES encryption between PHP and C#.

    First at all you must get the base 64 string with all the payment parameters. You can achieve it with this code:

    public static string GetParameters(string merchantCode, string terminal, int currency, string transactionType, decimal amount, string merchantOrder, string merchantIdentifier, string merchantPost, string urlOk, string urlKo)
            {
                var jsonValues = new Dictionary<string, string>
                {
                    { "Ds_Merchant_Amount", amount.ToString().Replace(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator, "") },
                    { "Ds_Merchant_Order", merchantOrder},
                    { "Ds_Merchant_MerchantCode", merchantCode },
                    { "Ds_Merchant_Currency", currency.ToString() },
                    { "Ds_Merchant_TransactionType", transactionType },
                    { "Ds_Merchant_Terminal", terminal },
                    { "Ds_Merchant_Identifier", merchantIdentifier },
                    { "Ds_Merchant_MerchantURL", merchantPost },
                    { "Ds_Merchant_UrlOK", urlOk},
                    { "Ds_Merchant_UrlKO",  urlKo}
                }.Select(kvp => "\"{0}\":\"{1}\"".Formato(kvp.Key.ToUpper(), kvp.Value));
    
                var jsonString = "{" + string.Join(",", jsonValues) + "}";
    
                return Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(jsonString));
            }
    

    Once you have the JSON string in base 64, you must apply 3DES to merchant order parameter with the key provided by RedSys:

    public static string GetTransactionEncryptionKey(string merchantOrder, string encryptKey)
            {
                using (var tdes = new TripleDESCryptoServiceProvider())
                {
                    tdes.IV = new byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 };
                    tdes.Key = Convert.FromBase64String(encryptKey);
                    tdes.Padding = PaddingMode.Zeros;
                    tdes.Mode = CipherMode.CBC;
    
                    var toEncrypt = ASCIIEncoding.ASCII.GetBytes(merchantOrder);
                    var result = tdes.CreateEncryptor().TransformFinalBlock(toEncrypt, 0, toEncrypt.Length);
    
                    return Convert.ToBase64String(result);
                }
            }
    

    As you can see, the encryption key provided by RedSys is base 64 string so you don't need to calculate the MD5 hash for the 3DES algorithm.

    Then we go for the SHA256 signature:

    public static string GetSignature(string base64Parameters, string base64tranEncryptKey)
            {
                using (var sha = new HMACSHA256(Convert.FromBase64String(base64tranEncryptKey)))
                {
                    var hash = sha.ComputeHash(ASCIIEncoding.ASCII.GetBytes(base64Parameters));
    
                    return Convert.ToBase64String(hash);
                }
            }
    

    Good luck!

    0 讨论(0)
  • 2020-12-20 05:01

    This code should do the trick:

    static byte[] hmacSHA256(String data, String key)
    {
        using (HMACSHA256 hmac = new HMACSHA256(Encoding.ASCII.GetBytes(key)))
        {
            return hmac.ComputeHash(Encoding.ASCII.GetBytes(data));
        }
    }
    

    If I call this code:

    Console.WriteLine(BitConverter.ToString(hmacSHA256("1234", "1234")).Replace("-", "").ToLower());
    

    It returns:

    4e4feaea959d426155a480dc07ef92f4754ee93edbe56d993d74f131497e66fb
    

    When I run this in PHP:

    echo hash_hmac('sha256', "1234", "1234", false);
    

    It returns

    4e4feaea959d426155a480dc07ef92f4754ee93edbe56d993d74f131497e66fb
    
    0 讨论(0)
提交回复
热议问题