如何在 Objective-C 中获取 QuickBlox 令牌

Posted

技术标签:

【中文标题】如何在 Objective-C 中获取 QuickBlox 令牌【英文标题】:How to get QuickBlox token in Objective-C 【发布时间】:2014-07-25 23:33:57 【问题描述】:

由于工作限制,必须使用 Quickblox 的 RESTful API 而不是他们的 ios SDK,并且无法根据their tutorial 生成有效签名。

以下是我的电话回复:

2014-07-25 16:19:12.646 test[2247:60b]response: <NSHTTPURLResponse: 0x10c41ae40>  URL:https://api.quickblox.com/session.json   status code: 422, headers 
"Access-Control-Allow-Origin" = "*";
"Cache-Control" = "no-cache";
Connection = "keep-alive";
"Content-Type" = "application/json; charset=utf-8";
Date = "Fri, 25 Jul 2014 23:19:12 GMT";
"QuickBlox-REST-API-Version" = "0.1.1";
Server = "nginx/1.0.15";
Status = "422 Unprocessable Entity";
"Transfer-Encoding" = Identity;
"X-Rack-Cache" = "invalidate, pass";
"X-Request-Id" = 8413fb7182cee06857619b14f363ed78;
"X-Runtime" = "0.004821";
"X-UA-Compatible" = "IE=Edge,chrome=1";
  data: "errors":"base":["Unexpected signature"] connectionError: (null)

以下是我正在使用的拨打电话的方法:

- (void) retrieveQBTokenTest

// Authentication data
NSString *appId = @"12345";
NSString *authKey = @"ypqdqEx7sOeWEQr";
NSString *timestamp = [NSString stringWithFormat:@"%.0f", [[NSDate date] timeIntervalSince1970]];
NSString *nonce = [NSString stringWithFormat:@"%i", (arc4random()%1000)+1];
NSString *authSecret = @"dtoA5bkNrBUZn5L";
NSString *dataStringForSig = [NSString stringWithFormat:@"application_id=%@&auth_key=%@&nonce=%@&timestamp=%@", appId, authKey, nonce, timestamp];
NSString *sig = [self hmacsha1:dataStringForSig secret:authSecret];

// Package authentication data into JSON
NSDictionary *dict = @
                       @"application_id":appId,
                       @"auth_key":authKey,
                       @"timestamp":timestamp,
                       @"nonce":nonce,
                       @"signature":sig
                       ;
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict
                                                   options:0
                                                     error:&error];
NSLog(@"%s: jsonString: %@", __FUNCTION__, [[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding]);

// Generate request
NSURL *url = [NSURL URLWithString:@"https://api.quickblox.com/session.json"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
    // Headers
    [request setHTTPMethod:@"POST"];
    [request setValue:@"0.1.1" forHTTPHeaderField:@"QuickBlox-REST-API-Version"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    NSString *length = [NSString stringWithFormat:@"%lu", (unsigned long)[jsonData length]];
    [request setValue:length forHTTPHeaderField:@"Content-Length"];

    // Body
    [request setHTTPBody:jsonData];

// Make call
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) 
    NSString *dataResponseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"%s: response: %@ data: %@ connectionError: %@", __FUNCTION__, response, dataResponseString, connectionError);
];

最后,我的哈希方法:

- (NSString *)hmacsha1:(NSString *)data secret:(NSString *)key 

const char *cKey  = [key cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];
unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
NSString *hash = [HMAC base64EncodedStringWithOptions:0];

return hash;

看起来我生成签名的方式不好,但不确定我在哪里搞砸了。有什么建议吗?

【问题讨论】:

【参考方案1】:

这是一个正确的签名生成方法:

+ (NSString *)signData:(NSData *)data withSecret:(NSString *)secret
    NSData *secretData = [secret dataUsingEncoding:NSUTF8StringEncoding];
    NSData *clearTextData = data;
    uint8_t digest[CC_SHA1_DIGEST_LENGTH] = 0;
    CCHmacContext hmacContext;
    CCHmacInit(&hmacContext, kCCHmacAlgSHA1, secretData.bytes, secretData.length);
    CCHmacUpdate(&hmacContext, clearTextData.bytes, clearTextData.length);
    CCHmacFinal(&hmacContext, digest);
    NSData *result = [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH];
    NSString *hash = [result description];
    hash = [hash stringByReplacingOccurrencesOfString:@" " withString:@""];
    hash = [hash stringByReplacingOccurrencesOfString:@"<" withString:@""];
    hash = [hash stringByReplacingOccurrencesOfString:@">" withString:@""];

    return hash;

及用法:

[SignHelper signData:[self rawBodyWithoutEncode] withSecret:[QBSettings authorizationSecret]]


- (NSData *)rawBodyWithoutEncode

    NSData *raw = nil;

    NSMutableString *params = [[NSMutableString alloc] init];

    // sort
    NSMutableArray *sortKeys = [NSMutableArray arrayWithArray:[parameters allKeys]];
    [sortKeys sortUsingSelector:@selector(compare:)];

    for (id s in sortKeys)
        [params appendFormat:@"%@=%@&", s, [parameters objectForKey:s]];
    
    [params deleteCharactersInRange:NSMakeRange([params length] - 1, 1)];

    raw = [params dataUsingEncoding:NSUTF8StringEncoding];

    return raw;

...

NSMutableDictionary *params = [NSMutableDictionary 
                               dictionary];
NSUInteger appID = [QBSettings applicationID];
NSString *authKey = [QBSettings authorizationKey];
NSUInteger nonce = arc4random()%1000;
NSUInteger timestamp = [[NSDate date] timeIntervalSince1970];

[params setValue:[NSString stringWithFormat:@"%lu",(unsigned long)appID] forKey:@"application_id"];
[params setValue:authKey forKey:@"auth_key"];
[params setValue:[NSString stringWithFormat:@"%lu",(unsigned long)nonce] forKey:@"nonce"];
[params setValue:[NSString stringWithFormat:@"%lu",(unsigned long)timestamp] forKey:@"timestamp"];

【讨论】:

以上是关于如何在 Objective-C 中获取 QuickBlox 令牌的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Objective-C 中获取用户的语言环境?

如何在 Objective-C 中使用符号断点获取参数

如何在 Objective-C 中获取当前工作目录的绝对路径?

如何在 Objective-C 中获取 QuickBlox 令牌

如何从objective-c中的simd_float4获取值

如何在不参考 NSEvent 的情况下在 Objective-C 中获取键盘状态