package es.uji.security.ui.applet;
import netscape.javascript.JSObject;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.log4j.Logger;
import es.uji.security.crypto.SupportedBrowser;
import es.uji.security.crypto.SupportedDataEncoding;
import es.uji.security.crypto.SupportedSignatureFormat;
import es.uji.security.crypto.config.ConfigManager;
import es.uji.security.crypto.config.OS;
import es.uji.security.ui.applet.io.InputParams;
import es.uji.security.ui.applet.io.OutputParams;
import es.uji.security.util.HexDump;
import es.uji.security.util.i18n.LabelManager;
/**
* Handles all the applet singularities such as applet parameters, applet installation, host
* navigator and keystore list
*/
public class AppHandler
{
private static Logger log = Logger.getLogger(AppHandler.class);
/* The singleton applet object */
private static AppHandler singleton;
/* The Applet or Application Main window who is referencing to */
private MainWindow _mw = null;
// This object interacts with the signature thread and wraps all the multisignature complexity
public SignatureHandler sigh = null;
// JavaScript Functions
private String jsSignOk = "onSignOk";
private String jsSignError = "onSignError";
private String jsSignCancel = "onSignCancel";
private String jsWindowShow = "onWindowShow";
// Signature output format
private SupportedSignatureFormat signatureFormat = SupportedSignatureFormat.CMS;
// Input data encoding format
private SupportedDataEncoding inputDataEncoding = SupportedDataEncoding.PLAIN;
private SupportedDataEncoding outputDataEncoding = SupportedDataEncoding.PLAIN;
// Host navigator
private SupportedBrowser navigator = SupportedBrowser.MOZILLA;
// Input/Output Data handling
private InputParams input;
private OutputParams output;
// XAdES signer role customization
private String[] signerRole;
private String xadesFilename;
private String xadesFileMimeType;
private String[] xadesBaseRef;
private boolean isBigFile = false;
private SSLSocketFactory defaultSocketFactory;
private String downloadURL;
// PDF options
private Map<String, String[]> bindValues;
private String[] reason;
private String[] location;
private String[] contact;
private String[] timestamping;
private String[] tsaURL;
private String[] visibleSignature;
private String[] visibleSignatureType;
private String[] visibleAreaX;
private String[] visibleAreaY;
private String[] visibleAreaX2;
private String[] visibleAreaY2;
private String[] visibleAreaPage;
private String[] visibleAreaTextSize;
private String[] visibleAreaImgFile;
private String[] visibleAreaRepeatAxis;
private String[] visibleAreaTextPattern;
private boolean cosign;
private boolean enveloped;
private boolean detached;
private String dniToCheckAgainsCertificate;
private String[] documentReference;
private String[] documentReferenceVerificationUrl;
public AppHandler() throws SignatureAppletException
{
this(null);
this.cosign = false;
this.enveloped = true;
log.debug("Running in desktop application mode");
}
/**
* Base constructor, instantiates an AppHandler object, setting up the target navigator and
* creating an available keystore mapping.
* <p/>
* That class should be used as a Sigleton so you must use getInstance in order to get this
* class object.
*/
public AppHandler(String downloadURL) throws SignatureAppletException
{
this.bindValues = new HashMap<String, String[]>();
this.downloadURL = downloadURL;
try
{
log.debug("Get JavaScript member: navigator");
JSObject document = (JSObject) JSCommands.getWindow().getMember("navigator");
log.debug("Get JavaScript member: userAgent");
String userAgent = (String) document.getMember("userAgent");
if (userAgent != null)
{
userAgent = userAgent.toLowerCase();
log.debug("Detected user agent " + userAgent);
log.debug("Is IE? " + SupportedBrowser.isIE(userAgent));
log.debug("Is Win? " + OS.isWindowsUpperEqualToNT());
log.debug("Is Chrome? " + SupportedBrowser.isChrome(userAgent));
log.debug("Is Mozilla? " + SupportedBrowser.isMozilla(userAgent));
if (SupportedBrowser.isIE(userAgent) || SupportedBrowser.isChromeWindows(userAgent))
{
this.navigator = SupportedBrowser.IEXPLORER;
try
{
this.install();
}
catch (Throwable e)
{
log.error("Error installing or loading the DLL file", e);
}
}
else if (SupportedBrowser.isMozilla(userAgent))
{
this.navigator = SupportedBrowser.MOZILLA;
}
}
}
catch (Exception exc)
{
log.error("Error accesing web browser window", exc);
}
log.debug("Navigator variable set to " + this.navigator);
// Keep a copy to restore its value
defaultSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
}
/**
* That method instantiates this Singleton class or returns the Object.
*/
public static AppHandler getInstance(String downloadURL) throws SignatureAppletException
{
if (singleton == null)
{
singleton = new AppHandler(downloadURL);
}
return singleton;
}
/**
* That method returns the appHandler object, the object must be previously instantiated.
*
* @return AppHandler The application handler object.
*/
public static AppHandler getInstance() throws SignatureAppletException
{
if (singleton == null)
{
singleton = new AppHandler();
}
return singleton;
}
/**
* Returns the Application invoker's main window for deal with him.
*
* @return MainWindow The MainWindow application object
*/
public MainWindow getMainWindow()
{
return _mw;
}
/**
* A method to obtain the selected inputParams depending on the input way (JS exported method)
*
* @return InputParams The inputParam class representing the input method.
*/
public InputParams getInputParams()
{
return input;
}
/**
* A method to obtain the selected outputParams depending on the output way (JS exported method)
*
* @return OutputParams The outputparam class representing the input method.
*/
public OutputParams getOutputParams()
{
return output;
}
/**
* A method for getting the selected signer role from setSignerRole JS function.
*
* @return signerRole The selected signerrole for XAdES output format
*/
public String[] getSignerRole()
{
return signerRole;
}
/**
* A method for getting the selected file name from setXadesFileName JS function.
*
* @return xadesFileName The selected file name for XAdES output format
*/
public String getXadesFileName()
{
return this.xadesFilename;
}
/**
* A method for getting the selected file Mime Type from setXadesFileName JS function.
*
* @return xadesFileName The selected file Mime Type for XAdES output format
*/
public String getXadesFileMimeType()
{
return this.xadesFileMimeType;
}
/**
* Returns a string representing the host browser over the applet is running
*
* @return string representing the browser
*/
public SupportedBrowser getNavigator()
{
return this.navigator;
}
/**
* A method for setting the signer role, that method is called from setSignerRole JS function.
*
* @param signerrole The signer role to be set for XAdES output format
*/
public void setSignerRole(String[] signerrole)
{
this.signerRole = signerrole;
}
/**
* A method for setting the filename, that method is called from setXadesFileName JS function.
*
* @param filename The file name to be set for XAdES output format
*/
public void setXadesFileName(String filename)
{
this.xadesFilename = filename;
}
/**
* A method for setting the selected file Mime Type from setXadesFileName JS function.
*/
public void setXadesFileMimeType(String xadesFileMimeType)
{
this.xadesFileMimeType = xadesFileMimeType;
}
/**
* This method sets a reference to the MainWindow's object.
*
* @param mw MainWindow application object
*/
public void setMainWindow(MainWindow mw)
{
_mw = mw;
}
/**
* Help method for install(), it downloads the dll and writes it down to the filesystem
*
* @param input URL where get the data from.
* @param output Destination path of the dll.
*/
private void dumpFile(String input, String output) throws IOException
{
URL url = new URL(input);
URLConnection uc = url.openConnection();
uc.connect();
InputStream in = uc.getInputStream();
FileOutputStream fos = new FileOutputStream(output);
fos.write(OS.inputStreamToByteArray(in));
fos.close();
in.close();
}
/**
* Installs the applet on the client, basically downloads and loads MicrosoftCryptoApi dll
*
* @throws SignatureAppletException with the message
* @throws
*/
public void installDLL(String downloadUrl, String completeDllPath)
throws SignatureAppletException
{
try
{
log.debug("Downloading " + downloadUrl + ". Complete DLL path is " + completeDllPath);
dumpFile(downloadUrl + "MicrosoftCryptoApi_0_3.dll", completeDllPath);
}
catch (Throwable e)
{
log.error(LabelManager.get("ERROR_CAPI_DLL_INSTALL"), e);
throw new SignatureAppletException(LabelManager.get("ERROR_CAPI_DLL_INSTALL"));
}
}
/**
* Installs the applet on the client, basically downloads and loads MicrosoftCryptoApi dll
*
* @throws SignatureAppletException with the message
* @throws
*/
public void install() throws SignatureAppletException
{
String destAbsolutePath = System.getenv("TEMP");
String completeDLLPath = destAbsolutePath + File.separator + "MicrosoftCryptoApi_0_3.dll";
File dllFile = new File(completeDLLPath);
if (!dllFile.exists())
{
log.info("MicrosoftCryptoApi_0_3.dll not found. Downloading DLL file");
installDLL(this.downloadURL, completeDLLPath);
}
else
{
log.info("MicrosoftCryptoApi_0_3.dll already exists. Verifying existing DLL file");
try
{
byte[] originalDLLHash = {0x0e, 0x15, (byte) 0x8d, (byte) 0x9f, 0x6a, (byte) 0xc5,
(byte) 0x8b, 0x31, 0x67, 0x30, (byte) 0xbe, (byte) 0x8f, 0x4d, 0x35, 0x71,
(byte) 0xab, (byte) 0xd4, (byte) 0xc9, (byte) 0xf9, (byte) 0x90};
FileInputStream dllFileStream = new FileInputStream(dllFile);
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
byte[] readed = new byte[dllFileStream.available()];
dllFileStream.read(readed);
messageDigest.update(readed);
byte[] currentDLLHash = messageDigest.digest();
log.debug("Original DLL digest: " + HexDump.xdump(originalDLLHash));
log.debug("Current DLL digest: " + HexDump.xdump(currentDLLHash));
// Compare original and current hash
for (int i = 0; i < currentDLLHash.length; i++)
{
if (currentDLLHash[i] != originalDLLHash[i])
{
log.info("DLL not valid. Downloading orginal DLL file");
installDLL(this.downloadURL, completeDLLPath);
break;
}
}
}
catch (Exception e)
{
throw new SignatureAppletException(e.getMessage(), false);
}
}
try
{
log.debug("Loading DLL with System.load " + completeDLLPath);
System.load(completeDLLPath);
}
catch (Throwable e)
{
log.error("Error loading " + completeDLLPath, e);
}
}
/**
* Calls the javascript function indicated as func with params arguments
*
* @param func The function that must be invoked
* @param params The parameters that must be passed to that function
*/
public void callJavaScriptCallbackFunction(String func, String[] params)
{
log.debug("Call JavaScript method: " + func);
JSCommands.getWindow().call(func, params);
}
/**
* Select the functions that must be called on signature ok, error and cancel
*
* @param onSignOk The name of the function to be called on signature ok
* @param onSignCancel The name of the function to be called on signature cancel
* @param onSignError The name of the function to be called on signature Error
*/
public void setJavaScriptCallbackFunctions(String onSignOk, String onSignError,
String onSignCancel, String onWindowShow)
{
jsSignOk = onSignOk;
jsSignError = onSignError;
jsSignCancel = onSignCancel;
jsWindowShow = onWindowShow;
}
/**
* Get method for the customized SignCancel method call
*
* @return jsSignCancel The name of the function to be called at DOM
*/
public String getJsSignCancel()
{
return jsSignCancel;
}
/**
* Method that allows to set the signCancel function
*
* @param jsSignCancel the name of the function to be called on cancel at DOM
*/
public void setJsSignCancel(String jsSignCancel)
{
if (jsSignCancel == null || jsSignCancel.length() == 0)
{
throw new IllegalArgumentException("Cancel javascript function can't be null or empty");
}
this.jsSignCancel = jsSignCancel;
}
/**
* Get the selected name for signature error function at DOM
*
* @return jsSignError The name of the function
*/
public String getJsSignError()
{
return jsSignError;
}
/**
* Set the name of the function to be called on signature error
*
* @param jsSignError The name of the error function
*/
public void setJsSignError(String jsSignError)
{
if (jsSignError == null || jsSignError.length() == 0)
{
throw new IllegalArgumentException("Error javascript function can't be null or empty");
}
this.jsSignError = jsSignError;
}
/**
* Get the selected name for signature ok function at DOM
*
* @return jsSignOk The name of the function
*/
public String getJsSignOk()
{
return jsSignOk;
}
/**
* Get the selected name for signature ok function at DOM
*
* @return jsSignOk The name of the function
*/
public String getJsWindowShow()
{
return jsWindowShow;
}
/**
* Set the Sign ok javascript method to be called on signature ok.
*
* @param jsSignOk The name of the function
*/
public void setJsSignOk(String jsSignOk)
{
if (jsSignOk == null || jsSignOk.length() == 0)
{
throw new IllegalArgumentException("Ok javascript function can't be null or empty");
}
this.jsSignOk = jsSignOk;
}
/**
* Get the output format of the signature
*
* @return signatureOutputFormat The name of the output format
*/
public SupportedSignatureFormat getSignatureFormat()
{
return this.signatureFormat;
}
/**
* Sets the output signature format.
*/
public void setSignatureOutputFormat(SupportedSignatureFormat signatureFormat)
{
this.signatureFormat = signatureFormat;
log.debug("Setting signOutputFormat to " + signatureFormat);
}
/**
* It returns the selected input data encoding
*
* @return inputDataEncoding the selected input data encoding
*/
public SupportedDataEncoding getInputDataEncoding()
{
return this.inputDataEncoding;
}
/**
* It returns the selected output data encoding
*
* @return outputDataEncoding the selected input data encoding
*/
public SupportedDataEncoding getOutputDataEncoding()
{
return this.outputDataEncoding;
}
/**
* A method for get the InputParams class
*
* @return input the InputParams implementation class
*/
public InputParams getInput()
{
return this.input;
}
/**
* A method for get the isBigFile attribute.
*
* @return boolean indicating if it is bigFile or not.
*/
public boolean getIsBigFile()
{
return this.isBigFile;
}
/**
* A method for setting the encoding type of the input data
*
* @param inputDataEncoding the encoding name
*/
public void setInputDataEncoding(SupportedDataEncoding inputDataEncoding)
{
this.inputDataEncoding = inputDataEncoding;
log.debug("Setting inputDataEncoding to " + inputDataEncoding);
}
/**
* A method for setting the encoding type for the output data
*
* @param outputDataEncoding the encoding name
*/
public void setOutputDataEncoding(SupportedDataEncoding outputDataEncoding)
{
this.outputDataEncoding = outputDataEncoding;
log.debug("Setting inputDataEncoding to " + outputDataEncoding);
}
/**
* Sets the InputParams for this signature
*
* @param input the InputParams implementation class
*/
public void setInput(InputParams input)
{
this.input = input;
}
/**
* Sets the OutputParams for this signature
*
* @param output the OutputParams implementation class
*/
public void setOutput(OutputParams output)
{
this.output = output;
}
/**
* This method computes the signature, that should be done by a thread
*/
public void doSign()
{
_mw.getShowSignatureCheckBox().setVisible(false);
sigh = new SignatureHandler(this);
sigh.doSign();
}
protected SignatureHandler getSignatureHandler()
{
return sigh;
}
/**
* This method allow the user to disable the SSL server certificate validation when connecting
* throughout an https connection
*/
public void setSSLCertificateVerfication(boolean validate)
{
if (validate)
{
HttpsURLConnection.setDefaultSSLSocketFactory(defaultSocketFactory);
}
else
{
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager()
{
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return null;
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs,
String authType)
{
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs,
String authType)
{
}
}};
// Install the all-trusting trust manager
try
{
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
}
catch (Exception e)
{
}
}
}
public void setIsBigFile(boolean isBigFile)
{
System.out.println("SET ISBIGFILE to: " + isBigFile);
this.isBigFile = isBigFile;
}
public void setXAdESBaseRef(String[] baseReference)
{
this.xadesBaseRef = baseReference;
}
public String[] getXAdESBaseRef()
{
return this.xadesBaseRef;
}
public static void initConfig(String downloadURL)
{
log.debug("Trying to retrieve ujiCrypto.conf from server ...");
try
{
// Retrieve ujiCrypto.conf file
URL url = new URL(downloadURL + "/ujiCrypto.conf");
URLConnection uc = url.openConnection();
uc.connect();
Properties properties = new Properties();
properties.load(uc.getInputStream());
// Load remote properties
ConfigManager.getInstance(properties);
log.debug("Remote ujiCrypto.conf loaded successfully!!");
}
catch (Exception e)
{
log.error("Cann't load ujiCrypto.conf from server. WARNING: Bundled local file will be loaded.");
}
}
public Map<String, String[]> getBindValues()
{
return bindValues;
}
public void setBindValues(Map<String, String[]> bindValues)
{
this.bindValues = bindValues;
}
public String[] getReason()
{
return reason;
}
public void setReason(String[] reason)
{
this.reason = reason;
}
public String[] getLocation()
{
return location;
}
public void setLocation(String[] location)
{
this.location = location;
}
public String[] getContact()
{
return contact;
}
public void setContact(String[] contact)
{
this.contact = contact;
}
public String[] getTimestamping()
{
return timestamping;
}
public void setTimestamping(String[] timestamping)
{
this.timestamping = timestamping;
}
public String[] getTsaURL()
{
return tsaURL;
}
public void setTsaURL(String[] tsaURL)
{
this.tsaURL = tsaURL;
}
public String[] getVisibleSignature()
{
return visibleSignature;
}
public void setVisibleSignature(String[] visibleSignature)
{
this.visibleSignature = visibleSignature;
}
public String[] getVisibleSignatureType()
{
return visibleSignatureType;
}
public void setVisibleSignatureType(String[] visibleSignatureType)
{
this.visibleSignatureType = visibleSignatureType;
}
public String[] getVisibleAreaX()
{
return visibleAreaX;
}
public void setVisibleAreaX(String[] visibleAreaX)
{
this.visibleAreaX = visibleAreaX;
}
public String[] getVisibleAreaY()
{
return visibleAreaY;
}
public void setVisibleAreaY(String[] visibleAreaY)
{
this.visibleAreaY = visibleAreaY;
}
public String[] getVisibleAreaX2()
{
return visibleAreaX2;
}
public void setVisibleAreaX2(String[] visibleAreaX2)
{
this.visibleAreaX2 = visibleAreaX2;
}
public String[] getVisibleAreaY2()
{
return visibleAreaY2;
}
public void setVisibleAreaY2(String[] visibleAreaY2)
{
this.visibleAreaY2 = visibleAreaY2;
}
public String[] getVisibleAreaPage()
{
return visibleAreaPage;
}
public void setVisibleAreaPage(String[] visibleAreaPage)
{
this.visibleAreaPage = visibleAreaPage;
}
public String[] getVisibleAreaTextSize()
{
return visibleAreaTextSize;
}
public void setVisibleAreaTextSize(String[] visibleAreaTextSize)
{
this.visibleAreaTextSize = visibleAreaTextSize;
}
public String[] getVisibleAreaImgFile()
{
return visibleAreaImgFile;
}
public void setVisibleAreaImgFile(String[] visibleAreaImgFile)
{
this.visibleAreaImgFile = visibleAreaImgFile;
}
public String[] getVisibleAreaRepeatAxis()
{
return visibleAreaRepeatAxis;
}
public void setVisibleAreaRepeatAxis(String[] visibleAreaRepeatAxis)
{
this.visibleAreaRepeatAxis = visibleAreaRepeatAxis;
}
public String[] getVisibleAreaTextPattern()
{
return visibleAreaTextPattern;
}
public void setVisibleAreaTextPattern(String[] visibleAreaTextPattern)
{
this.visibleAreaTextPattern = visibleAreaTextPattern;
}
public void setSignatureFormat(SupportedSignatureFormat signatureFormat)
{
this.signatureFormat = signatureFormat;
}
public boolean isCosign()
{
return cosign;
}
public void setCosign(boolean cosign)
{
this.cosign = cosign;
}
public boolean isEnveloped()
{
return enveloped;
}
public void setEnveloped(boolean enveloped)
{
this.enveloped = enveloped;
}
public void setDNIToCheckAgainsCertificate(String dni)
{
this.dniToCheckAgainsCertificate = dni;
}
public String getDniToCheckAgainsCertificate()
{
return this.dniToCheckAgainsCertificate;
}
public String[] getDocumentReference()
{
return documentReference;
}
public void setDocumentReference(String[] documentReference)
{
this.documentReference = documentReference;
}
public String[] getDocumentReferenceVerificationUrl()
{
return documentReferenceVerificationUrl;
}
public void setDocumentReferenceVerificationUrl(String[] documentReferenceVerificationUrl)
{
this.documentReferenceVerificationUrl = documentReferenceVerificationUrl;
}
public boolean isDetached()
{
return detached;
}
public void setDetached(boolean detached)
{
this.detached = detached;
}
}