I have the following problem, I implemented a solution to send images with Retrofit2, and my web service is on a server with SSL. The certificate was recently deployed, so it was already working. Seeing some implementations, I did the following implementation:
private void sendImage() {
OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
OkHttpClient okHttpClient = builder.build();
Gson gson = new GsonBuilder().setLenient().registerTypeAdapter(Usuario.class, new ImageDes()).create();
Retrofit retrofit = new Retrofit
.Builder()
.baseUrl(context.getResources().getString(R.string.sync_adapter_conn))
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
byte[] imageBinary = BinaryBytes.getResourceInBytes(image);
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), imageBinary);
ImageApi imageApi = retrofit.create(ImageApi.class);
Call<Request> call = imageApi.sendImgRequest(nameImage, requestBody);
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
Certificate[] certs;
try {
certs = session.getPeerCertificates();
} catch (SSLException e) {
return false;
}
X509Certificate x509 = (X509Certificate) certs[0];
// We can be case-insensitive when comparing the host we used to
// establish the socket to the hostname in the certificate.
String hostName = hostname.trim().toLowerCase(Locale.ENGLISH);
// Verify the first CN provided. Other CNs are ignored. Firefox, wget,
// curl, and Sun Java work this way.
String firstCn = getFirstCn(x509);
if (matches(hostName, firstCn)) {
return true;
}
for (String cn : getDNSSubjectAlts(x509)) {
if (matches(hostName, cn)) {
return true;
}
}
return false;
}
});
call.enqueue(new Callback<Request>() {
@Override
public void onResponse(Call<Request> call, Response<Request> response) {
Log.i("IMAGE_CALL", response.message());
}
@Override
public void onFailure(Call<Request> call, Throwable t) {
Log.e("ERRO_CALL", t.getMessage());
}
});
}
private String getFirstCn(X509Certificate cert) {
/*
* Sebastian Hauer's original StrictSSLProtocolSocketFactory used
* getName() and had the following comment:
*
* Parses a X.500 distinguished name for the value of the
* "Common Name" field. This is done a bit sloppy right
* now and should probably be done a bit more according to
* <code>RFC 2253</code>.
*
* I've noticed that toString() seems to do a better job than
* getName() on these X500Principal objects, so I'm hoping that
* addresses Sebastian's concern.
*
* For example, getName() gives me this:
* 1.2.840.113549.1.9.1=#16166a756c6975736461766965734063756362632e636f6d
*
* whereas toString() gives me this:
* [email protected]
*
* Looks like toString() even works with non-ascii domain names!
* I tested it with "花子.co.jp" and it worked fine.
*/
String subjectPrincipal = cert.getSubjectX500Principal().toString();
StringTokenizer st = new StringTokenizer(subjectPrincipal, ",");
while (st.hasMoreTokens()) {
String tok = st.nextToken();
int x = tok.indexOf("CN=");
if (x >= 0) {
return tok.substring(x + 3);
}
}
return null;
}
The error returned by the log within call
is as follows:
E / ERROR_CALL: Hostname [Server IP] not verified: certificate: [certificate]
DN: CN = [url]
subjectAltNames: [url, url]