package es.uji.security.crypto.openxades; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.security.PrivateKey; import java.security.Provider; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Date; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.log4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import es.uji.security.crypto.ISignFormatProvider; import es.uji.security.crypto.SignatureOptions; import es.uji.security.crypto.SignatureResult; import es.uji.security.crypto.config.ConfigManager; import es.uji.security.crypto.config.OS; import es.uji.security.crypto.openxades.digidoc.CertValue; import es.uji.security.crypto.openxades.digidoc.DataFile; import es.uji.security.crypto.openxades.digidoc.DigiDocException; import es.uji.security.crypto.openxades.digidoc.Signature; import es.uji.security.crypto.openxades.digidoc.SignedDoc; import es.uji.security.crypto.openxades.digidoc.TimestampInfo; import es.uji.security.crypto.openxades.digidoc.factory.CanonicalizationFactory; import es.uji.security.crypto.openxades.digidoc.factory.DigiDocFactory; import es.uji.security.crypto.openxades.digidoc.factory.FactoryManager; import es.uji.security.crypto.timestamp.TSResponse; import es.uji.security.crypto.timestamp.TSResponseToken; import es.uji.security.crypto.timestamp.TimeStampFactory; import es.uji.security.util.Base64; import es.uji.security.util.i18n.LabelManager; public class OpenXAdESSignatureFactory implements ISignFormatProvider { private Logger log = Logger.getLogger(OpenXAdESSignatureFactory.class); private String xadesFileName = "data.xml"; private String xadesFileMimeType = "application/binary"; public void setXadesFileName(String xadesFileName) { this.xadesFileName = xadesFileName; } public void setXadesFileMimeType(String xadesFileMimeType) { this.xadesFileMimeType = xadesFileMimeType; } public SignatureResult formatSignature(SignatureOptions signatureOptions) throws Exception { log.debug("Using XAdESSignatureFactory"); SignatureResult signatureResult = new SignatureResult(); // Signature data Provider provider = signatureOptions.getProvider(); X509Certificate certificate = signatureOptions.getCertificate(); PrivateKey privateKey = signatureOptions.getPrivateKey(); log.debug(provider.getName() + " provider found"); if (certificate == null) { signatureResult.setValid(false); signatureResult.addError(LabelManager.get("ERROR_DDOC_NOCERT")); return signatureResult; } if (privateKey == null) { signatureResult.setValid(false); signatureResult.addError(LabelManager.get("ERROR_DDOC_NOKEY")); return signatureResult; } // Retrieve DigiDoc configuration ConfigManager conf = ConfigManager.getInstance(); // If the file to sign is big, prepare temporal files File temporal = null; File ftoSign = new File("jar://data.xml"); if (signatureOptions.getSwapToFile()) { temporal = new File(OS.getSystemTmpDir() + "/signatureData.dat"); OS.dumpToFile(temporal, signatureOptions.getDataToSign()); xadesFileName = temporal.getAbsolutePath(); ftoSign = temporal; } // Check if input is a DigiDoc file for cosign SignedDoc signedDoc = null; //TODO: This can cause problems with big file management // If byte[] is not retrieved, the stream gets empty when readSignedDoc // is executed byte[] data = OS.inputStreamToByteArray(signatureOptions.getDataToSign()); int lengthDocs = 0; try { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); Document current = documentBuilder.parse(new ByteArrayInputStream(data)); NodeList docs = current.getElementsByTagNameNS("http://www.sk.ee/DigiDoc/v1.3.0#", "SignedDoc"); lengthDocs = docs.getLength(); if (docs.getLength() == 1) { DigiDocFactory digFac = FactoryManager.getDigiDocFactory(); signedDoc = digFac.readSignedDoc(new ByteArrayInputStream(data)); } else { throw new DigiDocException(-1, "", null); } } // Here we can get also SaxParseException. // catch (Exception dde) catch (Exception dde) { if (lengthDocs == 0) { signedDoc = new SignedDoc(SignedDoc.FORMAT_DIGIDOC_XML, SignedDoc.VERSION_1_3); // Add a new reference in Bas64 and establish body data DataFile df = signedDoc.addDataFile(ftoSign, this.xadesFileMimeType, DataFile.CONTENT_EMBEDDED_BASE64); signedDoc.getDataFile(0).setFileName(xadesFileName); signedDoc.getDataFile(0).setMimeType(xadesFileMimeType); if (!signatureOptions.getSwapToFile()) { df.setBody(data); df.setSize(data.length); } } else throw dde; } // Prepare the signature String[] roles; if (signatureOptions.getSignerRole() != null) { roles = new String[] { signatureOptions.getSignerRole() }; } else { roles = new String[] {}; } Signature signature = signedDoc.prepareSignature((X509Certificate) certificate, roles, null); CertValue certValue = null; // Sign byte[] sidigest = signature.getSignedContent(); //System.out.println("Contenido a firmar: " + new String(sidigest)); if (sidigest == null) { signatureResult.setValid(false); signatureResult.addError(LabelManager.get("ERROR_DDOC_NODIGEST")); return signatureResult; } java.security.Signature rsa = java.security.Signature.getInstance("SHA1withRSA", provider); rsa.initSign(privateKey); rsa.update(sidigest); byte[] res = rsa.sign(); if (res == null) { log.error(LabelManager.get("ERROR_DDOC_SIGNATURE")); signatureResult.setValid(false); signatureResult.addError(LabelManager.get("ERROR_DDOC_SIGNATURE")); return signatureResult; } log.debug("Signing XAdES info. XAdES signature length " + res.length); // Add the signature to the signed doc signature.setSignatureValue(res); // Get the timestamp and add it int tsaCount = conf.getIntProperty("DIGIDOC_TSA_COUNT", 0); if (tsaCount != 0) { String tsaUrl = conf.getProperty("DIGIDOC_TSA1_URL"); String tsa1_ca = conf.getProperty("DIGIDOC_TSA1_CA_CERT"); byte[] signatureValue = signature.getSignatureValue().toString().getBytes(); TSResponse response = TimeStampFactory.getTimeStampResponse(tsaUrl, signatureValue, true); X509Certificate xcaCert = ConfigManager.readCertificate(tsa1_ca); TSResponseToken responseToken = new TSResponseToken(response); TSResponse mmm = new TSResponse(Base64.decode(Base64.encodeBytes(response.getEncodedToken()))); if (! responseToken.verify(xcaCert, signatureValue)) { signatureResult.setValid(false); signatureResult.addError("Obtained timestamp is not valid"); return signatureResult; } TimestampInfo ts = new TimestampInfo("TS1", TimestampInfo.TIMESTAMP_TYPE_SIGNATURE); ts.setTimeStampResponse(response); ts.setSignature(signature); ts.setHash(responseToken.getMessageImprint()); signature.addTimestampInfo(ts); if (tsa1_ca == null) { signatureResult.setValid(false); signatureResult.addError(LabelManager.get("ERROR_DDOC_TSACA")); return signatureResult; } certValue = new CertValue(); certValue.setType(CertValue.CERTVAL_TYPE_TSA); certValue.setCert(xcaCert); certValue.setId(signature.getId() + "-TSA_CERT"); // Check the certificate validity against the timestamp Date d = ts.getTime(); try { certificate.checkValidity(d); } catch (CertificateException cex) { signatureResult.setValid(false); signatureResult.addError(LabelManager.get("ERROR_CERTIFICATE_EXPIRED")); return signatureResult; } } try { // Add TSA certificate if (tsaCount != 0) { signature.addCertValue(certValue); } // OCSP validation String certVerifier = conf.getProperty("DIGIDOC_CERT_VERIFIER").trim(); if (certVerifier.equals("OCSP") || certVerifier.equals("POST")) { signature.getConfirmation(); } else { signatureResult.setValid(false); signatureResult.addError("Unknows DIGIDOC_CERT_VERIFIER option. Must be OCSP or POST"); return signatureResult; } String tsaUrl = conf.getProperty("DIGIDOC_TSA1_URL"); String tsa1_ca = conf.getProperty("DIGIDOC_TSA1_CA_CERT"); byte[] completeCertificateRefs = signature.getUnsignedProperties() .getCompleteCertificateRefs().toXML(); byte[] completeRevocationRefs = signature.getUnsignedProperties() .getCompleteRevocationRefs().toXML(); CanonicalizationFactory canFac = FactoryManager.getCanonicalizationFactory(); byte[] canCompleteCertificateRefs = canFac.canonicalize(completeCertificateRefs, SignedDoc.CANONICALIZATION_METHOD_20010315); byte[] canCompleteRevocationRefs = canFac.canonicalize(completeRevocationRefs, SignedDoc.CANONICALIZATION_METHOD_20010315); byte[] refsOnlyData = new byte[canCompleteCertificateRefs.length + canCompleteRevocationRefs.length]; System.arraycopy(canCompleteCertificateRefs, 0, refsOnlyData, 0, canCompleteCertificateRefs.length); System.arraycopy(canCompleteRevocationRefs, 0, refsOnlyData, canCompleteCertificateRefs.length, canCompleteRevocationRefs.length); X509Certificate xcaCert = ConfigManager.readCertificate(tsa1_ca); TSResponse response = TimeStampFactory.getTimeStampResponse(tsaUrl, refsOnlyData, true); TSResponseToken responseToken = new TSResponseToken(response); if (! responseToken.verify(xcaCert, refsOnlyData)) { signatureResult.setValid(false); signatureResult.addError("Obtained timestamp is not valid"); return signatureResult; } TimestampInfo ts = new TimestampInfo("TS2", TimestampInfo.TIMESTAMP_TYPE_REFS_ONLY); ts.setTimeStampResponse(response); ts.setSignature(signature); ts.setHash(responseToken.getMessageImprint()); signature.addTimestampInfo(ts); log.debug("OCSP verification completed"); } catch (DigiDocException e) { log.debug("\n\n" + this.getClass().getName() + ": No se pudo realizar la confirmacion OCSP" + e.getMessage()); signatureResult.setValid(false); if (e.getCode() == DigiDocException.ERR_CERT_REVOKED) { signatureResult.addError(LabelManager.get("ERROR_DDOC_CERTREVOKED")); } else if (e.getCode() == DigiDocException.ERR_CERT_EXPIRED) { signatureResult.addError(LabelManager.get("ERROR_DDOC_CERTEXPIRED")); } else if (e.getCode() == DigiDocException.ERR_CA_CERT_READ) { signatureResult.addError(LabelManager.get("ERROR_DDOC_CERT_NOT_ALLOWED")); } else if (e.getCode() == DigiDocException.ERR_OCSP_READ_FILE || e.getCode() == DigiDocException.ERR_OCSP_ISSUER_CA_NOT_FOUND) { signatureResult.addError(LabelManager.get("ERROR_DDOC_CERT_NOT_ALLOWED")); } else { signatureResult.addError(LabelManager.get("ERROR_DDOC_CERTGENERIC")); } return signatureResult; } signatureResult.setValid(true); // If we sign a big file, we write the result into a file and get back a FileInputStream File random = new File(OS.getSystemTmpDir() + "/signature.xsig"); if (signatureOptions.getSwapToFile()) { signedDoc.writeToFile(random); signatureResult.setSignatureData(new FileInputStream(random)); } else { signatureResult.setSignatureData(new ByteArrayInputStream(signedDoc.toXML().getBytes())); } // Remove temporal files for big file signature if (signatureOptions.getSwapToFile()) { try { random.delete(); } catch (Exception e) { e.printStackTrace(); } } return signatureResult; } }