用于 TPV 支付的 iOS 3DES 加密

2024-01-25

我正在开发一个通过 TPV Redsys API 进行付款的 iOS 应用程序。我正在关注文档,但它不起作用(服务器由于签名不正确而返回错误),我猜这是因为 3DES 加密。

我正在使用文档中的测试数据,因此输出应该与文档中的相同。 这是我的代码:

- (void) payViaTPVWithAmount:(NSString *)amount andOrderId:(NSString *)orderId
{
    // I don't use my amount and orderId but those provided by the documentation to test
    // We need to obtain 3 fields:  DS_SIGNATURE_VERSION, DS_MERCHANTPARAMETERS and DS_SIGNATURE.

    // 1) Obtaining DS_SIGNATURE_VERSION
    NSString *dsSignatureVersion = @"HMAC_SHA256_V1"; 
    // >>>> This step is easy and of course is correct <<<<

    // 2) Obtaining DS_MERCHANTPARAMETERS
    // 2.1) Build params JSON -using test data-
    NSString *params = [NSString stringWithFormat: @"{\"DS_MERCHANT_AMOUNT\":\"%@\",\"DS_MERCHANT_ORDER\":\"%@\",\"DS_MERCHANT_MERCHANTCODE\":\"%@\",\"DS_MERCHANT_CURRENCY\":\"978\",\"DS_MERCHANT_TRANSACTIONTYPE\":\"0\",\"DS_MERCHANT_TERMINAL\":\"%@\",\"DS_MERCHANT_MERCHANTURL\":\"%@\",\"DS_MERCHANT_URLOK\":\"%@\",\"DS_MERCHANT_URLKO\":\"%@\"}",
                        @"145",
                        @"1446117555",
                        @"327234688",
                        @"1",
                        @"http:\\/\\/www.bancsabadell.com\\/urlNotificacion.php",
                        @"http:\\/\\/www.bancsabadell.com\\/urlOK.php",
                        @"http:\\/\\/www.bancsabadell.com\\/urlKO.php"];

    //2.2) convert JSON params to base64
    NSData *paramsData = [params dataUsingEncoding:NSUTF8StringEncoding];
    NSString *paramsBase64 = [paramsData base64EncodedStringWithOptions:0];

    NSLog(@"apiMacSHA256\n%@", params);
    NSLog(@"apiMacSHA256Base64\n%@", paramsBase64); 
    // >>>>> This output is identical to that included in the documents so this step 2 is ok. <<<<<

    // 3) Obtaining DS_SIGNATURE
    // 3.1) start with secret key -the document gives this for test purposes-
    NSString *merchantKey = @"sq7HjrUOBfKmC576ILgskD5srU870gJ7"; //32 bytes

    // 3.2) convert it to base64
    NSData *merchantKeyData = [merchantKey dataUsingEncoding:NSUTF8StringEncoding];
    NSString *merchantKeyBase64 = [merchantKeyData base64EncodedStringWithOptions:0];

    // 3.3) the documentation doesn't say to convert this key to Hex but I have the Android code (which is working) and it has this step. 
    // Anyway I've tested with both options, with this step and without this step.
    NSData *merchantKeyBase64Data = [merchantKeyBase64 dataUsingEncoding:NSUTF8StringEncoding];
    NSString *merchantHex = [merchantKeyBase64Data hexadecimalString];

    // 3.4) get 3DES from orderId and base 64 (or Hex depending on if step 3.3 is done) key just obtained
    NSData *operationKey = [self encrypt_3DSWithKey:merchantHex andOrderId:@"1446117555"];

    // 3.5) get HMAC SHA256 from operationkey and ds_merchantparameters
    NSData *signatureHmac256 = [self mac256WithKey:operationKey andString:paramsBase64];

    // 3.6) convert HMAC SHA256 to base64
    NSString *signatureHmac256Base64 = [signatureHmac256 base64EncodedStringWithOptions:0];

    NSLog(@"signatureHmac256Base64\n%@", signatureHmac256Base64);
    // This step is not working, the signature is not the correct one and the connection to the TPV Redsys system fails.

    // 4) Build the request
    // Set URL - using URL for development purposes-
    NSURL *url = [NSURL URLWithString:@"https://sis-t.redsys.es:25443/sis/realizarPago"];

    NSString *dsSignatureVersionEncoded = [dsSignatureVersion stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLUserAllowedCharacterSet]];
    NSString *paramsHTTPEncoded = [paramsBase64 stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLUserAllowedCharacterSet]];
    NSString *signatureHmac256HTTPEncoded = [signatureHmac256Base64 stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLUserAllowedCharacterSet]];

    NSString *body = [NSString stringWithFormat:@"DS_SIGNATURE=%@&DS_MERCHANTPARAMETERS=%@&DS_SIGNATUREVERSION=%@", signatureHmac256HTTPEncoded, paramsHTTPEncoded, dsSignatureVersionEncoded];

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL: url];
    [request setHTTPMethod: @"POST"];
    [request setHTTPBody: [body dataUsingEncoding: NSUTF8StringEncoding allowLossyConversion:false]];

    [_webViewer loadRequest:request];
}

- (NSData *)encrypt_3DSWithKey:(NSString *)key andOrderId:(NSString *)str
{
    NSData *plainData = [str dataUsingEncoding:NSUTF8StringEncoding];
    NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];

    size_t bufferSize = plainData.length + kCCKeySize3DES;
    NSMutableData *cypherData = [NSMutableData dataWithLength:bufferSize];
    size_t movedBytes = 0;

    CCCryptorStatus ccStatus;
    ccStatus = CCCrypt(kCCDecrypt,
                       kCCAlgorithm3DES,
                       kCCOptionECBMode,
                       keyData.bytes,
                       kCCKeySize3DES,
                       NULL,
                       plainData.bytes,
                       plainData.length,
                       cypherData.mutableBytes,
                       cypherData.length,
                       &movedBytes);

    cypherData.length = movedBytes;

    if(ccStatus == kCCSuccess)
    {
        NSLog(@"Data: %@",cypherData);
    }
    else
    {
        NSLog(@"Failed DES decrypt, status: %d", ccStatus);
    }
    return cypherData;
}

- (NSData *)mac256WithKey:(NSData *)key andString:(NSString *)str
{
    NSData *strData = [str dataUsingEncoding:NSUTF8StringEncoding];
    NSMutableData* hash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH ];
    CCHmac(kCCHmacAlgSHA256, key.bytes, key.length, strData.bytes, strData.length, hash.mutableBytes);
    return hash;
}

这个版本失败是因为我由于对齐而出现加密错误,所以我将加密修改为:

    NSString *initVec = @"\0\0\0\0\0\0\0\0";
    const void *vinitVec = (const void *) [initVec UTF8String];

    CCCryptorStatus ccStatus;
    ccStatus = CCCrypt(kCCDecrypt,
                       kCCAlgorithm3DES,
                       kCCOptionPKCS7Padding | kCCOptionECBMode,
                       keyData.bytes,
                       kCCKeySize3DES,
                       vinitVec,
                       plainData.bytes,
                       plainData.length,
                       cypherData.mutableBytes,
                       cypherData.length,
                       &movedBytes);

这次加密似乎成功了,但输出不是预期的。

我尝试过使用此代码那么问题 https://stackoverflow.com/questions/33799494/3des-encrypt-result-in-php-java-and-net-produces-different-result-from-3des-io但它也失败了(签名不正确并且连接失败)。

这段代码有错误吗?

我知道这很困难,但是使用 TPV Redsys API 成功付款的人可以看到问题出在哪里?

EDIT 1:

这是文档中关于如何获取 DS_SIGNATURE 的内容:

对请求数据进行签名

获得要签名的数据字符串和终端特定密钥后,必须按照以下步骤计算签名:

  1. 每次操作都会生成一个特定的密钥。为了获得 操作中要使用的特定键,必须执行 商户密钥之间的3DES加密,必须是 先前以 BASE 64 解码,以及订单 Id 的值 (Ds_Merchant_Order)。

  2. HMAC SHA256 必须根据参数值计算 Ds_MerchantParameters和上一步获取的key。

  3. 获得的结果必须以 BASE 64 进行编码,并且该结果 编码将是 Ds_Signature 参数的值。

由于我已经获得了适用于 Java 的 Redsys API SDK(没有适用于 iOS 的 SKD),这就是它生成 Ds_Signature 参数的作用:

public  String createMerchantSignature(final String merchantKey) throws Exception {
        String merchantParams = createMerchantParameters();

        byte [] key = decodeB64(merchantKey.getBytes("UTF-8"));
        String secretKc = toHexadecimal(key, key.length);
        byte [] secretKo = encrypt_3DES(secretKc, getOrder());

        // MAC with the operation key "Ko" and then coded in BASE64
        byte [] hash = mac256(merchantParams, secretKo);
        String res = encodeB64String(hash);
        return res;
}

服务器期望的是这样的:

预期 Ds_SignatureVersion

HMAC SHA256 _V1

预期 Ds_MerchantParameters

eyJEU19NRVJDSEFOVF9BTU9VTlQiOiIxNDUiLCJEU19NRVJDSEFOVF9PUkRFUiI6IjE0NDYwNjg1ODEiLCJEU19NRVJDSEFOVF9NRVJDSEFOVENPREUiOizMjcyMzQ2ODgiLCJEU19NRVJDSEFOVF9DVVJSRU5DWSI6Ij k3OCISIkRTX01FUkNIQU5UX1RSQU5TQUNUSU9OVFlQRSI6IjAiLCJEU19NRVJDSEFOVF9URVJNSU5BTCI6IjEiLCJEU19NRVJDSEFOVF9NRVJDSEFOVFVSTCI6Imh0dHA6XC9cL3d3dy5iYW5jc2FiYWRlbGwuY29tXC9 1cmxOb3RpZmljYWNpb24ucGhwIiwiRFNfTUVSQ0hBTlRfVVJMT0siOiJodHRwOlwvXC93d3cuYmFuY3NhYmFkZWxsLmNvbVwvdXJsT0sucGhwIiwiRFNfTUVSQ0hBTlRfVVJMS08iOiJodHRwO lwvXC93d3cuYmFuY3NhYmFkZWxsLmNvbVwvdXJSS08ucGhwIiwiRFNfTUVSQ0hBTlRfUEFOIjoiNDU0ODgxMjA0OTQwMDAwNCISIkRTX01FUkNIQU5UX0VYUELSWURBVEUiOiIxNTEyIiwiRFNfTUVSQ0h BTLRfQ1ZWMiI6IjEyMyJ9

通过我的代码,我得到了这个精确的 base64 字符串,所以代码没问题。

预期 Ds_Signature

QfLVUv4nF2Nw7jBAkw0w8H0eRlwh2E1w/ZlKHdA2Sq0=

使用我的代码,我的 Ds_Signature 比预期的要长: ly2hYyjVlXQF%2FvgdEXBOj0obUdC7r5IERdEpLPSPksA=

EDIT 2:

我根据 zaph 的评论做了一些更改。我删除了 URL 的编码,并重新计算了步骤 3.2 和 3.3。这是我现在的代码:

- (void) payViaTPVWithAmount:(NSString *)amount andOrderId:(NSString *)orderId
{
    // I don't use my amount and orderId but those provided by the documentation to test
    // We need to obtain 3 fields:  DS_SIGNATURE_VERSION, DS_MERCHANTPARAMETERS and DS_SIGNATURE.

    // 1) Obtaining DS_SIGNATURE_VERSION
    NSString *dsSignatureVersion = @"HMAC_SHA256_V1"; 
    // >>>> This step is easy and of course is correct <<<<

    // 2) Obtaining DS_MERCHANTPARAMETERS
    // 2.1) Build params JSON -using test data-
    NSString *params = [NSString stringWithFormat: @"{\"DS_MERCHANT_AMOUNT\":\"%@\",\"DS_MERCHANT_ORDER\":\"%@\",\"DS_MERCHANT_MERCHANTCODE\":\"%@\",\"DS_MERCHANT_CURRENCY\":\"978\",\"DS_MERCHANT_TRANSACTIONTYPE\":\"0\",\"DS_MERCHANT_TERMINAL\":\"%@\",\"DS_MERCHANT_MERCHANTURL\":\"%@\",\"DS_MERCHANT_URLOK\":\"%@\",\"DS_MERCHANT_URLKO\":\"%@\"}",
                        @"145",
                        @"1446117555",
                        @"327234688",
                        @"1",
                        @"http:\\/\\/www.bancsabadell.com\\/urlNotificacion.php",
                        @"http:\\/\\/www.bancsabadell.com\\/urlOK.php",
                        @"http:\\/\\/www.bancsabadell.com\\/urlKO.php"];

    //2.2) convert JSON params to base64
    NSData *paramsData = [params dataUsingEncoding:NSUTF8StringEncoding];
    NSString *paramsBase64 = [paramsData base64EncodedStringWithOptions:0];

    NSLog(@"apiMacSHA256\n%@", params);
    NSLog(@"apiMacSHA256Base64\n%@", paramsBase64); 
    // >>>>> This output is identical to that included in the documents so this step 2 is ok. <<<<<

    // 3) Obtaining DS_SIGNATURE
    // 3.1) start with secret key -the document gives this for test purposes-
    NSString *merchantKey = @"sq7HjrUOBfKmC576ILgskD5srU870gJ7"; //32 bytes

    // 3.2) convert it to base64
    NSData *merchantKeyData = [merchantKey dataUsingEncoding:NSUTF8StringEncoding];
    NSData *merchantKeyBase64Data = [merchantKeyData base64EncodedDataWithOptions:0];

    // 3.3) the documentation doesn't say to convert this key to Hex but I have the Android code (which is working) and it has this step. 
    // Anyway I've tested with both options, with this step and without this step.
    NSString *merchantHex = [merchantKeyBase64Data hexadecimalString];

    // 3.4) get 3DES from orderId and base 64 (or Hex depending on if step 3.3 is done) key just obtained
    NSData *operationKey = [self encrypt_3DSWithKey:merchantHex andOrderId:@"1446117555"];

    // 3.5) get HMAC SHA256 from operationkey and ds_merchantparameters
    NSData *signatureHmac256 = [self mac256WithKey:operationKey andString:paramsBase64];

    // 3.6) convert HMAC SHA256 to base64
    NSString *signatureHmac256Base64 = [signatureHmac256 base64EncodedStringWithOptions:0];

    NSLog(@"signatureHmac256Base64\n%@", signatureHmac256Base64);
    // This step is not working, the signature is not the correct one and the connection to the TPV Redsys system fails.

    // 4) Build the request
    // Set URL - using URL for development purposes-
    NSURL *url = [NSURL URLWithString:@"https://sis-t.redsys.es:25443/sis/realizarPago"];

    NSString *body = [NSString stringWithFormat:@"DS_SIGNATURE=%@&DS_MERCHANTPARAMETERS=%@&DS_SIGNATUREVERSION=%@", signatureHmac256HTTPEncoded, paramsHTTPEncoded, dsSignatureVersionEncoded];

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL: url];
    [request setHTTPMethod: @"POST"];
    [request setHTTPBody: [body dataUsingEncoding: NSUTF8StringEncoding allowLossyConversion:false]];

    [_webViewer loadRequest:request];
}

- (NSData *)encrypt_3DSWithKey:(NSString *)key andOrderId:(NSString *)str
{
    NSData *plainData = [str dataUsingEncoding:NSUTF8StringEncoding];
    NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];

    size_t bufferSize = plainData.length + kCCKeySize3DES;
    NSMutableData *cypherData = [NSMutableData dataWithLength:bufferSize];
    size_t movedBytes = 0;

    NSString *initVec = @"\0\0\0\0\0\0\0\0";
    const void *vinitVec = (const void *) [initVec UTF8String];

    CCCryptorStatus ccStatus;
    ccStatus = CCCrypt(kCCDecrypt,
                       kCCAlgorithm3DES,
                       kCCOptionPKCS7Padding | kCCOptionECBMode,
                       keyData.bytes,
                       kCCKeySize3DES,
                       vinitVec,
                       plainData.bytes,
                       plainData.length,
                       cypherData.mutableBytes,
                       cypherData.length,
                       &movedBytes);

    cypherData.length = movedBytes;

    if(ccStatus == kCCSuccess)
    {
        NSLog(@"Data: %@",cypherData);
    }
    else
    {
        NSLog(@"Failed DES decrypt, status: %d", ccStatus);
    }
    return cypherData;
}

- (NSData *)mac256WithKey:(NSData *)key andString:(NSString *)str
{
    NSData *strData = [str dataUsingEncoding:NSUTF8StringEncoding];
    NSMutableData* hash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH ];
    CCHmac(kCCHmacAlgSHA256, key.bytes, key.length, strData.bytes, strData.length, hash.mutableBytes);
    return hash;
}

我的 Ds_Signature 现在是 R8+4R6diEbm3nJR6KmonYDy53Zi4CZpuxdoMZtucGX4= (与预期不同),连接仍然失败。


None

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用于 TPV 支付的 iOS 3DES 加密 的相关文章

  • 两者都实现了类。将使用两者之一

    我有一个项目 它具有使用 SocketRocket 的依赖项 通过 CocoaPods 安装 并从 HeapAnalytics 导入了静态库 显然 HeapAnalytics 库已经使用了 SocketRocket 编译时没有出现错误 但在
  • AES 加密 Java/plsql

    我需要在Java和plsql DBMS CRYPTO for Oracle 10g 上实现相同的加密 解密应用程序 两种实现都工作正常 但这里的问题是我对相同纯文本的加密得到了不同的输出 下面是用于加密 解密过程的代码 Java 和 PLS
  • CBPeripheral 名称有时为 null

    我正在开发一个应用程序来与蓝牙 LE 外围设备进行通信 我目前正在测试的外围设备是其中之一these http www ti com tool cc2540dk mini 有趣的是 有时当我发现它时 我会得到它的正确名称 SimpleBLE
  • 使用 NSURLSessionDataTask 显示文件下载进度

    我想显示特定文件的文件下载进度 收到了多少字节 它与 NSURLSessionDownloadTask 配合得很好 我的问题是我想用 NSURLSessionDataTask 实现同样的效果 以下是将文件接收到 NSData 并写入文档文件
  • “同时创建 xib 文件”按钮已禁用

    我在创建时遇到这个问题UIView s子类 创建 例如 UIViewControllers or UITableViewCells没关系 为什么会出现这种情况 I create view using cmd N and Xcode Vers
  • 尝试复制文件时出错

    我正在尝试使用 NSFileManager 将临时文件复制到另一个位置 但是它失败并抱怨其中一个文件不存在 Copy temp file NSError error BOOL exists fileManager fileExistsAtP
  • iOS - NSNotificationCenter 多个UIKeyboard通知

    我有两个视图控制器 我们称它们为 A 和 B 1 在 A 中 我显示一个包含文本字段的 popOver 2 B中有一个UITextView用于简单的文本编辑 我必须管理 A 和 B 中的键盘才能滚动键盘隐藏的内容 我知道如何重新定位内容 我
  • 将带有地理位置数据的照片保存到照片库 Swift 3

    如何使用地理位置元数据将照片保存到照片库 我已请求 并允许 应用程序访问用户位置 private func allowAccessToUserLocation locationManager CLLocationManager locati
  • 为什么 Xcode 4 不会对我未完整实现 UITableViewDataSource 协议发出警告?

    如果我在 Xcode 中使用以下代码声明并不完全实现我自己的协议 一些协议 h protocol SomeProtocol
  • 如何从代码隐藏中设置 CarouselView 的项目?

    我有一个 CarouselView 它绑定到图像的 ItemsSource 但我想通过更改 CarouselView 的索引来更改当前显示的图像 我尝试使用 CarouselView Position 作为必须选择的元素的索引 但不幸的是这
  • 在后台任务中安排通知

    我正在为 iOS 开发一个日历 闹钟应用程序 它与网络服务器同步 当在服务器上添加活动时 会发出推送通知 以便 iOS 客户端可以获取新数据 并根据需要更新和安排下一次警报的时间 本地通知 但这仅在应用程序在客户端打开时才有效 我希望客户端
  • iOS UIButton 带有圆角和背景 bug

    我发现圆形 UIButton 存在一个奇怪的问题 这是我创建此按钮的代码块 let roundedButton UIButton type System roundedButton frame CGRectMake 100 100 100
  • UISearchController 保留问题

    我正在尝试使用 UISearchController 但是我遇到了无法解决的保留问题 MainTableview 有两个部分 第1节 基于某些正则表达式过滤数据 第2节 All Data 我将 UISearchController 添加到我
  • 无法使用 Xamarin 和 WCF 访问 Web 服务

    我想使用 Xamarin 和 WCF 来使用公共 Web 服务 对于这个演示 我将使用Xamarin iOS 这是我试图使用的 公共 网络服务 http www webservicex net globalweather asmx WSDL
  • 检查 touchend 是否在拖动后出现

    我有一些代码可以更改表的类 在手机上 有时表格对于屏幕来说太宽 用户将拖动 滚动来查看内容 但是 当他们触摸并拖动表格时 每次拖动都会触发 touchend 如何测试触摸端是否是触摸拖动的结果 我尝试跟踪dragstart和dragend
  • iOS WKWebView.scrollView委托导致BAD_ACCESS

    我的 viewController 有一个 UIView 属性 其中包含一个 WKWebView 我将 WKWebView rollView 委托设置为我的 viewController 它是我的 UIView 子类的公共函数 并在我的 v
  • 如何更改已上传的 Firebase 存储图像文件名?

    我需要更改已上传到 firebase 存储中的文件名 因为 在 firebase 存储中上传图像后 我将 url 保存在 firebase 数据库中的特定子 文件夹 下 但是 当我将图像移动到另一个子 文件夹 时 我需要根据新名称更改存储中
  • iOS7 中“-webkit-overflow-scrolling: touch” 最初的屏幕外元素被破坏

    既然转基因种子已经发布了 我们现在可以谈谈了 看起来 iOS7 中的 webkit overflow scrolling touch 已损坏 最初不在屏幕上的元素的触摸事件不会触发 或者在某些情况下只是不可靠 这是一个例子
  • 在 UIMenuItem 上设置accessibilityLabel

    我正在尝试设置accessibilityLabel of a UIMenuItem而且似乎没有效果 无论如何 VoiceOver 只是读取项目的标题 let foo UIMenuItem title foo action selector
  • ActionScript、NetStream.Play.Failed iOS AIR 移动设备

    我正在尝试以类似于 Tiberiu Ionu Stan http stackoverflow com questions 2036107 aac mp4 not working in actionscript 3s netstream 的方

随机推荐