Android Retrofit Parameter Encryption and Decryption SHA256 - android

In my application I am using webservices using Retrofit. I have to Encrypt Field (parameter) in Request and Decrypt it on PHP Server.
I have to Encrypt and Decrypt version parameter.
Here is my RetroApi.java
public interface RetroApi {
#FormUrlEncoded
#POST("index.php/api/check-version")
Call<String> getCheckVersion(#Field("version") String version, #Field("app") String app);
}
Creating instance of RetroApi.java
RetroApi retroApi;
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient httpClient = new OkHttpClient.Builder().addInterceptor(logging).build();
Gson gson = new GsonBuilder().setLenient().create();
Retrofit retrofit = new Retrofit.Builder().baseUrl(RetroApp.BASE_URL).addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson)).client(httpClient).build();
retroApi = retrofit.create(RetroApi.class);
Here is the Webservice call
Call<String> getResult = retroApi.getCheckVersion(Constants.SP_APP_VERSION, Constants.SP_APP_NAME);
getResult.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
}
#Override
public void onFailure(Call<String> call, Throwable t) {
t.printStackTrace();
}
});
Please assist me to accomplish this.

Basically what you can do is simply encrypt your parameters with the standard Android tools. Here is a simple example of how to do it from which you can start.
Basically is everything you need from an Android perspective, except for a way to store a secret - for that, you can use EncryptedSharedPreferences
After that, you can send those encrypted strings as your API arguments.
you can also create a centralized encryption factory for your requests like this:
OkHttpClient okHttpClient = SomeOkHttpImplementation();
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.callFactory(new Call.Factory() {
#Override
public Call newCall(Request request) {
Request encryptedRequest = someFunctionToEncryptRequestOrItsArgs(request);
return okHttpClient.newCall(encryptedRequest);
}
})
.baseUrl(sBaseUrl)
.build();
Or with custom Interceptor as shown here
The problem is that you will have to find a way to decrypt them on PHP side. I am not a PHP expert but I'm sure there are ways to do that.
For example here and here you can find the Java and PHP implementation of the similar ciphers.

Related

Redundancy requests with Retrofit

I need to build in redundancy into my app where if a server is down it will try a backup redundancy server upon failure of the first request.
Aside from doing
Call<LoginResult> loginCall = apiInterface.login(....);
loginCall.enqueue(new Callback<LoginResult>() {
#Override
public void onResponse(Call<LoginResult> call, Response<LoginResult> response) {
if(response.isSuccessful){
//do normal stuff
}else{
//try second url
}
}
#Override
public void onFailure(Call<LoginResult> call, Throwable t) {
//Try second url
}
}
I don't see a clean way to do this. Creating another retrofit request inside the error block or non-successful block would add a lot of code complexity.
Is there an easier way to handle this in Retrofit or OkHttp?
I have here an option with OkHttp interceptors. The idea is that if the request fails you replace the url and execute the request again.
The following is an api client to the OpenWeather Api. If you want to try out the example you'll need to sign up and get an api key. It should be free so I hope this is ok.
I'll post here the full code and then walk you through it.
private final static String API_KEY = "<API KEY HERE>";
private static class Weather {
#SerializedName("id")
#Expose
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
private static final String GOOD_HOST = "api.openweathermap.org";
private static final String BAD_ENDPOINT = "https://api.aaaaaaaaaaa.org";
interface WeatherApiClient {
#GET("/data/2.5/weather")
Call<Weather> get(
#Query("q") String query,
#Query("appid") String apiKey);
}
private static class ReplicaServerInterceptor implements Interceptor {
#Override public okhttp3.Response intercept(Chain chain)
throws IOException {
try {
okhttp3.Response response = chain.proceed(chain.request());
return response;
} catch (IOException e) {
// Let's build a new request based on the old one
Request failedRequest = chain.request();
HttpUrl replicaUrl = failedRequest.url()
.newBuilder()
.host(GOOD_HOST)
.build();
okhttp3.Request request = failedRequest.newBuilder()
.url(replicaUrl)
.build();
return chain.proceed(request);
}
}
}
public static void main(String[] args) {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new ReplicaServerInterceptor())
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BAD_ENDPOINT)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
WeatherApiClient weatherApiClient =
retrofit.create(WeatherApiClient.class);
weatherApiClient.get("Lisbon,pt", API_KEY)
.enqueue(new Callback<Weather>() {
#Override public void onResponse(
Call<Weather> call,
Response<Weather> response) {
// This might be null sometimes because
// the api is not super reliable, but I didn't
// add code for this
System.out.println(response.body().id);
}
#Override public void onFailure(
Call<Weather> call,
Throwable t) {
t.printStackTrace();
}
});
}
To be able to fake a server failure I prepare retrofit to call a non existent url - BAD_ENDPOINT. This will trigger the catch clause inside the interceptor.
The interceptor itself is obviously the key thing here. It intercepts every call from retrofit and executes the call. If the call throws an error because the server is down, then it will raise an IOException. Here I copy the request being made and change the url.
Changing the url means changing the host:
HttpUrl replicaUrl = failedRequest.url()
.newBuilder()
.host(GOOD_HOST)
.build();
If you just call url(<some url>) in the request builder, everything gets replaced. Query parameters, protocol, etc. This way, we preserve these from the original request.
(OkHttp offers newBuilder methods which copy the data from the current object and let you just edit what you want. Just like kotlin's copy. This is why we can simply change the url and be safe that everything else remains the same)
I then build the new request with the url and execute it:
okhttp3.Request request = failedRequest.newBuilder()
.url(replicaUrl)
.build();
return chain.proceed(request);
Interceptors work on a chain pattern, that's why calling proceed will call the next interceptor on the chain. In this case we just need to actually make the request.
I didn't bother copying the entire weather resource, so I'm just using the id. I think that's not the main focus of the question
As I said before, this is meant as a proof of concept. As you noticed I'm try-catching the execution of the call, but in your case it might be that the call actually succeeds executing, but the http response is not a 2XX. The okhttp response objects have methods that help you checking if the response was successful namely - isSuccessful(). The idea is the same - Build a new request and carry on if it's not successful.
I didn't bother treating any errors from the replica in this example. They'll just be forwarded to the retrofit client.
As you can see retrofit has no clue where the response is coming from. This might or not be good. Also, the response body needs to be the same from both servers, which I guess it's the case.
Lastly I'm sorry for the awkward okhttp3.Response name spacing there. I was using both Response from retrofit and okhttp and hence had to avoid the name clash.
Versions used for this example: Retrofit 2.3.0 and the okhttp bundled with that

getting a List back from a server using retrofit

I'm new to using Retrofit for networking in Android. So far I have successfully written code that sends a String to a PHP server. The server is configured to take my string, which contains a system ID and a password ("systemid=id&password=password"). It may seem odd to append parameters via a String like this but this is the way I have to do it for this project. Anyway, on the server if the id and password sent to it match certain criteria, it's sends back a list of file locations. So far, I am getting the proper response back from the server. In other words 'response.getMessage()' returns 'OK.' But how can I get the list that the server is trying to send back? Here is my implementation so far.
Interface:
public interface ExampleClient {
#POST("login/fake_name.php")
Call<String> sendStringToServer(#Body String string);
}
Here is my code within the Activity:
private void sendRequestToServer(String str) {
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl("http://www.example.com/")
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
PromoterKiosksClient client = retrofit.create(ExampleClient.class);
Call<List<String>> call = client.sendStringToServer(str);
call.enqueue(new Callback<Void>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
Log.d(TAG, response.toString());
Log.d(TAG, response.message());
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Toast.makeText(AdminActivity.this, "you suck! keep trying", Toast.LENGTH_SHORT).show();
}
});
}
Is there somewhere in onResponse() that I can do this or do I need a totally different implementation? Thanks in advance
You are looking for response.body() ? Do you get a JSON response from the server? If so you can use POJO to convert your response. Would you be able to share your response here? You can use POJO to convert your result and name it Result.java and set that as your return type.
private void sendRequestToServer(String str) {
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl("http://www.example.com/")
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
PromoterKiosksClient client = retrofit.create(ExampleClient.class);
Call<Result> call = client.sendStringToServer(str);
call.enqueue(new Callback<Void>() {
#Override
public void onResponse(Call<Result> call, Response<Result> response) {
Log.d(TAG, response.toString());
Log.d(TAG, response.message());
response.body(); // have your all data
}
#Override
public void onFailure(Call<Result> call, Throwable t) {
Toast.makeText(AdminActivity.this, "you suck! keep trying", Toast.LENGTH_SHORT).show();
}
});
}
Call<User> vs. Call<ResponseBody> vs. Call<Void>
Most endpoints will be declared with a specific return type, like Call. In this case Retrofit will always take the response body and try to convert it to Java objects. Of course, this takes time, memory and processing power.
If you can live without the mapped Java objects, you should choose Call. This makes the raw response payload available to you, but skips the mapping to Java objects. With this option, you still have the chance to analyze the payload (e.g., JSON).
The most efficient way is Call because it not only skips the conversion to Java objects, it also ignores the response body payload. When the response body is very large (e.g., a large JSON or an image), you can save a little bit of extra time and battery consumption by using Call. Of course, this results in the body() method of your response object returning null.
A good example to look at Consuming an API with retrofit and RXJava

Adding fields to URL using Retrofit

I am building an Android APP where I use the Internet Game Database API through Mashape market place. I am using Retrofit for the get requests and getting data from the API requires an API key.
I got it to work but the API only return game ids and I want the game names and other information, but I am not sure how to add the fields. This is how Mashape query it:
HttpResponse<String> response = Unirest.get("https://igdbcom-internet-game-database-v1.p.mashape.com/games/?fields=name%2Crelease_dates")
.header("X-Mashape-Key", "API KEY HERE")
.header("Accept", "application/json")
.asString();
and this is my Retrofit Interface
public interface GamesAPIService {
#GET("/games/")
Call<List<GamesResponse>> gameList(#Query("mashape-key") String apikey);
}
I tried to use this
#GET("/games/?fields=name,release_dates")
But no luck, I also tried with #Field but didn't work either. Any ideas? Thanks.
Edit: Just to clarify when I add the "?fields=name,release_dates" I get 401 Unauthorized Error.
Firstly I think you need to add mashape key to all your request.
OkHttpClient httpClient = new OkHttpClient();
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder()
.addHeader("X-Mashape-Key", "API_KEY_HERE")
.addHeader("Accept", "application/json")
.build();
return chain.proceed(request);
}
});
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://igdbcom-internet-game-database-v1.p.mashape.com")
.client(httpClient)
.build();
And then this is information query.
public interface GamesAPIService {
#GET("/games")
Call<List<GamesResponse>> gameList(#Query("fields") String value);
}
And last thing for calling.
GamesAPIService gamesAPIService = retrofit.create(GamesAPIService.class);
Call<List<GamesResponse>> call = gamesAPIService.gameList("name,release_dates");
if (call!=null){
call.enqueue(new Callback<List<GamesResponse>>() {
#Override
public void onResponse(Call<List<GamesResponse>> call, Response<List<GamesResponse>> response) {
// handle success
}
#Override
public void onFailure(Throwable t) {
// handle failure
}
});
}

Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $

What is this error ? How can I fix this? My app is running but can't load data. And this is my Error: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $
This is my fragment :
public class news extends Fragment {
private RecyclerView recyclerView;
private ArrayList<Deatails> data;
private DataAdapter adapter;
private View myFragmentView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
myFragmentView = inflater.inflate(R.layout.news, container, false);
initViews();
return myFragmentView;
}
private void initViews() {
recyclerView = (RecyclerView) myFragmentView.findViewById(R.id.card_recycler_view);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(layoutManager);
data = new ArrayList<Deatails>();
adapter = new DataAdapter(getActivity(), data);
recyclerView.setAdapter(adapter);
new Thread()
{
public void run()
{
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
loadJSON();
}
});
}
}
.start();
}
private void loadJSON() {
if (isNetworkConnected()){
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.retryOnConnectionFailure(true)
.connectTimeout(15, TimeUnit.SECONDS)
.build();
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.memaraneha.ir/")
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
RequestInterface request = retrofit.create(RequestInterface.class);
Call<JSONResponse> call = request.getJSON();
final ProgressDialog progressDialog = new ProgressDialog(getActivity());
progressDialog.show();
call.enqueue(new Callback<JSONResponse>() {
#Override
public void onResponse(Call<JSONResponse> call, Response<JSONResponse> response) {
progressDialog.dismiss();
JSONResponse jsonResponse = response.body();
data.addAll(Arrays.asList(jsonResponse.getAndroid()));
adapter.notifyDataSetChanged();
}
#Override
public void onFailure(Call<JSONResponse> call, Throwable t) {
progressDialog.dismiss();
Log.d("Error", t.getMessage());
}
});
}
else {
Toast.makeText(getActivity().getApplicationContext(), "Internet is disconnected", Toast.LENGTH_LONG).show();}
}
private boolean isNetworkConnected() {
ConnectivityManager cm = (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
if (ni == null) {
// There are no active networks.
return false;
} else
return true;
}
}
RequestInterface :
public interface RequestInterface {
#GET("Erfan/news.php")
Call<JSONResponse> getJSON();
}
UPDATE (read below text and find your problem)
most of the time, this error isn't about your json but it could be a
incorrect http request such as a missing or a incorrect header, first check your request with postman to verify the servers response and servers response headers. if nothing is wrong then the error mostly came from your programmed http request, also it could because the servers response is not json (in some cases response could be html).
This is a well-known issue and based on this answer you could add setLenient:
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
Now, if you add this to your retrofit, it gives you another error:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
This is another well-known error you can find answer here (this error means that your server response is not well-formatted); So change server response to return something:
{
android:[
{ ver:"1.5", name:"Cupcace", api:"Api Level 3" }
...
]
}
For better comprehension, compare your response with Github api.
Suggestion: to find out what's going on with your request/response add HttpLoggingInterceptor in your retrofit.
Based on this answer your ServiceHelper would be:
private ServiceHelper() {
httpClient = new OkHttpClient.Builder();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient.interceptors().add(interceptor);
Retrofit retrofit = createAdapter().build();
service = retrofit.create(IService.class);
}
Also don't forget to add:
compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
Using Moshi:
When building your Retrofit Service add .asLenient() to your MoshiConverterFactory. You don't need a ScalarsConverter. It should look something like this:
return Retrofit.Builder()
.client(okHttpClient)
.baseUrl(ENDPOINT)
.addConverterFactory(MoshiConverterFactory.create().asLenient())
.build()
.create(UserService::class.java)
Also this issue occurres when the response contenttype is not application/json. In my case response contenttype was text/html and i faced this problem. I changed it to application/json then it worked.
There was an error in understanding of return Type
Just add Header and it will solve your problem
#Headers("Content-Type: application/json")
I had same issue along with https://stackoverflow.com/a/57245058/8968137 and both solved after fixing the google-services.json
Im my case I forgot add #Headers("Accept: application/json") to Retrofit and have redirect on http page, with html in body razer json
I have faced this problem and I made research and didn't get anything, so I was trying and finally, I knew the cause of this problem.
the problem on the API, make sure you have a good variable name
I used $start_date and it caused the problem, so I try $startdate and it works!
as well make sure you send all parameter that declare on API, for example,
$startdate = $_POST['startdate'];
$enddate = $_POST['enddate'];
you have to pass this two variable from the retrofit.
as well if you use date on SQL statement, try to put it inside ''
like '2017-07-24'
I hope it helps you.
In my case ; what solved my issue was.....
You may had json like this, the keys without " double quotations....
{ name: "test", phone: "2324234" }
So try any online Json Validator to make sure you have right syntax...
Json Validator Online
I solved this problem very easily after finding out this happens when you aren't outputting a proper JSON object, I simply used the echo json_encode($arrayName); instead of print_r($arrayName); With my php api.
Every programming language or at least most programming languages should have their own version of the json_encode() and json_decode() functions.
This issue started occurring for me all of a sudden, so I was sure, there could be some other reason. On digging deep, it was a simple issue where I used http in the BaseUrl of Retrofit instead of https. So changing it to https solved the issue for me.
Also worth checking is if there are any errors in the return type of your interface methods. I could reproduce this issue by having an unintended return type like Call<Call<ResponseBody>>
Sometimes the error is displayed because the Relative link cannot find the data in the Base URL;
I experienced the same issue and counterchecking that there is no error between the relative URL and base URL worked
I solve this issue after spending around 3 hrs. I have used postman for Api testing.
there was mistake in json output. Please see images for more clarification (I got this error when output preview change from enter image description here JSON to HTML)

Retrofit 2.0 OnFailure - Raw Response

I'm using retrofit to call a web service and retrofit is throwing a failure, the the message from the 'Throwable` is giving me
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
I'm assuming that this is because the .Net web service is throwing an error and not returning JSON. But to prove this I need to be able to see the raw response in the onFailure. Is there anyway I can do this?
this is the code I'm using
public void userLoginRequestEvent(final AuthenticateUserEvent event) {
Call call = sApi.login(event.getUsername(), event.getPassword(), OS_TYPE, DeviceInfoUtils.getDeviceName());
call.enqueue(new Callback<LoggedInUser>() {
#Override
public void onResponse(Response<LoggedInUser> response, Retrofit retrofit) {
// response.isSuccess() is true if the response code is 2xx
if (response.isSuccess()) {
LoggedInUser user = response.body();
AppBus.getInstance()
.post(new UserIsAuthenticatedEvent(user, event.getUsername(),
event.getPassword()));
} else {
int statusCode = response.code();
// handle request errors yourself
}
}
#Override
public void onFailure(Throwable t) {
// handle execution failures like no internet connectivity
Log.d("ERROR", t.getMessage());
}
});
You can use the log interceptor that exists in the okhttp-logging-interceptor.
A good example can be found in Logging with Retrofit 2 as well.
Your server answer is just a string, not an object. Use an Interceptor to see your received response.
Add incerceptor dependency
compile 'com.squareup.okhttp3:logging-interceptor:3.4.0'
and then add it to your custom OkHttp client.
OKHttp client = ....
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
client.interceptors().add(interceptor);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("url")
.client(client) // add custom OkHttp client
You can check for BASIC, HEADERS and BODY. In your case you check for BODY to see body that you send and what server is sending as response body.

Categories

Resources