i am trying to do a generic - hmac verification with an iOS app and an express node.js app.
generating the hmac using given samples at: http://www.jokecamp.com/blog/examples-of-creating-base64-hashes-using-hmac-sha256-in-different-languages/#js
having the following problem:
subclassed AFHTTPRequestOperationManager to gain access to POST:
i want to hmac the nsdictionary parameters.
so convert the nsdicionary to json - hmac it - and set hmac header in request.
on the receiver side, i use crypto-js and express to access the req.body - and hmac the json object.
problem is! - those keys are not in same order, even if i force the sort on the keys in nsdictionary, the transoformed json does not come in the order way.
after hours of googling i accepted that json objects cannot be orderd, in an reliable way.
so what is the best-practice to hmac an nsdictionary, by ignoring the order?
(i could hmac on only a few keys, but that would be way to less generic, mean adding a dictionary key would require code change in ios and express)
generating the hmac only based on the URI - works fine, but its a way to open :)
UPDATE:
to be a bit more specific.
on the app i transform my nsdictionary to json by and then calculate the hmac of the json_string
NSDicationary * dic = #{#"key1", "value1",#"key2", "value2"}
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic
options:(NSJSONWritingOptions) (prettyPrint ? NSJSONWritingPrettyPrinted : 0)
error:&error];
NSString * json_str = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]
and i do a AFNetworking POST request calling passing my nsdictionary as parameters:
- (AFHTTPRequestOperation *)POST:NSString *)URLString parameters:(id)parameters success:(void (^)(AFHTTPRequestOperation *operation,id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
on the receiver app (node.js/express) i get the ctx.req.body containing the javascript object from the POST request -> but here it does not have the same order as in the json encoded string from the app (and i have not found a way to preserve order)
as i am not knowing what keys are in nsdictionary a static key-hmac would not work.
Since the JSON is just a string there is no ordering issue after it has been created. The point is to create the HMac value after converting the dictionary to a JSON string.
On the sender create the JSON representation and HMac the JSON. Send the HMAc result along with the JSON.
On the receiver HMac the received JSON and compare with the received HMac value.
The two HMacs should be equal.
Another option is to write a method on each side that examines each dictionary element key/item in the same predefined order and compose a string or data object from that. Then HMAC that result.
Related
I'm only familiar with making GET request from manually constructing a string of parameters with the url. I was told by the admin of the API that they are not set up to parse my parameters and that I have to send a JSON array of parameters. In this case, it is a parameter of phone numbers.
I'm familiar with sending data an an POST request via:
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:data completionHandler:^(NSData *data,NSURLResponse *response,NSError *error
But that doesn't seem like an option to me for GET methods.
Thanks!
For an URI, the query component (see section 3.4 RFC 3986) is additional "non-hierarchical" data that specifies the resource referenced in the hierarchical path component.
Frequently, the query component is a comma separated list of key/value pairs. But it doesn't need to. The semantics of this "string" can be defined by the server. This could be a certain JSON as well.
Perhaps, this is what the admin of the API actually is talking about.
If this is true, you can send a JSON as the query component. Ensure, the JSON string is properly encoded and then appended to the URI after appending the ? separator.
You can also use NSURLComponents to compose this URI:
Suppose, your "query" is some custom JSON which should contain an SQL where-clause:
NSURLComponents* components = [NSURLComponents componentsWithString:#"http://example.com"];
NSString* query = #"{\"where\":\"name='value1' OR name='value2'\"}";
components.query = query;
This example will construct a URI whose encoded query component will look as below:
%7B%22where%22:%22name='value1'%20OR%20name='value2'%22%7D
I have to pass JSON dictionary as POST data to a Webservice. One key involves an Amazon S3 URL string.
The sample request json which works has the URL as....
https:\/\/myappbucket.s3.amazonaws.com\/2014230407_102323.jpg?response-content-type=image\/png&Signature=123456%3D&Expires=139756222548&AWSAccessKeyId=ABCDEF
Notice the backslashes just before the forwardslashes? I have never seen a URL like that, but thats how I'm supposed to pass it.
I tried
stringByAddingPercentEscapesUsingEncoding
and
stringByReplacingPercentEscapesUsingEncoding
while using NSASCIIStringEncoding and NSUTF8StringEncoding
Can anyone make sense of this?
if we try to convert url into legal url trough stringByAddingPercentEscapesUsingEncoding than it adds all percent escapes necessary to convert the receiver into a legal URL string.Uses the given encoding to determine the correct percent escapes.
if we use stringByReplacingPercentEscapesUsingEncoding than it replaces all percent escapes with the matching characters as determined by the given encoding.
mostly to get valid url, we can use NSUTF8StringEncoding to remove backslashes just before the forwardslashes in url.
Generally, you should use a JSON serializer library (e.g. NSJSONSerialization) in order to obtain a JSON from a JSON representation and not try to create the JSON yourself.
A JSON representation is a NSDictionary or NSArray object containing other objects which recursively represent your JSON. Your URL will be represented as a NSString.
What you need to do is simply have a valid URL as a NSString, properly encoded according RFC 3968 and assign it the JSON representation, e.g.:
NSURL* url = ...;
NSDictionary* jsonObject = #{#"url": [url path]};
Now, you can serialize the JSON representation to a JSON:
NSError* error;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:jsonObejct
options:0
error:&error];
That's it, and you don't bother how the JSON encoded string looks like (encapsulated in the NSData object as a UTF-8 character sequence).
Purposefully, when you POST this JSON, you SHOULD specify a corresponding request header:
ContentType: application/json
which lets you just use the JSON data as body data as is:
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
request.HTTPBody = jsonData;
Side note: [url path] returns a URL as a string according RFC 1808 which is obsoleted by RFC 3968 since January 2005 already. Today, there are newer APIs since iOS 7.0, see NSURLComponents.
I'm trying to use a pattern similar to this post which describes doing a PUT/POST for RESTful API, but for a GET.
My original code looks almost the same, except I used a GET: keyword. I quickly discovered that rather than sending a JSON body, it instead url encodes the parameters. This is not mentioned in the documentation of the AFJSONSerializer class. You have to go to the superclass documentation (AFHTTPSerializer) and read through its properties, where you'll find the one about HTTPMethodsEncodingParametersInURI. By default that set is populated with HEAD, GET, and DELETE. So for those types of requests, the JSON serializer apparently reverts to its parent class for behavior?
So I put together the following code:
AFHTTPSessionManager* manager = [AFHTTPSessionManager manager];
manager.securityPolicy.allowInvalidCertificates = YES;
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.requestSerializer.HTTPMethodsEncodingParametersInURI = [NSSet set];
[manager.requestSerializer setAuthorizationHeaderFieldWithUsername: currentUser() password: currentPassword()];
[manager
GET: #"https://172.16.214.214:44321/trees"
parameters: [NSDictionary dictionary]
success:^(NSURLSessionDataTask* task, id responseObject){
NSLog(#"Response: %#", responseObject);}
failure:^(NSURLSessionDataTask* task, NSError* error){
NSLog(#"Error: %#", error);}];
The line that sets HTTPMethodsEncodingParametersInURI = [NSSet set] is intended to let me get JSON encoded parameters like I wanted for the GET too. Unfortunately, I see nothing at the server when I use this and get the following in my error console:
2013-12-10 10:11:14.149 myValve[957:60b] Error: Error Domain=NSURLErrorDomain Code=-1005 "The
network connection was lost." UserInfo=0x17e5d4b0
{NSErrorFailingURLStringKey=https://172.16.214.214:44321/trees,
NSErrorFailingURLKey=https://172.16.214.214:44321/trees, NSLocalizedDescription=The network
connection was lost., NSUnderlyingError=0x17e53240 "The network connection was lost."}
What am I still missing?
The rationale behind AFNetworking's behavior here is probably most concisely explained in the Stack Overflow question, HTTP GET with request body. One of those answers quotes Ray Fielding:
... In other words, any HTTP request message is allowed to contain a message body, and thus must parse messages with that in mind. Server semantics for GET, however, are restricted such that a body, if any, has no semantic meaning to the request. The requirements on parsing are separate from the requirements on method semantics.
So, yes, you can send a body with GET, and no, it is never useful to do so.
The HTTP/1.1 spec defines GET to "retrieve whatever information (in the form of an entity) is identified by the Request-URI" and does not contemplate information to be included in the body of the request. So the use of the body in GET request technically might not be prohibited, but, at best, it is non-standard. AFNetworking's choice not to support it is not entirely surprising.
So, you might not want to put JSON in the body for a GET requests. One would generally add the parameters to the URL. If you want to send JSON in the body of the request, then do a POST.
Sounds like it can't connect to the server, have you tried to make a call outside iOS?
For reference I normally make a JSON request like this:
NSURL *url = #"https://172.16.214.214:44321/trees";
NSData* data = [NSData dataWithContentsOfURL: url];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
I have an iOS app that is using RestKit 0.20.1 to pull data from a server. The server at this time can send JSON formatted data but is not able to receive JSON formatted data.
This is where my problem is. POST requests require HTTP Body and the server is set up to receive XML. I have implemented the RKXMLReaderSerialization add on so I can receive XML but I can't find any current way of sending an XML formatted HTTP Body with RestKit.
This question "Send post request in XML format using RestKit " is what I was looking for but the answer from Imran Raheem is now (as far as I can tell) obsolete due to changes in RestKit.
I am using this method for the POST
[[RKObjectManager sharedManager] postObject:nil path:#"/rest/search?ip=255.255.255.0" parameters:search success:nil failure:nil];
Here is what the RestKit objectManager says about the postObject method
/**
Creates an `RKObjectRequestOperation` with a `POST` request for the given object, and enqueues it to the manager's operation queue.
#param object The object with which to construct the object request operation. If `nil`, then the path must be provided.
#param path The path to be appended to the HTTP client's base URL and used as the request URL. If nil, the request URL will be obtained by consulting the router for a route registered for the given object's class and the `RKRequestMethodPOST` method.
#param parameters The parameters to be reverse merged with the parameterization of the given object and set as the request body.
#param success A block object to be executed when the object request operation finishes successfully. This block has no return value and takes two arguments: the created object request operation and the `RKMappingResult` object created by object mapping the response data of request.
#param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the resonse data. This block has no return value and takes two arguments:, the created request operation and the `NSError` object describing the network or parsing error that occurred.
If I have the MIMEType set to JSON the Trace shows my request.body is being populated like so request.body={"Search":"Trending"}.
However if I set the MIMEType to XML the Trace shows the request.body=(null)
Here is the line I use to change the MIMEType
[RKObjectManager sharedManager].requestSerializationMIMEType = RKMIMETypeJSON;
I am pretty new to iOS and Objective-C so I may be setting up the NSDictionary that is used in the parameters of the `postObject' method wrong. Here it is just in case....
NSArray *objects =[NSArray arrayWithObjects:#"trending", nil];
NSArray *keys =[NSArray arrayWithObjects: #"Search",nil];
NSDictionary *params = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
self.search=params;
Any help would be greatly appreciated! Given that I am new Snippets are especially helpful!
Oh and BTW if anyone can point me to REST method that accepts JSON input I would gladly pass it on to my server guy so I can just avoid XML all together.
As feared and suggested in other posts on this topic writing XML is not supported in version of RestKit 0.20.1. Here is a link to my conversation with Blake Watters on the subject.
https://github.com/RestKit/RestKit/issues/1430#issuecomment-19150316
I never did clear up why the request.body= (null) when the MIMEType is set to XML.
The manager of the server has agreed to set it up to receive JSON. That is how I plan to work around this.
I am trying to make an HTTP PUT request to a CouchDB server. I am converting an NSString instance to NSData. I then convert this NSData instance to an NSArray and attempt to PUT this to CouchDB. So something like this:
//convert string to NSData
NSData *docData = [#"{\"name\":\"nick\"}" dataUsingEncoding:NSUTF8StringEncoding];
//NSData to NSArray
NSArray *arrayJson = [NSArray arrayWithObjects:[NSJSONSerialization JSONObjectWithData:docData options:kNilOptions error:nil],nil];
I am then creating an HTTP PUT request using AFNetworking (which is rad btw ;))
The problem is that this creates a json string which looks like:
[{"name":"nick"}]
This is valid JSON but Couch complains with 400 Bad JSON. Removing the [ ]s fixes the problem. I'm sure these brackets are the result of converting NSData to NSArray but I'm not sure how else to accomplish this using NSJSONSerialization. Can anyone help?
Thanks!
Edit
For clarity sake let me explain the problem I was having further. I am creating a document using the CouchDB HTTP API using a PUT HTTP request. The document could be hand coded JSON and that is why I have this parsing challenge. Couch expects a single document to create. Therefore it expects a single JSON object. NOT an array of them. That seems to be why [{"key":"value"}] returns a 400 HTTP response from Couch. Even if the array contains a single object. Seems a bit picky that the API wouldn't just infer correctly based on the array length.. but I guess you could argue either way. See the selected answer below. This is what I was looking for. Thanks to all those who responded!
You can let NSJSONSerialization do the conversion from Foundation object to string. This guarantees proper escaping and makes the code more readable.
If you have a key-value pair as in your sample above, you could do:
NSError* error = nil;
NSDictionary* jsonDict = #{#"name": #"nick"};
NSData* putData = [NSJSONSerialization dataWithJSONObject:jsonDict options:kNilOptions error:&error];
You should be able to directly put that into the content of your PUT request, without wrapping it in an additional array. If the endpoint that handles the request requires you to send an array, you should create the array as an Foundation object (NSArray) and serialize that as JSON before sending it to the server:
NSArray* jsonArray = #[#{#"name": #"nick"}];
The title of your question is "Deserialize JSON without wrapping in [ ]" - Shouldn't that be "Serialize JSON without wrapping in [ ]"?
I changed it, please change it back if I misunderstood your question.