iOS接入快盘的HMAC-SHA1授权认证失败解决方案

最近帮别人看了一个使用HMAC-SHA1 进行加密授权快盘的一个认证,经过测试,第一步获取 oAuth-token 就会出现错误,错误原因是,签名失败,也就是说我们的签名某个地方错误了,签名这个地方看着好做,其实一步走错,就会全部错哦,我们就完全跳进坑了


一、 下面我们看一下,第一步我们要怎么去接入快盘

  1. 点击进入快盘开发者中心
  2. 我们进行一个账号注册,添加一个应用 后然后获取对应的 auth_consumer_keyconsumer_secret
  3. 点击进入开发者文档一栏,这些文档可以在使用的时候慢慢研究
  4. 点击 左侧的 OAuth 协议 【RFC58493.4 HMAC-SHA1】最下面的签名生成算法

我们会看到如下几个重要的地方

iOS接入快盘的HMAC-SHA1授权认证失败解决方案_第1张图片
第一步授权重要步骤

其实这个加密规则还算简单,就是encode 稍微有点麻烦,我们感觉快盘的其实只是提供了一些API接口,并没有什么SDK,所以我们要完全按照文档提示的操作步骤走

二、签名算法的生成

  • 我们先了解一下官方API的签名,官方提供给我们的是Python 写的实例,有些不懂的似乎看的不是太懂。我们看下官方提供的思路
假设服务器地址为 openapi.kuaipan.cn,现在需要向 
http://openapi.kuaipan.cn/1/fileops/create_folder 用GET方法发
出请求,请求参数 (parameters) 如下:
{
        'oauth_version': '1.0', 
        'oauth_token': 'fa361a4a1dfc4a739869020e586582f9', 
        'oauth_signature_method': 'HMAC-SHA1', 
        'oauth_nonce': '58456623', 
        'oauth_timestamp': 1328881571, 
        'oauth_consumer_key': '79a7578ce6cf4a6fa27dbf30c6324df4', 
        'path': '/[email protected]', 
        'root': 'kuaipan'
}
//Python  签名加密格式:
http_method + "&" +
url_encode( base_uri ) + "&" +
url_encode(
        “&”.join(
                sort( [url_encode ( k ) + "=" +url_encode ( v ) for k, v in paramesters.items() ]
        )
)



我们将加密规则翻译过来看一下iOS中我们要如何写(拼接字符串参数) 步骤如下:(下面步骤都是拼接关系,我们进行拆分)

  1. HTTPMethod: GET 大写
  2. 地址符号: &
  3. url_encode: base_uri utf8string] 对基地址进行URL编码
  4. join 前面的&:此处表示将一个数组拼接起来的意思,紧跟后面的sort 函数结果 ,并不是 此处用 & 去连接
  5. sort : 按照官方给予的解释就是将参数按照 字典的ASCII 码进行Key值排序,例子给出的是升序排列组合, Python 所谓的字典就是Map 集合,OC对应的就是NSDictionary -字典
  6. sort函数内部: 里面的for 循环代表是循环遍历字典,并且每个键值对 都进行一次URL encode。循环之后的键值对经过 & 进行连接到一起
  7. 最终我们将4、5、6的最终拼接完成的字符串,再次经过一次URL ,encdoe ,就得到官方所说的原串的编码拼接完毕。

我们看一下官方生成的签名格式:

GET&http%3A%2F%2Fopenapi.kuaipan.cn%2F1%2Ffileops%2Fcre
ate_folder&oauth_consumer_key%3D79a7578ce6cf4a6fa27
dbf30c6324df4%26oauth_nonce%3D58456623%26oauth_signat
ure_method%3DHMAC-
SHA1%26oauth_timestamp%3D1328881571
%26oauth_token%3Dfa361a4a1dfc4a739869020e586582f9%26
oauth_version%3D1.0%26path%3D%252Ftest%2540kingsoft.co
m%26root%3Dkuaipan

原串编码组合完毕形式如上所示:

  • 原串编码完毕,我们使用HMAC-SHA1进行秘钥加密:
  1. 生成签名加密的秘钥,格式如下:
    然后生成签名加密的密钥(记得有个&),注意假如没有 oauth_token的话,"&"之后的部分是不用包含的:
    c7ed87c12e784e48983e3bcdc6889dad&0183ce137e4d4170b2ac19d3a9fda677
  2. 使用密钥通过HMAC-SHA1算法签名字符基串,生成签名(先生成数字签名,然后再用base64 encode):
    pa7Fuh9GQnsPc+Lcn+Qu6G7LVEU=
  3. 最后把urlencode后的签名作为oauth_signature的值,向连接发出请求:
    url_encode(auth_signature) 进行最后一次编码,现在就可以直接发送请求了:
    通过终端或者通过官方提供的平台都可以测试:
    终端命令:
curl –k 
"http://openapi.kuaipan.cn/1/fileops/create_folder?oauth_version=1.0&oauth_signature=pa7Fuh9GQnsPc%2BLcn
%2BQu6G7LVEU%3D&oauth_token=fa361a4a1dfc4a739869020e586582f9&oauth_signature_method=HMAC-SHA1&oauth_nonc
e=58456623&oauth_timestamp=1328881571&path=%2Ftest%40kingsoft.com&oauth_consumer_key=79a7578ce6cf4a6fa27
dbf30c6324df4&root=kuaipan"

三、iOS中如何去操作这些加密规则?

我们直接代码接入测试:

  1. 准备好申请的Key 和 Secret ,我们这里只做第一步的签名认证:
    获取requestToken
    baseUri :
static NSString  * baseUrl = @"https://openapi.kuaipan.cn/open/requestToken";

参数准备:

 NSDictionary * prama = @{@"oauth_consumer_key":@"xcVaIWFCPRTVabGH",
                              @"oauth_signature_method":@"HMAC-SHA1",
                              @"oauth_timestamp":[self dateString],
                              @"oauth_nonce":[self onceString],
                              @"oauth_version":@"1.0",
                              };

涉及到的两个方法 dateStringonceString

- (NSString *)dateString{
    NSDate* dat = [NSDate dateWithTimeIntervalSinceNow:0];
    NSTimeInterval a=[dat timeIntervalSince1970]*1000;
    NSString *timeString = [NSString stringWithFormat:@"%.f", a];

    NSRange rang = {0,10};
    NSString *subString = [timeString substringWithRange:rang];

    return subString;
}

- (NSString *)onceString{
    NSString *string = [[NSString alloc]init];
    for (int i = 0; i<8; i++) {
        int number = arc4random() % 25;
        if (number < 10) {
            int figure = (arc4random() % 26) + 97;
            char character = figure;
            NSString *tempString = [NSString stringWithFormat:@"%c", character];
            string = [string stringByAppendingString:tempString];
            if (figure%2==0) {
                [string uppercaseString];
            }
        }else {

            int figure = arc4random() % 10;
            NSString *tempString = [NSString stringWithFormat:@"%d", figure];
            string = [string stringByAppendingString:tempString];
        }
    }
    return string;

}

我们所用到的URL——Encode方法如下

- (NSString *)encodeToPercentEscapeString: (NSString *) input
{
    NSString*
    outputStr = (__bridge NSString *)CFURLCreateStringByAddingPercentEscapes(

                                                                             NULL, /* allocator */

                                                                             (__bridge CFStringRef)input,

                                                                             NULL, /* charactersToLeaveUnescaped */

                                                                             (CFStringRef)@"!*'();:@&=+$,/?%#[]",
                                                                             
                                                                             kCFStringEncodingUTF8);
    
    
    return  outputStr;
}

HMAC-SHA1 签名之后,使用Base64编码
签名格式: HMAC-SHA1(原串,秘钥) -> Base64 编码
使用HMAC,时需要导入一下两个类库

#import 
#import 
+ (NSString *)hmac_sha1:(NSString *)key text:(NSString *)text{

    const char *cKey  = [key cStringUsingEncoding:NSUTF8StringEncoding];
    const char *cData = [text cStringUsingEncoding:NSUTF8StringEncoding];

    char cHMAC[CC_SHA1_DIGEST_LENGTH];

    CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);

    NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:CC_SHA1_DIGEST_LENGTH];
    NSString *hash = [HMAC base64Encoding];//base64Encoding函数在NSData+Base64中定义(NSData+Base64网上有很多资源)

    return hash;
}
  • 下面我们进行对参数进行排序:代码如下:
 NSArray * keyArray = [prama allKeys];
     NSArray * sortArray = [keyArray sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
         return [obj1 compare:obj2 options:NSLiteralSearch];

     }];
  • 字典排序完毕,我们要进行组合,并对其采用URL编码:
 NSMutableString * appendString = [NSMutableString string];

     [appendString appendString:@"GET&"];
     [appendString appendString:[self encodeToPercentEscapeString:baseUrl]];

     [appendString appendString:@"&"];
     NSMutableArray * tempArray = [NSMutableArray array];

     for (NSString * keys in sortArray) {
         [tempArray addObject:[NSString stringWithFormat:@"%@=%@",[self encodeToPercentEscapeString:keys],[self encodeToPercentEscapeString:prama[keys]]]];
     }
    NSString * comments  =  [tempArray componentsJoinedByString:@"&"];
     
     NSLog(@"com = %@",comments);
     [appendString appendString:[self encodeToPercentEscapeString:comments]];
  • 下面进行HMAC-SHA1签名:
NSString * macsha = [self.class hmac_sha1:@"申请应用所获得consumer_secret&" text:appendString];
 NSString * praurl = [NSString stringWithFormat:@"oauth_signature=%@&oauth_consumer_key=%@&oauth_nonce=%@&oauth_signature_method=%@&oauth_timestamp=%@&oauth_version=%@",[self encodeToPercentEscapeString:macsha],prama[@"oauth_consumer_key"],prama[@"oauth_nonce"],prama[@"oauth_signature_method"],prama[@"oauth_timestamp"],prama[@"oauth_version"]];
//     NSLog(@"prama = %@",praurl);
NSString * replceurl = [NSString stringWithFormat:@"%@?%@",baseUrl,praurl];
  • 签名组合完毕,进行如下请求验证获取第一次的Token:
 NSURL * url = [NSURL URLWithString:replceurl];
     NSLog(@"url = %@",url);

     NSMutableURLRequest * requestUrl = [NSMutableURLRequest requestWithURL:url];
     [requestUrl setHTTPMethod:@"GET"];
     NSURLSessionConfiguration * config = [NSURLSessionConfiguration defaultSessionConfiguration];
     NSURLSession * session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:nil];

     NSURLSessionDataTask * task = [session dataTaskWithRequest:requestUrl completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
         if (data) {
             NSLog(@"有数据");
             NSString * string = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
             NSLog(@"string1 = %@",string);
         }else{
             NSString * string = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
             NSLog(@"string2 = %@",string);

         }
         NSLog(@"responce = %d",[(NSHTTPURLResponse *)response statusCode]);

         if (error) {
             NSLog(@"error = %@",[error localizedFailureReason]);
         }

     }];
     [task resume];

如果在请求中返回如下 code :

iOS接入快盘的HMAC-SHA1授权认证失败解决方案_第2张图片
配置正确的情况下会出现 401 签名的问题

如图,选中的部分基本上是签名错误会返回的,这个问题看着很简单,因为如果不是按照规定的编码顺序,这个问题或者你要看上一天两天了,我在帮他调试的时候,最后一步的因为一个参数未编码,导致多浪费了半天时间,真的是自己挖坑哦。

总结:

以上是快盘接入APP进行的一个签名授权验证, 后续一直按照如上方法,一直到获取到accessToken 为止,如果还有不明白的,请留言!
如果以上有写错的地方,还望指出,给一些需要的人一些帮助。

你可能感兴趣的:(iOS接入快盘的HMAC-SHA1授权认证失败解决方案)