最近幫別人看了一個使用HMAC-SHA1 進行加密授權快盤的一個認證咱筛,經(jīng)過測試,第一步獲取 oAuth-token 就會出現(xiàn)錯誤,錯誤原因是饭于,簽名失敗,也就是說我們的簽名某個地方錯誤了维蒙,簽名這個地方看著好做掰吕,其實一步走錯,就會全部錯哦木西,我們就完全跳進坑了
一畴栖、 下面我們看一下,第一步我們要怎么去接入快盤
- 點擊進入快盤開發(fā)者中心
- 我們進行一個賬號注冊八千,添加一個應用 后然后獲取對應的
auth_consumer_key
和consumer_secret
- 點擊進入開發(fā)者文檔一欄吗讶,這些文檔可以在使用的時候慢慢研究
- 點擊 左側(cè)的 OAuth 協(xié)議 【RFC58493.4 HMAC-SHA1】最下面的簽名生成算法
我們會看到如下幾個重要的地方
其實這個加密規(guī)則還算簡單,就是encode 稍微有點麻煩恋捆,我們感覺快盤的其實只是提供了一些API接口照皆,并沒有什么SDK,所以我們要完全按照文檔提示的操作步驟走
二沸停、簽名算法的生成
- 我們先了解一下官方API的簽名膜毁,官方提供給我們的是Python 寫的實例,有些不懂的似乎看的不是太懂愤钾。我們看下官方提供的思路
假設服務器地址為 openapi.kuaipan.cn瘟滨,現(xiàn)在需要向
http://openapi.kuaipan.cn/1/fileops/create_folder 用GET方法發(fā)
出請求,請求參數(shù) (parameters) 如下:
{
'oauth_version': '1.0',
'oauth_token': 'fa361a4a1dfc4a739869020e586582f9',
'oauth_signature_method': 'HMAC-SHA1',
'oauth_nonce': '58456623',
'oauth_timestamp': 1328881571,
'oauth_consumer_key': '79a7578ce6cf4a6fa27dbf30c6324df4',
'path': '/test@kingsoft.com',
'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() ]
)
)
我們將加密規(guī)則翻譯過來看一下iOS中我們要如何寫(拼接字符串參數(shù)) 步驟如下:(下面步驟都是拼接關系能颁,我們進行拆分)
- HTTPMethod:
GET
大寫 - 地址符號:
&
- url_encode:
base_uri utf8string]
對基地址進行URL編碼 - join 前面的
&
:此處表示將一個數(shù)組拼接起來的意思杂瘸,緊跟后面的sort 函數(shù)結(jié)果
,并不是 此處用&
去連接 - sort : 按照官方給予的解釋就是將參數(shù)按照 字典的ASCII 碼進行Key值排序伙菊,例子給出的是升序排列組合败玉, Python 所謂的字典就是Map 集合,OC對應的就是NSDictionary -字典
- sort函數(shù)內(nèi)部: 里面的for 循環(huán)代表是循環(huán)遍歷字典镜硕,并且每個鍵值對 都進行一次URL encode运翼。循環(huán)之后的鍵值對經(jīng)過
&
進行連接到一起 - 最終我們將4、5兴枯、6的最終拼接完成的字符串血淌,再次經(jīng)過一次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進行秘鑰加密:
- 生成簽名加密的秘鑰峰伙,格式如下:
然后生成簽名加密的密鑰(記得有個&)疗疟,注意假如沒有 oauth_token的話,"&"之后的部分是不用包含的:
c7ed87c12e784e48983e3bcdc6889dad&0183ce137e4d4170b2ac19d3a9fda677
- 使用密鑰通過HMAC-SHA1算法簽名字符基串瞳氓,生成簽名(先生成數(shù)字簽名策彤,然后再用base64 encode):
pa7Fuh9GQnsPc+Lcn+Qu6G7LVEU=
- 最后把urlencode后的簽名作為oauth_signature的值,向連接發(fā)出請求:
url_encode(auth_signature) 進行最后一次編碼匣摘,現(xiàn)在就可以直接發(fā)送請求了:
通過終端或者通過官方提供的平臺都可以測試:
終端命令:
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中如何去操作這些加密規(guī)則?
我們直接代碼接入測試:
- 準備好申請的Key 和 Secret 音榜,我們這里只做第一步的簽名認證:
獲取requestToken
baseUri :
static NSString * baseUrl = @"https://openapi.kuaipan.cn/open/requestToken";
參數(shù)準備:
NSDictionary * prama = @{@"oauth_consumer_key":@"xcVaIWFCPRTVabGH",
@"oauth_signature_method":@"HMAC-SHA1",
@"oauth_timestamp":[self dateString],
@"oauth_nonce":[self onceString],
@"oauth_version":@"1.0",
};
涉及到的兩個方法 dateString
和 onceString
- (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 <CommonCrypto/CommonHMAC.h>
#import <CommonCrypto/CommonCryptor.h>
+ (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函數(shù)在NSData+Base64中定義(NSData+Base64網(wǎng)上有很多資源)
return hash;
}
- 下面我們進行對參數(shù)進行排序:代碼如下:
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(@"有數(shù)據(jù)");
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 :
如圖瞬场,選中的部分基本上是簽名錯誤會返回的买鸽,這個問題看著很簡單,因為如果不是按照規(guī)定的編碼順序贯被,這個問題或者你要看上一天兩天了眼五,我在幫他調(diào)試的時候,最后一步的因為一個參數(shù)未編碼彤灶,導致多浪費了半天時間看幼,真的是自己挖坑哦。
總結(jié):
以上是快盤接入APP進行的一個簽名授權驗證幌陕, 后續(xù)一直按照如上方法诵姜,一直到獲取到accessToken 為止,如果還有不明白的苞轿,請留言茅诱!
如果以上有寫錯的地方,還望指出搬卒,給一些需要的人一些幫助瑟俭。