I upload an audio file to an audio & video bucket, called demo, using the AcrCloud RESTful services. I am getting a 500 Internal Server Error. This indicates that my signature is correct (I was getting a 422 when the signature was incorrect). The part that I suspect is incorrect is the construction of the multipart post request
My Code:
import com.xperiel.common.logging.Loggers;
import com.google.api.client.http.ByteArrayContent;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpContent;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpMediaType;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.MultipartContent;
import com.google.api.client.http.MultipartContent.Part;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.BaseEncoding;
import com.google.common.io.CharStreams;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class TestAcrCloudSignature {
private static final String ACCESS_KEY = "xxxx"; // confidential
private static final String SECRET_KEY = "yyyy"; // confidential
private static final String URL = "https://api.acrcloud.com/v1/audios";
private static HttpRequestFactory requestFactory = new NetHttpTransport().createRequestFactory();
private static final Logger logger = Loggers.getLogger();
public static void main(String [] args) {
String filePath = "/Users/serena/Desktop/ArcCloudMusic/Fernando.m4a";
String httpMethod = HttpMethod.POST.toString();
String httpUri = "/v1/audios";
String signatureVersion = "1";
long timestamp = System.currentTimeMillis();
String stringToSign = getStringToSign(httpMethod, httpUri, signatureVersion, timestamp);
String signature = getSignature(stringToSign);
logger.log(Level.INFO, "Timestamp:\t" + timestamp);
HttpResponse response = null;
try {
ImmutableMap<String, String> params = ImmutableMap.of(
"title", "fernando",
"audio_id", "1",
"bucket_name", "demo",
"data_type", "audio");
byte[] audio = getAudioFileTo(filePath);
String strResponse = sendMultiPartPostRequest(
"",
params,
ImmutableMap.of("audio-file", new Pair<>("Fernando.m4a", audio)),
signatureVersion,
signature,
timestamp);
logger.log(Level.INFO, "RESPONSE:" + strResponse);
} catch (Exception e) {
logger.log(Level.WARNING, "Response: " + response);
logger.log(Level.WARNING, "Exception: " + e.getMessage());
e.printStackTrace();
}
}
private static String getStringToSign(String method, String httpUri, String signatureVersion, long timestamp) {
String stringToSign = method+"\n"+httpUri+"\n"+ACCESS_KEY+"\n"+signatureVersion+"\n"+timestamp;
logger.log(Level.INFO, "String to Sign:\t" + stringToSign);
return stringToSign;
}
private static String getSignature(String stringToSign) {
String signature = BaseEncoding.base64().encode(hmacSha1(stringToSign));
logger.log(Level.INFO, "Signature:\t" + signature);
return signature;
}
private static byte[] hmacSha1(String toSign) {
try {
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec(SECRET_KEY.getBytes(), "HmacSHA1"));
return mac.doFinal(toSign.getBytes());
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException(e);
}
}
private enum HttpMethod {
GET, POST, PUT, DELETE,
}
private static byte[] getAudioFileTo(String filePath){
File file = new File(filePath);
byte[] buffer = null;
try {
InputStream fis = new FileInputStream(file);
buffer = new byte[(int) file.length()];
fis.read(buffer, 0, buffer.length);
fis.close();
} catch (IOException e) {
logger.log(Level.WARNING, "IOException: " + e.getMessage());
}
return buffer;
}
private static String sendMultiPartPostRequest(
String path,
ImmutableMap<String, String> parameters,
ImmutableMap<String, Pair<String, byte[]>> blobData,
String signatureVersion,
String signature,
long timestamp) {
try {
MultipartContent multipartContent = new MultipartContent();
multipartContent.setMediaType(new HttpMediaType("multipart/form-data"));
multipartContent.setBoundary("--------------------------0e94e468d6023641");
for (Entry<String, String> currentParameter : parameters.entrySet()) {
HttpHeaders headers = new HttpHeaders();
headers.clear();
headers.setAcceptEncoding(null);
headers.set("Content-Disposition", "form-data; name=\"" + currentParameter.getKey() + '\"');
HttpContent content = new ByteArrayContent(null, currentParameter.getValue().getBytes());
Part part = new Part(content);
part.setHeaders(headers);
multipartContent.addPart(part);
}
for (Entry<String, Pair<String, byte[]>> current : blobData.entrySet()) {
ByteArrayContent currentContent = new ByteArrayContent("application/octet-stream", current.getValue().second);
HttpHeaders headers = new HttpHeaders();
headers.clear();
headers.setAcceptEncoding(null);
headers.set("Content-Disposition", "form-data; name=\"" + current.getKey() + "\"; filename=\"" + current.getValue().first + '\"');
headers.setContentType("application/octet-stream");
multipartContent.addPart(new Part(headers, currentContent));
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
multipartContent.writeTo(out);
HttpResponse response = requestFactory
.buildPostRequest(new GenericUrl(URL + path), multipartContent)
.setHeaders(new HttpHeaders()
.set("access-key", ACCESS_KEY)
.set("signature-version", signatureVersion)
.set("signature", signature)
.set("timestamp", timestamp))
.execute();
String responseString = CharStreams.toString(new InputStreamReader(response.getContent()));
return responseString;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static class Pair<A, B> {
final A first;
final B second;
Pair(A first, B second) {
this.first = first;
this.second = second;
}
}
}
The error message I am getting from AcrCloud is:
500
{"name":"Internal Server Error","message":"There was an error at the server.","code":0,"status":500}
I am able to upload an audio file using this cUrl command:
Command: $ curl -H "access-key: xxxx" -H "signature-version: 1" -H
"timestamp: 1439958502089" -H "signature:
Nom6oajEzon260F2WzLpK3PE9e0=" -F "title=fernando" -F "audio_id=100" -F
"bucket_name=demo" -F "data_type=audio" -F
"audio_file=#/Users/serena/Desktop/ArcCloudMusic/Fernando.m4a"
https://api.acrcloud.com/v1/audios
Does anyone have any tips on how to debug this? Or has anyone had success using this service programmatically with Java? Or can someone show me how to print the contents of the HttpPOST request?
UPDATE I have also tried using their java example on GITHUB found here:
https://github.com/acrcloud/webapi_example/blob/master/RESTful%20service/UploadAudios.java
I get the same 500 error
UPDATE I no longer get the 500 error when I run their code. I fiddled with the apache jar versions and now I can successfully use the java code found on git hub. For record, The version that I used that work with their github code is apache-http-codec-1.10, apache-http-client-4.5, apache-http-core-4.4.1, apache-http-mime-4.5. When i used apache-http-core-4.5 it did not work.
UPDATE I have written a file that prints out the signatures generated by the java code on github reference above, and my own code. The signatures match so I am convinced that issue in the way I am constructing the multipart post request. I have also written the contents of both post requests to file and the headers contain different information in a few spots.
Thanks Serena for your patience, our team is doing a detailed analysis on the code and the apache jars now. Hopefully will have an update soon.
For now, if anyone who has the same problems, please use the following jars as mentioned in https://github.com/acrcloud/webapi_example/blob/master/RESTful%20service/UploadAudios.java
// import commons-codec-<version>.jar, download from http://commons.apache.org/proper/commons-codec/download_codec.cgi
import org.apache.commons.codec.binary.Base64;
// import HttpClient, download from http://hc.apache.org/downloads.cgi
/**
*
* commons-codec-1.1*.jar
* commons-logging-1.*.jar
* httpclient-4.*.jar
* httpcore-4.4.1.jar
* httpmime-4.*.jar
*
* */
Related
I am using Google Bigquery V2 Java API. I am not able to find a way to get query results in JSON format.
In Bigquery Web UI we can see this JSON and Table form of results. see scrrenshot.
Is there any way to get the GetQueryResultsResponse as JSON, using Java API.
One option is to apply the TO_JSON_STRING function to the results of your query. For example,
#standardSQL
SELECT TO_JSON_STRING(t)
FROM (
SELECT x, y
FROM YourTable
WHERE z = 10
) AS t;
If you want all of the table's columns as JSON, you can use a simpler form:
#standardSQL
SELECT TO_JSON_STRING(t)
FROM YourTable AS t
WHERE z = 10;
I'm using a service account to access the BigQuery REST API to get the response in JSON format.
In order to use a service account, you will have to go to credentials (https://console.cloud.google.com/apis/credentials) and choose a project.
You will get a drop down like this:
Create a Service account for your project and download the secret file in the JSON format. Keep the JSON file in your file system and set the path to it. Check below image to set the file path:
So, now all you have to do in is use JAVA client api to consume the Big Query REST API.
Here's is a simple solution that I've been using for my project.
package com.example.bigquery;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import org.apache.log4j.Logger;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpContent;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.http.json.JsonHttpContent;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.common.io.CharStreams;
public class BigQueryDemo {
private static final String QUERY_URL_FORMAT = "https://www.googleapis.com/bigquery/v2/projects/%s/queries" + "?access_token=%s";
private static final String QUERY = "query";
private static final String QUERY_HACKER_NEWS_COMMENTS = "SELECT * FROM [bigquery-public-data:hacker_news.comments] LIMIT 1000";
private static final Logger logger = Logger.getLogger(BigQueryDemo.class);
static GoogleCredential credential = null;
static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
static final JsonFactory JSON_FACTORY = new JacksonFactory();
static {
// Authenticate requests using Google Application Default credentials.
try {
credential = GoogleCredential.getApplicationDefault();
credential = credential.createScoped(Arrays.asList("https://www.googleapis.com/auth/bigquery"));
credential.refreshToken();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void implicit() {
String projectId = credential.getServiceAccountProjectId();
String accessToken = generateAccessToken();
// Set the content of the request.
Dataset dataset = new Dataset().addLabel(QUERY, QUERY_HACKER_NEWS_COMMENTS);
HttpContent content = new JsonHttpContent(JSON_FACTORY, dataset.getLabels());
// Send the request to the BigQuery API.
GenericUrl url = new GenericUrl(String.format(QUERY_URL_FORMAT, projectId, accessToken));
logger.debug("URL: " + url.toString());
String responseJson = getQueryResult(content, url);
logger.debug(responseJson);
}
private static String getQueryResult(HttpContent content, GenericUrl url) {
String responseContent = null;
HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory();
HttpRequest request = null;
try {
request = requestFactory.buildPostRequest(url, content);
request.setParser(JSON_FACTORY.createJsonObjectParser());
request.setHeaders(
new HttpHeaders().set("X-HTTP-Method-Override", "POST").setContentType("application/json"));
HttpResponse response = request.execute();
InputStream is = response.getContent();
responseContent = CharStreams.toString(new InputStreamReader(is));
} catch (IOException e) {
logger.error(e);
}
return responseContent;
}
private static String generateAccessToken() {
String accessToken = null;
if ((System.currentTimeMillis() > credential.getExpirationTimeMilliseconds())) {
accessToken = credential.getRefreshToken();
} else {
accessToken = credential.getAccessToken();
}
System.out.println(accessToken);
return accessToken;
}
}
Following is the Github link to the code: https://github.com/vslala/BigQueryRestSample
It is just a demo project to fetch JSON data from the BQ REST API. Do not use it in your project directly.
Let me know if you have any questions.
Does anyone have any examples on how to create a page/wiki entry in Confluence using Confluence's RESTful API? I'm trying to write something in Java that can do this.
Thank you in advance...
Thank you, I already checked the documentation online but I couldn't find any examples THAT USE JAVA in the Confluence REST API. That's why I posted on here.
Regardless, I think I figured it out:
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
/**
* Creates a Confluence wiki page via the RESTul API
* using an HTTP Post command.
*/
public class ConfluenceRestApi2CreateEntry {
//private static final String BASE_URL = "http://localhost:1990/confluence";
private static final String BASE_URL = "https://<context>.atlassian.net/wiki";
private static final String USERNAME = "username";
private static final String PASSWORD = "password";
private static final String ENCODING = "utf-8";
public static String createContentRestUrl()throws UnsupportedEncodingException
{
return String.format("%s/rest/api/content/?&os_authType=basic&os_username=%s&os_password=%s", BASE_URL, URLEncoder.encode(USERNAME, ENCODING), URLEncoder.encode(PASSWORD, ENCODING));
}
public static void main(final String[] args) throws Exception
{
String wikiPageTitle = "My Awesome Page";
String wikiPage = "<h1>Things That Are Awesome</h1><ul><li>Birds</li><li>Mammals</li><li>Decapods</li></ul>";
String wikiSpace = "JOUR";
String labelToAdd = "awesome_stuff";
int parentPageId = 9994250;
JSONObject newPage = defineConfluencePage(wikiPageTitle,
wikiPage,
wikiSpace,
labelToAdd,
parentPageId);
createConfluencePageViaPost(newPage);
}
public static void createConfluencePageViaPost(JSONObject newPage) throws Exception
{
HttpClient client = new DefaultHttpClient();
// Send update request
HttpEntity pageEntity = null;
try
{
//2016-12-18 - StirlingCrow: Left off here. Was finally able to get the post command to work
//I can begin testing adding more data to the value stuff (see above)
HttpPost postPageRequest = new HttpPost(createContentRestUrl());
StringEntity entity = new StringEntity(newPage.toString(), ContentType.APPLICATION_JSON);
postPageRequest.setEntity(entity);
HttpResponse postPageResponse = client.execute(postPageRequest);
pageEntity = postPageResponse.getEntity();
System.out.println("Push Page Request returned " + postPageResponse.getStatusLine().toString());
System.out.println("");
System.out.println(IOUtils.toString(pageEntity.getContent()));
}
finally
{
EntityUtils.consume(pageEntity);
}
}
public static JSONObject defineConfluencePage(String pageTitle,
String wikiEntryText,
String pageSpace,
String label,
int parentPageId) throws JSONException
{
//This would be the command in Python (similar to the example
//in the Confluence example:
//
//curl -u <username>:<password> -X POST -H 'Content-Type: application/json' -d'{
// "type":"page",
// "title":"My Awesome Page",
// "ancestors":[{"id":9994246}],
// "space":{"key":"JOUR"},
// "body":
// {"storage":
// {"value":"<h1>Things That Are Awesome</h1><ul><li>Birds</li><li>Mammals</li><li>Decapods</li></ul>",
// "representation":"storage"}
// },
// "metadata":
// {"labels":[
// {"prefix":"global",
// "name":"journal"},
// {"prefix":"global",
// "name":"awesome_stuff"}
// ]
// }
// }'
// http://localhost:8080/confluence/rest/api/content/ | python -mjson.tool
JSONObject newPage = new JSONObject();
// "type":"page",
// "title":"My Awesome Page"
newPage.put("type","page");
newPage.put("title", pageTitle);
// "ancestors":[{"id":9994246}],
JSONObject parentPage = new JSONObject();
parentPage.put("id",parentPageId);
JSONArray parentPageArray = new JSONArray();
parentPageArray.put(parentPage);
newPage.put("ancestors", parentPageArray);
// "space":{"key":"JOUR"},
JSONObject spaceOb = new JSONObject();
spaceOb.put("key",pageSpace);
newPage.put("space", spaceOb);
// "body":
// {"storage":
// {"value":"<p><h1>Things That Are Awesome</h1><ul><li>Birds</li><li>Mammals</li><li>Decapods</li></ul></p>",
// "representation":"storage"}
// },
JSONObject jsonObjects = new JSONObject();
jsonObjects.put("value", wikiEntryText);
jsonObjects.put("representation","storage");
JSONObject storageObject = new JSONObject();
storageObject.put("storage", jsonObjects);
newPage.put("body", storageObject);
//LABELS
// "metadata":
// {"labels":[
// {"prefix":"global",
// "name":"journal"},
// {"prefix":"global",
// "name":"awesome_stuff"}
// ]
// }
JSONObject prefixJsonObject1 = new JSONObject();
prefixJsonObject1.put("prefix","global");
prefixJsonObject1.put("name","journal");
JSONObject prefixJsonObject2 = new JSONObject();
prefixJsonObject2.put("prefix","global");
prefixJsonObject2.put("name",label);
JSONArray prefixArray = new JSONArray();
prefixArray.put(prefixJsonObject1);
prefixArray.put(prefixJsonObject2);
JSONObject labelsObject = new JSONObject();
labelsObject.put("labels", prefixArray);
newPage.put("metadata",labelsObject);
return newPage;
}
}
Here's a project in GitHub that I created that also has an example of simply pulling wiki page entry using Java:
https://github.com/stirlingcrow/Confluence-AccessRestApiWithJava
What about using the official REST client?
https://mvnrepository.com/artifact/com.atlassian.confluence/confluence-rest-client
I'm not able to find any documentation on how to use it, tho.
I am facing problem while connecting with WooCommerce API (http://woothemes.github.io/woocommerce-rest-api-docs/#authentication). I am successfully able to generate the signature using java program and also checked the generated signature having same time stamp and nonce on linked in console(as given in the WooCommerce document) for any discrepancy but the generated signature is same on linked in and java output console.
The process of our working is described below :
1. We have generated the signature with the help of signature base string and secret key using java program. Signature base string looks like :
GET&http%3A%2F%2FEndPointURL%2Fwc-api%2Fv2%2Forders&oauth_consumer_key%3D%26oauth_nonce%3D70810941%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1433226349%26oauth_version%3D1.0
2. While we are trying to access the url http://EndPointURL/wc-api/v2/orders, It is showing error given below :
{"errors":[{"code":"woocommerce_api_authentication_error","message":"Invalid Signature - provided signature does not match"}]}
3.We have also generated the signature with the help of Linked in test Console using same value of time stamp and nonce and getting the same signature. But we are not able to access the data.
The java code that I am using is given below :
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
public class OAuthForWooCommerce {
private static String key = "consumer_Key";
private static String secret = "consumer_Secret";
private static final String HMAC_SHA1 = "HmacSHA1";
private static final String ENC = "UTF-8";
private static Base64 base64 = new Base64();
private static String getSignature(String url, String params)
throws UnsupportedEncodingException, NoSuchAlgorithmException,
InvalidKeyException {
/**
* base has three parts, they are connected by "&": 1) protocol 2) URL
* (need to be URLEncoded) 3) Parameter List (need to be URLEncoded).
*/
StringBuilder base = new StringBuilder();
base.append("GET&");
base.append(url);
base.append("&");
base.append(params);
System.out.println("String for oauth_signature generation:" + base);
// yea, don't ask me why, it is needed to append a "&" to the end of
// secret key.
byte[] keyBytes = (secret + "&").getBytes(ENC);
SecretKey key = new SecretKeySpec(keyBytes, HMAC_SHA1);
Mac mac = Mac.getInstance(HMAC_SHA1);
mac.init(key);
// encode it, base64 it, change it to string and return.
return new String(base64.encode(mac.doFinal(base.toString().getBytes(
ENC))), ENC).trim();
}
public static void main(String[] args) throws ClientProtocolException,
IOException, URISyntaxException, InvalidKeyException,
NoSuchAlgorithmException {
System.out.println("*** Welcome to WooCommerce Klipfolio integration Wizard ***");
HttpClient httpclient = new DefaultHttpClient();
List<NameValuePair> qparams = new ArrayList<NameValuePair>();
// These params should ordered in key
//qparams.add(new BasicNameValuePair("oauth_callback", "oob"));
qparams.add(new BasicNameValuePair("oauth_consumer_key", key));
String nonce = RandomStringUtils.randomAlphanumeric(32);
//String nonce2 = URLEncoder.encode(nonce1, "UTF-8");
qparams.add(new BasicNameValuePair("oauth_nonce", nonce));
//qparams.add(new BasicNameValuePair("oauth_nonce", ""+ (int) (Math.random() * 100000000)));
qparams.add(new BasicNameValuePair("oauth_signature_method",
"HMAC-SHA1"));
qparams.add(new BasicNameValuePair("oauth_timestamp", ""
+ (System.currentTimeMillis() / 1000)));
qparams.add(new BasicNameValuePair("oauth_version", "1.0"));
// generate the oauth_signature
String signature = getSignature(URLEncoder.encode(
"http://MY_END_URL/wc-api/v2/orders", ENC),
URLEncoder.encode(URLEncodedUtils.format(qparams, ENC), ENC));
System.out.println("Getting Oauth Signature...");
// add it to params list
qparams.add(new BasicNameValuePair("oauth_signature", signature));
// generate URI which lead to access_token and token_secret.
URI uri = URIUtils.createURI("http", "MY_END _URL", -1,
"wc-api/v2/orders",
URLEncodedUtils.format(qparams, ENC), null);
System.out.println("Connecting to the URL : \n"
+ uri.toString());
HttpGet httpget = new HttpGet(uri);
// output the response content.
System.out.println("Getting Response from the server :");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
int len;
byte[] tmp = new byte[2048];
while ((len = instream.read(tmp)) != -1) {
System.out.println(new String(tmp, 0, len, ENC));
}
}
}
}
Please let me know where I am doing wrong.
Thanks,
You must remove the version number from params, according to the documentation. I think also you should remove "&" you added to the secret key when generating signature, since I managed to get 200 response without it.
The require parameters are: oauth_consumer_key, oauth_timestamp, oauth_nonce, oauth_signature, and oauth_signature_method. oauth_version is not required and must be omitted.
You should also sort the parameters in byte order before adding them to the string you used to create the signature.
Also, I suggest you to debug by changing the message line to see the signature you sent to the server and the signature you should have sent.
For instance, you can change it to:
throw new Exception( __( 'Invalid Signature - provided signature does not match Secret:' . $user->woocommerce_api_consumer_secret . ', StringToSign: ' . $string_to_sign . ', TakenSign: ' . $consumer_signature . ', GeneratedSign: ' . $signature, 'woocommerce' ), 401 );
Thanks for your code Archit, this helped me jump through hoops.
I have updated your code and the below works.
package com.woocommerce.experiments;
import org.apache.commons.codec.EncoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.net.URLCodec;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class OAuthForWooCommerce {
private static String key = "ck_your_consumer_key";
private static String secret = "cs_your_consumer_secret";
private static final String HMAC_SHA1 = "HmacSHA1";
private static final String ENC = "UTF-8";
private static Base64 base64 = new Base64();
private static String getSignature(String url, String params)
throws UnsupportedEncodingException, NoSuchAlgorithmException,
InvalidKeyException, EncoderException {
/**
* base has three parts, they are connected by "&": 1) protocol 2) URL
* (need to be URLEncoded) 3) Parameter List (need to be URLEncoded).
*/
StringBuilder base = new StringBuilder();
base.append("GET&");
//follow Step 2 and encode
base.append(new URLCodec(Consts.UTF_8.displayName()).encode(url));
base.append("&");
base.append(params);
System.out.println("String for oauth_signature generation: " + base);
byte[] keyBytes = (String.format("%s", secret)).getBytes(ENC);
SecretKey key = new SecretKeySpec(keyBytes, HMAC_SHA1);
Mac mac = Mac.getInstance(HMAC_SHA1);
mac.init(key);
// encode it, base64 it, change it to string and return.
String signature = new String(base64.encode(mac.doFinal(base.toString().getBytes(ENC))), ENC).trim();
return signature;
}
public static void main(String[] args) throws IOException, URISyntaxException, InvalidKeyException,
NoSuchAlgorithmException, EncoderException {
String nonce = RandomStringUtils.randomAlphanumeric(32);
Map<String, String> paramMap = new TreeMap<>();
paramMap.put("oauth_consumer_key", key);
paramMap.put("oauth_timestamp", "" + (System.currentTimeMillis() / 1000));
paramMap.put("oauth_nonce", nonce);
paramMap.put("oauth_signature_method", "HMAC-SHA1");
List<NameValuePair> qparams = new ArrayList<>();
//uksort( $params, 'strcmp' ) mimic
paramMap.entrySet().stream().forEach(stringStringEntry -> qparams.add(new BasicNameValuePair(stringStringEntry.getKey(), stringStringEntry.getValue())));
//double encode Step 3 (in order to replace '%' with '%25') after sorting (Step 4)
String encodedParams = URLEncoder.encode(URLEncodedUtils.format(qparams, ENC), ENC);
System.out.println("Encoded Params "+ encodedParams);
String signature = getSignature("http://your_end_url/wc-api/v2/orders", encodedParams);
qparams.add(new BasicNameValuePair("oauth_signature", signature));
HttpClient httpclient = HttpClientBuilder.create().build();
System.out.println("Getting Oauth Signature...");
// generate URI which lead to access_token and token_secret.
URI uri = URIUtils.createURI("http", "your_end_url", -1, "wc-api/v2/orders", URLEncodedUtils.format(qparams, ENC), null);
System.out.println("Connecting to the URL : \n" + uri.toString());
HttpGet httpget = new HttpGet(uri);
httpget.setHeader("X-Stream" , "true");
// output the response content.
System.out.println("Getting Response from the server :");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
System.out.println("Response Code " + response.getStatusLine().getStatusCode());
if (entity != null) {
System.out.println("Json response" + IOUtils.toString(entity.getContent(), ENC));
}
}
}
I hope this helps you (hopefully not late!) and others. Step by step process for authentication can be found at WooCommerce REST API Documentation
I was able to create a Container in Storage Account and upload a blob to it through the Client Side Code.
I was able to make the blob available for Public access as well , such that when I hit the following query from my browser, I am able to see the image which I uploaded.
https://MYACCOUNT.blob.core.windows.net/MYCONTAINER/MYBLOB
I now have a requirement to use the rest service to retrieve the contents of the blob. I wrote down the following java code.
package main;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class GetBlob {
public static void main(String[] args) {
String url="https://MYACCOUNT.blob.core.windows.net/MYCONTAINER/MYBLOB";
try {
System.out.println("RUNNIGN");
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestProperty("Authorization", createQuery());
connection.setRequestProperty("x-ms-version", "2009-09-19");
InputStream response = connection.getInputStream();
System.out.println("SUCCESSS");
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(response));
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static String createQuery()
{
String dateFormat="EEE, dd MMM yyyy hh:mm:ss zzz";
SimpleDateFormat dateFormatGmt = new SimpleDateFormat(dateFormat);
dateFormatGmt.setTimeZone(TimeZone.getTimeZone("UTC"));
String date=dateFormatGmt.format(new Date());
String Signature="GET\n\n\n\n\n\n\n\n\n\n\n\n" +
"x-ms-date:" +date+
"\nx-ms-version:2009-09-19" ;
// I do not know CANOCALIZED RESOURCE
//WHAT ARE THEY??
// +"\n/myaccount/myaccount/mycontainer\ncomp:metadata\nrestype:container\ntimeout:20";
String SharedKey="SharedKey";
String AccountName="MYACCOUNT";
String encryptedSignature=(encrypt(Signature));
String auth=""+SharedKey+" "+AccountName+":"+encryptedSignature;
return auth;
}
public static String encrypt(String clearTextPassword) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(clearTextPassword.getBytes());
return new sun.misc.BASE64Encoder().encode(md.digest());
} catch (NoSuchAlgorithmException e) {
}
return "";
}
}
However , I get the following error when I run this main class...
RUNNIGN
java.io.IOException: Server returned HTTP response code: 403 for URL: https://klabs.blob.core.windows.net/delete/Blob_1
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(Unknown Source)
at main.MainClass.main(MainClass.java:61)
Question1: Why this error, did I miss any header/parameter?
Question2: Do I need to add headers in the first place, because I am able to hit the request from the browser without any issues.
Question3: Can it be an SSL issue? What is the concept of certificates, and how and where to add them? Do I really need them? Will I need them later, when I do bigger operations on my blob storage(I want to manage a thousand blobs)?
Will be thankful for any reference as well, within Azure and otherwise that could help me understand better.
:D
AFTER A FEW DAYS
Below is my new code for PutBlob I azure. I believe I have fully resolved all header and parameter issues and my request is perfect. However I am still getting the same 403. I do not know what the issue is. Azure is proving to be pretty difficult.
A thing to note is that the containers name is delete, and I want to create a blob inside it, say newBlob. I tried to initialize the urlPath in the code below with both "delete" and "delete/newBlob".
Does not work..
package main;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import com.sun.org.apache.xml.internal.security.utils.Base64;
public class Internet {
static String key="password";
static String account="klabs";
private static Base64 base64 ;
private static String createAuthorizationHeader(String canonicalizedString) throws InvalidKeyException, Base64DecodingException, NoSuchAlgorithmException, IllegalStateException, UnsupportedEncodingException {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(base64.decode(key), "HmacSHA256"));
String authKey = new String(base64.encode(mac.doFinal(canonicalizedString.getBytes("UTF-8"))));
String authStr = "SharedKey " + account + ":" + authKey;
return authStr;
}
public static void main(String[] args) {
System.out.println("INTERNET");
String key="password";
String account="klabs";
long blobLength="Dipanshu Verma wrote this".getBytes().length;
File f = new File("C:\\Users\\Dipanshu\\Desktop\\abc.txt");
String requestMethod = "PUT";
String urlPath = "delete";
String storageServiceVersion = "2009-09-19";
SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:sss");
fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
String date = fmt.format(Calendar.getInstance().getTime()) + " UTC";
String blobType = "BlockBlob";
String canonicalizedHeaders = "x-ms-blob-type:"+blobType+"\nx-ms-date:"+date+"\nx-ms-version:"+storageServiceVersion;
String canonicalizedResource = "/"+account+"/"+urlPath;
String stringToSign = requestMethod+"\n\n\n"+blobLength+"\n\n\n\n\n\n\n\n\n"+canonicalizedHeaders+"\n"+canonicalizedResource;
try {
String authorizationHeader = createAuthorizationHeader(stringToSign);
URL myUrl = new URL("https://klabs.blob.core.windows.net/" + urlPath);
HttpURLConnection connection=(HttpURLConnection)myUrl.openConnection();
connection.setRequestProperty("x-ms-blob-type", blobType);
connection.setRequestProperty("Content-Length", String.valueOf(blobLength));
connection.setRequestProperty("x-ms-date", date);
connection.setRequestProperty("x-ms-version", storageServiceVersion);
connection.setRequestProperty("Authorization", authorizationHeader);
connection.setDoOutput(true);
connection.setRequestMethod("POST");
System.out.println(String.valueOf(blobLength));
System.out.println(date);
System.out.println(storageServiceVersion);
System.out.println(stringToSign);
System.out.println(authorizationHeader);
System.out.println(connection.getDoOutput());
DataOutputStream outStream = new DataOutputStream(connection.getOutputStream());
// Send request
outStream.writeBytes("Dipanshu Verma wrote this");
outStream.flush();
outStream.close();
DataInputStream inStream = new DataInputStream(connection.getInputStream());
System.out.println("BULLA");
String buffer;
while((buffer = inStream.readLine()) != null) {
System.out.println(buffer);
}
// Close I/O streams
inStream.close();
outStream.close();
} catch (InvalidKeyException | Base64DecodingException | NoSuchAlgorithmException | IllegalStateException | UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
I know only a proper code reviewer might be able to help me, please do it if you can.
Thanks
Question1: Why this error, did I miss any header/parameter?
Most likely you're getting this error is because of incorrect signature. Please refer to MSDN documentation for creating correct signature: http://msdn.microsoft.com/en-us/library/azure/dd179428.aspx. Unless your signature is correct you'll not be able to perform operations using REST API.
Question2: Do I need to add headers in the first place, because I am
able to hit the request from the browser without any issues.
In your current scenario, because you can access the blob directly (which in turn means the container in which the blob exist has Public or Blob ACL) you don't really need to use REST API. You can simply make a HTTP request using Java and read the response stream which will have blob contents. You would need to go down this route if the container ACL is Private because in this case your requests need to be authenticated and the code above creates an authenticated request.
Question3: Can it be an SSL issue? What is the concept of
certificates, and how and where to add them? Do I really need them?
Will I need them later, when I do bigger operations on my blob
storage(I want to manage a thousand blobs)?
No, it is not an SSL issue. Its an issue with incorrect signature.
Finally found the mistake!!
In the code above , I was using a String "password" as key for my SHA2
base64.decode(key)
It should have been the key associated with my account with AZURE.
Silly One!! Took me 2 weeks to find.
Using the Rally Java Rest API, after I get the AttachmentContent object, how do I actually get the bytes which hold the content?
You may find the following example to be useful for what you are wanting to do. It's for a User Story rather than a Defect but the process would be identical for a Defect.
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.rallydev.rest.RallyRestApi;
import com.rallydev.rest.request.CreateRequest;
import com.rallydev.rest.request.DeleteRequest;
import com.rallydev.rest.request.GetRequest;
import com.rallydev.rest.request.QueryRequest;
import com.rallydev.rest.request.UpdateRequest;
import com.rallydev.rest.response.CreateResponse;
import com.rallydev.rest.response.DeleteResponse;
import com.rallydev.rest.response.GetResponse;
import com.rallydev.rest.response.QueryResponse;
import com.rallydev.rest.response.UpdateResponse;
import com.rallydev.rest.util.Fetch;
import com.rallydev.rest.util.QueryFilter;
import com.rallydev.rest.util.Ref;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.commons.codec.binary.Base64;
public class RestExample_DownloadAttachment {
public static void main(String[] args) throws URISyntaxException, IOException {
// Create and configure a new instance of RallyRestApi
// Connection parameters
String rallyURL = "https://rally1.rallydev.com";
String wsapiVersion = "1.43";
String applicationName = "RestExample_DownloadAttachment";
// Credentials
String userName = "user#company.com";
String userPassword = "topsecret";
RallyRestApi restApi = new RallyRestApi(
new URI(rallyURL),
userName,
userPassword
);
restApi.setWsapiVersion(wsapiVersion);
restApi.setApplicationName(applicationName);
// Workspace and Project Settings
String myWorkspace = "My Workspace";
String myProject = "My Project";
// FormattedID of Existing Test Case to Query
String existStoryFormattedID = "US43";
// Get reference to Workspace of interest
QueryRequest workspaceRequest = new QueryRequest("Workspace");
workspaceRequest.setFetch(new Fetch("Name", "Owner", "Projects"));
workspaceRequest.setQueryFilter(new QueryFilter("Name", "=", myWorkspace));
QueryResponse workspaceQueryResponse = restApi.query(workspaceRequest);
String workspaceRef = workspaceQueryResponse.getResults().get(0).getAsJsonObject().get("_ref").toString();
// Get reference to Project of interest
QueryRequest projectRequest = new QueryRequest("Project");
projectRequest.setFetch(new Fetch("Name", "Owner", "Projects"));
projectRequest.setQueryFilter(new QueryFilter("Name", "=", myProject));
QueryResponse projectQueryResponse = restApi.query(projectRequest);
String projectRef = projectQueryResponse.getResults().get(0).getAsJsonObject().get("_ref").toString();
// Query for existing User Story
System.out.println("Querying for User Story: " + existStoryFormattedID);
QueryRequest existUserStoryRequest = new QueryRequest("HierarchicalRequirement");
existUserStoryRequest.setFetch(new Fetch("FormattedID","Name","Attachments"));
existUserStoryRequest.setQueryFilter(new QueryFilter("FormattedID", "=", existStoryFormattedID));
QueryResponse userStoryQueryResponse = restApi.query(existUserStoryRequest);
JsonObject existUserStoryJsonObject = userStoryQueryResponse.getResults().get(0).getAsJsonObject();
String existUserStoryRef = userStoryQueryResponse.getResults().get(0).getAsJsonObject().get("_ref").toString();
JsonArray attachmentsJsonArray = existUserStoryJsonObject.getAsJsonArray("Attachments");
// Take first attachment
JsonObject attachmentObject = attachmentsJsonArray.get(0).getAsJsonObject();
String attachmentRef = attachmentObject.get("_ref").toString();
// Read attachment from Ref
System.out.println("Reading First Attachment: " + attachmentRef);
GetRequest attachmentRequest = new GetRequest(attachmentRef);
attachmentRequest.setFetch(new Fetch("Name","Content"));
GetResponse attachmentResponse = restApi.get(attachmentRequest);
// AttachmentContent object
JsonObject attachmentContentObject = attachmentResponse.getObject().get("Content").getAsJsonObject();
String attachmentContentRef = attachmentContentObject.get("_ref").toString();
// Read Content from Ref
System.out.println("Reading Attachment Content: " + attachmentRef);
GetRequest contentRequest = new GetRequest(attachmentContentRef);
contentRequest.setFetch(new Fetch("Content"));
GetResponse contentResponse = restApi.get(contentRequest);
// Read Content String of AttachmentContent
String attachmentContentBase64String = contentResponse.getObject().get("Content").getAsString();
// Grab attachment name
String attachmentName = attachmentResponse.getObject().get("Name").getAsString();
// Decode base64 string into bytes
byte[] imageBytes = Base64.decodeBase64(attachmentContentBase64String);
// Image output
String imageFilePath = "/Users/username/Desktop/";
String fullImageFile = imageFilePath + attachmentName;
// Write output file
System.out.println("Writing attachment to file: " + attachmentName);
try {
OutputStream imageOutputStream = new FileOutputStream(fullImageFile);
imageOutputStream.write(imageBytes);
imageOutputStream.flush();
imageOutputStream.close();
} catch (Exception e) {
System.out.println("Exception occurred while write image file ");
e.printStackTrace();
}
finally {
//Release all resources
restApi.close();
}
}
}