/**
*
*/
package at.gv.egiz.pdfas.impl.api;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Vector;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import at.gv.egiz.pdfas.api.PdfAs;
import at.gv.egiz.pdfas.api.analyze.AnalyzeParameters;
import at.gv.egiz.pdfas.api.analyze.AnalyzeResult;
import at.gv.egiz.pdfas.api.commons.Constants;
import at.gv.egiz.pdfas.api.commons.SignatureInformation;
import at.gv.egiz.pdfas.api.exceptions.PdfAsException;
import at.gv.egiz.pdfas.api.sign.SignParameters;
import at.gv.egiz.pdfas.api.sign.SignResult;
import at.gv.egiz.pdfas.api.verify.VerifyAfterAnalysisParameters;
import at.gv.egiz.pdfas.api.verify.VerifyParameters;
import at.gv.egiz.pdfas.api.verify.VerifyResult;
import at.gv.egiz.pdfas.api.verify.VerifyResults;
import at.gv.egiz.pdfas.commandline.CommandlineConnectorChooser;
import at.gv.egiz.pdfas.exceptions.ErrorCode;
import at.gv.egiz.pdfas.framework.config.SettingsHelper;
import at.gv.egiz.pdfas.framework.input.ExtractionStage;
import at.gv.egiz.pdfas.framework.input.PdfDataSource;
import at.gv.egiz.pdfas.framework.signator.SignatorInformation;
import at.gv.egiz.pdfas.framework.vfilter.VerificationFilterParameters;
import at.gv.egiz.pdfas.impl.api.analyze.AnalyzeResultImpl;
import at.gv.egiz.pdfas.impl.api.commons.DataSinkAdapter;
import at.gv.egiz.pdfas.impl.api.commons.PdfDataSourceAdapter;
import at.gv.egiz.pdfas.impl.api.commons.SignatureInformationAdapter;
import at.gv.egiz.pdfas.impl.api.commons.SignatureProfileImpl;
import at.gv.egiz.pdfas.impl.api.commons.TextDataSourceAdapter;
import at.gv.egiz.pdfas.impl.api.sign.ActualSignaturePositionAdapter;
import at.gv.egiz.pdfas.impl.api.sign.SignResultImpl;
import at.gv.egiz.pdfas.impl.api.verify.VerifyResultAdapter;
import at.gv.egiz.pdfas.impl.api.verify.VerifyResultsImpl;
import at.gv.egiz.pdfas.impl.input.DelimitedPdfDataSource;
import at.gv.egiz.pdfas.impl.vfilter.VerificationFilterParametersImpl;
import at.gv.egiz.pdfas.utils.ConfigUtils;
import at.knowcenter.wag.egov.egiz.PdfAS;
import at.knowcenter.wag.egov.egiz.PdfASID;
import at.knowcenter.wag.egov.egiz.cfg.SettingsReader;
import at.knowcenter.wag.egov.egiz.exceptions.OutOfMemoryException;
import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException;
import at.knowcenter.wag.egov.egiz.exceptions.PresentableException;
import at.knowcenter.wag.egov.egiz.exceptions.SignatureException;
import at.knowcenter.wag.egov.egiz.framework.SignatorFactory;
import at.knowcenter.wag.egov.egiz.pdf.BinarySignatureHolder;
import at.knowcenter.wag.egov.egiz.pdf.NoSignatureHolder;
import at.knowcenter.wag.egov.egiz.pdf.ObjectExtractor;
import at.knowcenter.wag.egov.egiz.pdf.SignatureHolder;
import at.knowcenter.wag.egov.egiz.pdf.TablePos;
import at.knowcenter.wag.egov.egiz.pdf.TextualSignatureHolder;
import at.knowcenter.wag.egov.egiz.sig.SignatureResponse;
import at.knowcenter.wag.egov.egiz.sig.SignatureTypeDefinition;
import at.knowcenter.wag.egov.egiz.sig.SignatureTypes;
/**
* Implementation of the {@link PdfAs} interface.
*
* @author wprinz
*/
public class PdfAsObject implements PdfAs
{
/**
* The log.
*/
private static Log log = LogFactory.getLog(CheckHelper.class);
/**
* This constructor is for internal use only - use
* {@link at.gv.egiz.pdfas.PdfAsFactory} instead.
* Note: IAIK JCE and IAIK ECC security providers are automatically registered.
*
* @param workDirectory
* The work directory.
* @throws PdfAsException
* Thrown, if the configuration cannot be processed.
*/
public PdfAsObject(File workDirectory) throws PdfAsException
{
this(workDirectory, SettingsReader.REGISTER_IAIK_PROVIDERS_ON_DEFAULT);
}
/**
* This constructor is for internal use only - use
* {@link at.gv.egiz.pdfas.PdfAsFactory} instead.
*
* @param workDirectory
* The work directory.
* @param registerProvider true
: automatically registers IAIK JCE and ECC Provider;
* false
: providers will NOT be automatically registered, providers
* needed have to be registered by the API user
* @throws PdfAsException
* Thrown, if the configuration cannot be processed.
*/
public PdfAsObject(File workDirectory, boolean registerProvider) throws PdfAsException
{
String path = workDirectory != null ? workDirectory.getPath() : null;
SettingsReader.initialize(path, path);
reloadConfig(registerProvider);
}
/**
* This constructor is for internal use only - use
* {@link at.gv.egiz.pdfas.PdfAsFactory} instead.
* Note: IAIK JCE and IAIK ECC security providers are automatically registered.
*
* @throws PdfAsException
* Thrown, if the configuration cannot be processed.
*/
public PdfAsObject() throws PdfAsException
{
this(null);
}
/**
* @see at.gv.egiz.pdfas.api.PdfAs#reloadConfig()
*/
public void reloadConfig() throws PdfAsException
{
ConfigUtils.initializeLogger();
SettingsReader.createInstance();
SignatureTypes.createInstance();
}
/**
* @param registerProvider true
: automatically registers IAIK JCE and ECC Provider;
* false
: providers will NOT be automatically registered, providers
* needed have to be registered by the API user
* @see at.gv.egiz.pdfas.api.PdfAs#reloadConfig()
*/
private void reloadConfig(boolean registerProvider) throws PdfAsException
{
ConfigUtils.initializeLogger();
SettingsReader.createInstance(registerProvider);
SignatureTypes.createInstance();
}
/**
* @see at.gv.egiz.pdfas.api.PdfAs#getProfileInformation()
*/
public List getProfileInformation() throws PdfAsException
{
log.debug("Collecting profile information.");
final String MOA_SIGN_KEY_IDENTIFIER_KEY = "moa.sign.KeyIdentifier";
SettingsReader settings = SettingsReader.getInstance();
final String defaultMoaKeyIdentifiert = settings.getSetting(MOA_SIGN_KEY_IDENTIFIER_KEY, null);
SignatureTypes types = SignatureTypes.getInstance();
List profiles = types.getSignatureTypeDefinitions();
List profileInformation = new ArrayList(profiles.size());
Iterator it = profiles.iterator();
while (it.hasNext())
{
SignatureTypeDefinition profile = (SignatureTypeDefinition) it.next();
final String profileId = profile.getType();
log.debug("Processing profile \"" + profileId + "\".");
final String moaKeyIdentifier = settings.getSetting("sig_obj." + profileId + "." + MOA_SIGN_KEY_IDENTIFIER_KEY, defaultMoaKeyIdentifiert);
// modified by tknall
SignatureProfileImpl signatureProfile = new SignatureProfileImpl(profileId, moaKeyIdentifier);
// start - added by tknall
// signature entries relevant to the search algorithm
Properties signatureEntries = new Properties();
// search for table entries
String parentPropertyKey = "sig_obj." + profileId + ".table";
log.debug("Looking for subkeys of \"" + parentPropertyKey + "\".");
Vector keysVector = settings.getSettingKeys(parentPropertyKey);
if (keysVector != null) {
Iterator keyIt = keysVector.iterator();
while (keyIt.hasNext()) {
String subKey = (String) keyIt.next();
if (subKey != null && subKey.length() > 0) {
String fullKey = parentPropertyKey + "." + subKey;
String value = settings.getValueFromKey(fullKey);
int lastIndex = fullKey.lastIndexOf(".");
if (lastIndex != -1) {
String endsWith = fullKey.substring(lastIndex + 1);
if (value != null && value.length() > 0) {
if (NumberUtils.isDigits(endsWith)) {
signatureEntries.setProperty(fullKey, value);
} else {
log.debug("Ignoring table entry \"" + fullKey + "\" because it does not end with a digit. Therefore it is not relevant for the seach algorithm.");
}
} else {
log.warn("Problem detected with key \"" + fullKey + "\". The value is empty.");
}
}
}
}
}
// search for table entries
parentPropertyKey = "sig_obj." + profileId + ".key";
log.debug("Looking for subkeys of \"" + parentPropertyKey + "\".");
keysVector = settings.getSettingKeys(parentPropertyKey);
if (keysVector != null) {
Iterator keyIt = keysVector.iterator();
while (keyIt.hasNext()) {
String subKey = (String) keyIt.next();
if (subKey != null && subKey.length() > 0) {
String fullKey = parentPropertyKey + "." + subKey;
String value = settings.getValueFromKey(fullKey);
if (value != null && value.length() > 0) {
signatureEntries.setProperty(fullKey, value);
} else {
log.warn("Problem detected with key \"" + fullKey + "\". The value is empty.");
}
}
}
}
// set properties
signatureProfile.setSignatureBlockEntries(signatureEntries);
// stop - added by tknall
profileInformation.add(signatureProfile);
}
return profileInformation;
}
/**
* @see at.gv.egiz.pdfas.api.PdfAs#sign(at.gv.egiz.pdfas.api.sign.SignParameters)
*/
public SignResult sign(SignParameters signParameters) throws PdfAsException
{
CheckHelper.checkSignParameters(signParameters);
signParameters.setDocument(PdfAS.applyStrictMode(signParameters.getDocument()));
if (signParameters.getSignatureProfileId() == null)
{
SettingsReader settings = SettingsReader.getInstance();
String defaultProfile = settings.getValueFromKey(SignatureTypes.DEFAULT_TYPE);
signParameters.setSignatureProfileId(defaultProfile);
}
PdfASID signatorId = null;
if (signParameters.getSignatureType().equals(Constants.SIGNATURE_TYPE_BINARY))
{
signatorId = SignatorFactory.MOST_RECENT_BINARY_SIGNATOR_ID;
}
if (signParameters.getSignatureType().equals(Constants.SIGNATURE_TYPE_TEXTUAL))
{
signatorId = SignatorFactory.MOST_RECENT_TEXTUAL_SIGNATOR_ID;
}
if (signParameters.getSignatureType().equals(Constants.SIGNATURE_TYPE_DETACHEDTEXTUAL))
{
signatorId = SignatorFactory.MOST_RECENT_DETACHEDTEXT_SIGNATOR_ID;
}
TablePos pos = PosHelper.formTablePos(signParameters.getSignaturePositioning());
String connectorId = CommandlineConnectorChooser.chooseCommandlineConnectorForSign(signParameters.getSignatureDevice());
try {
SignatorInformation si = PdfAS.signCommandline(
new PdfDataSourceAdapter(signParameters.getDocument()),
new DataSinkAdapter(signParameters.getOutput()),
signatorId,
connectorId,
signParameters.getSignatureProfileId(),
signParameters.getSignatureKeyIdentifier(),
pos,
signParameters.getTimeStamperImpl()
);
return new SignResultImpl(signParameters.getOutput(), si.getSignSignatureObject().getX509Certificate(),
new ActualSignaturePositionAdapter(si.getActualTablePos()), si.getNonTextualObjects());
} catch (java.lang.OutOfMemoryError e) {
throw new OutOfMemoryException(ErrorCode.OUT_OF_MEMORY_ERROR, "Insufficient memory allocated to virtual machine. Start Java with parameters \"-Xms128m -Xmx786m -XX:MaxPermSize=256m\".", e);
}
}
/**
* @see at.gv.egiz.pdfas.api.PdfAs#verify(at.gv.egiz.pdfas.api.verify.VerifyParameters)
*/
public VerifyResults verify(VerifyParameters verifyParameters) throws PdfAsException
{
CheckHelper.checkVerifyParameters(verifyParameters);
AnalyzeParameters ap = new AnalyzeParameters();
fillAnalyzeParametersWithVerifyParameters(ap, verifyParameters);
AnalyzeResult analyzeResult = analyze(ap);
if (verifyParameters.getSignatureToVerify() != Constants.VERIFY_ALL)
{
if (verifyParameters.getSignatureToVerify() >= analyzeResult.getSignatures().size())
{
throw new SignatureException(312, "The selected signature to be verified doesn't exist. " + verifyParameters.getSignatureToVerify());
}
Object stv = analyzeResult.getSignatures().get(verifyParameters.getSignatureToVerify());
List selectedSignature = new ArrayList(1);
selectedSignature.add(stv);
analyzeResult = new AnalyzeResultImpl(selectedSignature);
}
VerifyAfterAnalysisParameters vaap = new VerifyAfterAnalysisParameters();
vaap.setAnalyzeResult(analyzeResult);
fillVerifyAfterAnalysisParametersWithVerifyParameters(vaap, verifyParameters);
VerifyResults res = verify(vaap);
return res;
}
/**
* Copies all adequate parameters from the {@link VerifyParameters} to the
* {@link AnalyzeParameters}.
*
* @param ap
* The {@link AnalyzeParameters}.
* @param vp
* The {@link VerifyParameters}.
*/
protected void fillAnalyzeParametersWithVerifyParameters(AnalyzeParameters ap, VerifyParameters vp)
{
ap.setDocument(vp.getDocument());
ap.setVerifyMode(vp.getVerifyMode());
ap.setReturnNonTextualObjects(vp.isReturnNonTextualObjects());
}
/**
* Copies all adequate parameters from the {@link VerifyParameters} to the
* {@link VerifyAfterAnalysisParameters}.
*
* @param vaap
* The {@link VerifyAfterAnalysisParameters}.
* @param vp
* The {@link VerifyParameters}.
*/
protected void fillVerifyAfterAnalysisParametersWithVerifyParameters(VerifyAfterAnalysisParameters vaap, VerifyParameters vp)
{
vaap.setSignatureDevice(vp.getSignatureDevice());
vaap.setVerificationTime(vp.getVerificationTime());
vaap.setReturnHashInputData(vp.isReturnHashInputData());
}
/**
* @see at.gv.egiz.pdfas.api.PdfAs#analyze(at.gv.egiz.pdfas.api.analyze.AnalyzeParameters)
*/
public AnalyzeResult analyze(AnalyzeParameters analyzeParameters) throws PdfAsException
{
CheckHelper.checkAnalyzeParameters(analyzeParameters);
VerificationFilterParameters parametersConfig = SettingsHelper.readVerificationFilterParametersFromSettings();
boolean binaryOnly = parametersConfig.extractBinarySignaturesOnly();
if (analyzeParameters.getVerifyMode().equals(Constants.VERIFY_MODE_BINARY_ONLY))
{
binaryOnly = true;
}
boolean assumeOnlySB = parametersConfig.assumeOnlySignatureUpdateBlocks();
if (analyzeParameters.getVerifyMode().equals(Constants.VERIFY_MODE_SEMI_CONSERVATIVE))
{
assumeOnlySB = true;
}
if (analyzeParameters.getVerifyMode().equals(Constants.VERIFY_MODE_FULL_CONSERVATIVE))
{
assumeOnlySB = false;
}
VerificationFilterParameters parameters = new VerificationFilterParametersImpl(binaryOnly, assumeOnlySB, parametersConfig.scanForOldSignatures());
at.gv.egiz.pdfas.framework.input.DataSource inputDataSource = null;
if (analyzeParameters.getDocument().getMimeType().equals("application/pdf"))
{
inputDataSource = new PdfDataSourceAdapter(analyzeParameters.getDocument());
}
else
{
try
{
inputDataSource = new TextDataSourceAdapter(analyzeParameters.getDocument());
}
catch (UnsupportedEncodingException e)
{
throw new PresentableException(ErrorCode.DOCUMENT_CANNOT_BE_READ, "The characterEncoding is not supported." + analyzeParameters.getDocument().getCharacterEncoding(), e);
}
}
assert inputDataSource != null;
try {
ExtractionStage es = new ExtractionStage();
List signature_holders = es.extractSignatureHolders(inputDataSource, parameters);
// List sigInfs = new ArrayList(signature_holders.size());
List sigInfs = new ArrayList();
List noSigs = new ArrayList();
Iterator it = signature_holders.iterator();
while (it.hasNext())
{
SignatureHolder sh = (SignatureHolder)it.next();
if(sh instanceof NoSignatureHolder) {
noSigs.add(sh);
} else {
SignatureInformation si = new SignatureInformationAdapter(sh);
sigInfs.add(si);
if (analyzeParameters.isReturnNonTextualObjects()) {
si.setNonTextualObjects(doExtractNonTexualObjects(sh, (PdfDataSource) inputDataSource));
}
}
}
return new AnalyzeResultImpl(sigInfs, noSigs);
} catch (java.lang.OutOfMemoryError e) {
throw new OutOfMemoryException(ErrorCode.OUT_OF_MEMORY_ERROR, "Insufficient memory allocated to virtual machine. Start Java with parameters \"-Xms128m -Xmx786m -XX:MaxPermSize=256m\".", e);
}
}
private List doExtractNonTexualObjects(SignatureHolder sh, PdfDataSource pdfDataSource) {
if (sh == null) return null;
if (sh instanceof BinarySignatureHolder) {
BinarySignatureHolder bsh = (BinarySignatureHolder)sh;
return ObjectExtractor.extractNonTextInfo(bsh.getSignedPdf());
} else if (sh instanceof TextualSignatureHolder) {
TextualSignatureHolder tsh = (TextualSignatureHolder)sh;
if (tsh.getUiBlockEndPos() == 0) {
log.warn("uiblockendpos not available. Extract objects from final pdf document");
return ObjectExtractor.extractNonTextInfo(pdfDataSource);
}
DelimitedPdfDataSource dpds = new DelimitedPdfDataSource(pdfDataSource, tsh.getUiBlockEndPos());
return ObjectExtractor.extractNonTextInfo(dpds);
} else {
return null;
}
}
/**
* @see at.gv.egiz.pdfas.api.PdfAs#verify(at.gv.egiz.pdfas.api.verify.VerifyAfterAnalysisParameters)
*/
public VerifyResults verify(VerifyAfterAnalysisParameters verifyAfterAnalysisParameters) throws PdfAsException
{
CheckHelper.checkVerifyAfterAnalysisParameters(verifyAfterAnalysisParameters);
List signatures = verifyAfterAnalysisParameters.getAnalyzeResult().getSignatures();
// added by tknall
if (signatures == null || signatures.isEmpty()) {
throw new PDFDocumentException(ErrorCode.DOCUMENT_NOT_SIGNED, "PDF document not signed."); //$NON-NLS-1$
}
List signature_holders = new ArrayList(signatures.size());
Iterator it = signatures.iterator();
while (it.hasNext())
{
SignatureInformation si = (SignatureInformation) it.next();
SignatureHolder sh = (SignatureHolder) si.getInternalSignatureInformation();
signature_holders.add(sh);
}
assert signature_holders.size() == signatures.size();
try {
List results = PdfAS.verifySignatureHolders(signature_holders, verifyAfterAnalysisParameters.getSignatureDevice(), verifyAfterAnalysisParameters.isReturnHashInputData(), verifyAfterAnalysisParameters.getVerificationTime());
List vrs = new ArrayList(results.size());
assert signature_holders.size() == results.size() : "Not all signatures were verified.";
for (int i = 0; i < signature_holders.size(); i++)
{
SignatureResponse response = (SignatureResponse) results.get(i);
SignatureHolder holder = (SignatureHolder) signature_holders.get(i);
VerifyResult vr = new VerifyResultAdapter(response, holder, verifyAfterAnalysisParameters.getVerificationTime());
vr.setNonTextualObjects( ((SignatureInformation) signatures.get(i)).getNonTextualObjects());
vrs.add(vr);
}
VerifyResultsImpl verifyResults = new VerifyResultsImpl(vrs);
return verifyResults;
} catch (java.lang.OutOfMemoryError e) {
throw new OutOfMemoryException(ErrorCode.OUT_OF_MEMORY_ERROR, "Insufficient memory allocated to virtual machine. Start Java with parameters \"-Xms128m -Xmx786m -XX:MaxPermSize=256m\".", e);
}
}
}