最近幫別人看了一個使用HMAC-SHA1 進行加密授權(quán)快盤的一個認證,經(jīng)過測試,第一步獲取 oAuth-token 就會出現(xiàn)錯誤,錯誤原因是,簽名失敗,也就是說我們的簽名某個地方錯誤了,簽名這個地方看著好做,其實一步走錯,就會全部錯哦,我們就完全跳進坑了
一、 下面我們看一下,第一步我們要怎么去接入快盤
- 點擊進入快盤開發(fā)者中心
- 我們進行一個賬號注冊,添加一個應(yīng)用 后然后獲取對應(yīng)的
auth_consumer_key和consumer_secret - 點擊進入開發(fā)者文檔一欄,這些文檔可以在使用的時候慢慢研究
- 點擊 左側(cè)的 OAuth 協(xié)議 【RFC58493.4 HMAC-SHA1】最下面的簽名生成算法
我們會看到如下幾個重要的地方

其實這個加密規(guī)則還算簡單,就是encode 稍微有點麻煩,我們感覺快盤的其實只是提供了一些API接口,并沒有什么SDK,所以我們要完全按照文檔提示的操作步驟走
二、簽名算法的生成
- 我們先了解一下官方API的簽名,官方提供給我們的是Python 寫的實例,有些不懂的似乎看的不是太懂。我們看下官方提供的思路
假設(shè)服務(wù)器地址為 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ù)) 步驟如下:(下面步驟都是拼接關(guān)系,我們進行拆分)
- HTTPMethod:
GET大寫 - 地址符號:
& - url_encode:
base_uri utf8string]對基地址進行URL編碼 - join 前面的
&:此處表示將一個數(shù)組拼接起來的意思,緊跟后面的sort 函數(shù)結(jié)果,并不是 此處用&去連接 - sort : 按照官方給予的解釋就是將參數(shù)按照 字典的ASCII 碼進行Key值排序,例子給出的是升序排列組合, Python 所謂的字典就是Map 集合,OC對應(yīng)的就是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,時需要導(dǎo)入一下兩個類庫
#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:@"申請應(yīng)用所獲得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ù)未編碼,導(dǎo)致多浪費了半天時間,真的是自己挖坑哦。
總結(jié):
以上是快盤接入APP進行的一個簽名授權(quán)驗證, 后續(xù)一直按照如上方法,一直到獲取到accessToken 為止,如果還有不明白的,請留言!
如果以上有寫錯的地方,還望指出,給一些需要的人一些幫助。