AWS SignatureDoesNotMatch - android

Receiving:
SignatureDoesNotMatchThe request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.
With the following:
String associateTag = "example-20";
String awsAccessKeyId = "accessKeyId";
String awsSecretKey = "secretKey";
String endpoint = "webservices.amazon.com";
String uri = "/onca/xml";
String charset = "UTF8";
private String buildQueryString(String keywords) {
Map<String,String> params = new ArrayMap<>();
List<String> pairs = new ArrayList<>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
params.put("Service","AWSECommerceService");
params.put("Operation","ItemSearch");
params.put("AWSAccessKeyId",awsAccessKeyId);
params.put("AssociateTag",associateTag);
params.put("SearchIndex","All");
params.put("ResponseGroup","Images,ItemAttributes");
params.put("Timestamp",sdf.format(new Date()));
params.put("Keywords", keywords);
Map<String, String> treeMap = new TreeMap<>(params);
try {
for (Map.Entry<String, String> param : treeMap.entrySet()) {
pairs.add(URLEncoder.encode(param.getKey(), charset) + "=" + URLEncoder.encode(param.getValue(), charset));
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String queryString = "";
for (int i = 0; i < pairs.size(); i++) {
if (i != 0) {
queryString += "&";
}
queryString += pairs.get(i);
}
Log.d(TAG, "queryString: " + queryString);
return queryString;
}
private String buildSignature(String queryString) {
String hash = "";
try {
String message = "GET\n" + endpoint + "\n" + uri + "\n" + queryString;
Log.d(TAG, "message: " + message);
Mac sha_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(awsSecretKey.getBytes(charset), "HmacSHA256");
sha_HMAC.init(secret_key);
hash = Base64.encodeToString(sha_HMAC.doFinal(message.getBytes(charset)), Base64.DEFAULT);
}
catch (Exception e){
System.out.println("Error");
}
return hash;
}
public void searchProducts(String keywords) {
String requestUrl = "";
String queryString = buildQueryString(keywords);
String signature = buildSignature(queryString);
Log.d(TAG, "signature: " + signature);
try {
requestUrl = "http://" + endpoint + uri + "?" + queryString + "&Signature=" + URLEncoder.encode(signature, charset);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Log.d(TAG, "requestUrl: " + requestUrl);
Ion.with(context)
.load(requestUrl)
.asString()
.setCallback(new FutureCallback<String>() {
#Override
public void onCompleted(Exception e, String result) {
Log.d(TAG, "searchProducts result: " + result);
}
});
}
What could be the problem?

Make sure that your system clock is correct. It will be good idea to sync it using NTP. In the past I have seen signature errors when the time is out of sync.

What I've seen before is that this is normally down to permissions . Check access and secret key is correct and you have adequate permissions.

Changed:
Base64.DEFAULT;
To:
Base64.NO_WRAP;

It's hard to tell from the code. A few things to check:
Make sure the service you want to reach uses SigV2 signing (or query string signing). New services follow version 4 signing standard.
URLEncoder.encode doesn't meet AWS' encoding requirement RFC 3986. You need to apply some fixes to the encoded string.
Query strings should be sorted in a case insensitive way.
Your credentials are indeed correct.
It's a good idea to see how QueryStringSigner.java is implemented in the official SDK , and Signature Version 2 Signing Process.
PS: what's the reason of not using the offical SDK?

In the past when I have had this issue, it was to do with system time. Syncing time with NTP fix issue for me

Can be caused by
A space in the name (or path) of the file.
Characters that are not being properly encoded. Mostly / or +.
Generating new keys that does not contain these characters could help. More info on this issue or this one.

Related

Intent failed with Canceled on SpeechFactory.fromSubscription (LUIS azure, android sdk)

I am getting the following message when trying to analyze an utterance with LUIS using the cognitive service android SDK:
Final result received: Intent failed with Canceled. Did you enter your Language Understanding subscription? WebSocket Upgrade failed with an authentication error (403). Please check the subscription key or the authorization token, and the region name., intent:
I am able to get an utterance evaluation via REST using the same Subscription key , and App ID passed to the SpeechFactory methods.
Moreover, continuous recognition through the Android SDK works as well.
Anyone is getting my same issue ?
source available at https://github.com/Azure-Samples/cognitive-services-speech-sdk/blob/master/samples/java/android/sdkdemo/app/src/main/java/com/microsoft/cognitiveservices/speech/samples/sdkdemo/MainActivity.java .
Code here:
recognizeIntentButton.setOnClickListener(view -> {
final String logTag = "intent";
final ArrayList<String> content = new ArrayList<>();
disableButtons();
clearTextBox();
content.add("");
content.add("");
try {
final SpeechFactory intentFactory = SpeechFactory.fromSubscription(LanguageUnderstandingSubscriptionKey, LanguageUnderstandingServiceRegion);
final IntentRecognizer reco = intentFactory.createIntentRecognizerWithStream(createMicrophoneStream());
LanguageUnderstandingModel intentModel = LanguageUnderstandingModel.fromAppId(LanguageUnderstandingAppId);
for (Map.Entry<String, String> entry : intentIdMap.entrySet()) {
reco.addIntent(entry.getKey(), intentModel, entry.getValue());
}
reco.IntermediateResultReceived.addEventListener((o, intentRecognitionResultEventArgs) -> {
final String s = intentRecognitionResultEventArgs.getResult().getText();
Log.i(logTag, "Intermediate result received: " + s);
content.set(0, s);
setRecognizedText(TextUtils.join(System.lineSeparator(), content));
});
final Future<IntentRecognitionResult> task = reco.recognizeAsync();
setOnTaskCompletedListener(task, result -> {
Log.i(logTag, "Continuous recognition stopped.");
String s = result.getText();
if (result.getReason() != RecognitionStatus.Recognized) {
s = "Intent failed with " + result.getReason() + ". Did you enter your Language Understanding subscription?" + System.lineSeparator() + result.getErrorDetails();
}
String intentId = result.getIntentId();
String intent = "";
if (intentIdMap.containsKey(intentId)) {
intent = intentIdMap.get(intentId);
}
Log.i(logTag, "Final result received: " + s + ", intent: " + intent);
content.set(0, s);
content.set(1, " [intent: " + intent + "]");
setRecognizedText(TextUtils.join(System.lineSeparator(), content));
enableButtons();
});
} catch (Exception ex) {
System.out.println(ex.getMessage());
displayException(ex);
}
});
}

Android Retrofit Request Using AWS Signature Authorization

I'm trying to get data from the API which needs to handle aws-authentication, my question is how can I generate Authorization and X-Amz-Date?
I have to pass 3 parameter as header: Content-Type, Authorization and X-Amz-Date.
As you can find in image:
here is the function that generate Authorization String:
public static String gerateOAuthAWS(Context co) throws Exception {
JodaTimeAndroid.init(co);
DateTimeFormatter fmt = DateTimeFormat.forPattern("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z").withLocale(Locale.US);
String ZONE = "GMT";
DateTime dt = new DateTime();
DateTime dtLondon = dt.withZone(DateTimeZone.forID(ZONE)).plusHours(1);
String formattedDate = dtLondon.toString(fmt);
String oauth = "AWS4-HMAC-SHA256 Credential="+ ACCESS_KEY+"/us-east-1/execute-api/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature="+
getSignatureKey(SECRET_KEY,formattedDate,"us-east-1","execute-api");
return oauth;
}
static byte[] HmacSHA256(String data, byte[] key) throws Exception {
String algorithm="HmacSHA256";
Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(key, algorithm));
return mac.doFinal(data.getBytes("UTF8"));
}
static String getSignatureKey(String key, String dateStamp, String regionName, String serviceName) throws Exception {
byte[] kSecret = ("AWS4" + key).getBytes("UTF8");
byte[] kDate = HmacSHA256(dateStamp, kSecret);
byte[] kRegion = HmacSHA256(regionName, kDate);
byte[] kService = HmacSHA256(serviceName, kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
return Base64.encodeToString(kSigning,Base64.DEFAULT).replaceAll("\n", "");
}
Content-Type is "application/x-www-form-urlencoded"
and generate X-Amz-Date something as: "201805138T120046Z"
then pass them through retrofit methods:
#GET("prod/video")
Call<ArrayList<Video>> getAllVideos(#Header("Content-Type")String content_type,
#Header("X-Amz-Date")String amz_date,
#Header("Authorization")String auth);
the result returns null and I'm sure the issue is related the authorization since it worked before well.
thanks for your helps :)
i always said to my friends why do you use retrofit or valley , if it's seems complicated to you !
instead you can use JSOUP or OKHTTP it's much easier and I realy love JSOUP
an example that you can connect and send you data:
private void fcmIdentity(final String fcmKey) {
new Thread(new Runnable() {
#Override
public void run() {
try {
SSLHelper.enableSSLSocket();
Connection.Response response = Jsoup
.connect(Urls.identity)
.header("Accept", "application/json")
.header("KEY_2", "VALUE_2")
.method(Connection.Method.POST)
.ignoreContentType(true)
.ignoreHttpErrors(true)
.validateTLSCertificates(true)
.followRedirects(true)
.data("fcm", "" + fcmKey)
.data("identity", preferences.getString("FCM_ID", ""))
.execute();
Log.i("fcmIdentity", response.statusCode() + "");
Log.i("fcmIdentity", response.toString());
Log.d("fcmIdentity", response.headers().toString());
Log.i("fcmIdentity", response.body());
} catch (Exception e) {
e.printStackTrace();
if (e instanceof IOException) {
G.toast(getString(R.string.connection_error), true);
}
}
}
}).start();
}
about SSLHelper it help to connect to HTTPS
for more info check my topic https://answers.uncox.com/android/question/13003

Issue with azure cognitive translation services

After getting the following code to work reliably for a month or so, it stopped working reliably a couple of days ago. About half the time it returns a properly translated string and the other half of the time it returns one of the following two messages:
java.io.FileNotFoundException:
https://api.cognitive.microsoft.com/sts/v1.0/issueToken
java.net.UnknownHostException: Unable to resolve host
"api.microsofttranslator.com": No address associated with hostname
The timing of this problem's beginning coincided with the expiration of my free azure cognitive services account however I migrated to a pay-as-you-go account yesterday and the problem continues.
Why is this happening?
static class translateMessageX extends AsyncTask<String, Void, String>
{
//input string array of 3 items
//[0]is the message to be translated
//[1]is the from language i.e. "english"
//[2]is the to language i.e. "spanish"
//[3]"echo" or "received"
String retString;
String inString = null;
String messageType = null;
String URLHolder = ""; //hold the URL here while we are translating the text
#Override
protected String doInBackground(String... params)
{
inString = params[0];
String from = params[1];
String to = params[2];
messageType = params[3];
int urlStart = inString.indexOf("http");
if (!(urlStart == -1))
{
URLHolder = inString.substring(urlStart);
inString = inString.substring(0, urlStart -1);
}
else
{
URLHolder = "";
}
Integer mesChars = params[0].length();
Integer tCharsLeft = GlobalStuff.getTranslationsFromSP();
if (tCharsLeft > 0)
{
if (tCharsLeft < mesChars) //we charge for both 'echo' and 'received' translations
{
GlobalStuff.updateTranslationInventory(tCharsLeft * -1);
}
else
{
GlobalStuff.updateTranslationInventory(mesChars * -1);
}
GlobalStuff.notifyListeners(this, "#uui", "notused", "notused" );
try
{
Language fromLang = GlobalStuff.getLang(from);
Language toLang = GlobalStuff.getLang(to);
//retString = Translate.execute(inString, fromLang, toLang);
//String debugstr = "look at retStr";
String authenticationUrl = "https://api.cognitive.microsoft.com/sts/v1.0/issueToken";
HttpsURLConnection authConn = (HttpsURLConnection) new URL(authenticationUrl).openConnection();
authConn.setRequestMethod("POST");
authConn.setDoOutput(true);
authConn.setRequestProperty("Ocp-Apim-Subscription-Key", GlobalStuff.translateKey);
IOUtils.write("", authConn.getOutputStream(), "UTF-8");
String token = IOUtils.toString(authConn.getInputStream(), "UTF-8");
System.out.println(token);
// Using the access token to build the appid for the request url
String appId = URLEncoder.encode("Bearer "+token, "UTF-8");
String text = URLEncoder.encode(inString, "UTF-8");
String translatorTextApiUrl = String.format("https://api.microsofttranslator.com/v2/http.svc/Translate?appid=%s&text=%s&from=%s&to=%s", appId, text, fromLang, toLang);
HttpsURLConnection translateConn = (HttpsURLConnection) new URL(translatorTextApiUrl).openConnection();
translateConn.setRequestMethod("GET");
translateConn.setRequestProperty("Accept", "application/xml");
retString = IOUtils.toString(translateConn.getInputStream(), "UTF-8");
String debug = "look at retString";
}
catch (Exception e)
{
retString = e.toString();
}
}
else
{
retString = "OUT OF TRANSLATION CREDITS - " + inString;
}
return retString;
}
#Override
protected void onPostExecute(String result)
{
//rest of logic should be here??
String debug = "look at result";
String answer = extractTranslation(result);
.. . . .
Host not found looks like a simple connectivity error. These hosts do exist.
You can void the call to the token service by passing the key in the call to api.microsofttranslator.com directly:
https://cognitive.uservoice.com/knowledgebase/articles/1815385-api-translator-text-speech-using-the-api-key
That fixes one of the host not found problems, but not the other.
I would recommend though to not embed the key in the client application. It is safer to call the translator service from your own proxy service, where the proxy is able to safely identify your client as your client.

Receiving HTTP Bad Request when calling a .NET Webservice using SOAP only from Android

EDIT: For anyone using the same method as I am (building a Soap.java file and using sockets), stop! Use the kSoap-Android library as it automates pretty much everything and has little to no bugs like this.
I'm creating an android application where you can quicly check your timeroster for the classes in the next week. To do this, I use 2 servers:
The server that hosts the timerosters for every student in HTML form
(I do not manage this server)
The server that hosts my webservice (which I manage). It's a .NET webservice and is finished.
The android device connects to the Webservice-server and sends the function "login" with parameters "username": "usernameX" and "password": "passwordX".
The webservice-server then retrieves the login-page from the timeroster-server with the "username" and "password" fields as post parameters. The timeroster-server will respond with a html-page, that will be parsed by the webservice-server and depending on it's contents, the webservice will respond to the Android device with either "Success" or "AuthenticationFail".
To do this, I use the SOAP-architecture on my Android Device, which sends a SOAP-enveloppe to the webservice. The webservice then creates HttpWebRequest- and HttpWebResponse-objects that retrieve the html-source from the timeroster-server.
This approach has worked for me so far using the Socket-class and I have been able to login to the timeroster-server, using my android device. However, the problem lies with another function.
My webservice supports 2 functions: "login" and "GetList". I can perfectly call the "login"-function from my android and receive a "Success"-string. But when I call the "GetList"-function (that takes 3 parameters: a username, a password and a listType), it returns a 400 Bad Request.
When I send the exact same http-request to the webservice-server with Fiddler, I don't receive a 400 Bad Request error.
Why am I getting a 400 error ONLY when I use my android-device? Is there any way I can fix this?
Images for visualisation:
Login-function (using Android):
Login-function (using Fiddler):
GetList-function (using Android) - ERROR HERE:
GetList-function (using Fiddler):
Code used in Android to send data to socket:
public String sendRequest()
{
String s = "";
Socket socket = null;
try
{
socket = new Socket(Server, Port);
}
catch(Exception ex)
{
return "UnknownClientError";
}
String stringbuffer = "";
try
{
socket.getOutputStream();
boolean flag = true;
PrintWriterSuper printwriter = new PrintWriterSuper(socket.getOutputStream());
Scanner scanner = new Scanner(socket.getInputStream());
int i = 295 + MethodName.length() * 2 + XmlNamespace.length();
for(int j = 0; j < ParamNames.size(); j++)
{
String s1 = (String)ParamNames.elementAt(j);
String s2 = (String)ParamData.elementAt(j);
i += s1.length();
i += s2.length();
}
printwriter.println("POST " + WebServicePath + " HTTP/1.1");
printwriter.println("Host: " + Server);
printwriter.println("Content-Type: text/xml; charset=utf-8");
printwriter.println("Content-Length: " + String.valueOf(i));
if(!SoapAction.equals(""))
printwriter.println("SOAPAction: \"" + SoapAction + "\"");
printwriter.println("Connection: Close");
printwriter.println();
printwriter.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
printwriter.println("<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">");
printwriter.println("<soap:Body>");
printwriter.println("<" + MethodName + " xmlns=\"" + XmlNamespace + "\">");
for(int k = 0; k < ParamNames.size(); k++)
{
String s3 = (String)ParamNames.elementAt(k);
String s5 = (String)ParamData.elementAt(k);
printwriter.println("<" + s3 + ">" + s5 + "</" + s3 + ">");
}
printwriter.println("</" + MethodName + ">");
printwriter.println("</soap:Body>");
printwriter.println("</soap:Envelope>");
printwriter.println();
boolean flag1 = false;
int byte0 = 10;
long l = System.currentTimeMillis();
String s4;
while(scanner.hasNextLine() && !flag1)
{
s4 = scanner.nextLine();
stringbuffer += s4 + "\n";
if(System.currentTimeMillis() - l > (long)(1000 * byte0))
flag1 = true;
}
scanner.close();
if(!flag1)
{
String requestString = printwriter.toString();
String s6 = MethodName + "Result";
int i1 = stringbuffer.toString().indexOf("<" + s6 + ">") + s6.length() + 2;
int j1 = stringbuffer.toString().indexOf("</" + s6 + ">");
s = stringbuffer.substring(i1, j1);
} else
{
s = "Error: timed out by client";
}
try{
socket.close();
}catch(Exception ex){}
}
catch (Exception e) {
s = e.getMessage() + "\n" + e.getStackTrace().toString();
}
return s;
}

gData: get account(self contact) first and last name

I want to get first and last name of a google account using gdata library. I have the auth token(I take it from android device - send it to my java servlet- then should add an insert into a mysql db with first, last, display name and provider_uid(provider_uid is the form of https://www.google.com/accounts/o8/id?id=AItOawmyn...)).
I used Contacts feed like this(without success):
public String tryGoogleAuthentication(String auth_token){
ContactsService contactsService = new ContactsService("...");
contactsService.setUserToken(auth_token);
//contactsService.setAuthSubToken(auth_token);
ContactFeed feed = null;
try {
feed = contactsService.getFeed(new URL("https://www.google.com/m8/feeds/contacts/" + "someEmail#gmail.com" + "/full?max-results=10000"), ContactFeed.class);
} catch (IOException e) {
e.printStackTrace();
return CONST.GOOGLE_AUTH_INVALID_TOKEN;
} catch (ServiceException e) {
e.printStackTrace();
return CONST.GOOGLE_AUTH_INVALID_TOKEN;
} catch (NullPointerException e) {
e.printStackTrace();
return CONST.GOOGLE_AUTH_INVALID_TOKEN;
}
if (feed == null)
return "";
String externalId = feed.getId();
Person person = feed.getAuthors().get(0);
String email = person.getEmail();
String name = person.getName();
String nameLang = person.getNameLang();
String extensionLocalName = person.getExtensionLocalName();
String uri = person.getUri();
System.out.println("externalId: " + externalId);
System.out.println("email: " + email);
System.out.println("name: " + name);
System.out.println("nameLang: " + nameLang);
System.out.println("extension local name: " + extensionLocalName);
System.out.println("URI: " + uri);
System.out.println(feed.getSelf().getEntries().get(0).getTitle().getPlainText());
return CONST.STATUS_OK;
}
Also,
System.out.println("ID: " + feed.getSelf().getEntries().get(0).getId());
will output:
ID: http://www.google.com/m8/feeds/contacts/someEmail%40gmail.com/base/c....
but I want something like this:
https://www.google.com/accounts/o8/id?id=AItOawmyn...
I need this to insert into an existing data base.
Please note that I want the info only for the account, not for it's contacts.
Thanks,
Alex
Please see this answer from google groups for resolution. The problem is that I cannot access user profile with the auth_token taken from the android because it's a Client Login token, and Client Login does not have a scope for accessing user's profile. I integrated OAUTH login in android like this and with the token returned, I can access user's profile.
Alex.

Categories

Resources