Im trying to use Retrofit to make a call to this api: https://api.wheretheiss.at/v1/satellites/25544
Here is the code where the connection is being made:
retrieveButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASEURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
ISSApi api = retrofit.create(ISSApi.class);
Call<ISS> ISS = api.getLocation();
ISS.enqueue(new Callback<ISS>() {
#Override
public void onResponse(Response<ISS> response) {
System.out.println("booya");
}
#Override
public void onFailure(Throwable t) {
System.out.println("failure");
System.out.println(t.getMessage().toString());
t.printStackTrace();
}
});
So it seems to at least be able to find the API as the onFailure callback is being fired, but im getting this error:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
Some notes:
I've used OkHTTP before with an API that had SSL enabled and I never had to deal with certificates. Also, something i noticed, if i go to the API endpoint on my Nexus 5, i get this: http://i.imgur.com/he8821o.png. Perhaps thats related to the issue?
Any help appreciated. Using Retrofit 2.0.0.
The server you are trying to connect to does not have a valid SSL certificate for Android.
This is weird, because I can access it in my computer with no issues, but apparently Android is missing the CA that signed this certificate.
What you can do:
Manually add the certificate in your device -- not good if you are deploying it to many people.
Create a custom SslSocketFactory that accepts this server certificate.
The second approach is the best, but it brings a lot of problems to the table.
For example, if the server changes the certificate (they normally do from time to time), you will get these same errors everytime until you update your app.
An alternative is finding who is the certification authority of this server certificate and add it instead (it would work with #1 and #2).
Check this answer to learn how to override certificate validation using OkHttp.
EDIT: the certificate issuer seems to be RapidSSL (info here).
Related
I want to build an android client that can interact with the WooCommerce based site using the Rest Api provided by WooCommerce
This is my android code. I am using OkHttp library for networking.
public class MainActivity extends AppCompatActivity {
OkHttpClient client;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String cred = Credentials.basic("ck_...","cs_...");
OkHttpClient client = new OkHttpClient
.Builder()
.build();
Request req = new Request
.Builder()
.addHeader("Authorization",cred)
.url("http://10.0.2.2:8080/woocom/wp-json/wc/v2/products")
.build();
client.newCall(req).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.d("api resp", "onFailure: ");
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
Log.d("Api resp", "onResponse: "+response.body().string());
}
});
}
}
This is the error log after running the app
com.example.android.woocommerceapiintegration D/Api resp: onResponse: {"code":"woocommerce_rest_cannot_view","message":"Sorry, you cannot list resources.","data":{"status":401}}
What am I doing wrong here. I tried out the NodeJS client provided by WooCommerce which works fine.
Also I cannot access the rest api via curl according to the command given in the docs
Can someone tell me what I am doing wrong ?
Update: The selected answer is what needs to be done in production environments, and is the technically right thing to do. If you want to avoid the hassle of OAuth while on a development server, I have answered it seperately.
The 401 code indicates an authorization issue with your connection. Specifically your issue is caused by your usage of BasicAuth with an HTTP connection.
The WooCommerce REST API Documentation indicates that BasicAuth is only supported for HTTPS connections, and HTTP connections must use OAuth 1.0a "one-Legged" authentication to "ensure" your credentials aren't intercepted by an attacker.
It's important to note that even with OAuth 1.0a, an HTTP connection will never be truly secure and it's highly recommended to switch your app over to a secure HTTPS connection.
Meanwhile, in order to get your code working, you'll have to implement the OAuth 1.0a authentication method for your Android app.
You can find a complete set of instructions and a complete project example of OAuth 1.0a implementation for Android here. The GitHub Page has an excellent guide with step-by-step instructions on using the library linked above. Just make sure that when using the code provided you make sure to account for the fact that you're using OKHttp. Luckily, the author has commented the code in the instructions very well and makes a note of changes to make when using something like OkHttp.
You could use Retrofit and simply write an Interceptor which takes care of the 'nitty-gritty' part as detailed in the documentation here.
You could also follow the step by step guide detailed in the WooCommerce Documentation here to generate your OAuth signature and finally encodedUrl and then pass it to your http client. This processs involves: (see the Documentation for detailed specs for each section)
Collecting the request method and URL
Collecting all oauth_* parameters and encoding them into a single string using percent encoding and correct ordering. For Example (taken from WooCommerce Docs):
oauth_consumer_key=abc123&oauth_signature_method=HMAC-SHA1
Create the base string for the signature by joining the values from 1 and 2. For example: (once again from Docs):
GET&http%3A%2F%2Fwww.example.com%2Fwp-json%2Fwc%2Fv2%2Forders&oauth_consumer_key%3Dabc123%26oauth_signature_method%3DHMAC-SHA1
Finally generate the signature using HMAC-SHA1 (HMAC-SHA256 is also supported).
Personally I would recommend either the first or second approach. "No need to reinvent the wheel".
EDIT:
You can look at this question which discusses how you can use self-signed certificates in a local dev environment with OkHttp.
Thanks akseli for answering my question.I've also awarded you the bounty and thanks for adding to my knowledge. Despite everything, I've found a simple solution to this problem.
My concern was that while development, We generally don't have an https based server and hence have to go through that tedious OAuth based process which won't be used is production anyway as the server we will probably use will be https enabled.
So, to use basic authentication while on an http dev server, you need to go to [your wordpress directory]/wp-content/woocommerce/includes/api. Find out class-wc-rest-authentication.php. This class handles the api authentication. Find the authenticate function which looks like this
public function authenticate( $user_id ) {
// Do not authenticate twice and check if is a request to our endpoint in the WP REST API.
if ( ! empty( $user_id ) || ! $this->is_request_to_rest_api() ) {
return $user_id;
}
if ( is_ssl() ) {
return $this->perform_basic_authentication();
}
return $this->perform_oauth_authentication();
}
comment out the condition is_ssl and simply return $this->perform_basic_authentication(), so that in any case, basic authentication is performed.
Note:This is a hack just to avoid the hassle of OAuth authentication while in dev environment and is not at all recomended in production.
I have 2 devices in the wild that are not able to connect to my TLS v1.2 endpoint. All others seem able to, including browsers, PostMan and iOS devices.
The devices are running Android 5 & 7 (so there should not be a problem with the TLS v1.2 support).
Note: This is not a self-signed certificate. It is signed by Amazon.
Immediate thoughts were:
Android fragmentation - perhaps the devices (one is a Kindle Fire 7)
are not including the correct certificates into the OS. It wouldn't
be the first time that a device manufacturer made an odd decision
that breaks functionality.
API is being accessed via a proxy, and there actually is a Man-In-The-Middle, correctly being detected.
Fixing (1) means bundling our certificate, and leads to the usual problems when our cert expires.
I would prefer to get the user to install a debug build that confirms whether (1) or (2) is the problem. Such build would inspect the SSL Certificate provided by the server/proxy, and log that back to me.
Web Frameworks:
Retrofit v2.3.0
OkHttp v3.9.1
Question:
How do I inspect the information of the SSL Certificate that the device is seeing when hitting my endpoint?
Update per comment from #SangeetSuresh:
It turns out there are 2 different exceptions being thrown.
The Kindle Fire 7" Tablet (KFAUWI, OS 5.1.1) is throwing the one I have started to investigate, which this question is meant to have focused on. i.e. basic SSL failure.
java.security.cert.CertPathValidatorException:
Trust anchor for certification path not found.
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:331)
at com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:232)
at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:114)
The LG device (LG-SP200, OS 7.1.2) is having the connection closed by the peer, which should be addressed under a new question if not solved here:
javax.net.ssl.SSLHandshakeException:
Connection closed by peer
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(NativeCrypto.java)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:360)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:299)
Robby Cornelissen provided the basic answer in a comment referencing the OkHttp Response:
the information should be available from
response.handshake().peerCertificates().
A simple Interceptor was implemented to inspect the certificates, given a valid handshake:
private static class SslCertificateLogger implements Interceptor {
public static final String TAG = "SSL";
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response;
try {
response = chain.proceed(request);
} catch (Exception e) {
Log.d(TAG, "<-- HTTP FAILED: " + e);
throw e;
}
Handshake handshake = response.handshake();
if (handshake == null) {
Log.d(TAG, "no handshake");
return response;
}
Log.d(TAG, "handshake success");
List<Certificate> certificates = handshake.peerCertificates();
if (certificates == null) {
Log.d(TAG, "no peer certificates");
return response;
}
String s;
for (Certificate certificate : certificates) {
s = certificate.toString();
Log.d(TAG, s);
}
return response;
}
}
This gets added to the OkHttpClient as per normal:
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.addInterceptor(new SslCertificateLogger())
.build();
A similar solution was proposed by Sangeet Suresh that references the Retrofit Response object:
response?.raw()?.handshake() I think this will help you
Here the important information being the fact that Retrofit gives access to the raw OkHttp response in this manner.
This would not be used in an Interceptor but rather at a higher level, in the actual Retrofit handling code, after getting a Retrofit Response<> from the API.
Converting his Kotlin solution back to Java could yield something like this:
okhttp3.Response raw = httpResponse.raw();
if (raw != null) {
Handshake handshake = raw.handshake();
if (handshake != null) {
List<Certificate> certificates = handshake.peerCertificates();
if (certificates != null) {
for (Certificate certificate : certificates) {
Log.d(TAG, certificate.toString());
}
}
}
}
Both solutions work fine, provided the handshake() is not null i.e. when the handshake succeeds.
Given that this is an investigation into failed handshakes, a further step was required to "trust all certificates" (NB debug builds only!).
This has been documented many times - here is one such version:
Unsafe SSL Client (do not do this in production)
i need to get echo value i.e (yes or no) from php file to myresulttextview but unable to retrieve it says error :"com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. "
val barcode = data.getParcelableExtra<Barcode>(BarcodeCaptureActivity.BarcodeObject)
val p = barcode.cornerPoints
var m = barcode.displayValue.toString().trim()
val jsonobj=JSONObject()
jsonobj.put("email",m)
val url="https://192.168.2.11/verf1.php"
val que=Volley.newRequestQueue(this#MainActivity)
val req=JsonObjectRequest(Request.Method.POST,url,jsonobj,Response.Listener {
response ->mResultTextView.setText(response.toString())
},Response.ErrorListener {
response ->mResultTextView.setText(response.toString())
})
que.add(req)
my php file
<?php
require "conn.php";
$user_name=false;
if(isset($_POST["email"])){
$user_name = $_POST["email"];
}
$mysql_qry="select * from exitpass where email like '%".$user_name."%'";
$result=mysqli_query($conn,$mysql_qry);
if(mysqli_num_rows($result)>0)
{
echo"yes";
}
else{
echo"no";}
?>
The error is related to the TLS/SSL certificate associated with your 192.168.2.11 web server. There are a few different ways to solve this problem depending on what your setup is like.
If you have no cert installed on your web server then replace https with http to send over an unencrypted channel.
val url="http://192.168.2.11/verf1.php"
If the web server does have a cert installed then you could have a few issues. Common issues related to the stack trace are invalid certs, using the wrong (or no) domain name, wrong Java code for the encryption type and Java version, issue somewhere in the trust chain, expired cert, etc.
In summary, I believe you simply need to send over http, but if that doesn't work, you should troubleshoot it in this order.
Change the IP to the domain name associated with your cert
Verify you can access your resource in the browser and that the certificate is not expired
Figure out what type of encryption you are using (TLS 1.1, TLS 1.2, etc) and what Java you are using (1.6 or 1.7). There is a key difference between Java 6 and Java 7 as it relates to accessing TLS encrypted resources and I explain it in my highest rated post (as a reference).
If all else fails you will likely need to manually install the certificate in Androids JVM trust store. I believe you can do this from most GUIs and definitely from the terminal if you have access. You shouldn't have to do this, but sometimes you are forced to if the certificate chain breaks. It might not even be your cert causing the issue, it can be any cert in your trust chain.
Please let me know if the initial suggestion works. If it doesn't work please provide the public cert here ( or just the domain name and I will pull it down) and your Java version and I can better assist.
Using com.squareup.retrofit2:retrofit:2.0.1 with com.squareup.okhttp3:okhttp:3.2.0 on an AVD with Android 6.0.
I'm trying to implement public key pinning using a self signed certificate that is signed by a Root CA. That Root CA is in the system CA trust store.
Using the example provided by okhttp wiki with some small changes:
OkHttpClient client = new OkHttpClient.Builder().certificatePinner(
new CertificatePinner.Builder()
.add(pinningUrl, "sha256/invalidPIN")
.build()).build();
Request request = new Request.Builder()
.url(pinningUrl)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
for (Certificate certificate : response.handshake().peerCertificates()) {
System.out.println(CertificatePinner.pin(certificate));
}
What happens is that response.isSuccessful returns true, no exception is thrown, although the pin isn't correct. The only thing that is done correctly is the validation of the certificate with the Root CAs in systems CA trust store.
What I've found to be working, is adding this line before the for loop. But that isn't the right approach because the request is already sent, the pinning should work before TLS negotiation is finished. Also this line isn't mentioned in any sample code I've found.
client.certificatePinner().check(pinningUrl, response.handshake().peerCertificates());
throws
javax.net.ssl.SSLPeerUnverifiedException: Certificate pinning failure!
Is there a bug in the sample code provided by okhttp or am I doing something wrong?
You’re configuring it incorrectly. Replace pinningUrl with the hostname of the pinning URL. For example, you want example.com instead of http://example.com/. If you’d like to send a PR to make hostname validation more strict, it would be quite welcome.
Recently received a warning letter that my application security threatened.
---------- Forwarded message ----------
From: CERT Coordination Center <cert#cert.org>
Subject: SSL Vulnerability in ********* VU#582497
Cc: cert#cert.org
The letter contains the following information:
We've recently been evaluating with CERT Tapioca
http://www.cert.org/blogs/certcc/post.cfm?EntryID=203 the use of SSL
by Android apps. Through automated testing, we are logging apps that
cause traffic to be sent or received over an HTTPS connection that has
an invalid SSL certificate chain. The following application has demonstrated this incorrect behavior.
Some caveats that may affect the impact of the test results:
1) We have not yet investigated the content that is sent over HTTPS
with an invalid SSL certificate chain. If the information is not
sensitive, one might argue that the vulnerability does not really
have an impact. However, the other argument is that the use of
unvalidated SSL is a vulnerability that needs to be corrected,
regardless of the content sent or received.
2) It could be that your application itself uses SSL properly, but it
includes a third-party library that itself does improper SSL
validation. In such a case, this third-party library would need to
be updated. Or if a fix isn't available, the library's author
should be notified to let them know that they need to fix the
library.
3) Due to the UI automation used in the dynamic testing that we
performed, there is a small chance that the application or the
browser components used by the application did correctly warn the
user before proceeding. If the UI automation did happen to click
the button required to proceed despite an invalid certificate, then
this could be considered a false positive. If you believe this to
be the case, please respond and let us know.
For request, I use robospice-spring-android. ssl usage:
static {
try {
SSLContext sslc = SSLContext.getInstance("TLS");
TrustManager[] trustManagerArray = {new NullX509TrustManager()};
sslc.init(null, trustManagerArray, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new NullHostnameVerifier());
} catch (Exception e) {
}
}
private static class NullX509TrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
private static class NullHostnameVerifier implements HostnameVerifier {
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
Can anyone suggest on this issue. What is my fault?
Can anyone suggest on this issue. What is my fault?
You effectively disable any kind of authentication built into TLS. An attacker can thus easily mount a man-in-the-middle attack or a phishing attack, that is listen to and manipulate the encrypted traffic or claim to be the real server.
Such can usually easy be done with ARP or DHCP spoofing inside the local LAN or a public WLAN, so the problem described is not a theoretical but a real problem.
In detail:
TrustManager[] trustManagerArray = {new NullX509TrustManager()};
sslc.init(null, trustManagerArray, null);
Here you disable the check if the certificate is signed by a trusted CA. The attacker can now use any self-signed certificate or a certificate signed by an untrusted CA instead of the real one.
HttpsURLConnection.setDefaultHostnameVerifier(new NullHostnameVerifier());
Here you disable the check to verify the hostname inside the certificate against the host you want to access. Example:
the site you want to access is super-secure.example and you bought a certificate for it
the attacker has the site attacker.example and bought a certificate for it
Usually the client will verify that the name in the certificate matches the name the client connected to. But you explicitly disabled this check with the code above and thus the attackers certificate gets accepted.
You main fault is probably that you just copied some code from somewhere without understanding what it does. Bad idea in any case, but especially for anything related to security.