Importing certificate into a keystore on Android - android

I have a question about keystore.
Do android have a build in keystore?
If yes, where is the keystore?
Can I import my certificate into the keystore?
If yes, how can I do it manually and programmatically?
Please help, I have struggled with the SSLHandShakeException for a week.
I have used the keytool to generate a new keystore.bks and inserted the my certificate into it but I didn't know how can I use it for my android application.
I also tried the code that I have found online.
In MyHttpsClient.java
public class MyHttpsClient extends DefaultHttpClient{
final Context context;
public MyHttpsClient(Context context) {
System.out.println("context client http");
this.context = context;
}
#Override
protected ClientConnectionManager createClientConnectionManager(){
SchemeRegistry registry = new SchemeRegistry();
System.out.println("schema");
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", newSslSocketFactory(), 8080));
return new SingleClientConnManager(getParams(), registry);
}
private SSLSocketFactory newSslSocketFactory(){
try{
KeyStore trusted = KeyStore.getInstance("BKS");
System.out.println("context client getInstance..");
InputStream in = context.getResources().openRawResource(R.raw.server);
System.out.println("context client input" + in);
try{
System.out.println("try");
trusted.load(in, "changeit".toCharArray());
}finally{
in.close();
}
System.out.println("finally");
SSLSocketFactory sf = new SSLSocketFactory(trusted);
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
return sf;
}catch(Exception e){
throw new AssertionError(e);
}
}
}
In my Main.java
DefaultHttpClient client = new MyHttpsClient(getApplicationContext());
I have the code above in the onCreate() of my Main.java, but I have no idea why it doesn't go into my #Override function in MyHttpsClient.java
Can I read the original android emulator keystore and put the certificate into the keystore?

Related

Pinning webview Certificate inside onReceivedClientCertRequest method

Is that possible to pinning webview Certificate inside onReceivedClientCertRequest method.
I want to implement something like :
#Override
protected ClientConnectionManager createClientConnectionManager() {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// Register for port 443 our SSLSocketFactory with our keystore
// to the ConnectionManager
registry.register(new Scheme("https", newSslSocketFactory(), 443));
return new SingleClientConnManager(getParams(), registry);
}
private SSLSocketFactory newSslSocketFactory() {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");
// Get the raw resource, which contains the keystore with
// your trusted certificates (root and any intermediate certs)
InputStream in = MyHttpClient.context.getResources().openRawResource(R.raw.codeprojectssl); //name of your keystore file here
try {
// Initialize the keystore with the provided trusted certificates
// Provide the password of the keystore
trusted.load(in, "YourKeystorePassword".toCharArray());
} finally {
in.close();
}
// Pass the keystore to the SSLSocketFactory. The factory is responsible
// for the verification of the server certificate.
SSLSocketFactory sf = new SSLSocketFactory(trusted);
// Hostname verification from certificate
// http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER); // This can be changed to less stricter verifiers, according to need
return sf;
} catch (Exception e) {
throw new AssertionError(e);
}
}
inside this method that override from webview client :
#Override
#TargetApi(21)
public void onReceivedClientCertRequest (WebView view, ClientCertRequest request)
{
// By default pass to WebViewClient
super.onReceivedClientCertRequest(view, request);
}
is that possible, and is there a good example, because I can't find on google ?

No peer certificate error on android 2.3 devices but working fine on android 4+

I have created the bks file for my server's certificate. This was added in the project's sources in raw folder
I've created my https client as follows:
public class MyHttpsClient extends DefaultHttpClient {
final Context context;
public MyHttpsClient(Context context) {
this.context = context;
}
#Override
protected ClientConnectionManager createClientConnectionManager() {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// Register for port 443 our SSLSocketFactory with our keystore
// to the ConnectionManager
registry.register(new Scheme("https", newSslSocketFactory(), 443));
return new SingleClientConnManager(getParams(), registry);
}
private SSLSocketFactory newSslSocketFactory() {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");
// Get the raw resource, which contains the keystore with
// your trusted certificates (root and any intermediate certs)
InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
try {
// Initialize the keystore with the provided trusted certificates
// Also provide the password of the keystore
trusted.load(in, "testpassword".toCharArray());
} finally {
in.close();
}
// Pass the keystore to the SSLSocketFactory. The factory is responsible
// for the verification of the server certificate.
SSLSocketFactory sf = new SSLSocketFactory(trusted);
// Hostname verification from certificate
// http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
return sf;
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
Then I use it like:
DefaultHttpClient httpClient = new MyHttpsClient(context);
HttpConnectionParams.setConnectionTimeout(httpClient.getParams(), 30000);
HttpContext localContext = new BasicHttpContext();
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("Content-type", "application/json");
httpPost.setEntity(new StringEntity(jsonString));
response = httpClient.execute(httpPost, localContext);
HttpEntity entity = response.getEntity();
httpresponse = getResponse(entity);
Now here comes the funny part. This works just fine on android 4+ both real devices and emulator. THIS fails on android 2.3 with
javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
How can I make it work on android 2.3 without the known "trust all certificates" way ?
A few days age i solved same problem. The solution consisted in the following. I take all certificates installed on device(in my case it was samsung galaxy II) and go to the server side developer, who manage certificate chain installed on server. He analyzes ssl chain and detected that in chain there is one certificate (Thawte 2006) and other certificate (Thawte 2010). He removed oldest certificate, which issued in 2006, and ssl verification on android 2.x become working. I suggest you, before trying to get work local keystore, research your server side ssl chain and check that this chain doesn't have unnecessary certificates, because android 2.x devices cannot ignore not necessary certificates, but other platforms 3.x 4x and ios, windows phone can do it, i mean ignore "trash" in ssl certificate chain.

Certificate pinning in Android

I am trying to learn how to do certificate pinning in an Android application. I found the tutorial here. I wanted to clarify I doubt I have based on my testing this code.
I used the code as follows :
public class CertificatePinning {
static SSLSocketFactory constructSSLSocketFactory(Context context) {
SSLSocketFactory sslSocketFactory = null;
try {
AssetManager assetManager = context.getAssets();
InputStream keyStoreInputStream = assetManager.open("myapp.store");
KeyStore trustStore = KeyStore.getInstance("BKS");
trustStore.load(keyStoreInputStream, "somepass".toCharArray());
sslSocketFactory = new SSLSocketFactory(trustStore);
sslSocketFactory.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
}
catch(Exception e){
Log.d("Exception", e.getLocalizedMessage());
}
return sslSocketFactory;
}
public static HttpClient getNewHttpClient(Context context) {
DefaultHttpClient httpClient = null;
try {
SSLSocketFactory sslSocketFactory = constructSSLSocketFactory(context);
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sslSocketFactory, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
httpClient = new DefaultHttpClient(ccm, params);
} catch (Exception e) {
Log.d("Exception", e.getLocalizedMessage() );
return null;
}
return httpClient;
}
}
Quoting a statement from that tutorial :
On the client side, you simply need to distribute the signing certificate
with your app and validate against it.
On my web server, I have my own CA, which I created using open SSL, and used to sign certificates for different domain names that are used with my app.
This statement indicates that this tutorial is meant for the CA certificate I have. I tested the code using ca.pem (from my CA's crt file) and it works fine.
But I also tested the same code with a certificate I signed with that CA, e.g. server.pem ( from the signed server.crt), and still it works.
Did I do something wrong, or is this code meant for pinning either :
1) a CA certificate (covering all certificates signed by that CA) or
2) a particular certificate (signed by some CA) ?

SSL connection in android

I am in middle of developing an app in android, which requires me sslhandshake with server, using KSOAP2 libraries.
I am able to achieve the same on http sites, but fails on HTTPS sites,saying "could not validate certificate".
Can anybody help out
Please note that at least prior to 2.3 Android versions don't have the root CA for the RapidSSL CA among others.
You can check the issuer of a problematic certificate with sites such as http://www.digicert.com/help/
Another quick check is to try to load a HTTPs page in the stock browser and see if it complains about the certificate.
If this does not match your situation then ignore this answer.
If you have a certificate signed by this CA you must either
Handle it explicitly in your app by doing something like Danieles answer, but actually also comparing the certificate to a stored one for RapidSSL (or whichever you use).
Add an intermediate certificate to the chain at the web server in question to make the RapidSSL certificate certified by GeoTrust.
Check out
http://code.google.com/p/android/issues/detail?id=10807
https://knowledge.rapidssl.com/support/ssl-certificate-support/index?page=content&id=AR1549
It may be because the site you are trying to access may not have CA. It only may only have self-signed certificate. That is a issue you will get when you dealing with self-signed certificate.
Try these links and show us what you have implemented already
http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html
http://developer.android.com/reference/org/apache/http/conn/ssl/SSLSocketFactory.html
Can this code be of help?
https://github.com/mixare/mixare/blob/master/src/org/mixare/MixContext.java
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){
public boolean verify(String hostname, SSLSession session) {
return true;
}});
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[]{new 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];
}}}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(
context.getSocketFactory());
This code is used in mixare.org to accept self-signed certificates.
Please be aware that you are not safe from MITM attacks when using this approach.
HTH,
Daniele
You can Use SelfSignedCertificate. Just use this method as your HTTPClient:
public static HttpClient getNewHttpClient() {
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
return new DefaultHttpClient(ccm, params);
} catch (Exception e) {
return new DefaultHttpClient();
}
}

Self-signed SSL acceptance on Android

How do I accept a self-signed certificate in Java on Android?
A code sample would be perfect.
I've looked everywhere on the Internet and while some people claim to have found the solution, it either does not work or there is no sample code to back it up.
I have this functionality in exchangeIt, which connects to Microsoft exchange via WebDav. Here's some code to create an HttpClient which will connect to self signed cert's via SSL:
SchemeRegistry schemeRegistry = new SchemeRegistry();
// http scheme
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// https scheme
schemeRegistry.register(new Scheme("https", new EasySSLSocketFactory(), 443));
HttpParams params = new BasicHttpParams();
params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 30);
params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(30));
params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
The EasySSLSocketFactory is here, and the EasyX509TrustManager is here.
The code for exchangeIt is open source, and hosted on googlecode here, if you have any issues. I'm not actively working on it anymore, but the code should work.
Note that since Android 2.2 the process has changed a bit, so check this to make the code above work.
As EJP correctly commented, "Readers should note that this technique is radically insecure. SSL is not secure unless at least one peer is authenticated. See RFC 2246."
Having said that, here's another way, without any extra classes:
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.X509TrustManager;
private void trustEveryone() {
try {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){
public boolean verify(String hostname, SSLSession session) {
return true;
}});
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[]{new 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];
}}}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(
context.getSocketFactory());
} catch (Exception e) { // should never happen
e.printStackTrace();
}
}
I faced this issue yesterday, while migrating our company's RESTful API to HTTPS, but using self-signed SSL certificates.
I've looking everywhere, but all the "correct" marked answers I've found consisted of disabling certificate validation, clearly overriding all the sense of SSL.
I finally came to a solution:
Create Local KeyStore
To enable your app to validate your self-signed certificates, you need to provide a custom keystore with the certificates in a manner that Android can trust your endpoint.
The format for such custom keystores is "BKS" from BouncyCastle, so you need the 1.46 version of BouncyCastleProvider that you can download here.
You also need your self-signed certificate, I will assume it's named self_cert.pem.
Now the command for creating your keystore is:
<!-- language: lang-sh -->
$ keytool -import -v -trustcacerts -alias 0 \
-file *PATH_TO_SELF_CERT.PEM* \
-keystore *PATH_TO_KEYSTORE* \
-storetype BKS \
-provider org.bouncycastle.jce.provider.BouncyCastleProvider \
-providerpath *PATH_TO_bcprov-jdk15on-146.jar* \
-storepass *STOREPASS*
PATH_TO_KEYSTORE points to a file where your keystore will be created. It MUST NOT EXIST.
PATH_TO_bcprov-jdk15on-146.jar.JAR is the path to the downloaded .jar libary.
STOREPASS is your newly created keystore password.
Include KeyStore in your Application
Copy your newly created keystore from PATH_TO_KEYSTORE to res/raw/certs.bks (certs.bks is just the file name; you can use whatever name you wish)
Create a key in res/values/strings.xml with
<!-- language: lang-xml -->
<resources>
...
<string name="store_pass">*STOREPASS*</string>
...
</resources>
Create a this class that inherits DefaultHttpClient
import android.content.Context;
import android.util.Log;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;
import java.io.IOException;
import java.io.InputStream;
import java.security.*;
public class MyHttpClient extends DefaultHttpClient {
private static Context appContext = null;
private static HttpParams params = null;
private static SchemeRegistry schmReg = null;
private static Scheme httpsScheme = null;
private static Scheme httpScheme = null;
private static String TAG = "MyHttpClient";
public MyHttpClient(Context myContext) {
appContext = myContext;
if (httpScheme == null || httpsScheme == null) {
httpScheme = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
httpsScheme = new Scheme("https", mySSLSocketFactory(), 443);
}
getConnectionManager().getSchemeRegistry().register(httpScheme);
getConnectionManager().getSchemeRegistry().register(httpsScheme);
}
private SSLSocketFactory mySSLSocketFactory() {
SSLSocketFactory ret = null;
try {
final KeyStore ks = KeyStore.getInstance("BKS");
final InputStream inputStream = appContext.getResources().openRawResource(R.raw.certs);
ks.load(inputStream, appContext.getString(R.string.store_pass).toCharArray());
inputStream.close();
ret = new SSLSocketFactory(ks);
} catch (UnrecoverableKeyException ex) {
Log.d(TAG, ex.getMessage());
} catch (KeyStoreException ex) {
Log.d(TAG, ex.getMessage());
} catch (KeyManagementException ex) {
Log.d(TAG, ex.getMessage());
} catch (NoSuchAlgorithmException ex) {
Log.d(TAG, ex.getMessage());
} catch (IOException ex) {
Log.d(TAG, ex.getMessage());
} catch (Exception ex) {
Log.d(TAG, ex.getMessage());
} finally {
return ret;
}
}
}
Now simply use an instance of **MyHttpClient** as you would with **DefaultHttpClient** to make your HTTPS queries, and it will use and validate correctly your self-signed SSL certificates.
HttpResponse httpResponse;
HttpPost httpQuery = new HttpPost("https://yourserver.com");
... set up your query ...
MyHttpClient myClient = new MyHttpClient(myContext);
try{
httpResponse = myClient.(peticionHttp);
// Check for 200 OK code
if (httpResponse.getStatusLine().getStatusCode() == HttpURLConnection.HTTP_OK) {
... do whatever you want with your response ...
}
}catch (Exception ex){
Log.d("httpError", ex.getMessage());
}
Unless I missed something, the other answers on this page are DANGEROUS, and are functionally equivalent to not using SSL at all. If you trust self-signed certificates without doing further checks to make sure the certificates are the ones that you are expecting, then anyone can create a self-signed certificate and can pretend to be your server. At that point, you have no real security.
The only legitimate way to do this (without writing a full SSL stack) is to add an additional trusted anchor to be trusted during the certificate verification process. Both involve hard-coding the trusted anchor certificate into your app and adding it to whatever trusted anchors that the OS provides (or else you won't be able to connect to your site if you get a real certificate).
I'm aware of two ways to do this:
Create a custom trust store as described at http://www.ibm.com/developerworks/java/library/j-customssl/#8
Create a custom instance of X509TrustManager and override the getAcceptedIssuers method to return an array that contains your certificate:
public X509Certificate[] getAcceptedIssuers()
{
X509Certificate[] trustedAnchors =
super.getAcceptedIssuers();
/* Create a new array with room for an additional trusted certificate. */
X509Certificate[] myTrustedAnchors = new X509Certificate[trustedAnchors.length + 1];
System.arraycopy(trustedAnchors, 0, myTrustedAnchors, 0, trustedAnchors.length);
/* Load your certificate.
Thanks to http://stackoverflow.com/questions/11857417/x509trustmanager-override-without-allowing-all-certs
for this bit.
*/
InputStream inStream = new FileInputStream("fileName-of-cert");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(inStream);
inStream.close();
/* Add your anchor cert as the last item in the array. */
myTrustedAnchors[trustedAnchors.length] = cert;
return myTrustedAnchors;
}
Note that this code is completely untested and may not even compile, but should at least steer you in the right direction.
Brian Yarger's answer works in Android 2.2 as well if you modify the bigger createSocket method overload as follows. It took me a while to get self-signed SSLs working.
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
}
On Android, HttpProtocolParams accepts ProtocolVersion rather than HttpVersion.
ProtocolVersion pv = new ProtocolVersion("HTTP", 1, 1);
HttpProtocolParams.setVersion(params, pv);
#Chris - Posting this as an answer since I can't add comments (yet). I'm wondering if your approach is supposed to work when using a webView. I can't get it do so on Android 2.3 - instead I just get a white screen.
After some more searching, I came across this simple fix for handling SSL errors in a webView which worked like a charm for me.
In the handler I check to see if I'm in a special dev mode and call handler.proceed(), otherwise I call handler.cancel(). This allows me to do development against a self-signed cert on a local website.
There are a-lot alternatives for this use case. If you don't want to have any custom code in your code base such as custom TrustManager I would suggest to try GitHub - SSLContext Kickstart and the following code snippet:
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>sslcontext-kickstart</artifactId>
<version>6.7.0</version>
</dependency>
SSL configuration
SSLFactory sslFactory = SSLFactory.builder()
.withUnsafeTrustMaterial()
.build();
SSLContext sslContext = sslFactory.getSslContext();
SSLSocketFactory sslSocketFactory = sslFactory.getSslSocketFactory();
HttpClient configuration
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", sslSocketFactory, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
HttpClient httpClient = new DefaultHttpClient(ccm, params);
HttpsUrlConnection
HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);

Categories

Resources