Facebook Connect Android -- using stream.publish # http://api.facebook.com/restserver.php - java

I am getting the following error:
<error_code>104</error_code>
<error_msg>Incorrect signature</error_msg>
What should I be setting contentType type as? Should I set as:
String contentType = "application/x-www-form-urlencoded";
or
String contentType = "multipart/form-data; boundary=" + kStringBoundary;
This is how I am writing the stream:
HttpURLConnection conn = null;
OutputStream out = null;
InputStream in = null;
try {
conn = (HttpURLConnection) _loadingURL.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
if (method != null) {
conn.setRequestMethod(method);
if ("POST".equals(method)) {
//"application/x-www-form-urlencoded";
String contentType = "multipart/form-data; boundary=" + kStringBoundary;
//String contentType = "application/x-www-form-urlencoded";
conn.setRequestProperty("Content-Type", contentType);
}
// Cookies are used in FBPermissionDialog and FBFeedDialog to
// retrieve logged user
conn.connect();
out = conn.getOutputStream();
if ("POST".equals(method)) {
String body = generatePostBody(postParams);
if (body != null) {
out.write(body.getBytes("UTF-8"));
}
}
in = conn.getInputStream();
Here's the method I am using to publish the stream:
private void publishFeed(String themessage) {
//Intent intent = new Intent(this, FBFeedActivity.class);
// intent.putExtra("userMessagePrompt", themessage);
// intent.putExtra("attachment",
Map<String, String> getParams = new HashMap<String, String>();
// getParams.put("display", "touch");
// getParams.put("callback", "fbconnect://success");
// getParams.put("cancel", "fbconnect://cancel");
Map<String, String> postParams = new HashMap<String, String>();
postParams.put("api_key", _session.getApiKey());
postParams.put("method", "stream.publish");
postParams.put("session_key", _session.getSessionKey());
postParams.put("user_message", "TESTING 123");
// postParams.put("preview", "1");
postParams.put("attachment", "{\"name\":\"Facebook Connect for Android\",\"href\":\"http://code.google.com/p/fbconnect-android/\",\"caption\":\"Caption\",\"description\":\"Description\",\"media\":[{\"type\":\"image\",\"src\":\"http://img40.yfrog.com/img40/5914/iphoneconnectbtn.jpg\",\"href\":\"http://developers.facebook.com/connect.php?tab=iphone/\"}],\"properties\":{\"another link\":{\"text\":\"Facebook home page\",\"href\":\"http://www.facebook.com\"}}}");
// postParams.put("user_message_prompt", "22222");
try {
loadURL("http://api.facebook.com/restserver.php", "POST", getParams, postParams);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
Here is generatePostBody() :
private String generatePostBody(Map<String, String> params) {
StringBuilder body = new StringBuilder();
StringBuilder endLine = new StringBuilder("\r\n--").append(kStringBoundary).append("\r\n");
body.append("--").append(kStringBoundary).append("\r\n");
for (Entry<String, String> entry : params.entrySet()) {
body.append("Content-Disposition: form-data; name=\"").append(entry.getKey()).append("\"\r\n\r\n");
String value = entry.getValue();
if ("user_message_prompt".equals(entry.getKey())) {
body.append(value);
}
else {
body.append(CcUtil.encode(value));
}
body.append(endLine);
}
return body.toString();
}
Thanks.

This is from Facebook Developers Wiki: http://wiki.developers.facebook.com/index.php/API
Note: If you manually form your HTTP
POST requests to Facebook, you must
include the request data in the POST
body. In addition, you should include
a Content-Type: header of
application/x-www-form-urlencoded.
Use multipart/form-data when uploading files only (e.g. Photos.upload on Facebook API)
Also, Based on this API reference, http://wiki.developers.facebook.com/index.php/How_Facebook_Authenticates_Your_Application, this is what you're doing wrong.
1) Don't use HashMap to store Facebook parameters. Parameters must be sorted by their key. Rather use SortedMap interface and use TreeMap to store Facebook parameters.
2) Always include the sig parameter in the map before sending the call to Facebook. You're getting a "104 Incorrect signature" because Facebook doesn't find the sig parameter in your request.
The reference (http://wiki.developers.facebook.com/index.php/How_Facebook_Authenticates_Your_Application) shows exactly how to create a MD5 signature which facebook uses.
Here's how to quickly create a sig value for Facebook.
String hashString = "";
Map<String, String> sortedMap = null;
if (parameters instanceof TreeMap) {
sortedMap = (TreeMap<String, String>) parameters;
} else {
sortedMap = new TreeMap<String, String>(parameters);
}
try {
Iterator<String> iter = sortedMap.keySet().iterator();
StringBuilder sb = new StringBuilder();
synchronized (iter) {
while (iter.hasNext()) {
String key = iter.next();
sb.append(key);
sb.append("=");
String value = sortedMap.get(key);
sb.append(value == null ? "" : value);
}
}
sb.append(secret);
MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] digested = digest.digest(sb.toString().getBytes());
BigInteger bigInt = new BigInteger(1, digested);
hashString = bigInt.toString(16);
while (hashString.length() < 32) {
hashString = "0" + hashString;
}
} catch (NoSuchAlgorithmException nsae) {
// TODO: handle exception
logger.error(e.getLocalizedMessage(), e);
}
return hashString;

Related

SignatureDoesNotMatch error when deleting multiple objects from S3 bucket

I m adding code to delete multiple objects from S3. POST request to delete returns 403 status. I am adding this code in legacy code and unfortunately cannot change to change AWS S3 library. Not able to figure out if I am missing a header or if the logic is incorrect.
public Response deleteMultipleObjects(Delete object) throws MalformedURLException, IOException, StorageException {
HttpURLConnection request = null;
try(ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(out)) {
os.writeObject(object);
Calendar now = Calendar.getInstance();
Map<String, List<String>> headers = new HashMap<>();
headers.put(AWSAuthConnection.CONTENT_TYPE_HEADER, Arrays.asList("application/octet-stream"));
headers.put(AWSAuthConnection.CONTENT_LENGTH_HEADER, Arrays.asList(Integer.toString(out.toByteArray().length)));
headers.put(AWSAuthConnection.AMAZON_DATE_HEADER, Arrays.asList(convertDateToString(now, AWSAuthConnection.TIMESTAMP_FORMAT, AWSAuthConnection.TIME_ZONE_UTC)));
headers.put(AWSAuthConnection.AMAZON_CONTENT_SHA256_HEADER, Arrays.asList(buildHash(out.toByteArray(), SHA256_HASH, EncodingType.HEX)));
headers.put(AWSAuthConnection.CONTENT_MD5_HEADER, Arrays.asList(buildHash(out.toByteArray(), AWSAuthConnection.MD5_HASH, AWSAuthConnection.EncodingType.BASE64)));
request = makeRequest("POST", bucket+"/?delete", headers, null);
request.setDoOutput(true);
request.getOutputStream().write(object == null ? new byte[]{} : out.toByteArray());
return new Response(request);
}
}
private String buildHash(final byte[] contentToHash, final String hashMethod, final AWSAuthConnection.EncodingType encodingType) throws StorageException{
try {
MessageDigest digest = MessageDigest.getInstance(hashMethod);
byte[] contentHash = digest.digest(contentToHash);
if (encodingType == AWSAuthConnection.EncodingType.HEX) {
return Hex.encodeHexString(contentHash);
} else {
return Base64.encodeBase64String(contentHash);
}
} catch (NoSuchAlgorithmException e) {
throw new StorageException(StorageException.ERROR_INVALID_STORAGE_TYPE, e);
}
}
private HttpURLConnection makeRequest(String method, String resource, Map<String, List<String>> headers, StorageObject object) throws MalformedURLException, IOException {
URL url = makeURL(resource);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod(method);
connection.setUseCaches(true);
addHeaders(connection, headers);
if (object != null) addMetadataHeaders(connection, object.metadata);
addAuthHeader(connection, method, resource);
return connection;
}
private void addAuthHeader(HttpURLConnection connection, String method, String resource) {
String canonicalString =
Utils.makeCanonicalString(method, resource, connection.getRequestProperties());
String encodedCanonical = Utils.encode(this.info, canonicalString, false);
connection.setRequestProperty("Authorization", "AWS " + this.info + ":" + encodedCanonical);
}
Error from S3:
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>.......</Error>
Has anyone faced this issue before? If you know a way to fix, please help

Bitfinex API Error Message "Key symbol was not present."

I'm working with the Bitfinex API and the version of the API is 1.
But I have a problem that can not be solved.
When I use '/v1/order/new', the server sends to the message "Key symbol was not present."
I can not find which point is the problem.
The parameter settings are as belows.
Please advise.
========== ========== ========== ==========
/**
Create Header, Param
*/
JSONObject json = new JSONObject();
json.put("request", targetURL);
json.put("nonce", Long.toString(getNonce()));
String payload = json.toString();
String payload_base64 = Base64.getEncoder().encodeToString(payload.getBytes());
String payload_sha384hmac = hmacDigest(payload_base64, apiKeySecret, ALGORITHM_HMACSHA384);
HttpTask http = new HttpTask(URL, Method.POST);
http.addHeader("X-BFX-APIKEY", apiKey);
http.addHeader("X-BFX-PAYLOAD", payload_base64);
http.addHeader("X-BFX-SIGNATURE", payload_sha384hmac);
http.setContentType("x-www-urlencoded");
http.setAcceptType("application/xml");
http.addParam("symbol", "btcusd");
http.addParam("amount", "0.01");
http.addParam("price", "0.01");
http.addParam("side", "buy");
http.addParam("type", "exchange market");
http.addParam("is_hidden", "false");
http.addParam("is_postonly", "true");
http.addParam("use_all_available", "0");
http.addParam("exchange", "bitfinex");
http.addParam("ocoorder", "false");
http.addParam("buy_price_oco", "0");
/**
Parsing Param
*/
StringBuilder sb = new StringBuilder();
Set<String> key = m_params.keySet();
int totalCount = key.size();
if (totalCount > 0) {
int index = 0;
for (Iterator<String> iterator = key.iterator(); iterator.hasNext();) {
String keyValue = (String) iterator.next();
String valueValue = (String) m_params.get(keyValue);
sb.append(String.format("%s=%s", keyValue, valueValue));
if (index < totalCount - 1) {
sb.append("&");
}
index++;
}
query = sb.toString();
}
/**
send Param
*/
if (!query.isEmpty()) {
DataOutputStream wr;
try {
wr = new DataOutputStream(m_connection.getOutputStream());
wr.writeBytes(query);
wr.flush();
wr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
You need to put all params in payload object.
Here is my example on JAVASCRIPT:
auth_v1_request(path, params){
return new Promise((resolve, reject) => {
// console.log(this.account);
const apiKey = this.account.api_key;
const apiSecret = this.account.api_secret;
const apiPath = '/' + path;
const nonce = (Date.now() * 1000).toString();
const completeURL = `${ CONFIG.BITFINEX.API_URL }${apiPath}`;
params.nonce = nonce;
params.request = apiPath;
const payload = new Buffer(JSON.stringify(params))
.toString('base64');
const signature = crypto
.createHmac('sha384', apiSecret)
.update(payload)
.digest('hex');
const options = {
url: completeURL,
headers: {
'X-BFX-APIKEY': apiKey,
'X-BFX-PAYLOAD': payload,
'X-BFX-SIGNATURE': signature
},
body: JSON.stringify(params),
json: true
};
request.post(options, (error, response, res_body) => {
console.log(error);
console.log(res_body);
if(error) {
reject(error);
}
else {
let parsed;
try {
parsed = res_body;
if(parsed.message){
reject(parsed);
}
else {
resolve(parsed);
}
}
catch(err) {
reject(err);
}
}
})
});
}

how to post data in key/value pair? [duplicate]

This question already has answers here:
Java - sending HTTP parameters via POST method easily
(18 answers)
Closed 5 years ago.
i need to post data to particular url
in which in content i need to post html in content array and in meta headers in json format.
URL oracle = new URL("");
try (BufferedReader in = new BufferedReader(
new InputStreamReader(oracle.openStream()))) {
String inputLine1;
while ((inputLine1 = in.readLine()) != null) {
System.out.println(inputLine1);
com.eclipsesource.json.JsonObject object = Json.parse(inputLine1).asObject();
com.eclipsesource.json.JsonArray items = Json.parse(inputLine1).asObject().get("data").asArray();
for (JsonValue item : items) {
//System.out.println(item.toString());
String name = item.asObject().getString("id", "Unknown Item");
System.out.println(name);
String quantity = item.asObject().getString("url", "id");
// JSONArray jsonArray2 = new JSONArray(quantity);
System.out.println(quantity);
/* Platform.runLater(() ->{
try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
Logger.getLogger(HV1.class.getName()).log(Level.SEVERE, null, ex);
}*/
Img.load(quantity);
URL url;
InputStream is = null;
BufferedReader br;
String line;
url = new URL(quantity);
is = url.openStream(); // throws an IOException
br = new BufferedReader(new InputStreamReader(is));
while ((line = br.readLine()) != null) {
System.out.println(line);
byte[] postData= line.getBytes( StandardCharsets.UTF_8 );
wb2.load(line);
String originalUrl = "";
String newUrl = originalUrl.replace("ID", name);
System.out.println(newUrl);
String request = newUrl;
URL url1 = new URL( request );
HttpURLConnection conn= (HttpURLConnection) url1.openConnection();
conn.setDoOutput( true );
conn.setInstanceFollowRedirects( false );
conn.setRequestMethod( "POST" );
conn.setRequestProperty( "Content-Type", "text/plain");
conn.setRequestProperty( "charset", "utf-8");
//conn.setRequestProperty( "Content-Length", Integer.toString( line ));
conn.setUseCaches( false );
try( DataOutputStream wr = new DataOutputStream( conn.getOutputStream())) {
wr.write(postData);
System.out.println("200 ok");
this is what i tried but i had post in text/plain but i want to post in key/value pair.
updated code
URL oracle = new URL("");
try (BufferedReader in = new BufferedReader(
new InputStreamReader(oracle.openStream()))) {
String inputLine1;
while ((inputLine1 = in.readLine()) != null) {
System.out.println(inputLine1);
com.eclipsesource.json.JsonObject object = Json.parse(inputLine1).asObject();
com.eclipsesource.json.JsonArray items = Json.parse(inputLine1).asObject().get("data").asArray();
for (JsonValue item : items) {
//System.out.println(item.toString());
String name = item.asObject().getString("id", "Unknown Item");
System.out.println(name);
String quantity = item.asObject().getString("url", "id");
// JSONArray jsonArray2 = new JSONArray(quantity);
System.out.println(quantity);
/* Platform.runLater(() ->{
try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
Logger.getLogger(HV1.class.getName()).log(Level.SEVERE, null, ex);
}*/
Img.load(quantity);
URL url;
InputStream is = null;
BufferedReader br;
String line;
url = new URL(quantity);
is = url.openStream(); // throws an IOException
br = new BufferedReader(new InputStreamReader(is));
while ((line = br.readLine()) != null) {
System.out.println(line);
byte[] postData= line.getBytes( StandardCharsets.UTF_8 );
wb2.load(line);
String originalUrl = "";
String newUrl = originalUrl.replace("ID", name);
System.out.println(newUrl);
URL url1 = new URL(newUrl);
Map<String,Object> params = new LinkedHashMap<>();
params.put("content", postData);
params.put("meta", "abc");
StringBuilder postData1 = new StringBuilder();
for (Map.Entry<String,Object> param : params.entrySet()) {
if (postData1.length() != 0) postData1.append('&');
postData1.append(URLEncoder.encode(param.getKey(), "UTF-8"));
postData1.append('=');
postData1.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));
}
byte[] postDataBytes = postData1.toString().getBytes("UTF-8");
HttpURLConnection conn = (HttpURLConnection)url1.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
conn.setDoOutput(true);
conn.getOutputStream().write(postDataBytes);
Reader in1 = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
for (int c; (c = in1.read()) >= 0;)
System.out.print((char)c);
/* try{
Thread.sleep(400);
}catch(InterruptedException e){System.out.println(e);} */
}
}
}
this is my updted code(answer) this is how i solve my problem thanks for your precious time.
Take a look at this previous answer regarding HTTP Post parameters that exploit BasicNameValuePairs.
Name Value Pairs
Here is a pertinent piece of code from that answer.
HttpClient httpclient;
HttpPost httppost;
ArrayList<NameValuePair> postParameters;
httpclient = new DefaultHttpClient();
httppost = new HttpPost("your login link");
postParameters = new ArrayList<NameValuePair>();
postParameters.add(new BasicNameValuePair("param1", "param1_value"));
postParameters.add(new BasicNameValuePair("param2", "param2_value"));
httpPost.setEntity(new UrlEncodedFormEntity(postParameters, "UTF-8"));
HttpResponse response = httpclient.execute(httpPost);
Best would be using something like Spring and Jackson to create a JSON sending via a request, if you are not familiar with what you are trying to achieve:
This is just basic implementation
private final String uri = "yoururl.de/asdfasd";
private final HttpMethod httpMethod = HttpMethod.POST;
private final ContentType contentType = ContentType.json;
And EPO to transfer the Data
SendKeyValuePairsEPO implements Serializable{
private static final long serialVersionUID = 5311348008314829094L;
private final Integer startIndex;
private final Integer size;
private final Integer totalSize;
private final List<KeyValuePairEPO> values;
/**
* Contructor
*
* #param startIndex start searching index
* #param size requested result size
* #param totalSize total size of available records
* #param values the key value pairs
*/
public SendKeyValuePairsEPO(#JsonProperty("startIndex") final Integer startIndex,
#JsonProperty("size") final Integer size,
#JsonProperty("totalSize") final Integer totalSize,
#JsonProperty("values") final List<KeyValuePairEPO> values) {
this.startIndex = startIndex;
this.size = size;
this.totalSize = totalSize;
this.values = values;
}
and aswell a KeyValuePairEPO:
KeyValuePairEPO implements Serializable{
private static final long serialVersionUID = 5311348008314829094L;
private final String key;
private final String value;
private final String type; //maybe you need a type to tell what kind of value it is
...
And at last you will need to do something like:
/*package*/ <T> T sendRequest(Class<T> responseClass, Object requestEpo, String uri) {
try {
//Parse encapsulated COntent type to media type
HttpHeaders headers = new HttpHeaders();
MediaType requestContentType requestContentType = MediaType.APPLICATION_JSON;
//Set content type and accept header to this type
headers.setContentType(requestContentType);
headers.setAccept(Collections.singletonList(requestContentType));
//Parse the data object to a JSON
String requestJSONAsString = "";
if (request.getData() != null) {
try {
requestJSONAsString = RestObjectMapper.getInstance().writeValueAsString(requestEpo);
} catch (JsonProcessingException ex) {
throw new InternalServerErrorException(String.format("Error parsing: %s", requestEpo.getClass().getSimpleName()), ex);
}
}
//Perform the send request
return sendRequest(responseClass, uri, headers, httpMethod, requestJSONAsString);
} finally {
LOG.debug("Ended sendRequest");
}
}
private <T> T sendRequest(final Class<T> responseClass, final String uri, final HttpHeaders httpHeaders, final HttpMethod httpMethod, String requestJSON) {
try {
LOG.debug(String.format("Start sendRequest with:%s %s %s %s", uri, httpHeaders, httpMethod, requestJSON));
RestTemplate rest = new RestTemplate();
ClientHttpRequestFactory restFactory = rest.getRequestFactory();
if(restFactory instanceof SimpleClientHttpRequestFactory){
((SimpleClientHttpRequestFactory)restFactory).setReadTimeout(REQUEST_TIMEOUT);
((SimpleClientHttpRequestFactory)restFactory).setConnectTimeout(REQUEST_TIMEOUT);
}
HttpEntity<String> entity = new HttpEntity<>(requestJSON, httpHeaders);
final ResponseEntity<String> response = rest.exchange(uri, httpMethod, entity, String.class);
LOG.debug("Status:" + response.getStatusCode().toString());
String returnedPayload = response.getBody();
return RestObjectMapper.getInstance().readValue(returnedPayload, responseClass);
} catch (HttpStatusCodeException ex) {
LOG.error("HTTP Error in sendRequest: " + ex.getMessage());
switch (ex.getStatusCode()) {
case BAD_REQUEST:
throw new BadRequestException(uri, ex);
case NOT_FOUND:
throw new NotFoundException(uri, ex);
case FORBIDDEN:
throw new ForbiddenException(uri, ex);
case REQUEST_TIMEOUT:
throw new RequestTimeoutException(ex, REQUEST_TIMEOUT);
default:
throw new InternalServerErrorException(ex);
}
} catch (Exception ex) {
LOG.error("Error in sendRequest: " + ex.getMessage());
throw new InternalServerErrorException(ex);
} finally {
LOG.debug("Ended sendRequest");
}
}
where RestObjectMapper is:
public class RestObjectMapper extends ObjectMapper {
public static final String EMPTY_JSON = "{}";
private static final long serialVersionUID = 3924442982193452932L;
/**
* Singleton Instance
* Pattern: Initialization-on-demand holder idiom:
* <ul>
* <li>the class loader loads classes when they are first accessed (in this case Holder's only access is within the getInstance() method)</li>
* <li>when a class is loaded, and before anyone can use it, all static initializers are guaranteed to be executed (that's when Holder's static block fires)</li>
* <li>the class loader has its own synchronization built right in that make the above two points guaranteed to be threadsafe</li></ul>
*/
private static class INSTANCE_HOLDER {
private static final RestObjectMapper INSTANCE = new RestObjectMapper();
}
private RestObjectMapper() {
super();
configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
configure(DeserializationFeature.UNWRAP_ROOT_VALUE, false);
configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
/**
* Gets the singleton Instance of the JSON Mapper
*
* #return the singleton instance
*/
public static RestObjectMapper getInstance() {
return INSTANCE_HOLDER.INSTANCE;
}
By the way ResponseClass is another EPO the result (JSON) will be mapped to.

Verify OAuth1a signed request using Twitter joauth with RSA-SHA1?

I have a use case to authenticate OAuth1 request which is signed using RSA Private Key and verified at server end with RSA public key.
I found this library from Twitter which helps us authenticate/verify the Oauth signed requests. https://github.com/twitter/joauth
I want to leverage this library for verifying the request from Jersey or Spring MVC action method. The request from client would have been signed using private key. At my end I would use the public key of the client to verify the request. which means RSA-SHA1 algo.
Twitter joauth seem to be useful but I am missing the code that would transform HttpServletRequest to OAuthRequest
The library read-me file suggests this as facility but I could not find a code that does javax.servlet.http.HttpServletRequest --> com.twitter.joauth.OAuthRequest transformation.
The request verification happens in verify method which has following signature.
public VerifierResult verify(UnpackedRequest.OAuth1Request request, String tokenSecret, String consumerSecret);
Secondly I also want to know which is the most appropriate way to use/read RSA public key with twitter joauth when verify method takes String parameter ?
I have never used any library to authenticate users via Twitter. But I have just looked in the UnpackedRequest.OAuth1Request. You can create an instance of this class by filling all parameters. I have written Twitter OAuth Header creator, so you can just use it to fill those parameters or send POST requests directly without a library.
Here all classes what you need:
Signature - to generate an OAuth Signature.
public class Signature {
private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
public static String calculateRFC2104HMAC(String data, String key)
throws java.security.SignatureException
{
String result;
try {
SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM);
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(data.getBytes());
result = new String(Base64.encodeBase64(rawHmac));
} catch (Exception e) {
throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
}
return result;
}
}
NvpComparator - to sort parameters you need in the header.
public class NvpComparator implements Comparator<NameValuePair> {
#Override
public int compare(NameValuePair arg0, NameValuePair arg1) {
String name0 = arg0.getName();
String name1 = arg1.getName();
return name0.compareTo(name1);
}
}
OAuth - for URL encode.
class OAuth{
...
public static String percentEncode(String s) {
return URLEncoder.encode(s, "UTF-8")
.replace("+", "%20").replace("*", "%2A")
.replace("%7E", "~");
}
...
}
HeaderCreator - to create all needed parameters and generate an OAuth header param.
public class HeaderCreator {
private String authorization = "OAuth ";
private String oAuthSignature;
private String oAuthNonce;
private String oAuthTimestamp;
private String oAuthConsumerSecret;
private String oAuthTokenSecret;
public String getAuthorization() {
return authorization;
}
public String getoAuthSignature() {
return oAuthSignature;
}
public String getoAuthNonce() {
return oAuthNonce;
}
public String getoAuthTimestamp() {
return oAuthTimestamp;
}
public HeaderCreator(){}
public HeaderCreator(String oAuthConsumerSecret){
this.oAuthConsumerSecret = oAuthConsumerSecret;
}
public HeaderCreator(String oAuthConsumerSecret, String oAuthTokenSecret){
this(oAuthConsumerSecret);
this.oAuthTokenSecret = oAuthTokenSecret;
}
public String getTwitterServerTime() throws IOException, ParseException {
HttpsURLConnection con = (HttpsURLConnection)
new URL("https://api.twitter.com/oauth/request_token").openConnection();
con.setRequestMethod("HEAD");
con.getResponseCode();
String twitterDate= con.getHeaderField("Date");
DateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH);
Date date = formatter.parse(twitterDate);
return String.valueOf(date.getTime() / 1000L);
}
public String generatedSignature(String url, String method, List<NameValuePair> allParams,
boolean withToken) throws SignatureException {
oAuthNonce = String.valueOf(System.currentTimeMillis());
allParams.add(new BasicNameValuePair("oauth_nonce", oAuthNonce));
try {
oAuthTimestamp = getTwitterServerTime();
allParams.add(new BasicNameValuePair("oauth_timestamp", oAuthTimestamp));
}catch (Exception ex){
//TODO: Log!!
}
Collections.sort(allParams, new NvpComparator());
StringBuffer params = new StringBuffer();
for(int i=0;i<allParams.size();i++)
{
NameValuePair nvp = allParams.get(i);
if (i>0) {
params.append("&");
}
params.append(nvp.getName() + "=" + OAuth.percentEncode(nvp.getValue()));
}
String signatureBaseStringTemplate = "%s&%s&%s";
String signatureBaseString = String.format(signatureBaseStringTemplate,
OAuth.percentEncode(method),
OAuth.percentEncode(url),
OAuth.percentEncode(params.toString()));
String compositeKey = OAuth.percentEncode(oAuthConsumerSecret)+"&";
if(withToken) compositeKey+=OAuth.percentEncode(oAuthTokenSecret);
oAuthSignature = Signature.calculateRFC2104HMAC(signatureBaseString, compositeKey);
return oAuthSignature;
}
public String generatedAuthorization(List<NameValuePair> allParams){
authorization = "OAuth ";
Collections.sort(allParams, new NvpComparator());
for(NameValuePair nvm : allParams){
authorization+=nvm.getName()+"="+OAuth.percentEncode(nvm.getValue())+", ";
}
authorization=authorization.substring(0,authorization.length()-2);
return authorization;
}
}
Explain:
1. getTwitterServerTime
In oAuthTimestamp you need not your time of server but the time of a Twitter server. You can optimize it saving this param if you always send requests in the certain Twitter server.
2. HeaderCreator.generatedSignature(...)
url - logically url to twitter API
method - GET or POST. You must use always "POST"
allParams - Parameters which you know to generate signature ("param_name", "param_value");
withToken - if you know oAuthTokenSecret put true. Otherwise false.
3. HeaderCreator.generatedAuthorization(...)
Use this method after generatedSignature(...) to generate an OAuth header string.
allParams - it is parameters which you have used in generatedSignature(...) plus: nonce, signature, timestamp. Always use:
allParams.add(new BasicNameValuePair("oauth_nonce", headerCreator.getoAuthNonce()));
allParams.add(new BasicNameValuePair("oauth_signature", headerCreator.getoAuthSignature()));
allParams.add(new BasicNameValuePair("oauth_timestamp", headerCreator.getoAuthTimestamp()));
Now you can use it to fill UnpackedRequest.OAuth1Request in your library. Also here an example to authenticate user in SpringMVC without the library:
Requests - to send post requests.
public class Requests {
public static String sendPost(String url, String urlParameters, Map<String, String> prop) throws Exception {
URL obj = new URL(url);
HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
con.setRequestMethod("POST");
if(prop!=null) {
for (Map.Entry<String, String> entry : prop.entrySet()) {
con.setRequestProperty(entry.getKey(), entry.getValue());
}
}
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
int responseCode = con.getResponseCode();
BufferedReader in;
if(responseCode==200) {
in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
}else{
in = new BufferedReader(
new InputStreamReader(con.getErrorStream()));
}
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
return response.toString();
}
}
twAuth(...) - put it in your controller. Execute it when an user want to authenticate in your site via Twitter.
#RequestMapping(value = "/twauth", method = RequestMethod.GET)
#ResponseBody
public String twAuth(HttpServletResponse response) throws Exception{
try {
String url = "https://api.twitter.com/oauth/request_token";
List<NameValuePair> allParams = new ArrayList<NameValuePair>();
allParams.add(new BasicNameValuePair("oauth_callback", "http://127.0.0.1:8080/twlogin"));
allParams.add(new BasicNameValuePair("oauth_consumer_key", "2YhNLyum1VY10UrWBMqBnatiT"));
allParams.add(new BasicNameValuePair("oauth_signature_method", "HMAC-SHA1"));
allParams.add(new BasicNameValuePair("oauth_version", "1.0"));
HeaderCreator headerCreator = new HeaderCreator("RUesRE56vVWzN9VFcfA0jCBz9VkvkAmidXj8d1h2tS5EZDipSL");
headerCreator.generatedSignature(url,"POST",allParams,false);
allParams.add(new BasicNameValuePair("oauth_nonce", headerCreator.getoAuthNonce()));
allParams.add(new BasicNameValuePair("oauth_signature", headerCreator.getoAuthSignature()));
allParams.add(new BasicNameValuePair("oauth_timestamp", headerCreator.getoAuthTimestamp()));
Map<String, String> props = new HashMap<String, String>();
props.put("Authorization", headerCreator.generatedAuthorization(allParams));
String twitterResponse = Requests.sendPost(url,"",props);
Integer indOAuthToken = twitterResponse.indexOf("oauth_token");
String oAuthToken = twitterResponse.substring(indOAuthToken, twitterResponse.indexOf("&",indOAuthToken));
response.sendRedirect("https://api.twitter.com/oauth/authenticate?" + oAuthToken);
}catch (Exception ex){
//TODO: Log
throw new Exception();
}
return "main";
}
twLogin(...) - put it in your controller. It is callback from Twitter.
#RequestMapping(value = "/twlogin", method = RequestMethod.GET)
public String twLogin(#RequestParam("oauth_token") String oauthToken,
#RequestParam("oauth_verifier") String oauthVerifier,
Model model, HttpServletRequest request){
try {
if(oauthToken==null || oauthToken.equals("") ||
oauthVerifier==null || oauthVerifier.equals(""))
return "main";
String url = "https://api.twitter.com/oauth/access_token";
List<NameValuePair> allParams = new ArrayList<NameValuePair>();
allParams.add(new BasicNameValuePair("oauth_consumer_key", "2YhNLyum1VY10UrWBMqBnatiT"));
allParams.add(new BasicNameValuePair("oauth_signature_method", "HMAC-SHA1"));
allParams.add(new BasicNameValuePair("oauth_token", oauthToken));
allParams.add(new BasicNameValuePair("oauth_version", "1.0"));
NameValuePair oAuthVerifier = new BasicNameValuePair("oauth_verifier", oauthVerifier);
allParams.add(oAuthVerifier);
HeaderCreator headerCreator = new HeaderCreator("RUesRE56vVWzN9VFcfA0jCBz9VkvkAmidXj8d1h2tS5EZDipSL");
headerCreator.generatedSignature(url,"POST",allParams,false);
allParams.add(new BasicNameValuePair("oauth_nonce", headerCreator.getoAuthNonce()));
allParams.add(new BasicNameValuePair("oauth_signature", headerCreator.getoAuthSignature()));
allParams.add(new BasicNameValuePair("oauth_timestamp", headerCreator.getoAuthTimestamp()));
allParams.remove(oAuthVerifier);
Map<String, String> props = new HashMap<String, String>();
props.put("Authorization", headerCreator.generatedAuthorization(allParams));
String twitterResponse = Requests.sendPost(url,"oauth_verifier="+oauthVerifier,props);
//Get user id
Integer startIndexTmp = twitterResponse.indexOf("user_id")+8;
Integer endIndexTmp = twitterResponse.indexOf("&",startIndexTmp);
if(endIndexTmp<=0) endIndexTmp = twitterResponse.length()-1;
Long userId = Long.parseLong(twitterResponse.substring(startIndexTmp, endIndexTmp));
//Do what do you want...
}catch (Exception ex){
//TODO: Log
throw new Exception();
}
}

Google checkout IPN, “Requesting Notifications” returns “OK” instead of the message data

I'm using the sandbox "Notification Serial Number" node. I'm acknowledging fine and getting the serial-number. When I go to get the message as described in "Requesting Notifications", I get a response body of two letters: OK .. Instead I need the the notification message.
https://developers.google.com/checkout/developer/Google_Checkout_HTML_API_Notification_API#responding_to_notifications
Here is my code:
public static enum Account {
sandbox, production;
String merchantId, merchantKey, url;
static {
sandbox.merchantId = "9999999999999999";
sandbox.merchantKey = "Ran0mUPPERlowerCaArAcTeRs";
sandbox.url = "https://sandbox.google.com/checkout/";
production.merchantId = "...";
production.merchantKey = "...";
production.url = "https://checkout.google.com/";
}
}
// https://developers.google.com/checkout/developer/Google_Checkout_HTML_API_Notification_API
//notification api entry-point
public static void sandbox() throws IOException, GoogleCheckoutException,
ParseException {
String sn = request.params.get("serial-number");
if (sn == null)
error();//playframework sends an error response code
//GoogleCheckoutApi.Account.sandbox is an enum with my information
//try to process first (or exception), then confirm below
processNotification(GoogleCheckoutApi.Account.sandbox, sn);
// confirm request
// https://developers.google.com/checkout/developer/Google_Checkout_HTML_API_Notification_API#responding_to_notifications
String s = "<notification-acknowledgment xmlns=\"http://checkout.google.com/schema/2\" serial-number=\""
+ sn + "\" />";
response.out.write(s.getBytes());
}
static final String URL_REPORTS_FORM = "api/checkout/v2/reportsForm/Merchant/";
public static void processNotification(Account account, final String sn)
throws IOException, GoogleCheckoutException, ParseException {
System.out.println(new Date() + "\tGoogle Notification IN\t" + sn);
Map<String, String> parms = new HashMap<String, String>() {
{
put("_type", "notification-history-request");
put("serial-number", sn);
}
};
HttpURLConnection c = requestPost(account.url + URL_REPORTS_FORM
+ account.merchantId, authProperties(account), parms, null);
try {
if (c.getResponseCode() != 200)
throw new GoogleCheckoutException("Error Response Code "
+ c.getResponseCode() + " from google API\n"
+ c.getResponseMessage());
handleResponse(account, c.getResponseMessage());
} finally {
c.disconnect();
}
}
private static void handleResponse(Account account, String responseMessage)
throws IOException, ParseException {
if (responseMessage.equals("OK")) {
System.out.println(new Date() + "\tGoogle Notification IN\tOK");
return;
}
//...other code to log and save the message...
}
//...requestPost seems to work fine. I know I'm authenticating correctly.
public static HttpURLConnection requestPost(String requestURL,
Map<String, String> properties, Map<String, String> params,
String content) throws IOException {
// derived from
// http://www.codejava.net/java-se/networking/an-http-utility-class-to-send-getpost-request
URL url = new URL(requestURL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setInstanceFollowRedirects(false);
connection.setRequestMethod("POST");
if (properties != null)
for (String key : properties.keySet()) {
String value = properties.get(key);
connection.setRequestProperty(key, value);
}
connection.setUseCaches(false);
if (params != null && params.size() > 0) {
StringBuffer requestParams = new StringBuffer();
for (String key : params.keySet()) {
String value = params.get(key);
requestParams.append(URLEncoder.encode(key, "UTF-8"));
if (value != null)
requestParams.append("=").append(
URLEncoder.encode(value, "UTF-8"));
requestParams.append("&");
}
// remove &
requestParams.setLength(requestParams.length() - 1);//
connection.setDoOutput(true);
connection.setRequestProperty("Content-Length", String
.valueOf(requestParams.length()));
OutputStreamWriter writer = new OutputStreamWriter(connection
.getOutputStream());
writer.write(requestParams.toString());
writer.close();
} else if (content != null) {
connection.setDoOutput(true);
// sends POST data
connection.setRequestProperty("Content-Length", String
.valueOf(content.length()));
OutputStreamWriter writer = new OutputStreamWriter(connection
.getOutputStream());
writer.write(content);
writer.close();
}
return connection;
}
static final BASE64Encoder encoder = new BASE64Encoder();
private static Map<String, String> authProperties(final Account account) {
return new HashMap<String, String>() {
{
put("Content-Type", "application/xml; charset=UTF-8");
put("Accept", "application/xml; charset=UTF-8");
put(
"Authorization",
"Basic "
+ encoder
.encode((account.merchantId + ":" + account.merchantKey)
.getBytes()));
}
};
}
This code outputs:
Wed Mar 20 17:57:39 UTC 2013 Google Notification IN 748115991100000-00005-6
Wed Mar 20 17:57:39 UTC 2013 Google Notification IN OK
Instead of "OK" I was expecting to see the entire message.

Categories