我试图在 iOS 中实现 MasterCard MoneySend API。API 需要两条腿的 oauth 来授权请求。
标准是:
- OAuth 应该
RSA-SHA1
用于生成签名。 - 请求方法应该是
PUT
- 由于请求有正文,因此需要包含正文哈希。
我们拥有的是一个 ConsumerKey 和 PrivetKey 的 .p12 文件。我尝试了许多库,例如RestKit
, ASIHTTPRequest
,OAuthConsumer
等。
最后我得到了这段代码:
#import "NSStringAdditions.h"
#import "ASIHTTPRequest.h"
#import "sha.h"
#import "rsa.h"
#import "pem.h"
#import "objects.h"
#define SANDBOX_CONSUMER_KEY @""
#define PRIVETKEY @""
@interface MC_TestApi ()
<NSURLConnectionDelegate>
{
NSMutableData *responseData;
NSURLConnection *urlConnection;
}
@end
@implementation MC_TestApi
- (NSString*)oAuthBodyHash:(NSData*)body
{
unsigned char digest[SHA_DIGEST_LENGTH];
SHA1([body bytes], [body length], (unsigned char*)&digest);
unsigned c = SHA_DIGEST_LENGTH;
uint8_t *bytes = malloc(sizeof(*bytes) * c);
unsigned i;
for (i = 0; i < c; i++)
{
int byte = (unsigned int)digest[i];
bytes[i] = (uint8_t)byte;
}
NSData *strData = [NSData dataWithBytesNoCopy:bytes length:c freeWhenDone:YES];
return [NSString base64StringFromData:strData length:strData.length ];
}
- (NSString *)sha1RsaText: (NSString *)text
{
NSString *path = [[NSBundle mainBundle] pathForResource: @"MCOpenAPI" ofType: @"pem"];
FILE *secretFile = fopen([path cStringUsingEncoding: NSUTF8StringEncoding], "r");
if (secretFile==NULL){
printf("ERROR opening RSA Keys failed test.pem\n");
return nil;
}
NSData *clearTextData = [text dataUsingEncoding: NSUTF8StringEncoding];
unsigned char encryptedData[40];
RSA *rsa = (RSA *)PEM_read_RSAPrivateKey(secretFile, &rsa, NULL, NULL);
unsigned int encryptionLength = 40; //RSA_size(rsa);
RSA_sign(NID_sha1, [clearTextData bytes], [clearTextData length], encryptedData, &encryptionLength, rsa);
unsigned c = 20;
uint8_t *bytes = malloc(sizeof(*bytes) * c);
unsigned i;
for (i = 0; i < c; i++)
{
int byte = (unsigned int)encryptedData[i];
bytes[i] = (uint8_t)byte;
}
NSData *strData = [NSData dataWithBytesNoCopy:bytes length:c freeWhenDone:YES];
return [NSString base64StringFromData:strData length:strData.length ];
}
- (NSString *)urlEncodeValue:(NSString *)str
{
NSString *result = (NSString *) CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)str,
NULL,
CFSTR(":/?#[]@!$&'()*+,;="),
kCFStringEncodingUTF8));
return result;
}
-(void)requestWithOAuth {
NSUInteger time = [[NSDate date] timeIntervalSince1970];
NSString *timestamp = [NSString stringWithFormat: @"%d", time];
NSString *urlString = @"https://sandbox.api.mastercard.com/moneysend/eligibility/v1/pan";
NSString *nonce = @"1234567890";
NSString *consumerKey = SANDBOX_CONSUMER_KEY;
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@?Format=XML",urlString]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:@"PUT"];
[request setValue:@"application/xml" forHTTPHeaderField:@"content-type"];
NSString *bodyString = @"<?xml version=\"1.0\" encoding=\"UTF-8\" ?> <PanEligibilityRequest> <SendingAccountNumber>5555555555559999</SendingAccountNumber> </PanEligibilityRequest>";
NSData *body = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
NSString *bodyLength = [NSString stringWithFormat:@"%d",[body length]];
[request setValue:bodyLength forHTTPHeaderField:@"content-length"];
NSString *oAuthBodyHash = [self oAuthBodyHash:body];
NSString *header = [NSString stringWithFormat:@"oauth_timestamp=\"%@\",oauth_nonce=\"%@\",oauth_version=\"1.0\",oauth_body_hash=\"%@\",oauth_consumer_key=\"%@\",oauth_signature_method=\"RSA-SHA1\"",timestamp,nonce,oAuthBodyHash,SANDBOX_CONSUMER_KEY];
NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithObjectsAndKeys:
consumerKey,@"oauth_consumer_key",
nonce,@"oauth_nonce",
@"RSA-SHA1",@"oauth_signature_method",
timestamp,@"oauth_timestamp",
@"1.0",@"oauth_version",
oAuthBodyHash,@"oauth_body_hash",
@"XML",@"Format",
nil];
NSArray *keys = [parameters allKeys];
NSSortDescriptor* sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:nil ascending:YES selector:@selector(localizedCompare:)];
keys = [keys sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
NSString *test = @"";
for (NSUInteger i = 0; i<[keys count]; i++) {
NSString *key = [keys objectAtIndex:i];
NSString *str = [NSString stringWithFormat:@"%@=%@",key,[parameters objectForKey:key]];
if (i!=([parameters count]-1))
str = [str stringByAppendingString:@","];
test = [test stringByAppendingString:str];
}
NSString *signatureString = [NSString stringWithFormat: @"PUT&%@%%26%@",
[self urlEncodeValue: urlString],
[self urlEncodeValue: [test stringByReplacingOccurrencesOfString: @"," withString: @"&"]]];
NSString *signature = [self sha1RsaText: signatureString];
NSString *finalAuthHeader = [NSString stringWithFormat: @"OAuth %@,oauth_signature=\"%@\"", header, signature];
[request setValue:finalAuthHeader forHTTPHeaderField:@"Authorization"];
[request setHTTPBody:body];
urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[urlConnection start];
}
#pragma mark - NSURLConnectionDelegate
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int code = [httpResponse statusCode];
NSLog(@"in didReceiveResponse %i %@",code,[NSHTTPURLResponse localizedStringForStatusCode:code]);
responseData = nil;
responseData = [[NSMutableData alloc] init];
[responseData setLength:0];
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(@"in didReceiveData ");
[responseData appendData:data];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSLog(@"\n\n ERORR \n\n%@",[NSString stringWithFormat:@"Connection failed: %@", [error description]]);
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
if (responseData) {
NSString *newStr = [NSString stringWithUTF8String:[responseData bytes]];
NSLog(@"XML: %@",newStr);
}
responseData = nil;
}
@end
我的问题是:
我在这里做错了什么?
我可以使用 iOS 中的任何开源库吗?
伙计们请帮助我...谢谢和问候