package es.uji.security.ui.applet;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.net.ssl.SSLHandshakeException;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import org.apache.log4j.Logger;
import es.uji.security.crypto.ISignFormatProvider;
import es.uji.security.crypto.SignatureOptions;
import es.uji.security.crypto.SignatureResult;
import es.uji.security.crypto.SupportedDataEncoding;
import es.uji.security.crypto.SupportedSignatureFormat;
import es.uji.security.crypto.config.OS;
import es.uji.security.crypto.openxades.OpenXAdESSignatureFactory;
import es.uji.security.keystore.IKeyStore;
import es.uji.security.keystore.X509CertificateHandler;
import es.uji.security.ui.applet.io.InputParams;
import es.uji.security.ui.applet.io.OutputParams;
import es.uji.security.util.Base64;
import es.uji.security.util.HexEncoder;
import es.uji.security.util.i18n.LabelManager;
public class SignatureThread extends Thread
{
private Logger log = Logger.getLogger(SignatureThread.class);
private MainWindow _mw = null;
private int _end_percent = 0;
private int _ini_percent = 0, _step = 0;
private boolean hideWindow;
private boolean showSignatureOk;
private SignatureHandler signatureHandler;
private int currentIndex;
public SignatureThread(String str, int currentIndex)
{
super(str + "-" + currentIndex);
this.currentIndex = currentIndex;
}
public void setPercentRange(int ini_percent, int end_percent, int step)
{
this._step = step;
this._ini_percent = ini_percent;
this._end_percent = end_percent;
}
public void setHideWindowOnEnd(boolean hideWindow)
{
this.hideWindow = hideWindow;
}
public void setCallbackMethod(SignatureHandler signatureHandler)
{
this.signatureHandler = signatureHandler;
}
public void run()
{
guiInitialize();
JLabel infoLabelField = _mw.getInformationLabelField();
infoLabelField.setText(LabelManager.get("COMPUTING_SIGNATURE"));
int inc = (this._end_percent - this._ini_percent) / 10;
try
{
X509CertificateHandler selectedNode = getSelectedCertificate();
IKeyStore iksh = selectedNode.getKeyStore();
if (iksh == null)
{
showSignatureOk = false;
guiFinalize(false);
throw new SignatureAppletException("ERR_GET_KEYSTORE");
}
loadCertificateStore(infoLabelField, selectedNode, iksh);
InputParams inputParams = retrieveInputParams(inc);
OutputParams outputParams = retrieveOutputParams(inc);
_mw.getGlobalProgressBar().setValue(_ini_percent + 3 * inc);
log.debug("Loading signature format: " + _mw.getAppHandler().getSignatureFormat().toString());
// Creating an instance of the signature formater: CMS, XAdES, etc
Class<?> sf = Class.forName(_mw.getAppHandler().getSignatureFormat().toString());
ISignFormatProvider signer = (ISignFormatProvider) sf.newInstance();
SignatureOptions sigOpt = new SignatureOptions();
String[] roles = _mw.getAppHandler().getSignerRole();
if (roles != null && this._step < roles.length)
{
sigOpt.setSignerRole(roles[this._step]);
log.debug("Signer Role: " + sigOpt.getSignerRole());
}
if (_mw.getAppHandler().getSignatureFormat().equals(SupportedSignatureFormat.XADES))
{
String fname = (_mw.getAppHandler().getXadesFileName() != null) ? _mw
.getAppHandler().getXadesFileName() : "UNSET";
String fmimetype = (_mw.getAppHandler().getXadesFileMimeType() != null) ? _mw
.getAppHandler().getXadesFileMimeType() : "application/binary";
log.debug("File Name: " + fname);
log.debug("Content Type:" + fmimetype);
OpenXAdESSignatureFactory xs = (OpenXAdESSignatureFactory) signer;
xs.setXadesFileName(fname);
xs.setXadesFileMimeType(fmimetype);
}
_mw.getGlobalProgressBar().setValue(_ini_percent + 4 * inc);
if (_mw.jTree.getLastSelectedPathComponent() != null)
{
X509CertificateHandler xcert;
try
{
xcert = (X509CertificateHandler) ((DefaultMutableTreeNode) _mw.jTree
.getLastSelectedPathComponent()).getUserObject();
}
catch (NullPointerException e)
{
showSignatureOk = false;
guiFinalize(false);
throw new SignatureAppletException("ERROR_CERTIFICATE_NOT_SELECTED");
}
if (xcert.isDigitalSignatureCertificate() || xcert.isNonRepudiationCertificate() ||
(xcert.isEmailProtectionCertificate() &&
_mw.getAppHandler().getSignatureFormat().equals(SupportedSignatureFormat.CMS)))
{
checkDniAgainstCertificate(xcert);
log.debug("Selected a digital signature certificate");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
InputStream inputStream = new ByteArrayInputStream(OS.inputStreamToByteArray(inputParams.getSignData(currentIndex)));
SupportedDataEncoding encoding = _mw.getAppHandler().getInputDataEncoding();
inputStream = decodeInputData(inc, byteArrayOutputStream, inputStream, encoding);
if (_mw.isShowSignatureEnabled() && !_mw.getAppHandler().getIsBigFile())
{
int sel = JOptionPane.showConfirmDialog(_mw.getMainFrame(), _mw
.getShowDataScrollPane(OS.inputStreamToByteArray(inputStream)), LabelManager
.get("LABEL_SHOW_DATA_WINDOW"), JOptionPane.OK_CANCEL_OPTION);
if (sel != JOptionPane.OK_OPTION)
{
_mw.getAppHandler().callJavaScriptCallbackFunction(
_mw.getAppHandler().getJsSignCancel(), new String[]{});
showSignatureOk = false;
guiFinalize(true);
return;
}
inputStream.reset();
}
_mw.getGlobalProgressBar().setValue(_ini_percent + 6 * inc);
IKeyStore keyStore = xcert.getKeyStore();
log.debug(Collections.list(keyStore.aliases()));
SignatureResult signatureResult = signDocument(signer, sigOpt, xcert, inputStream, keyStore);
checkSignatureValidity(infoLabelField, signatureResult);
deliverOutputResult(inc, outputParams, signatureResult);
}
_mw.getGlobalProgressBar().setValue(_ini_percent + 8 * inc);
}
_mw.getGlobalProgressBar().setValue(_ini_percent + 10 * inc);
guiFinalize(hideWindow);
signatureHandler.callback(null);
}
catch (SSLHandshakeException e)
{
processError(infoLabelField, "ERROR_SSL", e);
}
catch (ClassCastException e)
{
processError(infoLabelField, "ERROR_CERTIFICATE_NOT_SELECTED", e);
}
catch (NullPointerException e)
{
processError(infoLabelField, "ERROR_COMPUTING_SIGNATURE", e);
}
catch (IOException e)
{
processError(infoLabelField, "ERROR_INPUT_SOURCE", e);
}
catch (Exception e)
{
processError(infoLabelField, e.getMessage(), e);
}
}
private SignatureResult signDocument(ISignFormatProvider signer, SignatureOptions sigOpt,
X509CertificateHandler xcert, InputStream inputStream, IKeyStore kAux)
throws SignatureAppletException
{
SignatureResult signatureResult;
try
{
initSignatureOptions(sigOpt, xcert, inputStream, kAux);
log.debug("Signing data");
signatureResult = signer.formatSignature(sigOpt);
}
catch (Exception e)
{
log.error(LabelManager.get("ERROR_COMPUTING_SIGNATURE"), e);
_mw.getAppHandler().callJavaScriptCallbackFunction(
_mw.getAppHandler().getJsSignError(),
new String[]{LabelManager.get("ERROR_COMPUTING_SIGNATURE") + ":" + e.getMessage()});
throw new SignatureAppletException(LabelManager.get("ERROR_COMPUTING_SIGNATURE"));
}
return signatureResult;
}
private void initSignatureOptions(SignatureOptions sigOpt, X509CertificateHandler xcert, InputStream inputStream,
IKeyStore kAux) throws Exception
{
sigOpt.setDataToSign(inputStream);
sigOpt.setCertificate(xcert.getCertificate());
PrivateKey privateKey = (PrivateKey) kAux.getKey(xcert.getAlias());
log.debug("Private key format: " + privateKey.getFormat());
log.debug("Private key algorithm: " + privateKey.getAlgorithm());
sigOpt.setPrivateKey(privateKey);
log.debug("Provider: " + kAux.getProvider().getName());
sigOpt.setProvider(kAux.getProvider());
sigOpt.setSwapToFile(_mw.getAppHandler().getIsBigFile());
if (_mw.getAppHandler().getSignatureFormat().equals(SupportedSignatureFormat.JXADES))
{
if (_mw.getAppHandler().getXAdESBaseRef() != null)
{
String[] baseRefs = _mw.getAppHandler().getXAdESBaseRef();
sigOpt.setReferences(Arrays.asList(baseRefs));
}
sigOpt.setCoSignEnabled(_mw.getAppHandler().isCosign());
sigOpt.setEnveloped(_mw.getAppHandler().isEnveloped());
sigOpt.setDetached(_mw.getAppHandler().isDetached());
}
if (_mw.getAppHandler().getSignatureFormat().equals(SupportedSignatureFormat.CMS_HASH))
{
sigOpt.setHash(true);
}
else if (_mw.getAppHandler().getSignatureFormat().equals(SupportedSignatureFormat.PDF) ||
_mw.getAppHandler().getSignatureFormat().equals(SupportedSignatureFormat.PADES))
{
definePDFSignatureOptions(sigOpt);
}
}
private InputStream decodeInputData(int inc, ByteArrayOutputStream ot, InputStream in, SupportedDataEncoding encoding) throws IOException
{
log.debug("Encoding: " + encoding);
_mw.getGlobalProgressBar().setValue(_ini_percent + 5 * inc);
if (encoding.equals(SupportedDataEncoding.HEX))
{
byte[] inData = OS.inputStreamToByteArray(in);
log.debug("Data from Hex is " + inData.length + " bytes long");
HexEncoder h = new HexEncoder();
h.decode(new String(inData), ot);
in = new ByteArrayInputStream(ot.toByteArray());
}
else if (encoding.equals(SupportedDataEncoding.BASE64))
{
byte[] inData = OS.inputStreamToByteArray(in);
log.debug("Data from Base64 is " + inData.length + " bytes long");
in = new ByteArrayInputStream(Base64.decode(inData));
}
return in;
}
private void checkDniAgainstCertificate(X509CertificateHandler xcert) throws SignatureAppletException
{
String dniToCheck = _mw.getAppHandler().getDniToCheckAgainsCertificate();
if (dniToCheck != null)
{
String dnCertificate = xcert.getCertificate().getSubjectX500Principal().getName();
if (!dnCertificate.contains(dniToCheck))
{
log.error("Error checking DNI " + dniToCheck + " against certificate DN");
_mw.getAppHandler().callJavaScriptCallbackFunction(
_mw.getAppHandler().getJsSignError(),
new String[]{LabelManager.get("ERROR_CHECKING_DNI_AGAINST_CERTIFICATE_DN")});
throw new SignatureAppletException(LabelManager.get("ERROR_CHECKING_DNI_AGAINST_CERTIFICATE_DN"));
}
}
}
private void loadCertificateStore(JLabel infoLabelField, X509CertificateHandler selectedNode, IKeyStore iksh)
throws Exception
{
log.debug("Loading certificate store");
try
{
iksh.load(_mw.getPasswordTextField().getText().toCharArray());
log.debug("Certificate store loaded");
}
catch (Exception e)
{
ByteArrayOutputStream os = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(os);
e.printStackTrace(ps);
String stk = new String(os.toByteArray()).toLowerCase();
if (stk.indexOf("incorrect") > -1)
{
showSignatureOk = false;
guiFinalize(false);
throw new SignatureAppletException("ERROR_INCORRECT_PWD");
}
else
{
infoLabelField.setText("Unexpected error!!!");
}
e.printStackTrace();
}
System.out.println("Certificate Alias: " + iksh.getAliasFromCertificate(selectedNode.getCertificate()));
}
private void processError(JLabel infoLabelField, String keyError, Exception e)
{
e.printStackTrace();
infoLabelField.setText(LabelManager.get(keyError) + ": " + e.getMessage());
signatureHandler.callback(e.getMessage());
try
{
showSignatureOk = false;
guiFinalize(false);
}
catch (Exception e1)
{
infoLabelField.setText(LabelManager.get("ERROR_CANNOT_CLOSE_WINDOW"));
signatureHandler.callback(LabelManager.get("ERROR_CANNOT_CLOSE_WINDOW"));
}
}
private void deliverOutputResult(int inc, OutputParams outputParams, SignatureResult signatureResult)
throws SignatureAppletException
{
SupportedDataEncoding encoding;
_mw.getGlobalProgressBar().setValue(_ini_percent + 7 * inc);
if (signatureResult != null && signatureResult.isValid())
{
log.debug("The signature is valid");
try
{
encoding = _mw.getAppHandler().getOutputDataEncoding();
InputStream res = null;
log.debug("Encoding for output " + encoding);
if (encoding.equals(SupportedDataEncoding.HEX))
{
byte[] tmp = OS.inputStreamToByteArray(signatureResult.getSignatureData());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
HexEncoder h = new HexEncoder();
h.encode(tmp, 0, tmp.length, bos);
res = new ByteArrayInputStream(bos.toByteArray());
}
else if (encoding.equals(SupportedDataEncoding.BASE64))
{
res = new ByteArrayInputStream(Base64.encodeBytesToBytes(
OS.inputStreamToByteArray(signatureResult.getSignatureData())));
}
else
{
res = signatureResult.getSignatureData();
}
outputParams.setSignData(res, currentIndex);
}
catch (Exception e)
{
log.error("Exception decoding data", e);
_mw.getAppHandler().callJavaScriptCallbackFunction(
_mw.getAppHandler().getJsSignError(),
new String[]{LabelManager.get("ERROR_CANNOT_SET_OUTPUT_DATA") + e.getMessage()});
throw new SignatureAppletException(LabelManager.get("ERROR_CANNOT_SET_OUTPUT_DATA"));
}
}
else
{
log.error("The signature is NOT valid");
}
}
private void checkSignatureValidity(JLabel infoLabelField, SignatureResult signatureResult) throws Exception
{
if (signatureResult == null || !signatureResult.isValid())
{
log.debug("The signature is not valid");
String errorMessage = LabelManager.get("ERROR_COMPUTING_SIGNATURE");
for (String msg : signatureResult.getErrors())
{
log.debug("Signature validation error: " + msg);
errorMessage += (" - " + msg);
}
log.error(errorMessage);
infoLabelField.setText(errorMessage);
showSignatureOk = false;
guiFinalize(false);
_mw.getAppHandler().getInputParams().flush();
throw new SignatureAppletException(errorMessage, false);
}
}
private void definePDFSignatureOptions(SignatureOptions sigOpt)
{
log.debug("Define PDF options");
Map<String, String> result = new HashMap<String, String>();
for (Entry<String, String[]> entry : _mw._aph.getBindValues().entrySet())
{
result.put(entry.getKey(), getValue(entry.getValue()));
log.debug("Bind key: " + entry.getKey() + ", Bind value: " + getValue(entry.getValue()));
}
sigOpt.setBindValues(result);
if (hasValue(_mw._aph.getReason()))
{
sigOpt.setReason(getValue(_mw._aph.getReason()));
log.debug("Reason: " + getValue(_mw._aph.getReason()));
}
if (hasValue(_mw._aph.getLocation()))
{
sigOpt.setLocation(getValue(_mw._aph.getLocation()));
log.debug("Location: " + getValue(_mw._aph.getLocation()));
}
if (hasValue(_mw._aph.getContact()))
{
sigOpt.setContact(getValue(_mw._aph.getContact()));
log.debug("Contact: " + getValue(_mw._aph.getContact()));
}
if (hasValue(_mw._aph.getTimestamping()))
{
sigOpt.setTimestamping(getValue(_mw._aph.getTimestamping()).equals("true"));
log.debug("Timestamping: " + getValue(_mw._aph.getTimestamping()));
}
if (hasValue(_mw._aph.getTsaURL()))
{
sigOpt.setTsaURL(getValue(_mw._aph.getTsaURL()));
log.debug("TsaURL: " + getValue(_mw._aph.getTsaURL()));
}
if (hasValue(_mw._aph.getVisibleSignature()))
{
sigOpt.setVisibleSignature(getValue(_mw._aph.getVisibleSignature()).equals("true"));
log.debug("VisibleSignature: " + getValue(_mw._aph.getVisibleSignature()));
}
if (hasValue(_mw._aph.getVisibleSignatureType()))
{
sigOpt.setVisibleSignatureType(getValue(_mw._aph.getVisibleSignatureType()));
log.debug("VisibleSignatureType: " + getValue(_mw._aph.getVisibleSignatureType()));
}
if (hasValue(_mw._aph.getVisibleAreaX()))
{
sigOpt.setVisibleAreaX(new Integer(getValue(_mw._aph.getVisibleAreaX())));
log.debug("VisibleAreaX: " + getValue(_mw._aph.getVisibleAreaX()));
}
if (hasValue(_mw._aph.getVisibleAreaY()))
{
sigOpt.setVisibleAreaY(new Integer(getValue(_mw._aph.getVisibleAreaY())));
log.debug("VisibleAreaY: " + getValue(_mw._aph.getVisibleAreaY()));
}
if (hasValue(_mw._aph.getVisibleAreaX2()))
{
sigOpt.setVisibleAreaX2(new Integer(getValue(_mw._aph.getVisibleAreaX2())));
log.debug("VisibleAreaX2: " + getValue(_mw._aph.getVisibleAreaX2()));
}
if (hasValue(_mw._aph.getVisibleAreaY2()))
{
sigOpt.setVisibleAreaY2(new Integer(getValue(_mw._aph.getVisibleAreaY2())));
log.debug("VisibleAreaY2: " + getValue(_mw._aph.getVisibleAreaY2()));
}
if (hasValue(_mw._aph.getVisibleAreaPage()))
{
sigOpt.setVisibleAreaPage(getValue(_mw._aph.getVisibleAreaPage()));
log.debug("VisibleAreaPage: " + getValue(_mw._aph.getVisibleAreaPage()));
}
if (hasValue(_mw._aph.getVisibleAreaTextSize()))
{
sigOpt.setVisibleAreaTextSize(new Integer(getValue(_mw._aph.getVisibleAreaTextSize())));
log.debug("VisibleAreaTextSize: " + getValue(_mw._aph.getVisibleAreaTextSize()));
}
if (hasValue(_mw._aph.getVisibleAreaImgFile()))
{
sigOpt.setVisibleAreaImgFile(getValue(_mw._aph.getVisibleAreaImgFile()));
log.debug("VisibleAreaImgFile: " + getValue(_mw._aph.getVisibleAreaImgFile()));
}
if (hasValue(_mw._aph.getVisibleAreaRepeatAxis()))
{
sigOpt.setVisibleAreaRepeatAxis(getValue(_mw._aph.getVisibleAreaRepeatAxis()));
log.debug("VisibleAreaRepeatAxis: " + getValue(_mw._aph.getVisibleAreaRepeatAxis()));
}
if (hasValue(_mw._aph.getVisibleAreaTextPattern()))
{
sigOpt.setVisibleAreaTextPattern(getValue(_mw._aph.getVisibleAreaTextPattern()));
log.debug("VisibleAreaTextPattern: " + getValue(_mw._aph.getVisibleAreaTextPattern()));
}
if (hasValue(_mw._aph.getDocumentReference()))
{
sigOpt.setDocumentReference(getValue(_mw._aph.getDocumentReference()));
log.debug("DocumentReference: " + getValue(_mw._aph.getDocumentReference()));
}
if (hasValue(_mw._aph.getDocumentReferenceVerificationUrl()))
{
sigOpt.setDocumentReferenceVerificationUrl(getValue(_mw._aph.getDocumentReferenceVerificationUrl()));
log.debug("DocumentReferenceVerificationUrl: " + getValue(_mw._aph.getDocumentReferenceVerificationUrl()));
}
}
private OutputParams retrieveOutputParams(int inc)
{
_mw.getGlobalProgressBar().setValue(_ini_percent + 2 * inc);
return (OutputParams) _mw.getAppHandler().getOutputParams();
}
private InputParams retrieveInputParams(int inc)
{
_mw.getGlobalProgressBar().setValue(_ini_percent + inc);
return (InputParams) _mw.getAppHandler().getInputParams();
}
private X509CertificateHandler getSelectedCertificate() throws Exception
{
X509CertificateHandler selectedNode;
log.debug("Getting selected certificate");
try
{
selectedNode = (X509CertificateHandler) ((DefaultMutableTreeNode) _mw.jTree
.getLastSelectedPathComponent()).getUserObject();
log.debug("Selected certificate:" + selectedNode.getCertificate().getSubjectDN());
}
catch (NullPointerException e)
{
throw new SignatureAppletException("ERROR_CERTIFICATE_NOT_SELECTED");
}
if (!selectedNode.isDigitalSignatureCertificate()
&& !selectedNode.isNonRepudiationCertificate())
{
showSignatureOk = false;
guiFinalize(false);
throw new SignatureAppletException("ERROR_CERTIFICATE_USE");
}
log.debug("Validating certificate");
try
{
selectedNode.getCertificate().checkValidity();
log.debug("The certificate is valid");
}
catch (CertificateException cex)
{
int selection = JOptionPane.showOptionDialog(_mw.getMainFrame(), LabelManager
.get("LABEL_CERTIFICATE_EXPIRED"), LabelManager
.get("LABEL_CERTIFICATE_EXPIRED_TITLE"), JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE, null, new String[]{"Yes", "No"}, "No"
);
if (selection == JOptionPane.NO_OPTION)
{
showSignatureOk = false;
guiFinalize(false);
throw new SignatureAppletException("ERROR_CERTIFICATE_EXPIRED");
}
}
return selectedNode;
}
private String getValue(String[] values)
{
if (values.length == 1 || this._step >= values.length)
{
return values[0];
}
else
{
return values[this._step];
}
}
private boolean hasValue(String[] element)
{
return (element != null && element.length > 0);
}
private void guiInitialize()
{
if (_mw != null)
{
_mw.getInformationLabelField().setText(LabelManager.get("COMPUTING_SIGNATURE"));
_mw.SignButton.setEnabled(false);
_mw.jTree.setEnabled(false);
_mw.getGlobalProgressBar().setIndeterminate(false);
_mw.getGlobalProgressBar().setVisible(true);
_mw.getGlobalProgressBar().setStringPainted(true);
}
}
private void guiFinalize(boolean hideWindow) throws Exception
{
if (_mw != null)
{
if (showSignatureOk && hideWindow == true)
{
JOptionPane.showMessageDialog(_mw.getMainFrame(), LabelManager
.get("SIGN_PROCESS_OK"), "", JOptionPane.INFORMATION_MESSAGE);
_mw.getAppHandler().getOutputParams().signOk();
}
_mw.getGlobalProgressBar().setVisible(false);
_mw.jTree.setEnabled(true);
_mw.SignButton.setEnabled(true);
if (hideWindow)
{
_mw.mainFrame.setVisible(false);
}
else
{
_mw.getShowSignatureCheckBox().setVisible(true);
}
}
this._ini_percent = 0;
this._end_percent = 100;
if (showSignatureOk && hideWindow == false)
{
_mw.getAppHandler().getOutputParams().signOk();
}
}
public void setMainWindow(MainWindow mw)
{
_mw = mw;
}
public void setShowSignatureOk(boolean b)
{
showSignatureOk = b;
}
}