支付宝iOS SDK Demo加密解密分析(1)

电商类app最终都会面临一个问题:如何让用户便捷地在线支付。由于支付行业本身的复杂性和特殊性,大部分公司会选择集成第三方支付。支付宝作为国内最大的第三方支付公司,由于其极其广泛的用户基础,成了很多公司首选的支付方案。支付宝提供了相对完善的sdk和文档,只要按照步骤操作,集成sdk不是件太难的事情,但是sdk背后涉及许多安全相关的内容。如果能认真理解支付宝sdk demo的加密解密逻辑,举一反三,我们也能应用到自己的项目里。本文会从sdk demo提供的源码出发,分析背后的加密解密流程。

SDK需要解决的问题

  1. 是哪家商户发起了调用?如何保证发起请求的就是那家商家?
  2. 如何保证商品数据在网络传输中没有被篡改?

解决方案

  1. 客户端用自己的私钥将特征码加密后,将此数据发给服务器,服务器使用商户的公钥对密文进行解密,如果解密成功可唯一确定这是用商户的私钥加密的密文。只要商户的私钥不被泄露,那么使用私钥的人肯定是那家商户。
  2. 我们可以对商品数据做SHA1签名,目的是得到一个和这个商品信息唯一相关的字符串,然后对这个字符串加密,然后将这个加密后的字符串给支付宝,支付宝拿到商品信息和这个加密后的字符串后,首先解密,还原为SHA1签名字符串,然后对商品信息进行SHA1签名,对比这两个SHA1签名字符串,如果相等,那么这个商品一定没有被篡改过。因为如果信息被篡改,那么这个SHA1签名肯定不一样;如果有人想要伪造这个SHA1签名,又需要私钥,所以这就保证了这个商品是你发的。

代码分析

支付宝sdk完整的一次加密解密涉及客户端和服务端两部分

  1. 客户端通过支付宝sdk发起调用,发送加密后的商品信息
  2. 支付宝sdk解析数据后跳转到支付宝app显示支付页面,用户执行支付操作
  3. 服务端解析支付宝支付完成后的回调,包含加密后的支付结果数据

本文分析客户端的实现,也就是上述的1和2:

/*
 *生成订单信息
 */
//将商品信息赋予AlixPayOrder的成员变量
Order *order = [[Order alloc] init];
order.partner = partner;
order.sellerID = seller;
order.outTradeNO = [self generateTradeNO]; //订单ID(由商家自行制定)
order.subject = product.subject; //商品标题
order.body = product.body; //商品描述
order.totalFee = [NSString stringWithFormat:@"%.2f",product.price]; //商品价格
order.notifyURL =  @"http://www.xxx.com"; //回调URL
    

这里设置了商品的信息,包括商品标题、商品描述和价格,要注意的是,这里order.notifyURL是服务端的回调地址,也就是执行上述的第三步。

//将商品信息拼接成字符串
NSString *orderSpec = [order description];
NSLog(@"orderSpec = %@",orderSpec);
    
//获取私钥并将商户信息签名,外部商户可以根据情况存放私钥和签名,只需要遵循RSA签名规范,并将签名字符串base64编码和UrlEncode
id<DataSigner> signer = CreateRSADataSigner(privateKey);
NSString *signedString = [signer signString:orderSpec];

这里的orderSpec包含了商品的所有信息。通过CreateRSADataSigner(privateKey)首先对商品信息执行sha1签名并执行RSA加密,其中的privateKey是商户自己的private key。具体的签名加密是通过下面这个函数执行的

//该签名方法仅供参考,外部商户可用自己方法替换
- (NSString *)signString:(NSString *)string {
    ......
    const char *message = [string cStringUsingEncoding:NSUTF8StringEncoding];
    int messageLength = (int)strlen(message);
    unsigned char *sig = (unsigned char *)malloc(256);
    unsigned int sig_len;
    int ret = rsa_sign_with_private_key_pem((char *)message, messageLength, sig, &sig_len, (char *)[path UTF8String]);
    //签名成功,需要给签名字符串base64编码和UrlEncode,该两个方法也可以根据情况替换为自己函数
    if (ret == 1) {
        NSString * base64String = base64StringFromData([NSData dataWithBytes:sig length:sig_len]);
        //NSData * UTF8Data = [base64String dataUsingEncoding:NSUTF8StringEncoding];
        signedString = [self urlEncodedString:base64String];
    }
    
    free(sig);
    return signedString;    
    
}

其中rsa_sign_with_private_key_pem函数实现如下

int rsa_sign_with_private_key_pem(char *message, int message_length
                                  , unsigned char *signature, unsigned int *signature_length
                                  , char *private_key_file_path)
{
    SHA1((unsigned char *)message, message_length, sha1);
    ......
    int rsa_sign_valid = RSA_sign(NID_sha1
                              , sha1, 20
                              , signature, signature_length
                              , rsa_private);
    return success;
}

通过OpenSSL提供的SHA1((unsigned char *)message, message_length, sha1);方法生成了sha1签名,RSA_sign(NID_sha1, sha1, 20, signature, signature_length, rsa_private)生成了RSA加密后的signature

//将签名成功字符串格式化为订单字符串,请严格按照该格式
NSString *orderString = nil;
if (signedString != nil) {
    orderString = [NSString stringWithFormat:@"%@&sign=\"%@\"&sign_type=\"%@\"",
                   orderSpec, signedString, @"RSA"];
    
    [[AlipaySDK defaultService] payOrder:orderString fromScheme:appScheme callback:^(NSDictionary *resultDic) {
        NSLog(@"reslut = %@",resultDic);
    }];
}

这里通过payOrder发起了对支付宝app的调用。这里的orderSpec就是商品的明文信息,signedString是加密后的SHA1签名, orderString包含了商品明文信息和加密后的SHA1签名。payOrder方法不是开源的,我们可以大致猜测下它的实现。

  1. 支付宝服务端接收到明文消息orderSpec和签名signedString
  2. 支付宝服务端使用商户的公钥和orderSpec验证签名signedString
  3. 如果验签成功,解析商品详细信息,并跳转到支付宝app执行支付流程;如果验签失败,则提示错误信息。
  4. 用户完成支付,跳回原来的app,并进入客户端callback执行NSLog(@"reslut = %@",resultDic);。至此,客户端sdk这边的工作就完成了。

下一篇文章会基于支付宝提供的PHP demo分析服务端的实现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容