I am trying to do certificate pinning. The network library my company used doesn't support pinning. So I have to do it manually.
This is the code I use
protected Void doInBackground(Void... params) {
String actualKey = "OpenSSLRSAPublicKey{modulus=ccf0883ebc511bb86f7f6e360385cf3a" +
"8720fa0d9f3367278baf2fd43d29c21b4384f09ae14207beeb429563639d4388aca65a3" +
"a5f5d2c902bf33e6df904598e6a5a1c037add731bdce606c664368cbc4bb7e269bbda82" +
"ff20bd9ca484f5bd660d5628bca4a8f376acf1cab07f0d9476df283ef44d3bf52d4b730" +
"3187cf587cbb2ce981e01b6cb32ba4f9b197b60013ff19215abb7d2ca9608007df82641" +
"b05127ec9557927e8bd68ff183f8b72720f93152f207f89b446e38fc7aa3db4928f5fb7" +
"92f33898381e7bc5ddb612d2e3a3191854797add8e0d47ed9f7da709e55a89aa7369620" +
"2d90275ada9d43fb462a16839787b6ea3c83df66a1d6e528a38d0d,publicExponent=1" +
"0001}";
try {
SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket("prisonvoicemail.com", 443);
socket.startHandshake();
Certificate[] certs = socket.getSession().getPeerCertificates();
Certificate cert = certs[0];
String key = cert.getPublicKey().toString();
Log.d(LOG_TAG, key);
if(key.equals(actualKey)){
Log.d(LOG_TAG, "Success");
} else {
Log.d(LOG_TAG, "Failure");
}
} catch (IOException e){
e.printStackTrace();
}
return null;
But for some reason it doesn't work. When I connect normally it get success, when I connect through a proxy (mitmproxy) to test a different certificate simulating a man in the middle attack, I also get success. It's like its completely bypassing the proxy and going straight to the normal certificiate. I don't know why this is.
I am using SSLSocket to connect to a server. I want to use ssl SNI extension. I can use SSLParameter.setServerNames in java. But it appears in android it is not available till sdk 24. What are my options here?
I had the same problem, solved by using reflection.
Connect the socket using:
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) sf.createSocket(uri.getHost(), 443);
setSNIHost(sf, socket, uri.getHost());
SSLSession s = socket.getSession();
where setSNIHost is declared as:
public void setSNIHost(final SSLSocketFactory factory, final SSLSocket socket, final String hostname) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
Log.i(TAG, "Setting SNI via SSLParameters");
SNIHostName sniHostName = new SNIHostName(hostname);
SSLParameters sslParameters = socket.getSSLParameters();
List<SNIServerName> sniHostNameList = new ArrayList<>(1);
sniHostNameList.add(sniHostName);
sslParameters.setServerNames(sniHostNameList);
socket.setSSLParameters(sslParameters);
} else if (factory instanceof android.net.SSLCertificateSocketFactory) {
Log.i(TAG, "Setting SNI via SSLCertificateSocketFactory");
((android.net.SSLCertificateSocketFactory)factory).setHostname(socket, hostname);
} else {
Log.i(TAG, "Setting SNI via reflection");
try {
socket.getClass().getMethod("setHostname", String.class).invoke(socket, hostname);
} catch (Throwable e) {
Log.e(TAG, "Could not call SSLSocket#setHostname(String) method ", e);
}
}
}
I don't really remember where I copied the code for the reflection, I simply added the support for API 24.
I try establish a https server in android for other phones to connect,but
only iphone6 sometimes can connected , ipod ,android browser all failed to get the webserver content.
(the browser message is fail to establish safe connect)
I use nanohttpd's simpleServer Class to establish it.
my CA is here
http://www.mediafire.com/download/53f6e9uveb47kqv/ca.cer
// for client to download
http://www.mediafire.com/download/v9i58n38yb85co5/server.p12
//for server to load keystore
CA password both are singuler .
Here is my sslServerSocket Code
char[]kspass = KEYSTOREPASS.toCharArray();
char[]ctpass = KEYSTOREPASS.toCharArray();
try {
KeyStore ks = KeyStore.getInstance("PKCS12");
//ks.load(new FileInputStream("file:///android_asset/singuler.keystore"),kspass);
ks.load(getResources().getAssets().open("server.p12"),kspass);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, ctpass);
TrustManagerFactory tmFactory = TrustManagerFactory.getInstance("X509");
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), null, null);
//webServer.makeSecure(NanoHTTPD.makeSSLSocketFactory(ks, kmf.getKeyManagers()));
webServerSSL.makeSecure(sc.getServerSocketFactory());
} catch (Exception e) {
// TODO: handle exceptionser
Log.i("test", e.toString());
}
try {
webServer.start(15);
webServerSSL.start(15);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
webServer = null;
webServerSSL=null;
Log.i("test", e.toString());
}
Can any one help me ?
Thank you.
I found the answer
Whe start nanohttpd server , need set the timeout millonseconds for every connect socket,especial for https request.
I'm using gottox socket.io java client for an Android chat application. I could connect to both web-socket and Xhr transport in HTTP mode. But when i switch to HTTPS only Xhr mode is working. i used the default SSL Context as below
SocketIO.setDefaultSSLSocketFactory(SSLContext.getInstance("Default"));
This works fine in Xhr mode. But in websocket transport there are no responses or errors.
Update
It might be that with new versions IO.setDefaultSSLContext and IO. setDefaultHostnameVerifier methods are not available. Instead now we can create our own OkHttpClient and set the hostname verifier and ssl socket factory etc on it as mentioned on socket.io-client-java usage. Here is the sniplet from there:
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.hostnameVerifier(myHostnameVerifier)
.sslSocketFactory(mySSLContext.getSocketFactory(), myX509TrustManager)
.build(); // default settings for all sockets
IO.setDefaultOkHttpWebSocketFactory(okHttpClient);
IO.setDefaultOkHttpCallFactory(okHttpClient);
Initial Answer:
I had the same issue with io.socket:socket.io-client:0.7.0 version of socket.io library on Android for long. It used to work fine for http protocol, however for https protocol it had trouble establishing connection giving xhr poll errors.
Following solution works for me without modifying the library itself:
// Socket connection
private Socket mSocket;
// Configure options
IO.Options options = new IO.Options();
// ... add more options
// End point https
String yourEndpoint = "https://whatever.yoururl.com"
String yourHostName = "yoururl.com"
// If https, explicitly tell set the sslContext.
if (yourEndpoint.startsWith("https://")) {
try {
// Default settings for all sockets
// Set default ssl context
IO.setDefaultSSLContext(SSLContext.getDefault());
// Set default hostname
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
return hv.verify(yourHostName, session);
}
};
IO.setDefaultHostnameVerifier(hostnameVerifier);
// set as an option
options.sslContext = SSLContext.getDefault();
options.hostnameVerifier = hostnameVerifier;
options.secure = true;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
// Instantiate the socket
mSocket = IO.socket(mEndpoint, options);
Hope this helps.
It works but you have to do some modifications on io.socket library.
Instead of using the socketio.jar, import into src folder the io.socket library (You'll find inside socket.io-java-client package). There, you have to edit the WebsocketTransport class.
Here you have the solution
https://github.com/Gottox/socket.io-java-client/issues/60
public WebsocketTransport(URI uri, IOConnection connection) {
super(uri);
this.connection = connection;
SSLContext context = null;
try {
context = SSLContext.getInstance("TLS", "HarmonyJSSE");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
try {
context.init(null, null, null);
} catch (KeyManagementException e) {
e.printStackTrace();
}
if("wss".equals(uri.getScheme()) && context != null) {
this.setWebSocketFactory(new DefaultSSLWebSocketClientFactory(context));
}
}
And remember to call the setDefaultSSLSocketFactory like this:
socket = new SocketIO();
socket.setDefaultSSLSocketFactory(SSLContext.getDefault());
socket.connect("https://www.myHttpsServer.com:443/");
Hope it helps someone ;)
Websocket with SSL working in AndroidAsync. Using that for now.
This question already has answers here:
Https Connection Android
(11 answers)
Closed 7 months ago.
I am trying to connect to a URL from a my Android app in Andorid Version 4.1.1, and I get the error indicated in the Title of my question, but when I tried to connect the same URL from Andorid Version 4.0.4 or 3.1, all works fine.
The Code fragment :
try {
.
.
.
URL url = new URL(urlStr);
Log.i(TAG,"[ URL ] " + urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
int size = conn.getContentLength();
int responsecode = conn.getResponseCode();
Log.d(TAG, "Responsecode: " + responsecode);
.
.
.
} catch (Exception e) {
e.printStackTrace();
}
private static void trustAllHosts() {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
} };
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection
.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
System.out.println("IOException : HTTPSRequest::trustAllHosts");
e.printStackTrace();
}
}
But here i clear one thing is that "Maybe certificate is that self-signed certificates and is not including them in a KeyStore.
I do not understand why this excepton occure only in Android Verison 4.1.1 OS
Thanks.
FULL STACK TRACE
01-31 10:26:08.348: W/System.err(3158): java.io.IOException: Hostname <URL> was not verified
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpConnection.verifySecureSocketHostname(HttpConnection.java:223)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:446)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:289)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:239)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:273)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpURLConnectionImpl.getHeaderField(HttpURLConnectionImpl.java:130)
01-31 10:26:08.348: W/System.err(3158): at java.net.URLConnection.getHeaderFieldInt(URLConnection.java:544)
01-31 10:26:08.348: W/System.err(3158): at java.net.URLConnection.getContentLength(URLConnection.java:316)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpsURLConnectionImpl.getContentLength(HttpsURLConnectionImpl.java:191)
01-31 10:26:08.348: W/System.err(3158): at com.ih.util.HelpVideoServices$downloadTask.run(HelpVideoServices.java:172)
In case you are running with certificates that doesn't mean anything and you want to bypass them you also need to add a null host name verifier to make this code work
HttpsURLConnection.setDefaultHostnameVerifier(new NullHostNameVerifier());
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[]{new NullX509TrustManager()}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
And the code for the host:
import javax.net.ssl.HostnameVerifier ;
import javax.net.ssl.SSLSession;
public class NullHostNameVerifier implements HostnameVerifier {
#Override
public boolean verify(String hostname, SSLSession session) {
Log.i("RestUtilImpl", "Approving certificate for " + hostname);
return true;
}
}
This needs to run once, but if you are making changes to your connection object you might need to run it again.
In addition to #Noam's answer, this is a complete example:
/**
* Disables the SSL certificate checking for new instances of {#link HttpsURLConnection} This has been created to
* aid testing on a local box, not for use on production.
*/
private static void disableSSLCertificateChecking() {
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
// not implemented
}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
// not implemented
}
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
try {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
Hope it helps
This might happen because the CN (Common Name) you have declared on your SSL does not mach the actual URL you are sending your HTTP request too.
If so, create a new SSL and enter the currect CN. That should fix the problem.
I experienced this problem in 4.1.1 and 4.1.2, using HTTPSUrlConnection.
After some poking around I discovered that it was because the Apache server I am dealing with has multiple virtual hosts serving https traffic, resulting in SNI issues in Android - at least prior to JellyBean (I have unconfirmed reports that it was working in JB).
In my case there were 3 virtual hosts serving https traffic:
mydomain.com
api.mydomain.com (the one I was trying to deal with)
admin.mydomain.com
Probing api.* with openssl_client like this:
openssl s_client -debug -connect api.mydomain.com:443
... always returned the root domain's certificate - buried in the output was something like:
Certificate chain
0 s:/OU=Domain Control Validated/CN=mydomain.com
...
... specifying the server name in the openssl_client command-line:
openssl s_client -debug -servername api.mydomain.com -connect api.mydomain.com:443
... returned the certificate I was expecting to see:
Certificate chain
0 s:/OU=Domain Control Validated/CN=api.mydomain.com
I was able to resolve the problem by moving the root domain virtual-host to a different physical host.
It seems that the Android HostnameVerifier can live with multiple sub-domain's side-by-side as virtual hosts, but having the root domain as a virtual-host in the same apache caused issues.
I am not a sys-admin/dev-ops and so it is possible that there are Apache config options that could have resolved the problem that I am not aware of.
Please note SSL Certificate work only by Domain not work by IP address.
if you use IP ,insert below code
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier()
{
#Override
public boolean verify(String hostname, SSLSession session)
{
if(hostname.equals("127.0.0.1"))
return true;
}
});
Android can't set up SSL connection, I suppose. Maybe your certificate for other host name, not the one you establish connection to. Read docs here and here.
This works better for me --> CHANGING StrictHostnameVerifier()
https://developer.android.com/reference/org/apache/http/conn/ssl/StrictHostnameVerifier
Example
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
HostnameVerifier hv = new StrictHostnameVerifier();
return hv.verify("example.com", session);
}
};
Use Example https://developer.android.com/training/articles/security-ssl#java
// Tell the URLConnection to use our HostnameVerifier
URL url = new URL("https://example.org/");
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setHostnameVerifier(hostnameVerifier);
From Amazon documentation:
Bucket Restrictions
"When using virtual hosted–style buckets with SSL, the SSL wild card certificate only matches buckets that do not contain periods. To work around this, use HTTP or write your own certificate verification logic."
The easiest way seems to create a unique bucket name without periods:
Instead of "bucketname.mycompany.com", something like "bucketnamemycompany" or any other DNS-compliant bucket name.
In Kotlin:
fun HttpsURLConnection.trustCert() {
try {
//Accepts every hostname
this.hostnameVerifier = HostnameVerifier { hostname, _ ->
println(hostname) //To be hardcoded/as needed
true
}
val trustMgr:Array<TrustManager> = arrayOf(object : X509TrustManager {
override fun checkClientTrusted(certs: Array<out X509Certificate>?, authType: String?) {}
override fun checkServerTrusted(certs: Array<out X509Certificate>?, authType: String?) {}
override fun getAcceptedIssuers(): Array<X509Certificate>? = null
})
this.sslSocketFactory = SSLContext.getInstance("TLS").also {
it.init(null, trustMgr, SecureRandom())
}.socketFactory
} catch (e: Exception) {
prinntln("SSL self-signed certificate processing error due to ${e.message}")
}
}
Usage:
val conn = URL(Uri.Builder().also {
it.scheme("https")
it.encodedAuthority("$serverIp:$serverPort")
}.build().toString()).openConnection() as HttpsURLConnection
conn.trustCert()
val respCode = conn.responseCode
if(respCode == 200) {
//do something (eg: read inputStream)
}