Thursday, February 14, 2013

Android Trusting SSL certificate and Download file using HttpsUrlConnection

The problem which I faced in developing one of application for android want to share with you guys,

My task was to download a .pdf file from a trusted URL in which I need to request to the web server secured with a signed certificate and authenticated URL with some User Name and Password

The problems which I faced step by step are as bellows:-

Problem 1:- I was trying to set up a secure connection via SSL I get a SSL handshake failure when the server requests the certificate.

Solution :- Android: Trusting SSL certificates
  1. Grab all required certificates (root and any intermediate CA’s)
  2. Create a keystore with keytool and the BouncyCastle provider and import the certs
  3. Load the keystore in your android app and use it for the secured connections 
I grabbed my certificate using firefox18.0.2 (Ubuntu 12.04 LTS) in Edit---> Preferences--->Advanced--->Encryption tab ----> View Cerificates --- Inside certificate manager dialog box  ----> servers tab ---->Add Exceptions---> Add security Exception dialog

after pressing "Get Certifcate" button if certificate is valid  then view button will activate by own

Export the certificate from the panel above which will view a file with some extension rename it by "my_certificate.pem"


Create the Keystore

Download the BouncyCastle Provider and store it to a known location.

In Ubuntu I downloaded BouncyCastle using standard command

1- wget http://bouncycastle.org/download/bcprov-jdk16-146.jar

2- keytool -importcert -file my_certificate.pem -keystore yourapp.store -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-jdk16-146.jar -storetype BKS


Put yourapp.store in the “assets” directory of your Android app. Now all you need to do is validate against it. To make a standard HTTPS request:

the block of code need to focus:

AssetManager assetManager = context.getAssets(); 

InputStream keyStoreInputStream = assetManager.open("yourapp.store");  

KeyStore trustStore = KeyStore.getInstance("BKS"); 

trustStore.load(keyStoreInputStream, "somepass".toCharArray());  

TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");

tmf.init(trustStore); 

SSLContext sslContext = SSLContext.getInstance("TLS");  

sslContext.init(null, tmf.getTrustManagers(), null);


 URL url = new URL("url from file have to download");
 HttpsURLConnection ucon = (HttpsURLConnection) url.openConnection();

 ucon.setConnectTimeout(30000);
         ucon.setReadTimeout(30000);
         ucon.setInstanceFollowRedirects(true);

//basic header password witht the Url

//Initially I was getting the FilenotFoundException in Inputstream so need to add basic header authentication with he url request

        String authStr = "UserName" + ":" + "password";
         String authEncoded = Base64.encodeToString(authStr.getBytes(), android.util.Base64.NO_WRAP);
         ucon.setRequestProperty("Authorization", "Basic " + authEncoded);

//My .pdf content type
         ucon.setRequestProperty("Content-Type", "application/octet-stream");
         ucon.setSSLSocketFactory(context.getSocketFactory());

         ucon.setDoInput(true);
         ucon.connect();

InputStream is = ucon.getInputStream();
         FileOutputStream out = new FileOutputStream(outputFile);
         int byteRead = 0;
         byte[] buf = new byte[1024];
         while ((byteRead = is.read(buf)) != -1) {
            out.write(buf, 0, byteRead);
         }
         out.flush();
         out.close();


I created Contentdownloader AsyncTask for secured connection as per my flexibility for an app modify it as per your need.


import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.util.List;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import org.apache.http.conn.ssl.SSLSocketFactory;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.res.AssetManager;
import android.os.AsyncTask;
import android.util.Base64;
import android.util.Log;
import android.widget.Toast;
import com.nvison.pdfsignerapp.util.Util;

public class ContentDownloader extends AsyncTask<Boolean, Void, Boolean> {

   private static final String TAG = ContentDownloader.class.getSimpleName();
   private String              directoryLocation;
   private String              downloadURL;
   private String              fileName;
   private String              absolutePath;
   private File                outputFile;
   private ProgressDialog      dialog;
   private Context             context;
   private SSLSocketFactory    sf;

   public ContentDownloader(Context context, String downloadURL, String directoryLocation, String fileName) {
      this.context = context;
      this.downloadURL = downloadURL;
      this.directoryLocation = directoryLocation;
      this.fileName = fileName;
   }

   @Override
   protected void onPreExecute() {
      super.onPreExecute();
      String loadingString = "Downloading pdf...";

      dialog = ProgressDialog.show(context, "", loadingString, true);
   }

   public void download() throws IOException {
      if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
         outputFile = new File(android.os.Environment.getExternalStorageDirectory(), Util.APPLIACTION_ROOT_FOLDER + File.separator
                  + directoryLocation);
         if (outputFile.exists()) {
            return;
         }
      }
   }

   @Override
   protected Boolean doInBackground(Boolean... params) {

      try {
         download();
         AssetManager assetManager = context.getAssets();
         InputStream keyStoreInputStream = assetManager.open("yourapp.store");
         KeyStore keyStore = KeyStore.getInstance("BKS");
 // password input at a time of generating a keystore using bouncy
         keyStore.load(keyStoreInputStream, "password".toCharArray());
         TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
         tmf.init(keyStore);

         SSLContext context = SSLContext.getInstance("TLS");
         context.init(null, tmf.getTrustManagers(), null);

         File outputFile = new File(Util.getAppRootFolder(), directoryLocation + File.separator + fileName);
         absolutePath = outputFile.getAbsolutePath();
         Log.e(TAG, "Absolute path--" + outputFile.getAbsolutePath());
         Log.e(TAG, "Saving content----" + downloadURL + "---into---" + directoryLocation);
         URL url = new URL(downloadURL);

         HttpsURLConnection ucon = (HttpsURLConnection) url.openConnection();
         // Authenticator.setDefault(new MyAuthenticator());
         ucon.setConnectTimeout(30000);
         ucon.setReadTimeout(30000);
         ucon.setInstanceFollowRedirects(true);
         String authStr = "username" + ":" + "password";
         String authEncoded = Base64.encodeToString(authStr.getBytes(), android.util.Base64.NO_WRAP);
         ucon.setRequestProperty("Authorization", "Basic " + authEncoded);
         ucon.setRequestProperty("Content-Type", "application/octet-stream");
         ucon.setSSLSocketFactory(context.getSocketFactory());

         ucon.setDoInput(true);
         ucon.connect();
         Map<String, List<String>> headerFields = ucon.getHeaderFields();
         int status = ucon.getResponseCode();
         if (status == HttpURLConnection.HTTP_OK) {
            Log.e("TAG", "perfect response code ");
         } else {
            Log.e("TAG", "Wrong response code ");
         }
         InputStream is = ucon.getInputStream();
         FileOutputStream out = new FileOutputStream(outputFile);
         int byteRead = 0;
         byte[] buf = new byte[1024];
         while ((byteRead = is.read(buf)) != -1) {
            out.write(buf, 0, byteRead);
         }
         out.flush();
         out.close();
         return true;
      } catch (Exception e) {

         Log.e(TAG, "Problem while downloading inside doinbackground");
         e.printStackTrace();
      }
      return false;
   }

   @Override
   protected void onPostExecute(Boolean result) {
      if (dialog.isShowing()) {
         dialog.dismiss();
      }
      if (result) {
         Toast.makeText(context, "download result--" + result, Toast.LENGTH_SHORT).show();
      } else {
         Log.e(TAG, "Downloading failed");
      }

   }

}



Friday, May 13, 2011

Android Programmatically create a gradient view



Here are some views which I have customized via shader and shapedrawable class. We can create more style via this code on changing shape and Shader.TileMode.MIRROR. I have attached eclipse running code at last.

View view= findViewById(R.id.anyViewLayout);

 int[] gradientColors = new int[2];
// http://developer.android.com/reference/android/graphics/Color.html
//Color.argb(Alpha, Red, Green, Blue) ;
 gradientColors [0] = Color.argb( 255, 198, 194, 194);
 gradientColors [1] = Color.argb( 255,  50, 50, 50);
createGradientBackground(view, gradientColors );// method which will draw a gradient for your view u can pass  any view to this method either button or any layout.

     
//method for creating gradient background
      private void createGradientBackground(View view, int[] gradientColors){

//Android View. setPadding(int left, int top, int right, int bottom) only accepts values in px
for more go to here .
          view.setPadding(7, 3, 7, 5);               
          final Shader upperShader = new LinearGradient(0, 0, 0, 40, gradientColors[0], gradientColors[1], Shader.TileMode.MIRROR);   shader
       
 float[] roundedCorner = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 };// used for rounding the view either button or any layout
            
         
          ShapeDrawable normal = new ShapeDrawable(new RoundRectShape(roundedCorner, null, null));
          normal.getPaint().setShader(upperShader);
         
//          final Shader lowerShader = new LinearGradient (0, 0, 0,30, new int[] {  0x55FFFFFF, 0x22FFFFFF}, null, TileMode.CLAMP);
//          normal.getPaint().setShader(lowerShader);
         
          normal.setPadding(7, 3, 7, 5);
          // Create a state list (I suppressed settings for pressed).
          StateListDrawable stateList = new StateListDrawable();
          stateList.addState(new int[] { }, normal);
          view.setBackgroundDrawable(stateList);
       }
You can download eclipse project from here

Feel free to comment........