Translate Python requests code to Java (UniRest) - java

I am working with the Snapchat API to make a Java Client. I am using an endpoint which takes the following parameters from an HTTP POST:
{
username: snapchat username,
timestamp: UNIX timestamp,
media_id: random string,
type: 0,
req_token: request token,
data: encrypted data
}
I have no problem generating the params hash, and I have the data as a File object.
In Python I have confirmed that the following works:
f = open('encrypted.jpg')
params = { ... all params besides data ... }
files = { 'data' : f }
r = requests.post(path, params, files=files)
That Python code gets me a 200. I am using tokens and data/files generated by Java code, so the data sent is identical.
In Java I am doing the following with UniRest:
Map<String, Object> params = ... same params ...;
File f = new File('encrypted.jpg');
HttpRequestWithBody req = Unirest.post(path);
req.fields(params);
req.field("data", f);
HttpResponse<String> resp = req.asString();
However this gives me a 500 response from the server. How can I write Java that emulates the Python exactly? Or how can I snoop my own network traffic to see the difference in what the code for each is doing? Seems crazy to me that one works and the other does not.

try to chain methods, i.e.
req = Unirest.post(path).fields(params).field("data", f);
or change the lines:
req = req.fields(params);
req = req.field("data", f);

Related

AWS S3 Pre Signed URL with Metadata

Here is the code snippet where I am trying to upload a document with some custom metadata using AWS S3 pre signed URL.
try (CloseableHttpClient httpClient = HttpClients.createSystem()) {
var tempFile = File.createTempFile(document.getName(), FilenameUtils.getExtension(document.getOriginalFilename()));
document.transferTo(tempFile);
var fileEntity = new FileEntity(tempFile);
var httpPut = new HttpPut(url);
// Here 403 httpPut.setHeader("x-amz-meta-title", "Test Title");
httpPut.setEntity(fileEntity);
var response = httpClient.execute(httpPut);
log.info("HTTP response code {}", response.getStatusLine().getStatusCode());
} catch (IOException e) {
log.error("Oops! Error", e);
}
Unfortunately setting a custom header as above throws 403. It works fine when I take out the header. Even works okay when you add an irrelevant metadata key say "abc" - Obviously not adding metadata, but returns 200 OK. The issue seems only when you specify "x-amz-meta-".
Any thoughts?
The metadata of a presigned url are set, when you create it. They are prefilled and can't be modified by the client.
The only thing you can do is set an expected value and optionally a condition. In the condition you can specify to reject the upload, if the expected meta-data value isn't supplied.

Why is verification failing for a JWS signature?

I'm trying to sign the message with a detached payload using the Nimbus JOSE JWT library in Java. The verification goes through locally but whenever I try to send it to the server using Postman I get: "The signature header x-jws-signature was parsed and has a valid JOSE header that complies with the specification. However, the signature itself could not be verified"
JWSSigner signer = new RSASSASigner(privateKey);
HashMap<String, Object> criticalParameters = new HashMap<>();
criticalParameters.put("http://openbanking.org.uk/iat", 1501497671);
criticalParameters.put("http://openbanking.org.uk/iss", orgId);
criticalParameters.put("http://openbanking.org.uk/tan", "openbankingtest.org.uk");
JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.PS256)
.type(JOSEObjectType.JOSE)
.keyID(keyID)
.criticalParams(criticalParameters.keySet())
.customParams(criticalParameters)
.build();
// With encoding the payload
JWSObject jwsObject = new JWSObject(header, payload);
jwsObject.sign(signer);
String jws = jwsObject.serialize(true);
JWSObject parsedJWSObject = JWSObject.parse(jws, payload);
if (parsedJWSObject.verify(new RSASSAVerifier(publicKey, criticalParameters.keySet()))) {
System.out.println(parsedJWSObject.serialize(true));
} else {
System.out.println("Invalid");
}
//=============================
// Without encoding the payload
Base64URL signature = signer.sign(header, (header.toBase64URL().toString() + "." + payload).getBytes());
JWSVerifier verifier = new RSASSAVerifier(publicKey, criticalParameters.keySet());
boolean isValid = verifier.verify(header, (header.toBase64URL().toString() + "." + payload).getBytes(), signature);
System.out.println(header.toBase64URL().toString() + ".." + signature.toString());
System.out.println(isValid);
//=============================
Both of the functions successfully sign and verify the JWS but for some reason, it doesn't work. If it helps, I'm trying to access the Open Banking API.
Got a similar problem very recently. I would suggest you to check the following:
Is the payload in the request exactly the same as the one used for the JW signature (without escaping or formatting characters)?
What's the order of the JSON properties in the payload and does the financial entity you are trying to interact with have specific requirements when it comes to the order of those JSON fields?
I know it's very questionable to expect the json properties in the payload to be in a specific order, but by experience I found out that some open banking implementations are assuming a specific order (not even alphabetical) and they will fail with that error when the order is not the one they expect.
JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.PS256)
.type(JOSEObjectType.JOSE)
.keyID(keyID)
.criticalParams(criticalParameters.keySet())
.customParams(criticalParameters)
.build();
//simplyfy your payload json string before..remove all spaces.
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
JsonElement el = JsonParser.parseString(payload);
String simplePayload=gson.toJson(el);
// With encoding the payload
Payload detachedPayload =new Payload(new Base64URL(simplePayload).toString());
JWSObject jwsObject = new JWSObject(header, detachedPayload );
jwsObject.sign(signer);
String jws = jwsObject.serialize(true);
JWSObject parsedJWSObject = JWSObject.parse(jws, detachedPayload );

Convert GetEntity().GetContent() from Java to C#

I need to convert the following code from Java to C# when I'm using restAPI in C#.
In java :
HttpGet statusGet = new HttpGet(fileUrl);
statusGet.setHeader("X-API-TOKEN", API_TOKEN);
HttpResponse response = httpClient.execute(statusGet);
// Extract exported file
ZipInputStream zs = new ZipInputStream(response.getEntity().getContent());
In C# this is what I have:
var client1 = new RestClient(fileUrl);
var request1 = new RestRequest(Method.GET);
request1.AddHeader("X-API-TOKEN", "API_TOKEN");
request1.AddHeader("content-type", "application/json");
request1.AddParameter("application/json", "{\n\t\"format\" : \"csv\",\n\t\"surveyId\" : \"_surveyId\"\n}", ParameterType.RequestBody);
IRestResponse responsedata = client1.Execute(request1);
var download=client1.DownloadData(request1);
MemoryStream stream = new MemoryStream(download);
ZipInputStream zs = new ZipInputStream(stream);
using (ZipFile zip1 = ZipFile.Read(zs))
I have no clue how to implement response.getEntity().getContent(). I believe it is getting the Stream(Containing a zip file?)
Updated: So I get the byte array from client1.DownloadData(request1), looks like it is not right to convert it to stream (has readtimeout exception). and it will not be able to read from zipfile.read
Thank you so much for your help
Are you getting any specific errors? It looks like you are implementing this using RestSharp. Have you followed their examples and read through their documentation?
I have not personally used this third-party solution, but immediately on their front page they have the following example that does exactly what you are trying to do:
var client = new RestClient("http://example.com");
// client.Authenticator = new HttpBasicAuthenticator(username, password);
var request = new RestRequest("resource/{id}", Method.POST);
request.AddParameter("name", "value"); // adds to POST or URL querystring based on Method
request.AddUrlSegment("id", "123"); // replaces matching token in request.Resource
// easily add HTTP Headers
request.AddHeader("header", "value");
// add files to upload (works with compatible verbs)
request.AddFile(path);
// execute the request
IRestResponse response = client.Execute(request);
var content = response.Content; // raw content as string
// or automatically deserialize result
// return content type is sniffed but can be explicitly set via RestClient.AddHandler();
RestResponse<Person> response2 = client.Execute<Person>(request);
var name = response2.Data.Name;
// easy async support
client.ExecuteAsync(request, response => {
Console.WriteLine(response.Content);
});
// async with deserialization
var asyncHandle = client.ExecuteAsync<Person>(request, response => {
Console.WriteLine(response.Data.Name);
});
// abort the request on demand
asyncHandle.Abort();
It looks like you would want to access the IRestResponse.Content property, or to deserialize using the RestClient.Execute<T>(RestRequest request) function.

UTF-8 characters in HTTP POST query form parameters

I am trying to transfer form data from an Android application to a NodeJs server.
My client code is the following (the strings that can contain UTF-8 characters are the values of params):
final HttpPost post = new HttpPost(url);
final MultipartEntityBuilder mpb = MultipartEntityBuilder.create()
.setCharset(Charset.forName("UTF-8")) // tried with or without this line
.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); // tried with or without this line
for (final Entry<String, String> e : params.entrySet()) {
mpb.addTextBody(e.getKey(), e.getValue());
}
post.setEntity(mpb.build());
final HttpClient httpClient = new DefaultHttpClient();
final HttpResponse response = httpClient.execute(request);
And my server code is the following:
app.post('/accesspoint', function(req, res) {
var body = req.body;
var form = new formidable.IncomingForm();
form.encoding = 'utf-8';
form.parse(req, function(err, fields, files) {
console.log(fields);
...
When my input java params has a value containing an UTF-8 character, the log I get server side prints the corresponding value without this character, so it is kind of swallowed at some point. For instance if my input string is "ê", then my server log will print a "" value.
I use a multipart form as I read that it was the best way to send data that can contain non-ASCII characters. Formidable is also apparently the best node package to handle form that can contain UTF-8 characters.
My client side uses Apache HttpClient 4.3.3.
What am I doing wrong?
Ok so I tested with a simple query and the key value ("foo","[ê]") looked at the headers and I saw that my query was still using ISO-8859-1
Content-Disposition: form-data; name="foo"
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
[]
in contradiction to my builder parameter.
I found the solution via https://stackoverflow.com/a/21020435/592392 and changed my code to:
for (final Entry<String, String> e : params.entrySet()) {
mpb.addTextBody(e.getKey(), e.getValue(), ContentType.create("text/plain", Charset.forName("UTF-8")));
}
And now the server gets the UTF8 chars :)
Anyway, the form builder is quite misleading in my opinion.

I am not able to get signature from java sever side(java servlet)

I take the signature on the server side(java servlet) to pass it to the html client.
This is the code:
Cloudinary cloudinary = new Cloudinary(Cloudinary.asMap("cloud_name", "dvg8fiorp", "api_key", "742866863611915", "api_secret", "zF-GJqVyWjih_MqQGsYeSOVVmJ8"));
String timestamp = (new Long(System.currentTimeMillis() / 1000L)).toString();
Map<String, Object> params = new HashMap<String, Object>();
Map options = Cloudinary.emptyMap();
boolean returnError = Cloudinary.asBoolean(options.get("return_error"), false);
String apiKey = Cloudinary.asString(options.get("api_key"), cloudinary.getStringConfig("api_key"));
if (apiKey == null)
throw new IllegalArgumentException("Must supply api_key");
String apiSecret = Cloudinary.asString(options.get("api_secret"), cloudinary.getStringConfig("api_secret"));
if (apiSecret == null)
throw new IllegalArgumentException("Must supply api_secret");
params.put("callback", "http://localhost:8080/SimpleServlet/js/cloudinary_js/html/cloudinary_cors.html");
params.put("timestamp", timestamp);
String expected_signature = cloudinary.apiSignRequest(params, apiSecret);
Unfortunately, the last line of code fail and i don't know how get signature to pass it to html client
First, note that your API Secret Should not be publicly revealed. I strongly advise that you generate a new pair or API key and secret from your Security settings page (https://cloudinary.com/console/settings/security).
Second, this actually generated the correct signature, I managed to have successful uploads with it.
In order to further understand the issue:
What kind of an error response did you get?
Please share your upload request code.
This is the server's response:
java.lang.NoSuchMethodError: org.apache.commons.lang.StringUtils.join(Ljava/util/Collection;Ljava/lang/String;)Ljava/lang/String;
com.cloudinary.Cloudinary.apiSignRequest(Cloudinary.java:93)
us.souther.simple.UploadServersideServlet

Categories

Resources