package ve.gob.cenditel.tibisaymovil; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.security.cert.X509Certificate; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import ee.sk.digidoc.DataFile; import ee.sk.digidoc.DigiDocException; import ee.sk.digidoc.SignedDoc; import ee.sk.digidoc.factory.DigiDocFactory; import ee.sk.digidoc.factory.Pkcs12SignatureFactory; import ee.sk.utils.ConfigManager; import ee.sk.xmlenc.EncryptedData; import ee.sk.xmlenc.factory.EncryptedDataParser; import ve.gob.cenditel.tibisaymovil.R; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; import android.view.Window; import android.webkit.MimeTypeMap; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.RadioButton; import android.widget.TextView; import android.widget.AdapterView.OnItemClickListener; import android.widget.Toast; public class PKCS12ToDecryptActivity extends Activity implements OnItemClickListener, OnClickListener { private File cwd; private File selected; private FileBrowserView viewHolder; private FileListAdapter listAdapter; private String filterMImeType = "application/*"; // cadena que mantiene la ruta del archivo descifrar private String fileToDecrypt = null; // cadena que mantiene la ruta del archivo descifrado private String decryptedFile = null; // cadena que mantiene la ruta del archivo PKCS12 private String pkcs12File = null; // contrasena del archivo p12 private String pkcs12Password = null; @Override protected void onCreate(Bundle savedInstanceState) { //Estilando la barra de titulo final boolean customTitleSupported = requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); super.onCreate(savedInstanceState); this.viewHolder = new FileBrowserView(); if (savedInstanceState != null) { if(savedInstanceState.getString("selected") != null) this.selected = new File(savedInstanceState.getString("selected")); if (this.selected != null) { PKCS12ToDecryptActivity.this.updateButton (PKCS12ToDecryptActivity.this.viewHolder.accept,true); } this.cwd = new File(savedInstanceState.getString("cwd")); this.viewHolder.fileList.setAdapter(this.listAdapter = new FileListAdapter(this.cwd.getAbsolutePath(), filterMImeType)); } else { this.selected = null; this.viewHolder.fileList.setAdapter(this.listAdapter = new FileListAdapter()); } boolean enabled = false; if (this.selected != null) enabled = this.viewHolder.accept.isEnabled(); PKCS12ToDecryptActivity.this.updateButton (PKCS12ToDecryptActivity.this.viewHolder.accept,enabled); //Estilando Barra de titulo if(customTitleSupported) getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.title_bar); //Capturando archivo original que se debe descifrar fileToDecrypt = getIntent().getExtras().getString("fileToDecrypt"); } /** * Provides the data to be shown in the file browser ListView. * * @author José M. Prieto (jmprieto@emergya.com) */ private class FileListAdapter extends BaseAdapter { private final ArrayList directories; private final ArrayList files; private FileListAdapter() { this("/",filterMImeType); } private FileListAdapter(String location) { this(location, ""); } private FileListAdapter(String location, String filterMimeType) { directories = new ArrayList(); files = new ArrayList(); //Obtiene etiqueta que se colocará antes del path que visualizará el usuario String toPathText = PKCS12ToDecryptActivity.this.getString(R.string.pathstring)+": "; //Coloca el texto de la etiqueta en la vista PKCS12ToDecryptActivity.this.viewHolder.pathString.setText(toPathText); //Coloca el texto con el path actual PKCS12ToDecryptActivity.this.viewHolder.path.setText(location); //Crea un objeto file cwd con la ubicacion dada en location PKCS12ToDecryptActivity.this.cwd = new File(location); //Obtiene el directorio padre del objeto file cwd File parent = PKCS12ToDecryptActivity.this.cwd.getParentFile(); //Si tiene un padre, lo agrega en la posición cero de la lista de directorios if (parent != null) { directories.add(0, parent); } //Crea un arreglo con las lista de archivos contenidos en el directorio cwd File[] ls = PKCS12ToDecryptActivity.this.cwd.listFiles(); if (ls != null) { for (File f : ls) { //recorre todos los archivos contenidos en el directorio if (FsUtils.isHiddenOrNotReadable(f)) { // Si son ocultos no hace nada continue; } // Si son directorios los agrega a la lista de directorios a partir de la posición 1 // En la posición 0 se encuentra el directorio padre if (f.isDirectory()) { directories.add(f); } else // De lo contrario lo agrega a la lista de archivos { //Valida tipo de archivo a mostrar Uri selectedUri = Uri.fromFile(f); String fileExtension = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString()); String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension); // Toast.makeText(PKCS12ToDecryptActivity.this, // "FileExtension: " + fileExtension + "\n" + // "MimeType: " + mimeType, // Toast.LENGTH_LONG).show(); // en principio no filtrar ningun archivo if(filterMimeType.isEmpty() || filterMimeType == mimeType || fileExtension.equals("p12") || fileExtension.equals("pfx")) files.add(f); } } } Collections.sort(directories); // Ordena los directorios alfabeticamente Collections.sort(files); // Ordena los archivos alfabeticamente } /** * Retorna cantidad total de elementos que se listarán en el directorio. */ @Override public int getCount() { return directories.size() + files.size(); } /** * Dada una posición en el listado del directorio, retorna un archivo o directorio * según corresponda. */ @Override public File getItem(int position) { if (position < directories.size()) { return directories.get(position); } else { return files.get(position - directories.size()); } } /** * Retorna un código hash para el archivo, permite comparar si dos archivos son los mismos */ @Override public long getItemId(int position) { return getItem(position).hashCode(); } /** * Crea la visualización de cada item del fileBrowser */ @Override public View getView(int position, View convertView, ViewGroup parent) { //Crea la vista de cada fila del filebrowser a partir del layout if (convertView == null) { LayoutInflater inflater = LayoutInflater.from(PKCS12ToDecryptActivity.this); convertView = inflater.inflate(R.layout.file_to_verify_bdoc_signature_item, parent, false); } // Se enlaza a cada componente del layout ImageView image = (ImageView) convertView.findViewById(R.id.type_image); TextView fileName = (TextView) convertView.findViewById(R.id.filename_text); TextView modified = (TextView) convertView.findViewById(R.id.filename_modified); // Se obtiene el archivo ubicado en position File file = getItem(position); //RadioButton RadioButton radio = (RadioButton) convertView.findViewById(R.id.file_radio); radio.setFocusable(false); // Se asignan los iconos según el tipo de archivo y se oculta el radio en los directorios if (file.isDirectory()) { image.setImageResource(R.drawable.ic_carpeta); radio.setVisibility(View.INVISIBLE); radio.setChecked(false); } else { image.setImageResource(R.drawable.ic_archivo); radio.setVisibility(View.VISIBLE); if (PKCS12ToDecryptActivity.this.selected == null || PKCS12ToDecryptActivity.this.selected.hashCode() != file.hashCode()){ radio.setChecked(false); } else{ radio.setChecked(true); } } // Si es el directorio que hace referencia al padre le coloca como nombre ".." if (file.isDirectory() && position == 0 && ! "/".equals(PKCS12ToDecryptActivity.this.cwd.getAbsolutePath())) { fileName.setText(".."); } else { fileName.setText(file.getName()); } //Datos de modificación del archivo SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy"); Date date = new Date(file.lastModified()); String dateString = PKCS12ToDecryptActivity.this.getString(R.string.modified)+": "; if (file.lastModified()>0) modified.setText(dateString+sdf.format(date)); else modified.setText(dateString+"-"); return convertView; } /** * Controla la selección de cada item del fileBrowser */ public void select(ListView parent, int position) { File item = getItem(position); //Si es un directorio el seleccionado se hace un llamado del fileBrowser del directorio if (item.isDirectory()) { parent.setAdapter(PKCS12ToDecryptActivity.this.listAdapter = new FileListAdapter(item.getAbsolutePath(), filterMImeType)); } else { // Si es un archivo //Se agrega el archivo a la lista de seleccionados si no se encuentra en la misma if (PKCS12ToDecryptActivity.this.selected == null || PKCS12ToDecryptActivity.this.selected.hashCode() != item.hashCode()){ PKCS12ToDecryptActivity.this.selected = item; PKCS12ToDecryptActivity.this.updateButton(PKCS12ToDecryptActivity.this.viewHolder.accept,true); } else{ // De lo contrario se elimina de la lista de seleccionados PKCS12ToDecryptActivity.this.selected = null; PKCS12ToDecryptActivity.this.updateButton(PKCS12ToDecryptActivity.this.viewHolder.accept,false); } notifyDataSetChanged(); } } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putParcelable("intent", this.getIntent()); outState.putString("cwd", this.cwd.getAbsolutePath()); if(this.selected != null) outState.putString("selected", this.selected.getAbsolutePath()); } private void updateButton(View v, boolean bool) { try{ v.setEnabled(bool); if (v instanceof TextView){ Drawable icon = ((TextView)v).getCompoundDrawables()[1]; if (icon!=null) if (bool) icon.setAlpha(255); else icon.setAlpha(127); } if (v instanceof ImageView){ ImageView result = (ImageView) v; if (bool) result.setAlpha(255); else result.setAlpha(127); } }catch(NullPointerException e){} } private void updateButton(LinearLayout layout, boolean bool) { try{ layout.setEnabled(bool); for (int i=0; i parent, View view, int position, long id) { if (parent.getId() == R.id.file_list) { // user selects a file this.listAdapter.select((ListView) parent, position); } else { // user de-selects a file //this.listAdapter.addIfSameDirectory(selectedAdapter.doUnselect((ListView) parent, position)); } //this.viewHolder.numSelected.setText(Integer.toString(this.selected.size())); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.button_clear_zone: this.selected = null; this.listAdapter.notifyDataSetChanged(); //this.viewHolder.numSelected.setText(""+this.selected.size()); this.updateButton(this.viewHolder.accept, false); break; case R.id.button_accept_zone: // lanzar intent para compartir el archivo seleccionado pkcs12File = PKCS12ToDecryptActivity.this.selected.getAbsolutePath(); // crear dialogo para solicitar la contrasena del PKCS12 Dialog d = onCreateDialog(); d.show(); Toast.makeText(getApplicationContext(), "PKCS12ToDecryptActivity - PKCS12: "+pkcs12File, Toast.LENGTH_SHORT).show(); break; } } // funcion para compartir el archivo private void shareIt() { Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); File file = new File(fileToDecrypt); Uri uri = Uri.fromFile(file); Log.i("DEBUG", file.getPath()); //Log.d("******", getMimeType(file.getPath())); //shareIntent.setDataAndType(uri, getMimeType(file.getPath())); shareIntent.putExtra(Intent.EXTRA_STREAM, uri); shareIntent.setType("application/*"); startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.share_it_using))); } /** * Selecciona el certificado del destinatario del directorio de certificados * para cifrado * @return void */ // funcion para desplegar el gestor de certificados de destinatarios para cifrar private void selectCertificateToEncrypt(String fileToEncrypt) { // desplegar la actividad de gestor de certificados de destinatarios // chequear disponibilidad de directorio de certificados if (!checkCertificatesDirectoryAvailability()){ Toast.makeText(getApplicationContext(), "PKCS12ToDecryptActivity: directorio no disponible", Toast.LENGTH_SHORT).show(); finish(); return; }else{ // lanzar el activity SelectCeritificateToEncryptActivity Intent intent = new Intent(this, SelectCertificateToEncryptActivity.class); intent.putExtra("fileToEncrypt", fileToEncrypt); startActivity(intent); } } // fin de selectRecipientCertificate() /** * Chequea la disponibilidad del directorio de /TibisayMovil/CertificatesToEncrypt * @return boolean */ private boolean checkCertificatesDirectoryAvailability() { // verificar acceso al directorio /mnt/sdcard/TibisayMovil/CertificatesToEncrypt boolean mExternalStorageAvailable = false; boolean mExternalStorageWriteable = false; String state = Environment.getExternalStorageState(); String certificatesDir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + getResources().getString(R.string.app_name) + "/" + getResources().getString(R.string.certificate_dir_files) + "/"; AlertDialog.Builder builder = new AlertDialog.Builder(PKCS12ToDecryptActivity.this); if (Environment.MEDIA_MOUNTED.equals(state)) { // We can read and write the media mExternalStorageAvailable = mExternalStorageWriteable = true; Toast.makeText(getApplicationContext(), "We can read and write the media", Toast.LENGTH_SHORT).show(); // Crear directorio CertificatesToEncrypt donde se almacenan los certificados de // destinatarios para cifrado /* String certificatesDir = Environment.getExternalStorageDirectory() + "/" + getResources().getString(R.string.app_name) + "/" + getResources().getString(R.string.certificates_dir) + "/"; */ if (prepareDirectory(certificatesDir)){ return true; }else{ builder.setMessage("No existe el directorio "+certificatesDir+" para almacenar certificados.").setTitle("Error:"); } } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { // We can only read the media mExternalStorageAvailable = true; mExternalStorageWriteable = false; Toast.makeText(getApplicationContext(), "We can only read the media", Toast.LENGTH_SHORT).show(); builder.setMessage("Directorio "+certificatesDir+ " montado de solo lectura. No se pueden almancenar certificados.").setTitle("Error:"); } else { // Something else is wrong. It may be one of many other states, but all we need // to know is we can neither read nor write mExternalStorageAvailable = mExternalStorageWriteable = false; Toast.makeText(getApplicationContext(), "we can neither read nor write", Toast.LENGTH_SHORT).show(); builder.setMessage("Directorio "+certificatesDir+ " no está disponible. No se pueden almancenar certificados.").setTitle("Error:"); } builder.setNegativeButton("Cancelar", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // User cancelled the dialog PKCS12ToDecryptActivity.this.finish(); } }); AlertDialog dialog = builder.create(); dialog.show(); return false; } // fin de checkCertificatesDirectoryAvailability /** * Prepara directorio * @return boolean */ boolean prepareDirectory(String dir) { try { if (makedirs(dir)) { return true; } else { return false; } } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, "Could not initiate File System.. Is Sdcard mounted properly?", Toast.LENGTH_LONG).show(); return false; } } /** * Crea directorio utilizando la variable tmpDir * @return boolean */ private boolean makedirs(String dir) { //File tempdir = new File(extractedDirFiles); File tempdir = new File(dir); if (!tempdir.exists()) tempdir.mkdirs(); return (tempdir.isDirectory()); } /** * Crea dialogo para obtener la contrasena del archivo P12 * * @return Dialog */ public Dialog onCreateDialog() { LayoutInflater inflater = LayoutInflater.from(this); final View dialogview = inflater.inflate(R.layout.pkcs12_password, null); AlertDialog dialogDetails = null; AlertDialog.Builder dialogbuilder = new AlertDialog.Builder(this); dialogbuilder.setTitle(R.string.title_pkcs12_password); dialogbuilder.setView(dialogview); final EditText password = (EditText) dialogview.findViewById(R.id.password); final Button okButton = (Button) dialogview.findViewById(R.id.button_accept); okButton.setEnabled(true); okButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(PKCS12ToDecryptActivity.this," Password : "+ password.getText().toString(), Toast.LENGTH_LONG).show(); // asignar la contrasena pkcs12Password = password.getText().toString(); // ejecutar el proceso de descifrado. decryptFile(fileToDecrypt, pkcs12File, pkcs12Password); // borrar el contenido de pkcs12Password pkcs12Password = ""; } }); dialogDetails = dialogbuilder.create(); return dialogDetails; } /** * Crea dialogo para obtener la contrasena del archivo P12 * * @return Dialog */ private void decryptFile(String fileToDecrypt, String pkcs12File, String pkcs12Password) { Toast.makeText(getApplicationContext(), "Decrypting file: " + fileToDecrypt, Toast.LENGTH_SHORT).show(); ConfigManager.init("jar://jdigidoc.cfg"); /** signed doc object if used */ SignedDoc m_sdoc; m_sdoc = null; /** encrypted data object if used */ EncryptedData m_cdoc; m_cdoc = null; String outFile = null, keystoreFile = null, keystorePasswd = null, keystoreType="PKCS12"; keystoreFile = pkcs12File; keystorePasswd = pkcs12Password; Log.d("Reading encrypted file: ", fileToDecrypt); try { EncryptedDataParser dencFac = ConfigManager.instance().getEncryptedDataParser(); m_cdoc = dencFac.readEncryptedData(fileToDecrypt); } catch(Exception ex) { System.err.println("ERROR: reading encrypted file: " + fileToDecrypt + " - " + ex); Log.d("ERROR: reading encrypted file: ", ex.getMessage()); ex.printStackTrace(System.err); Toast.makeText(getApplicationContext(), ex.getMessage(), Toast.LENGTH_SHORT).show(); showDialog("Error:", ex.getMessage()); return; } int nKey = -1; try{ Pkcs12SignatureFactory p12fac = new Pkcs12SignatureFactory(); p12fac.init(); System.out.println("p12fac.init " ); p12fac.load(keystoreFile, keystoreType, keystorePasswd); System.out.println("p12fac.load()"); X509Certificate cert = p12fac.getAuthCertificate(0, keystorePasswd); System.out.println("p12fac.getAuthCertificate: " + nKey); nKey = m_cdoc.getRecvIndex(cert); System.out.println("Using recipient: " + nKey); if (nKey == -1) { throw(new ArrayIndexOutOfBoundsException("")); } } // fin del try interno catch(DigiDocException ex){ System.err.println("ERROR: finding cdoc recipient: " + ex); Log.d("Error finding cdoc recipient: ", ex.getMessage()); ex.printStackTrace(System.err); Toast.makeText(getApplicationContext(), ex.getMessage(), Toast.LENGTH_SHORT).show(); if (ex.getCode() == DigiDocException.ERR_TOKEN_LOGIN) { // el pasword del PKCS12 no es correcto showDialog(getResources().getString(R.string.msg_encryption_error), "password incorrecto"); return; } showDialog(getResources().getString(R.string.msg_encryption_error), ex.getMessage()); return; } catch(ArrayIndexOutOfBoundsException ex) { showDialog(getResources().getString(R.string.msg_encryption_error), getResources().getString(R.string.error_decrypting_file_index_out_of_bounds)); return; } System.err.println("**** antes de ejecutar operacion m_cdoc.decryptPkcs12(nKey, keystoreFile, keystorePasswd, keystoreType)"); // ejecutar la operacion de descifrado: try { m_cdoc.decryptPkcs12(nKey, keystoreFile, keystorePasswd, keystoreType); String [] absolutePathOriginalFile = fileToDecrypt.split(".cdoc"); //String fileName = split[0]; outFile = absolutePathOriginalFile[0]; String [] path = absolutePathOriginalFile[0].split("/"); String originalFileName = path[path.length-1]; FileOutputStream fos = new FileOutputStream( outFile ); Log.d("Decrypting file", "antes de escribir archivo " + outFile); fos.write(m_cdoc.getData()); Log.d("Decrypting file", "despues de escribir archivo " + outFile); fos.close(); Log.d("Decrypting file", "despues de cerrar archivo " + outFile); DigiDocFactory digFac = ConfigManager.instance().getDigiDocFactory(); Log.d("Decrypting file", "despues ConfigManager.instance().getDigiDocFactory()"); File file = new File(outFile); InputStream selectedFile = null; selectedFile = new BufferedInputStream(new FileInputStream(file)); m_sdoc = digFac.readSignedDocFromStreamOfType(selectedFile, false); Log.d("----", "leyo el ddoc"); Toast.makeText(getApplicationContext(), "leyó el ddoc", Toast.LENGTH_SHORT).show(); selectedFile.close(); // ******* Log.d("Decrypting file", "antes de m_sdoc.getDataFile(0)"); DataFile df = m_sdoc.getDataFile(0); Log.d("Decrypting file", "despues de m_sdoc.getDataFile(0)"); String decryptedDir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + getResources().getString(R.string.app_name) + "/" + getResources().getString(R.string.decrypted_dir_files) + "/"; decryptedFile = decryptedDir+originalFileName; fos = new FileOutputStream(decryptedFile); InputStream is = df.getBodyAsStream(); if(is == null) { System.err.println("DataFile has no data!"); showDialog(getResources().getString(R.string.msg_encryption_error), "DataFile has no data!"); return; } byte[] data = new byte[4096]; int n = 0, m = 0; while((n = is.read(data)) > 0) { fos.write(data, 0, n); m += n; } fos.close(); is.close(); // borrar el archivo ddoc File ddocFile = new File(outFile); ddocFile.delete(); Toast.makeText(getApplicationContext(), "Descifrado correctamente: " + decryptedFile, Toast.LENGTH_SHORT).show(); showDialog("Información:", "Archivo descifrado exitosamente."); // TODO lanzar la actividad para mostrar el resultado del cifrado //showDecryptionResults(fileToDecrypt, decryptedFile); } catch (DigiDocException e) { showDialog(getResources().getString(R.string.msg_encryption_error), e.getMessage()); return; } catch (FileNotFoundException e) { showDialog(getResources().getString(R.string.msg_encryption_error), e.getMessage()); return; } catch (IOException e) { showDialog(getResources().getString(R.string.msg_encryption_error), e.getMessage()); return; } } // fin de void decryptFile(String fileToDecrypt, String pkcs12File) /** * Crea un dialogo con el titulo y mensaje como argumentos y lo despliega * * @return void */ public void showDialog(String title, String msg) { // 1. Instantiate an AlertDialog.Builder with its constructor AlertDialog.Builder builder = new AlertDialog.Builder(PKCS12ToDecryptActivity.this); // 2. Chain together various setter methods to set the dialog characteristics builder.setMessage(msg) .setTitle(title); builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // User clicked OK button Toast.makeText(getApplicationContext(), "User clicked OK button", Toast.LENGTH_LONG).show(); finish(); // TODO lanzar la actividad para mostrar el resultado del cifrado showDecryptionResults(fileToDecrypt, decryptedFile); } }); // 3. Get the AlertDialog from create() AlertDialog dialog = builder.create(); dialog.show(); } /** * Muestra la actividad de información del proceso de descifrado * * @return void */ public void showDecryptionResults(String fileToDecrypt, String decryptedFile) { // TODO lanzar el activity EncryptionResultActivity Intent intent = new Intent(this, DecryptionResultActivity.class); intent.putExtra("fileToDecrypt", fileToDecrypt); intent.putExtra("decryptedFile", decryptedFile); startActivity(intent); } }