diff options
69 files changed, 2419 insertions, 2607 deletions
@@ -19,6 +19,10 @@ pdf-as-tests/src/test/test-suites/*/ pdf-as-tests/src/test/test-suites/**/index.html pdf-as-tests/src/test/test-suites/**/test_result.html pdf-as-tests/src/test/test-suites/**/out +pdf-as-tests/src/test/public_pdfbox1/* +pdf-as-tests/src/test/public_pdfbox1/*/ + +pdf-as-lib/src/main/resources/config/config.zip **/generated/** diff --git a/build.gradle b/build.gradle index be66b393..9eb8a547 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ allprojects { url "https://repo.spring.io/milestone/" } } - version = '4.3.0-SNAPSHOT' + version = '4.3.3-SNAPSHOT' } subprojects { @@ -58,8 +58,9 @@ subprojects { } dependencies { - compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.24' - annotationProcessor group: 'org.projectlombok', name: 'lombok', version: '1.18.24' + implementation "org.projectlombok:lombok:1.18.28" + annotationProcessor "org.projectlombok:lombok:1.18.28" + testAnnotationProcessor "org.projectlombok:lombok:1.18.28" testImplementation 'junit:junit:4.13.2' } @@ -83,7 +84,7 @@ subprojects { revision = getCheckedOutGitCommitHash() //tomcatVersion = '7.0.54'; //tomcatVersion = '8.0.36'; - tomcatVersion = '9.0.46'; + tomcatVersion = '9.0.73'; slf4jVersion = '1.7.36' cxfVersion = '3.5.5' } @@ -187,21 +188,9 @@ task releases(type: Copy) { def getCheckedOutGitCommitHash() { - def gitFolder = "$projectDir/.git/" - def takeFromHash = 40 - /* - * '.git/HEAD' contains either - * in case of detached head: the currently checked out commit hash - * otherwise: a reference to a file containing the current commit hash - */ - def head = new File(gitFolder + "HEAD").text.split(":") // .git/HEAD - def isCommit = head.length == 1 // e5a7c79edabbf7dd39888442df081b1c9d8e88fd - // def isRef = head.length > 1 // ref: refs/heads/master - - if(isCommit) return head[0].trim().take(takeFromHash) // e5a7c79edabb - - def refHead = new File(gitFolder + head[1].trim()) // .git/refs/heads/master - refHead.text.trim().take takeFromHash + def takeFromHash = 40 + 'git rev-parse --verify HEAD'.execute().text.trim().take takeFromHash + } /* task docs(type: Javadoc) { diff --git a/doc/AnbindungExterneWebanwendung.docx b/doc/AnbindungExterneWebanwendung.docx Binary files differindex 764b9517..6a98e7a1 100644 --- a/doc/AnbindungExterneWebanwendung.docx +++ b/doc/AnbindungExterneWebanwendung.docx diff --git a/doc/AnbindungExterneWebanwendung.pdf b/doc/AnbindungExterneWebanwendung.pdf Binary files differindex 00338b3c..a1869361 100644 --- a/doc/AnbindungExterneWebanwendung.pdf +++ b/doc/AnbindungExterneWebanwendung.pdf diff --git a/doc/PDFAS4_Dokumentation.docx b/doc/PDFAS4_Dokumentation.docx Binary files differindex 9112d5b5..4ebc4a33 100644 --- a/doc/PDFAS4_Dokumentation.docx +++ b/doc/PDFAS4_Dokumentation.docx diff --git a/doc/PDFAS4_Dokumentation.pdf b/doc/PDFAS4_Dokumentation.pdf Binary files differindex 3dbafb00..cd01a613 100644 --- a/doc/PDFAS4_Dokumentation.pdf +++ b/doc/PDFAS4_Dokumentation.pdf diff --git a/doc/PDFAS4_WebDokumentation.docx b/doc/PDFAS4_WebDokumentation.docx Binary files differindex 880a3dcc..0e7b21ba 100644 --- a/doc/PDFAS4_WebDokumentation.docx +++ b/doc/PDFAS4_WebDokumentation.docx diff --git a/doc/PDFAS4_WebDokumentation.pdf b/doc/PDFAS4_WebDokumentation.pdf Binary files differindex 83207e5d..a75ad5da 100644 --- a/doc/PDFAS4_WebDokumentation.pdf +++ b/doc/PDFAS4_WebDokumentation.pdf diff --git a/pdf-as-cli/build.gradle b/pdf-as-cli/build.gradle index 94087093..3033cd00 100644 --- a/pdf-as-cli/build.gradle +++ b/pdf-as-cli/build.gradle @@ -35,7 +35,7 @@ dependencies { implementation group: 'commons-collections', name: 'commons-collections', version: '3.2.2' implementation group: 'commons-cli', name: 'commons-cli', version: '1.2' implementation group: 'javax.activation', name: 'activation', version: '1.1.1' - implementation 'ch.qos.logback:logback-classic:1.2.11' + implementation 'ch.qos.logback:logback-classic:1.2.12' testImplementation group: 'junit', name: 'junit', version: '4.+' } diff --git a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/processing/DocumentToSign.java b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/processing/DocumentToSign.java index 6cc3a933..7d1928df 100644 --- a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/processing/DocumentToSign.java +++ b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/processing/DocumentToSign.java @@ -19,4 +19,8 @@ public class DocumentToSign implements Serializable { String profile; + boolean placeholderSearchEnabled = true; + + String placeHolderId; + } diff --git a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASPropertyEntry.java b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASPropertyEntry.java index d02c7bbc..0b9b21af 100644 --- a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASPropertyEntry.java +++ b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASPropertyEntry.java @@ -1,11 +1,15 @@ package at.gv.egiz.pdfas.api.ws; +import java.io.Serializable; + import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlType; -@XmlType(name="PropertyEntry") -public class PDFASPropertyEntry { - String key; +@XmlType(name="PropertyEntry") +public class PDFASPropertyEntry implements Serializable { + private static final long serialVersionUID = -312145729002273058L; + + String key; String value; @XmlElement(required = true, nillable = false, name="key") diff --git a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASPropertyMap.java b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASPropertyMap.java index 34dca6d2..c3949849 100644 --- a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASPropertyMap.java +++ b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASPropertyMap.java @@ -1,5 +1,6 @@ package at.gv.egiz.pdfas.api.ws; +import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -11,8 +12,10 @@ import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlType; @XmlType(name="PropertyMap") -public class PDFASPropertyMap { - List<PDFASPropertyEntry> propertyEntries; +public class PDFASPropertyMap implements Serializable { + private static final long serialVersionUID = -8099703140108251423L; + + List<PDFASPropertyEntry> propertyEntries; @XmlElement(required = true, nillable = false, name="propertyEntries") public List<PDFASPropertyEntry> getPropertyEntries() { diff --git a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/exceptions/SLPdfAsException.java b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/exceptions/SLPdfAsException.java index f22fc134..74462a03 100644 --- a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/exceptions/SLPdfAsException.java +++ b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/exceptions/SLPdfAsException.java @@ -28,7 +28,7 @@ import java.util.List; public class SLPdfAsException extends PdfAsException { - private static final List<Integer> ERRORCODES_ON_INFO_LEVEL = Arrays.asList(6001, 6002); + private static final List<Integer> ERRORCODES_ON_INFO_LEVEL = Arrays.asList(6000, 6001, 6002); /** * diff --git a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/settings/IProfileConstants.java b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/settings/IProfileConstants.java index 3f07f774..95eaa8ea 100644 --- a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/settings/IProfileConstants.java +++ b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/settings/IProfileConstants.java @@ -94,6 +94,7 @@ public interface IProfileConstants { public final static String SIG_PDFA1B_VALID = "SIG_PDFA1B_VALID"; public final static String SIG_PDFA_VALID = "SIG_PDFA_VALID"; public final static String SIG_PDFUA_FORCE = "SIG_PDFUA_FORCE"; + public final static String SIG_NEWPAGE_FORCE = "SIGNED_NEWPAGE_FORCE"; public final static String LATIN1_ENCODING = "latin1_encoding"; public final static String SIGNATURE_BLOCK_PARAMETER = "sbp"; diff --git a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/settings/ISettings.java b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/settings/ISettings.java index 42cdb8ab..43139966 100644 --- a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/settings/ISettings.java +++ b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/settings/ISettings.java @@ -29,6 +29,22 @@ import java.util.Vector; public interface ISettings { public String getValue(String key); public boolean hasValue(String key); + + /** + * Get boolean configuration value. + * @param key Configuration key + * @return <code>true</code> if configuration exists and has value <code>true</code>, otherwise <code>false</code> + */ + public boolean isValue(String key); + + /** + * Get boolean configuration value. + * @param key Configuration key + * @param defaultValue Value if configuration does not exist + * @return <code>true</code> if configuration exists and has value <code>true</code>, otherwise default value + */ + public boolean isValue(String key, boolean defaultValue); + public boolean hasPrefix(String prefix); public Map<String, String> getValuesPrefix(String prefix); public Vector<String> getFirstLevelKeys(String prefix); diff --git a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/settings/SignatureProfileSettings.java b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/settings/SignatureProfileSettings.java index 7f047278..65722f88 100644 --- a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/settings/SignatureProfileSettings.java +++ b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/settings/SignatureProfileSettings.java @@ -3,19 +3,19 @@ * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a * joint initiative of the Federal Chancellery Austria and Graz University of * Technology. - * + * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. - * + * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works @@ -24,7 +24,6 @@ package at.gv.egiz.pdfas.common.settings; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import org.slf4j.Logger; @@ -35,241 +34,222 @@ import at.gv.egiz.pdfas.common.exceptions.PDFASError; public class SignatureProfileSettings implements IProfileConstants { - private static final Logger logger = LoggerFactory - .getLogger(SignatureProfileSettings.class); + private static final Logger logger = LoggerFactory + .getLogger(SignatureProfileSettings.class); - private Map<String, SignatureProfileEntry> profileInformations = new HashMap<String, SignatureProfileEntry>(); + private final Map<String, SignatureProfileEntry> profileInformations = + new HashMap<>(); - private Map<String, String> profileSettings = new HashMap<String, String>(); + private final Map<String, String> profileSettings = new HashMap<>(); - private String profileID; + private final String profileID; - private String pdfAVersion = null; + private String pdfAVersion = null; - private ISettings configuration; + private final ISettings configuration; - public SignatureProfileSettings(String profileID, ISettings configuration) throws PDFASError { - - if (!configuration.hasPrefix(SIG_OBJ + profileID)) { + public SignatureProfileSettings(String profileID, ISettings configuration) throws PDFASError { + + if (!configuration.hasPrefix(SIG_OBJ + profileID)) { throw new PDFASError(ErrorConstants.ERROR_SIG_INVALID_PROFILE, PDFASError.buildInfoString(ErrorConstants.ERROR_SIG_INVALID_PROFILE, profileID)); } - - this.profileID = profileID; - String profilePrefix = SIG_OBJ + profileID + KEY_SEPARATOR; - String keysPrefix = profilePrefix + PROFILE_KEY; - String valuesPrefix = profilePrefix + PROFILE_VALUE; - String tablePrefix = profilePrefix + TABLE; - this.configuration = configuration; - - logger.debug("Reading Profile: " + profileID); - logger.debug("Keys Prefix: " + keysPrefix); - logger.debug("Values Prefix: " + valuesPrefix); - logger.debug("Table Prefix: " + tablePrefix); - - Map<String, String> keys = configuration.getValuesPrefix(keysPrefix); - Map<String, String> values = configuration.getValuesPrefix(valuesPrefix); - - if (keys != null) { - Iterator<String> keyIterator = keys.keySet().iterator(); - - while (keyIterator.hasNext()) { - String key = keyIterator.next(); - key = key.substring(key.lastIndexOf('.') + 1); - String valueKey = keys.get(keysPrefix + KEY_SEPARATOR + key); - - String valueValue = values.get(valuesPrefix + KEY_SEPARATOR - + key); - - // Lookup default values - if(valueKey == null) { - valueKey = DefaultSignatureProfileSettings.getDefaultKeyCaption(key); - } - - if(valueValue == null) { - valueValue = DefaultSignatureProfileSettings.getDefaultKeyValue(key); - } - - SignatureProfileEntry entry = new SignatureProfileEntry(); - entry.setKey(key); - entry.setCaption(valueKey); - entry.setValue(valueValue); - profileInformations.put(key, entry); - logger.debug(" " + entry.toString()); - } - } - - if (values != null) { - // Find entries where only values exists - Iterator<String> valuesIterator = values.keySet().iterator(); - - while (valuesIterator.hasNext()) { - String key = valuesIterator.next(); - key = key.substring(key.lastIndexOf('.') + 1); - - String valueValue = values.get(valuesPrefix + KEY_SEPARATOR - + key); - - // Lookup default values - if(valueValue == null) { - valueValue = DefaultSignatureProfileSettings.getDefaultKeyValue(key); - } - - SignatureProfileEntry entry = profileInformations.get(key); - if (entry == null) { - entry = new SignatureProfileEntry(); - entry.setKey(key); - entry.setCaption(null); - entry.setValue(valueValue); - profileInformations.put(key, entry); - } - - logger.debug(" " + entry.toString()); - } - } - - Map<String, String> others = configuration - .getValuesPrefix(profilePrefix); - - if(others != null) { - Iterator<String> otherIterator = others.keySet().iterator(); - - while (otherIterator.hasNext()) { - String key = otherIterator.next(); - - logger.trace("Checking key " + key); - if (key.startsWith(keysPrefix) || key.startsWith(valuesPrefix) - || key.startsWith(tablePrefix)) { - continue; - } - - String value = others.get(key); - key = key.substring(key.lastIndexOf('.') + 1); - - profileSettings.put(key, value); - - logger.debug(" Settings: " + key + " : " + value); - } - } - - Iterator<SignatureProfileEntry> dumpIterator = - profileInformations.values().iterator(); - - logger.debug("Settings for profile {}", profileID); - while(dumpIterator.hasNext()) { - SignatureProfileEntry entry = dumpIterator.next(); - logger.debug(" " + entry.toString()); - } - } - - public String getCaption(String key) { - SignatureProfileEntry entry = profileInformations.get(key); - if (entry != null) { - return entry.getCaption(); - } - return null; - } - - protected String getDefaultValue(String key) { - String profilePrefix = SIG_OBJ + profileID + KEY_SEPARATOR; - logger.debug("Searching default value for: " + key); - if (key.startsWith(profilePrefix)) { - key = key.substring(profilePrefix.length()); - } - key = "default." + key; - logger.debug("Searching default value for: " + key); - return this.configuration.getValue(key); - } - - public String getValue(String key) { - logger.debug("Searching: " + key); - SignatureProfileEntry entry = profileInformations.get(key); - if (entry != null) { - String value = entry.getValue(); - - if (value == null) { - return getDefaultValue(key); - } - - return value; - } - String v = profileSettings.get(key); - if (v != null) { - return v; - } - return getDefaultValue(key); - } - - public String getProfileID() { - return profileID; - } - - public String getSigningReason() { - return this.getValue(SIGNING_REASON); - } - - public String getSignFieldValue() { - return this.getValue(SIGNFIELD_VALUE); - } - - public String getProfileTimeZone() { - return this.getValue(TIMEZONE_BASE); - } - - public void setPDFAVersion(String version) { - this.pdfAVersion = version; - } - - public boolean isPDFA() { - - if(this.pdfAVersion != null) { - return "1".equals(this.pdfAVersion); - } - - SignatureProfileEntry entry = profileInformations.get(SIG_PDFA_VALID); - if (entry != null) { - String value = entry.getCaption(); - return "true".equals(value); - } - - entry = profileInformations.get(SIG_PDFA1B_VALID); - if (entry != null) { - String value = entry.getCaption(); - return "true".equals(value); - } - return false; - } - - public boolean isPDFUA() { - SignatureProfileEntry entry = profileInformations.get(SIG_PDFUA_FORCE); - if (entry != null) { - String value = entry.getCaption(); - return "true".equals(value); - } - return false; - } - - - public boolean isLatin1Encoding() { - SignatureProfileEntry entry = profileInformations.get(LATIN1_ENCODING); - if (entry != null) { - String value = entry.getCaption(); - return "true".equals(value); - } - return false; - } - - public boolean isPDFA3() { - if(this.pdfAVersion != null) { - return "3".equals(this.pdfAVersion); - } - - SignatureProfileEntry entry = profileInformations.get(SIG_PDFA_VALID); - if (entry != null) { - String value = entry.getCaption(); - return "true".equals(value); - } - return false; - } + + this.profileID = profileID; + final String profilePrefix = SIG_OBJ + profileID + KEY_SEPARATOR; + final String keysPrefix = profilePrefix + PROFILE_KEY; + final String valuesPrefix = profilePrefix + PROFILE_VALUE; + final String tablePrefix = profilePrefix + TABLE; + this.configuration = configuration; + + logger.debug("Reading Profile: " + profileID); + logger.debug("Keys Prefix: " + keysPrefix); + logger.debug("Values Prefix: " + valuesPrefix); + logger.debug("Table Prefix: " + tablePrefix); + + final Map<String, String> keys = configuration.getValuesPrefix(keysPrefix); + final Map<String, String> values = configuration.getValuesPrefix(valuesPrefix); + + for (String key : keys.keySet()) { + key = key.substring(key.lastIndexOf('.') + 1); + String valueKey = keys.get(keysPrefix + KEY_SEPARATOR + key); + + String valueValue = values.get(valuesPrefix + KEY_SEPARATOR + + key); + + // Lookup default values + if (valueKey == null) { + valueKey = DefaultSignatureProfileSettings.getDefaultKeyCaption(key); + } + + if (valueValue == null) { + valueValue = DefaultSignatureProfileSettings.getDefaultKeyValue(key); + } + + final SignatureProfileEntry entry = new SignatureProfileEntry(); + entry.setKey(key); + entry.setCaption(valueKey); + entry.setValue(valueValue); + profileInformations.put(key, entry); + logger.debug(" " + entry.toString()); + } + + for (String key : values.keySet()) { + key = key.substring(key.lastIndexOf('.') + 1); + + String valueValue = values.get(valuesPrefix + KEY_SEPARATOR + + key); + + // Lookup default values + if (valueValue == null) { + valueValue = DefaultSignatureProfileSettings.getDefaultKeyValue(key); + } + + SignatureProfileEntry entry = profileInformations.get(key); + if (entry == null) { + entry = new SignatureProfileEntry(); + entry.setKey(key); + entry.setCaption(null); + entry.setValue(valueValue); + profileInformations.put(key, entry); + } + + logger.debug(" " + entry.toString()); + } + + final Map<String, String> others = configuration + .getValuesPrefix(profilePrefix); + + if (others != null) { + for (String key : others.keySet()) { + logger.trace("Checking key " + key); + if (key.startsWith(keysPrefix) || key.startsWith(valuesPrefix) + || key.startsWith(tablePrefix)) { + continue; + } + + final String value = others.get(key); + key = key.substring(key.lastIndexOf('.') + 1); + + profileSettings.put(key, value); + + logger.debug(" Settings: " + key + " : " + value); + } + } + + logger.debug("Settings for profile {}", profileID); + for (final SignatureProfileEntry entry : profileInformations.values()) { + logger.debug(" " + entry.toString()); + } + } + + public String getCaption(String key) { + final SignatureProfileEntry entry = profileInformations.get(key); + if (entry != null) { + return entry.getCaption(); + } + return null; + } + + protected String getDefaultValue(String key) { + final String profilePrefix = SIG_OBJ + profileID + KEY_SEPARATOR; + logger.debug("Searching default value for: " + key); + if (key.startsWith(profilePrefix)) { + key = key.substring(profilePrefix.length()); + } + key = "default." + key; + logger.debug("Searching default value for: " + key); + return this.configuration.getValue(key); + } + + public String getValue(String key) { + logger.debug("Searching: " + key); + final SignatureProfileEntry entry = profileInformations.get(key); + if (entry != null) { + final String value = entry.getValue(); + + if (value == null) { + return getDefaultValue(key); + } + + return value; + } + final String v = profileSettings.get(key); + if (v != null) { + return v; + } + return getDefaultValue(key); + } + + public String getProfileID() { + return profileID; + } + + public String getSigningReason() { + return this.getValue(SIGNING_REASON); + } + + public String getSignFieldValue() { + return this.getValue(SIGNFIELD_VALUE); + } + + public String getProfileTimeZone() { + return this.getValue(TIMEZONE_BASE); + } + + public void setPDFAVersion(String version) { + this.pdfAVersion = version; + } + + public boolean isPDFA() { + + if (this.pdfAVersion != null) { + return "1".equals(this.pdfAVersion); + } + + SignatureProfileEntry entry = profileInformations.get(SIG_PDFA_VALID); + if (entry != null) { + final String value = entry.getCaption(); + return "true".equals(value); + } + + entry = profileInformations.get(SIG_PDFA1B_VALID); + if (entry != null) { + final String value = entry.getCaption(); + return "true".equals(value); + } + return false; + } + + public boolean isPDFUA() { + final SignatureProfileEntry entry = profileInformations.get(SIG_PDFUA_FORCE); + if (entry != null) { + final String value = entry.getCaption(); + return "true".equals(value); + } + return false; + } + + public boolean isLatin1Encoding() { + final SignatureProfileEntry entry = profileInformations.get(LATIN1_ENCODING); + if (entry != null) { + final String value = entry.getCaption(); + return "true".equals(value); + } + return false; + } + + public boolean isPDFA3() { + if (this.pdfAVersion != null) { + return "3".equals(this.pdfAVersion); + } + + final SignatureProfileEntry entry = profileInformations.get(SIG_PDFA_VALID); + if (entry != null) { + final String value = entry.getCaption(); + return "true".equals(value); + } + return false; + } } diff --git a/pdf-as-common/src/main/resources/resources/messages/common.properties b/pdf-as-common/src/main/resources/resources/messages/common.properties index d8695619..f3baebad 100644 --- a/pdf-as-common/src/main/resources/resources/messages/common.properties +++ b/pdf-as-common/src/main/resources/resources/messages/common.properties @@ -1,3 +1,6 @@ +# Configuration errors +error.config.sl20.01=Signing of SL2.0 messages are enabled, but not Keystore available + # PDF Permission Errors error.pdf.perm.01=You do not have permission to extract images diff --git a/pdf-as-common/src/main/resources/resources/messages/error.properties b/pdf-as-common/src/main/resources/resources/messages/error.properties index 4f67fff6..dd873f1e 100644 --- a/pdf-as-common/src/main/resources/resources/messages/error.properties +++ b/pdf-as-common/src/main/resources/resources/messages/error.properties @@ -21,6 +21,8 @@ 11017=Failed to retrieve certificate 11018=Given Alias contains no private key 11019=Signature was created for wrong certificate +11020=Failed to process PDF document. Reason: {0} +11021=Signer certificate is not valid, because notBefore or notAfter does not match 13001=Invalid Configuration Objects 13002=Given certificate is invalid diff --git a/pdf-as-common/src/test/java/at/gv/egiz/pdfas/common/test/utils/CheckSignatureParametersTest.java b/pdf-as-common/src/test/java/at/gv/egiz/pdfas/common/test/utils/CheckSignatureParametersTest.java new file mode 100644 index 00000000..5ec0541b --- /dev/null +++ b/pdf-as-common/src/test/java/at/gv/egiz/pdfas/common/test/utils/CheckSignatureParametersTest.java @@ -0,0 +1,48 @@ +package at.gv.egiz.pdfas.common.test.utils; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; + +import at.gv.egiz.pdfas.common.settings.DefaultSignatureProfileSettings; +import at.gv.egiz.pdfas.common.utils.CheckSignatureBlockParameters; + +@RunWith(BlockJUnit4ClassRunner.class) +public class CheckSignatureParametersTest { + + @Test + public void singleTest() { + assertTrue("valid characters are not possilbe", + CheckSignatureBlockParameters.isValid("Güssing", + DefaultSignatureProfileSettings.SIG_BLOCK_PARAMETER_DEFAULT_VALUE_REGEX)); + + } + + @Test + public void specialCharactersCompiletimeConfig() { + Map<String, String> toTest = new HashMap<>(); + toTest.put("test", "Güssing"); + + assertTrue("valid characters are not possilbe", + CheckSignatureBlockParameters.checkSignatureBlockParameterMapIsValid(toTest , null, null)); + + } + + @Test + public void specialCharactersExampleConfig() { + Map<String, String> toTest = new HashMap<>(); + toTest.put("test", "Güssing"); + + assertFalse("valid characters are not possilbe", + CheckSignatureBlockParameters.checkSignatureBlockParameterMapIsValid(toTest , + "^([A-za-z]){1,20}$", "^([\\p{Print}]){1,100}$")); + + } + +} diff --git a/pdf-as-legacy/src/main/java/at/gv/egiz/pdfas/api/commons/DynamicSignatureProfileImpl.java b/pdf-as-legacy/src/main/java/at/gv/egiz/pdfas/api/commons/DynamicSignatureProfileImpl.java index cc51d5e4..ce36fb9f 100644 --- a/pdf-as-legacy/src/main/java/at/gv/egiz/pdfas/api/commons/DynamicSignatureProfileImpl.java +++ b/pdf-as-legacy/src/main/java/at/gv/egiz/pdfas/api/commons/DynamicSignatureProfileImpl.java @@ -186,19 +186,7 @@ public class DynamicSignatureProfileImpl implements DynamicSignatureProfile { cfg = (ISettings)configuration; String parentKey = "sig_obj." + parentProfile + "."; - Map<String, String> properties = cfg.getValuesPrefix(parentKey); - //Properties props = cfg.getProperties(); - // DTI: props.keys() does not support default properties, therefore we should better use props.propertyNames() -// for (Enumeration e = props.keys(); e.hasMoreElements();) { - /*for (Enumeration e = props.propertyNames(); e.hasMoreElements();) { - String oldKey = (String) e.nextElement(); - if (oldKey.startsWith("sig_obj." + parentProfile + ".")) { - String newKey = StringUtils.replace(oldKey, parentProfile, name); - String val = props.getProperty(oldKey); - this.newProps.put(newKey, val); - } - }*/ - + Map<String, String> properties = cfg.getValuesPrefix(parentKey); Iterator<String> keyIt = properties.keySet().iterator(); while(keyIt.hasNext()) { diff --git a/pdf-as-lib/build.gradle b/pdf-as-lib/build.gradle index e63dfe2d..67e4b6ac 100644 --- a/pdf-as-lib/build.gradle +++ b/pdf-as-lib/build.gradle @@ -69,7 +69,7 @@ dependencies { api group: 'javax.activation', name: 'activation', version: '1.1.1' api group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1' api group: 'com.google.code.gson', name: 'gson', version: '2.10.1' - api group: 'org.bitbucket.b_c', name: 'jose4j', version: '0.9.2' + api group: 'org.bitbucket.b_c', name: 'jose4j', version: '0.9.3' api group: 'commons-io', name: 'commons-io', version: '2.11.0' api group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '2.3.3' api 'org.apache.commons:commons-collections4:4.4' @@ -80,8 +80,8 @@ dependencies { api files('libs/iaik_cms-5.1.1.jar') api group: 'org.slf4j', name: 'slf4j-api', version: slf4jVersion api group: 'org.slf4j', name: 'jcl-over-slf4j', version: slf4jVersion - api group: 'com.google.zxing', name: 'core', version: '3.4.1' - api group: 'com.google.zxing', name: 'javase', version: '3.4.1' + api group: 'com.google.zxing', name: 'core', version: '3.5.0' + api group: 'com.google.zxing', name: 'javase', version: '3.5.0' ws group: 'org.apache.cxf', name: 'cxf-tools', version: cxfVersion ws group: 'org.apache.cxf', name: 'cxf-tools-wsdlto-databinding-jaxb', version: cxfVersion diff --git a/pdf-as-lib/src/configuration/cfg/advancedconfig.properties b/pdf-as-lib/src/configuration/cfg/advancedconfig.properties index 7eb1ec37..d00f2872 100644 --- a/pdf-as-lib/src/configuration/cfg/advancedconfig.properties +++ b/pdf-as-lib/src/configuration/cfg/advancedconfig.properties @@ -41,6 +41,8 @@ # MATCH_MODE_SORTED = 3
#placeholder_mode=
+# Allow profile over-write from placeholder content [true|false], default=true
+#placeholder_profile_overwrite=true
### stop processing it signature-block can not valid placed on document
sigblock.placement.less.space.failing=false
diff --git a/pdf-as-lib/src/configuration/cfg/log4j.properties b/pdf-as-lib/src/configuration/cfg/log4j.properties deleted file mode 100644 index 9cc9f155..00000000 --- a/pdf-as-lib/src/configuration/cfg/log4j.properties +++ /dev/null @@ -1,23 +0,0 @@ -log4j.rootLogger = INFO, CONSOLE -#, ROLLINGFILE - -# DETAIL LEVELS -log4j.logger.at.gv.egiz = INFO -log4j.logger.at.knowcenter = INFO -# Statistical logger -#log4j.logger.statistic = INFO, STATISTIC - -# CONSOLE -log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender -log4j.appender.CONSOLE.layout = org.apache.log4j.PatternLayout -log4j.appender.CONSOLE.layout.ConversionPattern = %-4r [%t] %-5p %c %x - %m%n -log4j.appender.CONSOLE.threshold = DEBUG -log4j.appender.CONSOLE.Target = System.out - -# ROLLINGFILE -#log4j.appender.ROLLINGFILE = org.apache.log4j.RollingFileAppender -#log4j.appender.ROLLINGFILE.File = ${catalina.base}/logs/pdf-as.log -#log4j.appender.ROLLINGFILE.MaxFileSize = 10240KB -#log4j.appender.ROLLINGFILE.MaxBackupIndex = 1 -#log4j.appender.ROLLINGFILE.layout = org.apache.log4j.PatternLayout -#log4j.appender.ROLLINGFILE.layout.ConversionPattern = [%-5p@%d{dd.MM.yyyy HH:mm:ss}] %c:%M:%L - %m%n diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/IConfigurationConstants.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/IConfigurationConstants.java index 21a8e3b9..d031e2f7 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/IConfigurationConstants.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/IConfigurationConstants.java @@ -45,9 +45,13 @@ public interface IConfigurationConstants { public static final String LEGACY_40_POSITIONING = ".legacy40.pos"; public static final String MIN_WIDTH = "minWidth"; - public static final String PLACEHOLDER_WEB_ID = "placeholder_web_id"; public static final String PLACEHOLDER_ID = "placeholder_id"; public static final String PLACEHOLDER_MODE = "placeholder_mode"; + public static final String PLACEHOLDER_PROFILE_OVERWRITE = "placeholder_profile_overwrite"; + + + public static final String PLACEHOLDER_WEB_ID = "placeholder_web_id"; + public static final String PLACEHOLDER_WEB_ENABLED = "placeholder_web_enabled"; public static final String PLACEHOLDER_SEARCH_ENABLED = "enable_placeholder_search"; public static final String DEFAULT_SIGNATURE_PROFILE = SIG_OBJECT + SEPERATOR + TYPE + SEPERATOR + DEFAULT; diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/sign/SignParameter.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/sign/SignParameter.java index 6a7ccf24..e123d453 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/sign/SignParameter.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/sign/SignParameter.java @@ -54,6 +54,39 @@ public interface SignParameter extends PdfAsParameter { */ public void setSignaturePosition(String signaturePosition); + + /** + * Get Id of a placeholder that should be used for positioning. + * + * @return Id of a placeholder + */ + String getPlaceHolderId(); + + + /** + * Set Id of a placeholder that should be used for positioning. + * + * @param id Id of a placeholder + */ + void setPlaceHolderId(String id); + + /** + * Is QR-Code placeholder search enabled for this request. + * + * @return <code>true</code> if it's enabled, otherwise <code>false</code> + */ + boolean isPlaceHolderSearchEnabled(); + + + /** + * Enable / disable QR-Code placeholder search on request level. + * + * <p>Default value is <code>true</code></p> + * + * @param flag <code>true</code> to enable, <code>false</code> to disable + */ + void setPlaceHolderSearchEnabled(boolean flag); + /** * Sets the signer to use * diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/ErrorExtractor.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/ErrorExtractor.java index 9b2a8d79..bcf04611 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/ErrorExtractor.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/ErrorExtractor.java @@ -5,6 +5,7 @@ import org.slf4j.LoggerFactory; import at.gv.egiz.pdfas.common.exceptions.ErrorConstants; import at.gv.egiz.pdfas.common.exceptions.PDFASError; +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; import at.gv.egiz.pdfas.common.exceptions.PdfAsMOAException; import at.gv.egiz.pdfas.common.exceptions.SLPdfAsException; import at.gv.egiz.pdfas.lib.impl.status.OperationStatus; @@ -42,6 +43,10 @@ public class ErrorExtractor implements ErrorConstants { } else { return new PDFASError(code, e); } + + } else if(e instanceof PdfAsException) { + return new PDFASError(11020, e.getMessage(), e); + } // TODO: Handle more exceptions diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java index 47e46bcf..ebd8ec90 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java @@ -27,6 +27,7 @@ import java.awt.Image; import java.io.File; import java.io.IOException; import java.util.Calendar; +import java.util.Date; import java.util.Iterator; import java.util.List; @@ -165,8 +166,9 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants, status.setRequestedSignature(requestedSignature); - try { - requestedSignature.setCertificate(status.getSignParamter().getPlainSigner().getCertificate(parameter)); + try { + requestedSignature.setCertificate(getValidCertificate( + status.getSignParamter().getPlainSigner().getCertificate(parameter))); } finally { if (parameter instanceof BKUHeaderHolder) { @@ -208,9 +210,10 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants, // Create signature try { - signer.signPDF(status.getPdfObject(), requestedSignature, signer - .buildSignaturInterface(status.getSignParamter() - .getPlainSigner(), parameter, requestedSignature)); + signer.signPDF(status.getPdfObject(), requestedSignature, + signer.buildSignaturInterface(status.getSignParamter().getPlainSigner(), + parameter, requestedSignature)); + } finally { if (parameter instanceof BKUHeaderHolder) { final BKUHeaderHolder holder = (BKUHeaderHolder) parameter; @@ -266,6 +269,22 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants, } } + private X509Certificate getValidCertificate(X509Certificate certificate) throws PDFASError { + Date notAfter = certificate.getNotAfter(); + Date notBefore = certificate.getNotBefore(); + Date now = new Date(); + + if (now.after(notAfter) || now.before(notBefore)) { + logger.warn("Signer certificate is not valid. notBefore:{} | notAfter:{} | now:{}", + notBefore, notAfter, now); + throw new PDFASError(11021); + + } else { + return certificate; + + } + } + @Override public List<VerifyResult> verify(VerifyParameter parameter) throws PDFASError { @@ -400,6 +419,7 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants, } catch (final Throwable e) { logger.warn("process", e); throw ErrorExtractor.searchPdfAsError(e, status); + } } else if (request.needSignature()) { request.setNeedSignature(false); diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/SignParameterImpl.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/SignParameterImpl.java index d2786f53..06b1b34f 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/SignParameterImpl.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/SignParameterImpl.java @@ -25,9 +25,7 @@ package at.gv.egiz.pdfas.lib.impl; import java.io.OutputStream; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import javax.activation.DataSource; @@ -35,10 +33,21 @@ import at.gv.egiz.pdfas.lib.api.Configuration; import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner; import at.gv.egiz.pdfas.lib.api.sign.SignParameter; import at.gv.egiz.sl.util.BKUHeader; +import lombok.Getter; +import lombok.Setter; public class SignParameterImpl extends PdfAsParameterImpl implements SignParameter, BKUHeaderHolder { protected String signatureProfileId = null; protected String signaturePosition = null; + + @Getter + @Setter + protected String placeHolderId; + + @Getter + @Setter + protected boolean placeHolderSearchEnabled; + protected DataSource output = null; protected IPlainSigner signer = null; protected OutputStream outputStream = null; diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/configuration/ConfigurationImpl.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/configuration/ConfigurationImpl.java index bfc05c85..8c841301 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/configuration/ConfigurationImpl.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/configuration/ConfigurationImpl.java @@ -3,19 +3,19 @@ * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a * joint initiative of the Federal Chancellery Austria and Graz University of * Technology. - * + * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. - * + * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works @@ -23,147 +23,166 @@ ******************************************************************************/ package at.gv.egiz.pdfas.lib.impl.configuration; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Properties; import java.util.Vector; +import org.apache.commons.lang3.StringUtils; + import at.gv.egiz.pdfas.common.settings.ISettings; import at.gv.egiz.pdfas.lib.api.Configuration; import at.gv.egiz.pdfas.lib.settings.Settings; public class ConfigurationImpl implements ISettings, Configuration { - protected Properties overwrittenProperties = new Properties(); - - protected ISettings settings; - - public ConfigurationImpl(ISettings settings) { - this.settings = settings; - } - - public void setValue(String key, String value) { - overwrittenProperties.setProperty(key, value); - } - - public String getValue(String key) { - if(overwrittenProperties.containsKey(key)) { - return overwrittenProperties.getProperty(key); - } else { - return this.settings.getValue(key); - } - } - - public boolean hasValue(String key) { - if(overwrittenProperties.containsKey(key)) { - return true; - } else { - return this.settings.hasValue(key); - } - } - - public Map<String, String> getValuesPrefix(String prefix) { - - Map<String, String> valueMap = null; - valueMap = this.settings.getValuesPrefix(prefix); - if(valueMap == null) { - valueMap = new HashMap<String, String>(); - } - - Iterator<Object> keyIterator = overwrittenProperties.keySet().iterator(); + protected Properties overwrittenProperties = new Properties(); + + protected ISettings settings; + + public ConfigurationImpl(ISettings settings) { + this.settings = settings; + } + + @Override + public void setValue(String key, String value) { + overwrittenProperties.setProperty(key, value); + } + + @Override + public String getValue(String key) { + if (overwrittenProperties.containsKey(key)) { + return overwrittenProperties.getProperty(key); + } else { + return this.settings.getValue(key); + } + } + + @Override + public boolean isValue(String key) { + return isValue(key, false); + } + + @Override + public boolean isValue(String key, boolean defaultValue) { + if (overwrittenProperties.containsKey(key)) { + String value = overwrittenProperties.getProperty(key); + if (StringUtils.isNotEmpty(value)) { + return Boolean.valueOf(value); - while(keyIterator.hasNext()) { - String key = keyIterator.next().toString(); - - if(key.startsWith(prefix)) { - valueMap.put(key, overwrittenProperties.getProperty(key)); - } - } - - if(valueMap.isEmpty()) { - return null; - } - - return valueMap; - } - - public Vector<String> getFirstLevelKeys(String prefix) { - - Vector<String> valueMap = this.settings.getFirstLevelKeys(prefix); - if(valueMap == null) { - valueMap = new Vector<String>(); - } - - - String mPrefix = prefix.endsWith(".")?prefix:prefix+"."; - Iterator<Object> keyIterator = overwrittenProperties.keySet().iterator(); + } else { + return defaultValue; - while(keyIterator.hasNext()) { - String key = keyIterator.next().toString(); - - if(key.startsWith(prefix)) { - int keyIdx = key.indexOf('.', mPrefix.length()) > 0 ? key.indexOf('.', mPrefix.length()) : key.length(); - String firstLevels = key.substring(0, keyIdx); - if(!valueMap.contains(firstLevels)) { - valueMap.add(firstLevels); - } - } - } - - if(valueMap.isEmpty()) { - return null; - } - - return valueMap; - } - - public boolean hasPrefix(String prefix) { - - if(this.settings.hasPrefix(prefix)) { - return true; - } - - Iterator<Object> keyIterator = overwrittenProperties.keySet().iterator(); - while(keyIterator.hasNext()) { - String key = keyIterator.next().toString(); - - if(key.startsWith(prefix)) { - return true; - } - } - return false; - } - - public String getWorkingDirectory() { - return this.settings.getWorkingDirectory(); - } - - public void cloneProfile(String originalPrefix, String clonedPrefix) { - Map<String, String> source = getValuesPrefix(originalPrefix); - - Iterator<String> keyIt = source.keySet().iterator(); - - while(keyIt.hasNext()) { - String origKey = keyIt.next(); - String cloneKey = origKey.replace(originalPrefix, clonedPrefix); - this.overwrittenProperties.setProperty(cloneKey, source.get(origKey)); - } - } - - public void removeProfile(String configurationPrefix) { - Iterator<Object> keyIterator = overwrittenProperties.keySet().iterator(); - while(keyIterator.hasNext()) { - String key = keyIterator.next().toString(); - - if(key.startsWith(configurationPrefix)) { - overwrittenProperties.remove(key); - } + } + } else { + return this.settings.isValue(key, defaultValue); + + } + } + + @Override + public boolean hasValue(String key) { + if (overwrittenProperties.containsKey(key)) { + return true; + } else { + return this.settings.hasValue(key); + } + } + + @Override + public Map<String, String> getValuesPrefix(String prefix) { + final Map<String, String> valueMap = this.settings.getValuesPrefix(prefix); + final Iterator<Object> keyIterator = overwrittenProperties.keySet().iterator(); + + while (keyIterator.hasNext()) { + final String key = keyIterator.next().toString(); + + if (key.startsWith(prefix)) { + valueMap.put(key, overwrittenProperties.getProperty(key)); + } + } + + return valueMap; + } + + @Override + public Vector<String> getFirstLevelKeys(String prefix) { + + Vector<String> valueMap = this.settings.getFirstLevelKeys(prefix); + if (valueMap == null) { + valueMap = new Vector<>(); + } + + final String mPrefix = prefix.endsWith(".") ? prefix : prefix + "."; + final Iterator<Object> keyIterator = overwrittenProperties.keySet().iterator(); + + while (keyIterator.hasNext()) { + final String key = keyIterator.next().toString(); + + if (key.startsWith(prefix)) { + final int keyIdx = key.indexOf('.', mPrefix.length()) > 0 ? key.indexOf('.', mPrefix.length()) + : key.length(); + final String firstLevels = key.substring(0, keyIdx); + if (!valueMap.contains(firstLevels)) { + valueMap.add(firstLevels); } - } + } + } + + if (valueMap.isEmpty()) { + return null; + } + + return valueMap; + } + + @Override + public boolean hasPrefix(String prefix) { + + if (this.settings.hasPrefix(prefix)) { + return true; + } + + final Iterator<Object> keyIterator = overwrittenProperties.keySet().iterator(); + while (keyIterator.hasNext()) { + final String key = keyIterator.next().toString(); + + if (key.startsWith(prefix)) { + return true; + } + } + return false; + } + + @Override + public String getWorkingDirectory() { + return this.settings.getWorkingDirectory(); + } + + @Override + public void cloneProfile(String originalPrefix, String clonedPrefix) { + final Map<String, String> source = getValuesPrefix(originalPrefix); + + for (final String origKey : source.keySet()) { + final String cloneKey = origKey.replace(originalPrefix, clonedPrefix); + this.overwrittenProperties.setProperty(cloneKey, source.get(origKey)); + } + } + + @Override + public void removeProfile(String configurationPrefix) { + final Iterator<Object> keyIterator = overwrittenProperties.keySet().iterator(); + while (keyIterator.hasNext()) { + final String key = keyIterator.next().toString(); + + if (key.startsWith(configurationPrefix)) { + overwrittenProperties.remove(key); + } + } + } + + public void debugDumpProfileSettings(String profileName) { + ((Settings) settings).debugDumpProfileSettings(profileName); + } - public void debugDumpProfileSettings(String profileName) { - ((Settings)settings).debugDumpProfileSettings(profileName); - } - } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/configuration/PlaceholderConfiguration.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/configuration/PlaceholderConfiguration.java index 326ed142..a3719168 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/configuration/PlaceholderConfiguration.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/configuration/PlaceholderConfiguration.java @@ -3,19 +3,19 @@ * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a * joint initiative of the Federal Chancellery Austria and Graz University of * Technology. - * + * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. - * + * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works @@ -25,60 +25,57 @@ package at.gv.egiz.pdfas.lib.impl.configuration; import at.gv.egiz.pdfas.common.settings.ISettings; import at.gv.egiz.pdfas.lib.api.IConfigurationConstants; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; -import java.util.Map; +@Slf4j +public class PlaceholderConfiguration extends SpecificBaseConfiguration + implements IConfigurationConstants { + public PlaceholderConfiguration(ISettings configuration) { + super(configuration); + } -public class PlaceholderConfiguration extends SpecificBaseConfiguration - implements IConfigurationConstants { + public boolean isGlobalPlaceholderEnabled() { + if (configuration.hasValue(PLACEHOLDER_SEARCH_ENABLED)) { + final String value = configuration.getValue(PLACEHOLDER_SEARCH_ENABLED); + if (value.equalsIgnoreCase(TRUE)) { + return true; + } + } + return false; + } - private static final Logger logger = LoggerFactory.getLogger(PlaceholderConfiguration.class); + /** + * Match selected Profile for Placeholder Enables to activate placeholder + * search/match for different profiles + * + * @return + */ + public boolean isProfileConfigurationEnabled(String profileID) { + log.trace("Check if placeHolders are enabled for profile: {}", profileID); + + final String profileMatch = SIG_OBJECT + SEPERATOR + profileID + SEPERATOR + + PLACEHOLDER_SEARCH_ENABLED; + final String value = configuration.getValue(profileMatch); + if (TRUE.equalsIgnoreCase(value)) { + log.debug("Placeholders enabled for profile: {} ", profileID); + return true; + + } + return false; + + } - public PlaceholderConfiguration(ISettings configuration) { - super(configuration); - } + /** + * Get placeholderId for a specific profile. + * + * @param selectedProfileID ProfileName + * @return Placeholder Id + */ + public String getProfilePlaceholderID(String selectedProfileID) { + log.info("SelectedProfileID in ProfileConfEnabled: " + selectedProfileID); + final String profileMatch = SIG_OBJECT + SEPERATOR + selectedProfileID + SEPERATOR + PLACEHOLDER_ID; + return configuration.getValue(profileMatch); - public boolean isGlobalPlaceholderEnabled() { - if (configuration.hasValue(PLACEHOLDER_SEARCH_ENABLED)) { - String value = configuration.getValue(PLACEHOLDER_SEARCH_ENABLED); - if (value.equalsIgnoreCase(TRUE)) { - return true; - } - } - return false; - } - - /** - * Match selected Profile for Placeholder - * Enables to activate placeholder search/match for different profiles - * @return - */ - public boolean isProfileConfigurationEnabled(String selectedProfileID) - { - logger.info("SelectedProfileID in ProfileConfEnabled: "+selectedProfileID); - String profileMatch = SIG_OBJECT+SEPERATOR+selectedProfileID+SEPERATOR+PLACEHOLDER_SEARCH_ENABLED; - if (configuration.getValuesPrefix(profileMatch)!=null) { - Map<String, String> map = configuration.getValuesPrefix(profileMatch); - String value = map.get(profileMatch); - if (value.equalsIgnoreCase(TRUE)) { - logger.info("Configuration has Value: "+value); - return true; - } - } - return false; - } - - public String getProfilePlaceholderID(String selectedProfileID) - { - logger.info("SelectedProfileID in ProfileConfEnabled: "+selectedProfileID); - String profileMatch = SIG_OBJECT+SEPERATOR+selectedProfileID+SEPERATOR+PLACEHOLDER_ID; - if (configuration.getValuesPrefix(profileMatch)!=null) { - Map<String, String> map = configuration.getValuesPrefix(profileMatch); - return map.get(profileMatch); - } - return null; - } + } } - diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/configuration/PlaceholderWebConfiguration.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/configuration/PlaceholderWebConfiguration.java deleted file mode 100644 index 3a78f24f..00000000 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/configuration/PlaceholderWebConfiguration.java +++ /dev/null @@ -1,24 +0,0 @@ -package at.gv.egiz.pdfas.lib.impl.configuration; - -import java.util.Properties; - -public class PlaceholderWebConfiguration { - - protected static Properties properties = new Properties(); - - //todo properties not cleaned - public static void setValue(String key, String value) - { - properties.clear(); - properties.setProperty(key,value); - } - public static String getValue(String key) - { - return properties.getProperty(key); - } - - public static void clear () { - properties.clear(); - } - -} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/PlaceholderExtractor.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/PlaceholderExtractor.java index 0a55b834..436024cd 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/PlaceholderExtractor.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/PlaceholderExtractor.java @@ -3,10 +3,7 @@ package at.gv.egiz.pdfas.lib.impl.placeholder; import at.gv.egiz.pdfas.common.exceptions.PdfAsException; import at.gv.egiz.pdfas.lib.impl.status.PDFObject; -import java.util.List; - public interface PlaceholderExtractor { - SignaturePlaceholderData extract(PDFObject doc, String placeholderId, int matchMode) throws PdfAsException; - - List<SignaturePlaceholderData> extractList(PDFObject pdfObject, String placeholderID, int placeholderMode) throws PdfAsException; + SignaturePlaceholderData extract(PDFObject pdfObject, String placeholderID, int placeholderMode) throws PdfAsException; + } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/PlaceholderFilter.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/PlaceholderFilter.java index 99c09295..1615482f 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/PlaceholderFilter.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/PlaceholderFilter.java @@ -24,145 +24,77 @@ package at.gv.egiz.pdfas.lib.impl.placeholder; import java.io.IOException; -import java.util.List; + +import org.apache.commons.lang3.StringUtils; import at.gv.egiz.pdfas.common.exceptions.PDFASError; import at.gv.egiz.pdfas.common.exceptions.PdfAsErrorCarrier; import at.gv.egiz.pdfas.common.exceptions.PdfAsException; import at.gv.egiz.pdfas.common.settings.ISettings; import at.gv.egiz.pdfas.lib.api.IConfigurationConstants; -import at.gv.egiz.pdfas.lib.impl.configuration.PlaceholderWebConfiguration; import at.gv.egiz.pdfas.lib.impl.status.OperationStatus; -import org.apache.commons.lang3.StringUtils; +import lombok.extern.slf4j.Slf4j; +@Slf4j public class PlaceholderFilter implements IConfigurationConstants, PlaceholderExtractorConstants { - + public static SignaturePlaceholderData checkPlaceholderSignatureLocation( - OperationStatus status, ISettings settings, String signatureLocation) throws PdfAsException, - IOException { - - String placeholderID; - - if (status.getPlaceholderConfiguration().isGlobalPlaceholderEnabled()) { - PlaceholderExtractor extractor = status.getBackend().getPlaceholderExtractor(); - - if(StringUtils.isNotEmpty(signatureLocation)) { - placeholderID = signatureLocation; - } else { - placeholderID = PlaceholderWebConfiguration.getValue(PLACEHOLDER_WEB_ID); - if(StringUtils.isEmpty(placeholderID)) { - placeholderID = settings.getValue(PLACEHOLDER_ID); - } - } - - String placeholderModeString = settings.getValue(PLACEHOLDER_MODE); - int placeholderMode = PLACEHOLDER_MATCH_MODE_MODERATE; - if (StringUtils.isNotEmpty(placeholderModeString)) { - try { - placeholderMode = Integer.parseInt(placeholderModeString); - if (placeholderMode < PLACEHOLDER_MODE_MIN - || placeholderMode > PLACEHOLDER_MODE_MAX) { - throw new PdfAsErrorCarrier(new PDFASError( - PDFASError.ERROR_INVALID_PLACEHOLDER_MODE)); - } - } catch (NumberFormatException e) { - throw new PdfAsErrorCarrier(new PDFASError( - PDFASError.ERROR_INVALID_PLACEHOLDER_MODE, e)); - } - } - SignaturePlaceholderData signaturePlaceholderData = extractor.extract(status.getPdfObject(), placeholderID, placeholderMode); - return signaturePlaceholderData; - - } else if (status.getPlaceholderConfiguration().isProfileConfigurationEnabled(status.getRequestedSignature().getSignatureProfileID())) { - //filter for local placeholder in selected profiles - PlaceholderExtractor extractor = status.getBackend().getPlaceholderExtractor(); - int placeholderMode = PLACEHOLDER_MATCH_MODE_SORTED; - - placeholderID = status.getPlaceholderConfiguration().getProfilePlaceholderID(status.getRequestedSignature().getSignatureProfileID()); - if(StringUtils.isNotEmpty(placeholderID)) { - placeholderMode = PLACEHOLDER_MATCH_MODE_MODERATE; - } - String placeholderModeString = settings.getValue(PLACEHOLDER_MODE); - if (StringUtils.isNotEmpty(placeholderModeString)) { - try { - placeholderMode = Integer.parseInt(placeholderModeString); - if (placeholderMode < PLACEHOLDER_MODE_MIN - || placeholderMode > PLACEHOLDER_MODE_MAX) { - throw new PdfAsErrorCarrier(new PDFASError( - PDFASError.ERROR_INVALID_PLACEHOLDER_MODE)); - } - } catch (NumberFormatException e) { - throw new PdfAsErrorCarrier(new PDFASError( - PDFASError.ERROR_INVALID_PLACEHOLDER_MODE, e)); - } - } - SignaturePlaceholderData signaturePlaceholderData = extractor.extract(status.getPdfObject(), placeholderID, placeholderMode); - return signaturePlaceholderData; - } + OperationStatus status, ISettings settings, String placeholderId) throws PdfAsException, IOException { + + String signingProfile = status.getRequestedSignature().getSignatureProfileID(); + + if (status.getSignParamter().isPlaceHolderSearchEnabled()) { + if (status.getPlaceholderConfiguration().isGlobalPlaceholderEnabled()) { + String defaultPlaceHolderId = settings.getValue(PLACEHOLDER_ID); + return status.getBackend().getPlaceholderExtractor().extract( + status.getPdfObject(), getPlaceHolderId(placeholderId, defaultPlaceHolderId) , + getPlaceHolderMode(settings, PLACEHOLDER_MATCH_MODE_SORTED)); + + } else if (status.getPlaceholderConfiguration().isProfileConfigurationEnabled(signingProfile)) { + String defaultPlaceHolderId = status.getPlaceholderConfiguration().getProfilePlaceholderID(signingProfile); + return status.getBackend().getPlaceholderExtractor().extract( + status.getPdfObject(), getPlaceHolderId(placeholderId, defaultPlaceHolderId), + getPlaceHolderMode(settings, + StringUtils.isNotEmpty(defaultPlaceHolderId) ? PLACEHOLDER_MATCH_MODE_MODERATE : PLACEHOLDER_MATCH_MODE_SORTED)); + } + } else { + log.debug("Searching placeholders are disabled for this request"); + + } + return null; } - - public static List<SignaturePlaceholderData> checkPlaceholderSignatureLocationList(OperationStatus status, ISettings settings, String signatureLocation) throws PdfAsException, - IOException { - String placeholderID; - - if (status.getPlaceholderConfiguration().isGlobalPlaceholderEnabled()) { - PlaceholderExtractor extractor = status.getBackend().getPlaceholderExtractor(); - - if(StringUtils.isNotEmpty(signatureLocation)) { - placeholderID = signatureLocation; - } else { - placeholderID = PlaceholderWebConfiguration.getValue(PLACEHOLDER_WEB_ID); - if(StringUtils.isEmpty(placeholderID)) { - placeholderID = settings.getValue(PLACEHOLDER_ID); - } - } - - String placeholderModeString = settings.getValue(PLACEHOLDER_MODE); - int placeholderMode = PLACEHOLDER_MATCH_MODE_MODERATE; - if (StringUtils.isNotEmpty(placeholderModeString)) { - try { - placeholderMode = Integer.parseInt(placeholderModeString); - if (placeholderMode < PLACEHOLDER_MODE_MIN - || placeholderMode > PLACEHOLDER_MODE_MAX) { - throw new PdfAsErrorCarrier(new PDFASError( - PDFASError.ERROR_INVALID_PLACEHOLDER_MODE)); - } - } catch (NumberFormatException e) { - throw new PdfAsErrorCarrier(new PDFASError( - PDFASError.ERROR_INVALID_PLACEHOLDER_MODE, e)); - } - } - return extractor.extractList(status.getPdfObject(), placeholderID, - placeholderMode); - - } else if (status.getPlaceholderConfiguration().isProfileConfigurationEnabled(status.getRequestedSignature().getSignatureProfileID())) { - //filter for local placeholder in selected profiles - PlaceholderExtractor extractor = status.getBackend().getPlaceholderExtractor(); - int placeholderMode = PLACEHOLDER_MATCH_MODE_SORTED; - - placeholderID = status.getPlaceholderConfiguration().getProfilePlaceholderID(status.getRequestedSignature().getSignatureProfileID()); - if(StringUtils.isNotEmpty(placeholderID)) { - placeholderMode = PLACEHOLDER_MATCH_MODE_MODERATE; - } - String placeholderModeString = settings.getValue(PLACEHOLDER_MODE); - if (StringUtils.isNotEmpty(placeholderModeString)) { - try { - placeholderMode = Integer.parseInt(placeholderModeString); - if (placeholderMode < PLACEHOLDER_MODE_MIN - || placeholderMode > PLACEHOLDER_MODE_MAX) { - throw new PdfAsErrorCarrier(new PDFASError( - PDFASError.ERROR_INVALID_PLACEHOLDER_MODE)); - } - } catch (NumberFormatException e) { - throw new PdfAsErrorCarrier(new PDFASError( - PDFASError.ERROR_INVALID_PLACEHOLDER_MODE, e)); - } - } - return extractor.extractList(status.getPdfObject(), placeholderID, - placeholderMode); - } - return null; + + private static int getPlaceHolderMode(ISettings settings, int defaultValue) throws PdfAsErrorCarrier { + String placeholderModeString = settings.getValue(PLACEHOLDER_MODE); + if (StringUtils.isNotEmpty(placeholderModeString)) { + try { + int placeholderMode = Integer.parseInt(placeholderModeString); + if (placeholderMode < PLACEHOLDER_MODE_MIN || placeholderMode > PLACEHOLDER_MODE_MAX) { + throw new PdfAsErrorCarrier(new PDFASError(PDFASError.ERROR_INVALID_PLACEHOLDER_MODE)); + + } + return placeholderMode; + + } catch (NumberFormatException e) { + throw new PdfAsErrorCarrier(new PDFASError( + PDFASError.ERROR_INVALID_PLACEHOLDER_MODE, e)); + } + + } else { + return defaultValue; + + } } + + private static String getPlaceHolderId(String requestId, String defaultValue) { + if (StringUtils.isEmpty(requestId)) { + return defaultValue; + + } else { + return requestId; + + } + } }
\ No newline at end of file diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/SignaturePlaceholderContext.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/SignaturePlaceholderContext.java deleted file mode 100644 index 3c8a6d76..00000000 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/SignaturePlaceholderContext.java +++ /dev/null @@ -1,95 +0,0 @@ -/******************************************************************************* - * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> - * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a - * joint initiative of the Federal Chancellery Austria and Graz University of - * Technology. - * - * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * http://www.osor.eu/eupl/ - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. - ******************************************************************************/ -/** - * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> - * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a - * joint initiative of the Federal Chancellery Austria and Graz University of - * Technology. - * - * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * http://www.osor.eu/eupl/ - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. - */ -package at.gv.egiz.pdfas.lib.impl.placeholder; - -/** - * Store and retrieve {@link SignaturePlaceholderData} in/from a thread local context. - * - * @author exthex - * - */ -public class SignaturePlaceholderContext { - - private ThreadLocal<SignaturePlaceholderData> sigHolder = new ThreadLocal<SignaturePlaceholderData>(); - - private static SignaturePlaceholderContext instance = new SignaturePlaceholderContext(); - - /** - * Constructor. Private because this is a singleton. - */ - private SignaturePlaceholderContext() { - - } - - /** - * Get the {@link SignaturePlaceholderData} which is currently bound to this thread. - * Might be null. - * - * @return - */ - public static SignaturePlaceholderData getSignaturePlaceholderData(){ - return instance.sigHolder.get(); - } - - /** - * - * @return true if there is currently a {@link SignaturePlaceholderData} bound to this thread, false otherwise. - */ - public static boolean isSignaturePlaceholderDataSet() { - return instance.sigHolder.get() != null; - } - - /** - * Bind a {@link SignaturePlaceholderData} to this thread. - * If the given data is null, the context will be cleared. - * - * @param data if null, clears the ThreadLocal, else binds the data to the current thread. - */ - public static void setSignaturePlaceholderData(SignaturePlaceholderData data) { - instance.sigHolder.set(data); - } -} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/settings/Settings.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/settings/Settings.java index c5b6dc57..8138f061 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/settings/Settings.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/settings/Settings.java @@ -35,438 +35,380 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Vector; +import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOCase; import org.apache.commons.io.filefilter.WildcardFileFilter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.commons.lang3.StringUtils; import at.gv.egiz.pdfas.common.exceptions.PdfAsSettingsException; import at.gv.egiz.pdfas.common.settings.IProfileConstants; import at.gv.egiz.pdfas.common.settings.ISettings; import at.gv.egiz.pdfas.common.settings.Profiles; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class Settings implements ISettings, IProfileConstants { + protected Properties properties = new Properties(); + protected File workDirectory; + + public Settings(File workDirectory) { + try { + this.workDirectory = workDirectory; + loadSettings(workDirectory); + } catch (final PdfAsSettingsException e) { + log.error(e.getMessage(), e); + } + } -public class Settings implements ISettings, IProfileConstants { + private void loadSettingsRecursive(File workDirectory, File file) + throws RuntimeException { + try { + final String configDir = workDirectory.getAbsolutePath() + File.separator + + CFG_DIR; + final Properties tmpProps = new Properties(); + log.debug("Loading: " + file.getName()); + tmpProps.load(new FileInputStream(file)); - private static final Logger logger = LoggerFactory - .getLogger(Settings.class); + properties.putAll(tmpProps); - protected Properties properties = new Properties(); + this.getValuesPrefix(INCLUDE, tmpProps).values() + .forEach(el -> loadAdditionConfigFile(el, configDir)); - protected File workDirectory; + } catch (final IOException e) { + throw new RuntimeException(e); - public Settings(File workDirectory) { - try { - this.workDirectory = workDirectory; - loadSettings(workDirectory); - } catch (PdfAsSettingsException e) { - logger.error(e.getMessage(), e); - } } + } - private void loadSettingsRecursive(File workDirectory, File file) - throws PdfAsSettingsException { - try { - String configDir = workDirectory.getAbsolutePath() + File.separator - + CFG_DIR; - Properties tmpProps = new Properties(); - logger.debug("Loading: " + file.getName()); - tmpProps.load(new FileInputStream(file)); - - properties.putAll(tmpProps); - - Map<String, String> includes = this.getValuesPrefix(INCLUDE, - tmpProps); - File contextFolder = new File(configDir); - if (includes != null) { - Iterator<String> includeIterator = includes.values().iterator(); - while (includeIterator.hasNext()) { - contextFolder = new File(configDir); - String includeFileName = includeIterator.next(); - - File includeInstruction = new File(contextFolder, - includeFileName); - contextFolder = includeInstruction.getParentFile(); - String includeName = includeInstruction.getName(); - - WildcardFileFilter fileFilter = new WildcardFileFilter( - includeName, IOCase.SENSITIVE); - Collection<File> includeFiles = null; - - if (contextFolder != null && contextFolder.exists() - && contextFolder.isDirectory()) { - includeFiles = FileUtils.listFiles(contextFolder, - fileFilter, null); - } - if (includeFiles != null && !includeFiles.isEmpty()) { - logger.debug("Including '" + includeFileName + "'."); - for (File includeFile : includeFiles) { - loadSettingsRecursive(workDirectory, includeFile); - } - } - } - } - - } catch (IOException e) { - throw new PdfAsSettingsException("Failed to read settings!", e); - } - } + private void loadAdditionConfigFile(String includeFileName, String configDir) { + File contextFolder = new File(configDir); - private void showAugments(Profiles profiles) { - if (!profiles.getAugments().isEmpty()) { - logger.debug("\tAugments for {}", profiles.getName()); - for (int i = 0; i < profiles.getAugments().size(); i++) { - logger.debug("\t\t{}", profiles.getAugments().get(i).getName()); - } - } + final File includeInstruction = new File(contextFolder, includeFileName); + contextFolder = includeInstruction.getParentFile(); + final String includeName = includeInstruction.getName(); + + final WildcardFileFilter fileFilter = new WildcardFileFilter( + includeName, IOCase.SENSITIVE); + Collection<File> includeFiles = null; + + if (contextFolder != null && contextFolder.exists() + && contextFolder.isDirectory()) { + includeFiles = FileUtils.listFiles(contextFolder, + fileFilter, null); + } + if (includeFiles != null && !includeFiles.isEmpty()) { + log.debug("Including '" + includeFileName + "'."); + for (final File includeFile : includeFiles) { + loadSettingsRecursive(workDirectory, includeFile); + } } - private boolean isAugmentsReady(Profiles profiles) { - Iterator<Profiles> augmentingProfiles = profiles.getAugments().iterator(); - boolean allInitialized = true; - while (augmentingProfiles.hasNext()) { - if (!augmentingProfiles.next().isInitialized()) { - allInitialized = false; - } + } + + private void showAugments(Profiles profiles) { + if (!profiles.getAugments().isEmpty()) { + log.debug("\tAugments for {}", profiles.getName()); + for (final Profiles element : profiles.getAugments()) { + log.debug("\t\t{}", element.getName()); + } + } + } + + private boolean isAugmentsReady(Profiles profiles) { + boolean allInitialized = true; + for (final Profiles element : profiles.getAugments()) { + if (!element.isInitialized()) { + allInitialized = false; + } + } + return allInitialized; + } + + private boolean isParentReady(Profiles profiles) { + if (profiles.getParent() != null) { + return profiles.getParent().isInitialized(); + } else { + return false; + } + } + + private void performAugmentConfiguration(Profiles profiles) { + final String childBase = "sig_obj." + + profiles.getName(); + + for (final Profiles augmentingProfile : profiles.getAugments()) { + final String augmentingBase = "sig_obj." + augmentingProfile.getName(); + + for (final String key : this.getKeys( + augmentingBase + ".")) { + final String keyToCopy = key.substring(augmentingBase + .length()); + // log.debug("Profile: {} => {}", + // key, childBase+keyToCopy); + final String sourceKey = augmentingBase + keyToCopy; + final String targetKey = childBase + keyToCopy; + + if (!this.hasValue(targetKey)) { + properties.setProperty(targetKey, + this.getValue(sourceKey)); + // log.debug("Replaced: {} with Value from {}", + // childBase+keyToCopy, parentBase+keyToCopy); + } else { + // log.debug("NOT Replaced: {} with Value from {}", + // childBase+keyToCopy, parentBase+keyToCopy); } - return allInitialized; + } } - - private boolean isParentReady(Profiles profiles) { - if (profiles.getParent() != null) { - return profiles.getParent().isInitialized(); + } + + private void performParentConfiguration(Profiles profiles) { + if (profiles.getParent() != null) { + // If Parent is initialized Copy Properties from Parent + // to this profile + final String parentBase = "sig_obj." + profiles.getParent().getName(); + final String childBase = "sig_obj." + + profiles.getName(); + + for (final String key : this.getKeys( + parentBase + ".")) { + final String keyToCopy = key.substring(parentBase + .length()); + // log.debug("Profile: {} => {}", + // key, childBase+keyToCopy); + final String sourceKey = parentBase + keyToCopy; + final String targetKey = childBase + keyToCopy; + + if (!this.hasValue(targetKey)) { + properties.setProperty(targetKey, + this.getValue(sourceKey)); + // log.debug("Replaced: {} with Value from {}", + // childBase+keyToCopy, parentBase+keyToCopy); } else { - return false; + // log.debug("NOT Replaced: {} with Value from {}", + // childBase+keyToCopy, parentBase+keyToCopy); } + } + } + } + + private void buildProfiles() { + final Map<String, Profiles> profiles = new HashMap<>(); + + for (final String key : this.getFirstLevelKeys("sig_obj.types.")) { + final String profile = key.substring("sig_obj.types.".length()); + // System.out.println("[" + profile + "]: " + this.getValue(key)); + if (this.getValue(key).equals("on")) { + final Profiles prof = new Profiles(profile); + profiles.put(profile, prof); + } } - private void performAugmentConfiguration(Profiles profiles) { - Iterator<Profiles> augmentingProfiles = profiles.getAugments().iterator(); - - String childBase = "sig_obj." - + profiles.getName(); - - while (augmentingProfiles.hasNext()) { - Profiles augmentingProfile = augmentingProfiles.next(); - String augmentingBase = "sig_obj." + augmentingProfile.getName(); - - Iterator<String> augmentingKeyIt = this.getKeys( - augmentingBase + ".").iterator(); - - while (augmentingKeyIt.hasNext()) { - String key = augmentingKeyIt.next(); - String keyToCopy = key.substring(augmentingBase - .length()); - //logger.debug("Profile: {} => {}", - // key, childBase+keyToCopy); - String sourceKey = augmentingBase + keyToCopy; - String targetKey = childBase + keyToCopy; - - if (!this.hasValue(targetKey)) { - properties.setProperty(targetKey, - this.getValue(sourceKey)); - //logger.debug("Replaced: {} with Value from {}", - // childBase+keyToCopy, parentBase+keyToCopy); - } else { - //logger.debug("NOT Replaced: {} with Value from {}", - // childBase+keyToCopy, parentBase+keyToCopy); - } - } - } + for (final Entry<String, Profiles> entry : profiles.entrySet()) { + entry.getValue().findParent(properties, profiles); } - private void performParentConfiguration(Profiles profiles) { - if (profiles.getParent() != null) { - // If Parent is initialized Copy Properties from Parent - // to this profile - String parentBase = "sig_obj." + profiles.getParent().getName(); - String childBase = "sig_obj." - + profiles.getName(); - - Iterator<String> parentKeyIt = this.getKeys( - parentBase + ".").iterator(); - while (parentKeyIt.hasNext()) { - String key = parentKeyIt.next(); - String keyToCopy = key.substring(parentBase - .length()); - //logger.debug("Profile: {} => {}", - // key, childBase+keyToCopy); - String sourceKey = parentBase + keyToCopy; - String targetKey = childBase + keyToCopy; - - if (!this.hasValue(targetKey)) { - properties.setProperty(targetKey, - this.getValue(sourceKey)); - //logger.debug("Replaced: {} with Value from {}", - // childBase+keyToCopy, parentBase+keyToCopy); - } else { - //logger.debug("NOT Replaced: {} with Value from {}", - // childBase+keyToCopy, parentBase+keyToCopy); - } - } - } + for (final Entry<String, Profiles> entry : profiles.entrySet()) { + if (entry.getValue().getParent() == null) { + log.debug("Got Profile: [{}] : {}", entry.getKey(), entry.getValue().getName()); + showAugments(entry.getValue()); + } else { + log.debug("Got Profile: [{}] : {} (Parent {})", entry.getKey(), + entry.getValue().getName(), entry.getValue().getParent().getName()); + showAugments(entry.getValue()); + } } - private void buildProfiles() { - Map<String, Profiles> profiles = new HashMap<String, Profiles>(); - - Iterator<String> itKeys = this.getFirstLevelKeys("sig_obj.types.") - .iterator(); - while (itKeys.hasNext()) { - String key = itKeys.next(); - String profile = key.substring("sig_obj.types.".length()); - //System.out.println("[" + profile + "]: " + this.getValue(key)); - if (this.getValue(key).equals("on")) { - Profiles prof = new Profiles(profile); - profiles.put(profile, prof); - } - } + log.debug("Configured Settings: {}", + properties.size()); + + // Resolve Parent Structures ... + while (!profiles.isEmpty()) { + final List<String> removes = new ArrayList<>(); + for (final Entry<String, Profiles> entry : profiles.entrySet()) { + // Remove all base Profiles ... + if (entry.getValue().getParent() == null && entry.getValue().getAugments().isEmpty()) { + // Has neither parent or augmenting profiles + + entry.getValue().setInitialized(true); + removes.add(entry.getKey()); + } else if (entry.getValue().getParent() == null) { + // Has augmenting profiles but no parent + + // check if all augmenting profiles are initialized if so + // add them + + final Profiles profile = entry.getValue(); + if (this.isAugmentsReady(profile)) { + this.performAugmentConfiguration(profile); + // Copy done + entry.getValue().setInitialized(true); + removes.add(entry.getKey()); + } else { + log.debug("Not all augmenting profiles are ready yet for {}", entry.getValue().getName()); + } + } else if (entry.getValue().getAugments().isEmpty()) { + + // Has parent but no augmenting profiles + final Profiles profile = entry.getValue(); + + if (this.isParentReady(profile)) { + this.performParentConfiguration(profile); + // Copy done + entry.getValue().setInitialized(true); + removes.add(entry.getKey()); + } + } else { - // Initialize Parent Structure ... - Iterator<Entry<String, Profiles>> profileIterator = profiles.entrySet() - .iterator(); - while (profileIterator.hasNext()) { - Entry<String, Profiles> entry = profileIterator.next(); - entry.getValue().findParent(properties, profiles); - } + // Has parent and augmenting profiles - // Debug Output - Iterator<Entry<String, Profiles>> profileIteratorDbg = profiles.entrySet() - .iterator(); - while (profileIteratorDbg.hasNext()) { - Entry<String, Profiles> entry = profileIteratorDbg.next(); - if (entry.getValue().getParent() == null) { - logger.debug("Got Profile: [{}] : {}", entry.getKey(), entry.getValue().getName()); - showAugments(entry.getValue()); - } else { - logger.debug("Got Profile: [{}] : {} (Parent {})", entry.getKey(), - entry.getValue().getName(), entry.getValue().getParent().getName()); - showAugments(entry.getValue()); - } - } + final Profiles profile = entry.getValue(); + if (this.isAugmentsReady(profile) && this.isParentReady(profile)) { + // order is curcial, augments preceed over parent configuration + this.performAugmentConfiguration(profile); + this.performParentConfiguration(profile); - logger.debug("Configured Settings: {}", - properties.size()); - - // Resolve Parent Structures ... - while (!profiles.isEmpty()) { - List<String> removes = new ArrayList<String>(); - Iterator<Entry<String, Profiles>> profileIt = profiles.entrySet() - .iterator(); - while (profileIt.hasNext()) { - Entry<String, Profiles> entry = profileIt.next(); - - // Remove all base Profiles ... - if (entry.getValue().getParent() == null && entry.getValue().getAugments().isEmpty()) { - // Has neither parent or augmenting profiles - - entry.getValue().setInitialized(true); - removes.add(entry.getKey()); - } else if (entry.getValue().getParent() == null) { - // Has augmenting profiles but no parent - - // check if all augmenting profiles are initialized if so - // add them - - Profiles profile = entry.getValue(); - if (this.isAugmentsReady(profile)) { - this.performAugmentConfiguration(profile); - // Copy done - entry.getValue().setInitialized(true); - removes.add(entry.getKey()); - } else { - logger.debug("Not all augmenting profiles are ready yet for {}", entry.getValue().getName()); - } - } else if (entry.getValue().getAugments().isEmpty()) { - - // Has parent but no augmenting profiles - Profiles profile = entry.getValue(); - - if (this.isParentReady(profile)) { - this.performParentConfiguration(profile); - // Copy done - entry.getValue().setInitialized(true); - removes.add(entry.getKey()); - } - } else { - - // Has parent and augmenting profiles - - Profiles profile = entry.getValue(); - if (this.isAugmentsReady(profile) && this.isParentReady(profile)) { - // order is curcial, augments preceed over parent configuration - this.performAugmentConfiguration(profile); - this.performParentConfiguration(profile); - - // Copy done - entry.getValue().setInitialized(true); - removes.add(entry.getKey()); - } - } - } - - // Remove all Profiles from Remove List - - if (removes.isEmpty() && !profiles.isEmpty()) { - logger.error("Failed to build inheritant Profiles, running in infinite loop! (aborting ...)"); - logger.error("Profiles that cannot be resolved completly:"); - Iterator<Entry<String, Profiles>> failedProfiles = profiles.entrySet().iterator(); - while (failedProfiles.hasNext()) { - Entry<String, Profiles> entry = failedProfiles.next(); - logger.error("Problem Profile: [{}] : {}", entry.getKey(), entry.getValue().getName()); - } - return; - } - - Iterator<String> removeIt = removes.iterator(); - while (removeIt.hasNext()) { - profiles.remove(removeIt.next()); - } + // Copy done + entry.getValue().setInitialized(true); + removes.add(entry.getKey()); + } } + } - logger.debug("Derived Settings: {}", - properties.size()); + // Remove all Profiles from Remove List - } - - public void debugDumpProfileSettings(String profileName) { - Iterator<String> keysIterator = this.getKeys("sig_obj." + profileName + ".").iterator(); - - logger.debug("Configuration for {}", profileName); - while(keysIterator.hasNext()) { - String key = keysIterator.next(); - logger.debug(" {}: {}", key, this.getValue(key)); + if (removes.isEmpty() && !profiles.isEmpty()) { + log.error("Failed to build inheritant Profiles, running in infinite loop! (aborting ...)"); + log.error("Profiles that cannot be resolved completly:"); + for (final Entry<String, Profiles> entry : profiles.entrySet()) { + log.error("Problem Profile: [{}] : {}", entry.getKey(), entry.getValue().getName()); } - } + return; + } - public void loadSettings(File workDirectory) throws PdfAsSettingsException { - // try { - String configDir = workDirectory.getAbsolutePath() + File.separator - + CFG_DIR; - String configFile = configDir + File.separator + CFG_FILE; - loadSettingsRecursive(workDirectory, new File(configFile)); - buildProfiles(); - /* - * logger.debug("Loading cfg file: " + configFile); - * - * - * properties.load(new FileInputStream(configFile)); - * - * Map<String, String> includes = this.getValuesPrefix(INCLUDE); File - * contextFolder = new File(configDir); if (includes != null) { - * Iterator<String> includeIterator = includes.values().iterator(); - * while (includeIterator.hasNext()) { String includeFileName = - * includeIterator.next(); if (includeFileName.contains("*")) { - * WildcardFileFilter fileFilter = new WildcardFileFilter( - * includeFileName, IOCase.SENSITIVE); Collection<File> includeFiles = - * null; - * - * if (contextFolder != null && contextFolder.exists() && - * contextFolder.isDirectory()) { includeFiles = - * FileUtils.listFiles(contextFolder, fileFilter, null); } if - * (includeFiles != null && !includeFiles.isEmpty()) { - * logger.info("Including '" + includeFileName + "'."); for (File - * includeFile : includeFiles) { properties .load(new - * FileInputStream(includeFile)); } } } else { String includeFile = - * configDir + File.separator + includeFileName; - * logger.debug("Loading included cfg file: " + includeFile); try { - * properties.load(new FileInputStream(includeFile)); } catch (Throwable - * e) { logger.error("Failed to load cfg file " + includeFile, e); } } } - * } - */ - // logger.debug("Configured Properties:"); - /* - * if(logger.isDebugEnabled()) { properties.list(System.out); } - */ - - // } catch (IOException e) { - // throw new PdfAsSettingsException("Failed to read settings!", e); - // } + for (final String element : removes) { + profiles.remove(element); + } } - public String getValue(String key) { - return properties.getProperty(key); - } + log.debug("Derived Settings: {}", + properties.size()); - public boolean hasValue(String key) { - return properties.containsKey(key); - } + } - private Map<String, String> getValuesPrefix(String prefix, Properties props) { - Iterator<Object> keyIterator = props.keySet().iterator(); - Map<String, String> valueMap = new HashMap<String, String>(); - while (keyIterator.hasNext()) { - String key = keyIterator.next().toString(); + public void debugDumpProfileSettings(String profileName) { + log.debug("Configuration for {}", profileName); + for (final String key : this.getKeys("sig_obj." + profileName + ".")) { + log.debug(" {}: {}", key, this.getValue(key)); + } + } + public void loadSettings(File workDirectory) throws PdfAsSettingsException { + try { + final String configDir = workDirectory.getAbsolutePath() + File.separator + + CFG_DIR; + final String configFile = configDir + File.separator + CFG_FILE; + loadSettingsRecursive(workDirectory, new File(configFile)); + buildProfiles(); - if (key.startsWith(prefix)) { - valueMap.put(key, props.getProperty(key)); - } - } + } catch (final RuntimeException e) { + throw new PdfAsSettingsException("Failed to read settings!", e); - if (valueMap.isEmpty()) { - return null; - } - - return valueMap; } - - public Map<String, String> getValuesPrefix(String prefix) { - return getValuesPrefix(prefix, properties); + } + + @Override + public String getValue(String key) { + return properties.getProperty(key); + } + + public boolean isValue(String key) { + return isValue(key, false); + + } + public boolean isValue(String key, boolean defaultValue) { + String value = getValue(key); + if (StringUtils.isNotEmpty(value)) { + return Boolean.valueOf(value); + + } else { + return defaultValue; + } - - public Vector<String> getKeys(String prefix) { - Iterator<Object> keyIterator = properties.keySet().iterator(); - Vector<String> valueMap = new Vector<String>(); - while (keyIterator.hasNext()) { - String key = keyIterator.next().toString(); - - if (key.startsWith(prefix)) { - valueMap.add(key); - } - } - return valueMap; + } + + @Override + public boolean hasValue(String key) { + return properties.containsKey(key); + } + + private Map<String, String> getValuesPrefix(String prefix, Properties props) { + return props.entrySet().stream() + .filter(el -> el.getKey().toString().startsWith(prefix)) + .collect(Collectors.toMap(key -> key.getKey().toString(), value -> value.getValue().toString())); + + } + + @Override + public Map<String, String> getValuesPrefix(String prefix) { + return getValuesPrefix(prefix, properties); + } + + public Vector<String> getKeys(String prefix) { + final Iterator<Object> keyIterator = properties.keySet().iterator(); + final Vector<String> valueMap = new Vector<>(); + while (keyIterator.hasNext()) { + final String key = keyIterator.next().toString(); + + if (key.startsWith(prefix)) { + valueMap.add(key); + } } - - public Vector<String> getFirstLevelKeys(String prefix) { - String mPrefix = prefix.endsWith(".") ? prefix : prefix + "."; - Iterator<Object> keyIterator = properties.keySet().iterator(); - Vector<String> valueMap = new Vector<String>(); - while (keyIterator.hasNext()) { - String key = keyIterator.next().toString(); - - if (key.startsWith(prefix)) { - int keyIdx = key.indexOf('.', mPrefix.length()) > 0 ? key - .indexOf('.', mPrefix.length()) : key.length(); - String firstLevels = key.substring(0, keyIdx); - if (!valueMap.contains(firstLevels)) { - valueMap.add(firstLevels); - } - } - } - - if (valueMap.isEmpty()) { - return null; + return valueMap; + } + + @Override + public Vector<String> getFirstLevelKeys(String prefix) { + final String mPrefix = prefix.endsWith(".") ? prefix : prefix + "."; + final Iterator<Object> keyIterator = properties.keySet().iterator(); + final Vector<String> valueMap = new Vector<>(); + while (keyIterator.hasNext()) { + final String key = keyIterator.next().toString(); + + if (key.startsWith(prefix)) { + final int keyIdx = key.indexOf('.', mPrefix.length()) > 0 ? key + .indexOf('.', mPrefix.length()) : key.length(); + final String firstLevels = key.substring(0, keyIdx); + if (!valueMap.contains(firstLevels)) { + valueMap.add(firstLevels); } + } + } - return valueMap; + if (valueMap.isEmpty()) { + return null; } - public boolean hasPrefix(String prefix) { - Iterator<Object> keyIterator = properties.keySet().iterator(); - while (keyIterator.hasNext()) { - String key = keyIterator.next().toString(); + return valueMap; + } - if (key.startsWith(prefix)) { - return true; - } - } - return false; - } + @Override + public boolean hasPrefix(String prefix) { + final Iterator<Object> keyIterator = properties.keySet().iterator(); + while (keyIterator.hasNext()) { + final String key = keyIterator.next().toString(); - public String getWorkingDirectory() { - return this.workDirectory.getAbsolutePath(); + if (key.startsWith(prefix)) { + return true; + } } + return false; + } + + @Override + public String getWorkingDirectory() { + return this.workDirectory.getAbsolutePath(); + } } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/SignatureUtils.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/SignatureUtils.java index 6e584f32..6282d9c1 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/SignatureUtils.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/SignatureUtils.java @@ -1,144 +1,138 @@ package at.gv.egiz.pdfas.lib.util; -import iaik.asn1.CodingException; -import iaik.asn1.ObjectID; -import iaik.asn1.structures.Attribute; -import iaik.cms.CMSException; -import iaik.cms.SignedData; -import iaik.cms.SignerInfo; -import iaik.smime.ess.ESSCertID; -import iaik.smime.ess.ESSCertIDv2; -import iaik.smime.ess.SigningCertificate; -import iaik.smime.ess.SigningCertificateV2; -import iaik.x509.X509Certificate; - import java.io.ByteArrayInputStream; import java.io.IOException; -import java.security.NoSuchAlgorithmException; import java.security.SignatureException; -import java.security.cert.CertificateException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import at.gv.egiz.pdfas.common.exceptions.ErrorConstants; import at.gv.egiz.pdfas.common.exceptions.PDFASError; -import at.gv.egiz.pdfas.common.exceptions.PdfAsSignatureException; -import at.gv.egiz.pdfas.common.utils.StreamUtils; import at.gv.egiz.pdfas.lib.api.verify.VerifyResult; import at.gv.egiz.pdfas.lib.impl.verify.VerifyResultImpl; +import iaik.asn1.ObjectID; +import iaik.asn1.structures.Attribute; +import iaik.cms.CMSException; +import iaik.cms.SignedData; +import iaik.cms.SignerInfo; +import iaik.smime.ess.SigningCertificate; +import iaik.smime.ess.SigningCertificateV2; +import iaik.x509.X509Certificate; public class SignatureUtils implements ErrorConstants { - - private static final Logger logger = LoggerFactory - .getLogger(SignatureUtils.class); - - public static VerifyResult verifySignature(byte[] signature, byte[] input) - throws PDFASError { - // List<VerifyResult> results = new ArrayList<VerifyResult>(); - try { - SignedData signedData = new SignedData(new ByteArrayInputStream( - signature)); - - signedData.setContent(input); - - // get the signer infos - SignerInfo[] signerInfos = signedData.getSignerInfos(); - if (signerInfos.length == 0) { - logger.warn("Invalid signature (no signer information)"); - throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG); - } - - if (signerInfos.length != 1) { - logger.warn("Invalid signature (multiple signer information)"); - throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG); - } - // verify the signatures - // for (int i = 0; i < signerInfos.length; i++) { - VerifyResultImpl verifyResult = new VerifyResultImpl(); - // results.add(verifyResult); - try { - logger.debug("Signature Algo: {}, Digest {}", - signedData.getSignerInfos()[0].getSignatureAlgorithm(), - signedData.getSignerInfos()[0].getDigestAlgorithm()); - // verify the signature for SignerInfo at index i - X509Certificate signer_cert = signedData.verify(0); - - // Must include Signing Certificate! - Attribute signedCertificate = signerInfos[0] - .getSignedAttribute(ObjectID.signingCertificate); - - if (signedCertificate == null) { - signedCertificate = signerInfos[0] - .getSignedAttribute(ObjectID.signingCertificateV2); - if (signedCertificate == null) { - logger.warn("Signature ERROR missing signed Signing Certificate: "); - - throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG); - } else { - // Validate signingCertificate2 - try { - SigningCertificateV2 signingCert = (SigningCertificateV2)signedCertificate.getAttributeValue(); - - if (signingCert.isSignerCertificate(signer_cert)) { - // OK - logger.debug("Found and verified SigningCertificateV2"); - } else { - logger.error("Signature ERROR certificate missmatch, misbehaving Signature Backend?"); - - throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG); - } - } catch (Throwable e) { - logger.error("Signature ERROR wrong encoding for ESSCertIDv2, misbehaving Signature Backend?"); - - throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG, e); - } - } - } else { - // Validate signingCertificate - try { - SigningCertificate signingCert = (SigningCertificate)signedCertificate.getAttributeValue(); - if (signingCert.isSignerCertificate(signer_cert)) { - // OK - logger.debug("Found and verified SigningCertificate"); - } else { - logger.warn("Signature ERROR certificate missmatch, misbehaving Signature Backend?"); - - throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG); - } - } catch (Throwable e) { - logger.error("Signature ERROR wrong encoding for ESSCertIDv2, misbehaving Signature Backend?"); - - throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG, e); - } - } - - // if the signature is OK the certificate of the - // signer is returned - logger.debug("Signature OK"); - verifyResult.setSignerCertificate(signer_cert); - - } catch (SignatureException ex) { - // if the signature is not OK a SignatureException - // is thrown - logger.warn( - "Signature ERROR from signer: " - + signedData.getCertificate( - signerInfos[0].getSignerIdentifier()) - .getSubjectDN(), ex); - - verifyResult.setSignerCertificate(signedData - .getCertificate(signerInfos[0].getSignerIdentifier())); - throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG, ex); - } - - return verifyResult; - // } - } catch (CMSException e) { - throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG, e); - } catch (IOException e) { - throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG, e); - } - - } + private static final Logger logger = LoggerFactory.getLogger(SignatureUtils.class); + + public static VerifyResult verifySignature(byte[] signature, byte[] input) + throws PDFASError { + // List<VerifyResult> results = new ArrayList<VerifyResult>(); + try { + final SignedData signedData = new SignedData(new ByteArrayInputStream(signature)); + signedData.setContent(input); + + // get the signer infos + final SignerInfo[] signerInfos = signedData.getSignerInfos(); + if (signerInfos.length == 0) { + logger.warn("Invalid signature (no signer information)"); + throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG); + + } + + if (signerInfos.length != 1) { + logger.warn("Invalid signature (multiple signer information)"); + throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG); + + } + // verify the signatures + // for (int i = 0; i < signerInfos.length; i++) { + final VerifyResultImpl verifyResult = new VerifyResultImpl(); + // results.add(verifyResult); + try { + logger.debug("Signature Algo: {}, Digest {}", + signedData.getSignerInfos()[0].getSignatureAlgorithm(), + signedData.getSignerInfos()[0].getDigestAlgorithm()); + // verify the signature for SignerInfo at index i + final X509Certificate signer_cert = signedData.verify(0); + + // Must include Signing Certificate! + Attribute signedCertificate = signerInfos[0].getSignedAttribute(ObjectID.signingCertificate); + + if (signedCertificate == null) { + signedCertificate = signerInfos[0].getSignedAttribute(ObjectID.signingCertificateV2); + + if (signedCertificate == null) { + logger.warn("Signature ERROR missing signed Signing Certificate: "); + throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG); + + } else { + // Validate signingCertificate2 + try { + final SigningCertificateV2 signingCert = (SigningCertificateV2) signedCertificate.getAttributeValue(); + if (signingCert.isSignerCertificate(signer_cert)) { + // OK + logger.debug("Found and verified SigningCertificateV2"); + + } else { + logger.error("Signature ERROR certificate missmatch, misbehaving Signature Backend?"); + + throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG); + } + + } catch (final Throwable e) { + logger.error("Signature ERROR wrong encoding for ESSCertIDv2, misbehaving Signature Backend?"); + throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG, e); + + } + } + + } else { + // Validate signingCertificate + try { + final SigningCertificate signingCert = (SigningCertificate) signedCertificate.getAttributeValue(); + if (signingCert.isSignerCertificate(signer_cert)) { + // OK + logger.debug("Found and verified SigningCertificate"); + } else { + logger.warn("Signature ERROR certificate missmatch, misbehaving Signature Backend?"); + + throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG); + } + } catch (final Throwable e) { + logger.error("Signature ERROR wrong encoding for ESSCertIDv2, misbehaving Signature Backend?"); + + throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG, e); + } + } + + // if the signature is OK the certificate of the + // signer is returned + logger.debug("Signature OK"); + verifyResult.setSignerCertificate(signer_cert); + + } catch (final SignatureException ex) { + // if the signature is not OK a SignatureException + // is thrown + logger.warn( + "Signature ERROR from signer: " + + signedData.getCertificate( + signerInfos[0].getSignerIdentifier()) + .getSubjectDN(), ex); + + verifyResult.setSignerCertificate(signedData + .getCertificate(signerInfos[0].getSignerIdentifier())); + throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG, ex); + + } + + return verifyResult; + // } + } catch (final CMSException e) { + throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG, e); + + } catch (final IOException e) { + throw new PDFASError(ERROR_SIG_INVALID_BKU_SIG, e); + + } + + } } diff --git a/pdf-as-lib/src/test/java/at/gv/egiz/pdfas/lib/test/stamping/CertificateAndRequestParameterResolverTest.java b/pdf-as-lib/src/test/java/at/gv/egiz/pdfas/lib/test/stamping/CertificateAndRequestParameterResolverTest.java index fdc8fa7e..e94d21e8 100644 --- a/pdf-as-lib/src/test/java/at/gv/egiz/pdfas/lib/test/stamping/CertificateAndRequestParameterResolverTest.java +++ b/pdf-as-lib/src/test/java/at/gv/egiz/pdfas/lib/test/stamping/CertificateAndRequestParameterResolverTest.java @@ -4,8 +4,10 @@ import static org.junit.Assert.assertEquals; import java.io.IOException; import java.security.cert.CertificateException; +import java.util.HashMap; import java.util.Map; import java.util.Vector; +import java.util.stream.Collectors; import org.junit.Before; import org.junit.Test; @@ -13,6 +15,7 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import at.gv.egiz.pdfas.common.exceptions.PDFASError; +import at.gv.egiz.pdfas.common.settings.IProfileConstants; import at.gv.egiz.pdfas.common.settings.ISettings; import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings; import at.gv.egiz.pdfas.lib.api.sign.SignParameter; @@ -76,18 +79,23 @@ public class CertificateAndRequestParameterResolverTest { } private ISettings buildDummySettings() { - return new ISettings() { - + Map<String, String> configMap = new HashMap<>(); + configMap.put(IProfileConstants.SIG_OBJ + "test", "test"); + + return new ISettings() { + @Override public boolean hasValue(String key) { - // TODO Auto-generated method stub - return false; + return configMap.containsKey(key); } @Override public boolean hasPrefix(String prefix) { - // TODO Auto-generated method stub - return false; + return configMap.keySet().stream() + .filter(el -> el.startsWith(prefix)) + .findFirst() + .isPresent(); + } @Override @@ -98,14 +106,16 @@ public class CertificateAndRequestParameterResolverTest { @Override public Map<String, String> getValuesPrefix(String prefix) { - // TODO Auto-generated method stub - return null; + return configMap.entrySet().stream() + .filter(el -> el.getKey().startsWith(prefix)) + .collect(Collectors.toMap(key -> key.getKey(), value -> value.getValue())); + } @Override public String getValue(String key) { - // TODO Auto-generated method stub - return null; + return configMap.get(key); + } @Override @@ -113,6 +123,16 @@ public class CertificateAndRequestParameterResolverTest { // TODO Auto-generated method stub return null; } - }; + + @Override + public boolean isValue(String key) { + return isValue(key, false); + } + + @Override + public boolean isValue(String key, boolean defaultValue) { + return hasValue(key) ? Boolean.valueOf(getValue(key)) : defaultValue; + } + }; } } diff --git a/pdf-as-moa/build.gradle b/pdf-as-moa/build.gradle index 0e1032e2..d0fe8c0b 100644 --- a/pdf-as-moa/build.gradle +++ b/pdf-as-moa/build.gradle @@ -35,7 +35,7 @@ project.ext { generatedWsdlDir = file("src/generated/java") wsdlsToGenerate = [ ['-xjc', - "$wsdlDir/MOA-SPSS-1.5.2.wsdl"], + "$wsdlDir/MOA-SPSS-2.0.0.wsdl"], ] } diff --git a/pdf-as-moa/src/generated/java/at/gv/e_government/reference/namespace/moa/_20020822_/SignatureCreationService.java b/pdf-as-moa/src/generated/java/at/gv/e_government/reference/namespace/moa/_20020822_/SignatureCreationService.java index 67ad0fc0..1dabeb00 100644 --- a/pdf-as-moa/src/generated/java/at/gv/e_government/reference/namespace/moa/_20020822_/SignatureCreationService.java +++ b/pdf-as-moa/src/generated/java/at/gv/e_government/reference/namespace/moa/_20020822_/SignatureCreationService.java @@ -1,11 +1,12 @@ package at.gv.e_government.reference.namespace.moa._20020822_; import java.net.URL; + import javax.xml.namespace.QName; +import javax.xml.ws.Service; import javax.xml.ws.WebEndpoint; import javax.xml.ws.WebServiceClient; import javax.xml.ws.WebServiceFeature; -import javax.xml.ws.Service; /** * This class was generated by Apache CXF 3.0.1 @@ -14,7 +15,7 @@ import javax.xml.ws.Service; * */ @WebServiceClient(name = "SignatureCreationService", - wsdlLocation = "/wsdl/MOA-SPSS-1.5.2.wsdl", + wsdlLocation = "/wsdl/MOA-SPSS-2.0.0.wsdl", targetNamespace = "http://reference.e-government.gv.at/namespace/moa/20020822#") public class SignatureCreationService extends Service { @@ -23,14 +24,14 @@ public class SignatureCreationService extends Service { public final static QName SERVICE = new QName("http://reference.e-government.gv.at/namespace/moa/20020822#", "SignatureCreationService"); public final static QName SignatureCreationPort = new QName("http://reference.e-government.gv.at/namespace/moa/20020822#", "SignatureCreationPort"); static { - URL url = SignatureCreationService.class.getResource("/wsdl/MOA-SPSS-1.5.2.wsdl"); + URL url = SignatureCreationService.class.getResource("/wsdl/MOA-SPSS-2.0.0.wsdl"); if (url == null) { - url = SignatureCreationService.class.getClassLoader().getResource("/wsdl/MOA-SPSS-1.5.2.wsdl"); + url = SignatureCreationService.class.getClassLoader().getResource("/wsdl/MOA-SPSS-2.0.0.wsdl"); } if (url == null) { java.util.logging.Logger.getLogger(SignatureCreationService.class.getName()) .log(java.util.logging.Level.INFO, - "Can not initialize the default wsdl from {0}", "/wsdl/MOA-SPSS-1.5.2.wsdl"); + "Can not initialize the default wsdl from {0}", "/wsdl/MOA-SPSS-2.0.0.wsdl"); } WSDL_LOCATION = url; } diff --git a/pdf-as-moa/src/generated/java/at/gv/e_government/reference/namespace/moa/_20020822_/SignatureVerificationService.java b/pdf-as-moa/src/generated/java/at/gv/e_government/reference/namespace/moa/_20020822_/SignatureVerificationService.java index 2de494b7..f5ec7b54 100644 --- a/pdf-as-moa/src/generated/java/at/gv/e_government/reference/namespace/moa/_20020822_/SignatureVerificationService.java +++ b/pdf-as-moa/src/generated/java/at/gv/e_government/reference/namespace/moa/_20020822_/SignatureVerificationService.java @@ -1,11 +1,12 @@ package at.gv.e_government.reference.namespace.moa._20020822_; import java.net.URL; + import javax.xml.namespace.QName; +import javax.xml.ws.Service; import javax.xml.ws.WebEndpoint; import javax.xml.ws.WebServiceClient; import javax.xml.ws.WebServiceFeature; -import javax.xml.ws.Service; /** * This class was generated by Apache CXF 3.0.1 @@ -14,7 +15,7 @@ import javax.xml.ws.Service; * */ @WebServiceClient(name = "SignatureVerificationService", - wsdlLocation = "/wsdl/MOA-SPSS-1.5.2.wsdl", + wsdlLocation = "/wsdl/MOA-SPSS-2.0.0.wsdl", targetNamespace = "http://reference.e-government.gv.at/namespace/moa/20020822#") public class SignatureVerificationService extends Service { @@ -23,14 +24,14 @@ public class SignatureVerificationService extends Service { public final static QName SERVICE = new QName("http://reference.e-government.gv.at/namespace/moa/20020822#", "SignatureVerificationService"); public final static QName SignatureVerificationPort = new QName("http://reference.e-government.gv.at/namespace/moa/20020822#", "SignatureVerificationPort"); static { - URL url = SignatureVerificationService.class.getResource("/wsdl/MOA-SPSS-1.5.2.wsdl"); + URL url = SignatureVerificationService.class.getResource("/wsdl/MOA-SPSS-2.0.0.wsdl"); if (url == null) { - url = SignatureVerificationService.class.getClassLoader().getResource("/wsdl/MOA-SPSS-1.5.2.wsdl"); + url = SignatureVerificationService.class.getClassLoader().getResource("/wsdl/MOA-SPSS-2.0.0.wsdl"); } if (url == null) { java.util.logging.Logger.getLogger(SignatureVerificationService.class.getName()) .log(java.util.logging.Level.INFO, - "Can not initialize the default wsdl from {0}", "/wsdl/MOA-SPSS-1.5.2.wsdl"); + "Can not initialize the default wsdl from {0}", "/wsdl/MOA-SPSS-2.0.0.wsdl"); } WSDL_LOCATION = url; } diff --git a/pdf-as-pdfbox-2/build.gradle b/pdf-as-pdfbox-2/build.gradle index 6a2a0bfa..f6f14a5d 100644 --- a/pdf-as-pdfbox-2/build.gradle +++ b/pdf-as-pdfbox-2/build.gradle @@ -32,7 +32,7 @@ releases.dependsOn sourcesJar dependencies { implementation project (':pdf-as-lib') implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4jVersion - implementation 'org.slf4j:jcl-over-slf4j:1.7.35' + implementation 'org.slf4j:jcl-over-slf4j:1.7.36' api group: 'org.apache.pdfbox', name: 'pdfbox', version: '2.0.27' api group: 'org.apache.pdfbox', name: 'pdfbox-tools', version: '2.0.27' api group: 'org.apache.pdfbox', name: 'preflight', version: '2.0.27' @@ -42,6 +42,9 @@ dependencies { api group: 'com.github.jai-imageio', name: 'jai-imageio-core', version: '1.4.0' api group: 'com.levigo.jbig2', name: 'levigo-jbig2-imageio', version: '2.0' implementation group: 'javax.activation', name: 'activation', version: '1.1.1' + + testImplementation 'ch.qos.logback:logback-classic:1.2.12' + testImplementation 'ch.qos.logback:logback-core:1.2.12' } diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXObject.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXObject.java index 891ebe39..53b87d86 100644 --- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXObject.java +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXObject.java @@ -1,14 +1,10 @@ package at.gv.egiz.pdfas.lib.impl.pdfbox2; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; import javax.activation.DataSource; -import org.apache.pdfbox.io.MemoryUsageSetting; import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.pdmodel.font.PDFont; import at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2.PDFAsFontCache; import at.gv.egiz.pdfas.lib.impl.status.OperationStatus; @@ -17,9 +13,7 @@ import at.gv.egiz.pdfas.lib.impl.status.PDFObject; public class PDFBOXObject extends PDFObject { private PDDocument doc; - - private Map<String, PDFont> fontCache = new HashMap<String, PDFont>(); - + private PDFAsFontCache sigBlockFontCache = new PDFAsFontCache(); public PDFAsFontCache getSigBlockFontCache() { @@ -54,11 +48,6 @@ public class PDFBOXObject extends PDFObject { } } - private MemoryUsageSetting getMemoryUsageSettings() { - // TODO: allow fine tuning of memory usage (divided main memory vs file memory) - return MemoryUsageSetting.setupMainMemoryOnly(); - } - public void setOriginalDocument(DataSource originalDocument) throws IOException { this.originalDocument = originalDocument; if(doc != null) { diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/PDFBoxPlaceholderExtractor.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/PDFBoxPlaceholderExtractor.java index 63b006bf..ad874bc0 100644 --- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/PDFBoxPlaceholderExtractor.java +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/PDFBoxPlaceholderExtractor.java @@ -1,5 +1,7 @@ package at.gv.egiz.pdfas.lib.impl.pdfbox2.placeholder; +import java.io.IOException; + import at.gv.egiz.pdfas.common.exceptions.PDFIOException; import at.gv.egiz.pdfas.common.exceptions.PdfAsException; import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject; @@ -7,9 +9,6 @@ import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderExtractor; import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData; import at.gv.egiz.pdfas.lib.impl.status.PDFObject; -import java.io.IOException; -import java.util.List; - public class PDFBoxPlaceholderExtractor implements PlaceholderExtractor { @@ -18,27 +17,8 @@ public class PDFBoxPlaceholderExtractor implements PlaceholderExtractor { if (doc instanceof PDFBOXObject) { PDFBOXObject object = (PDFBOXObject) doc; try { - SignaturePlaceholderExtractor extractor = new SignaturePlaceholderExtractor(placeholderId, - matchMode, object.getDocument()); - return extractor.extract(object.getDocument(), - placeholderId, matchMode); - } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e2) { - throw new PDFIOException("error.pdf.io.04", e2); - } - - } - throw new PdfAsException("INVALID STATE"); - } - - @Override - public List<SignaturePlaceholderData> extractList(PDFObject doc, String placeholderId, int matchMode) throws PdfAsException { - if (doc instanceof PDFBOXObject) { - PDFBOXObject object = (PDFBOXObject) doc; - try { - SignaturePlaceholderExtractor extractor = new SignaturePlaceholderExtractor(placeholderId, - matchMode, object.getDocument()); - return extractor.extractList(object.getDocument(), - placeholderId, matchMode); + SignaturePlaceholderExtractor extractor = new SignaturePlaceholderExtractor(); + return extractor.extract(object.getDocument(), placeholderId, matchMode); } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e2) { throw new PDFIOException("error.pdf.io.04", e2); } diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignatureFieldsAndPlaceHolderExtractor.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignatureFieldsAndPlaceHolderExtractor.java index 609f8254..8e5e5d4e 100644 --- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignatureFieldsAndPlaceHolderExtractor.java +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignatureFieldsAndPlaceHolderExtractor.java @@ -1,106 +1,58 @@ package at.gv.egiz.pdfas.lib.impl.pdfbox2.placeholder; -import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderExtractorConstants; -import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData; +import java.util.ArrayList; +import java.util.List; + import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDField; import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderExtractorConstants; +import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData; public class SignatureFieldsAndPlaceHolderExtractor { - //Search for empty signature fields - public static List<String> findEmptySignatureFields(PDDocument doc) - { - PDSignature signature; - List<PDField> signatureField; - List<String> signatureFieldNames = new ArrayList<>(); - PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm(); - if (acroForm != null) { - signatureField = acroForm.getFields(); - for (PDField pdField : signatureField) { - if(pdField instanceof PDSignatureField && pdField.getPartialName()!=null) - { - signature = ((PDSignatureField) pdField).getSignature(); - if(signature == null) signatureFieldNames.add(pdField.getPartialName()); - } - } - } - return signatureFieldNames; - } - /* - Needed by PDF-OVER - */ - - /** - * Returns the next unused signature placeholder - * @param doc The document to be searched for signature placeholders - * @return The next unused signature placeholder or null in case there is none - */ - public static SignaturePlaceholderData getNextUnusedSignaturePlaceHolder(PDDocument doc) { - try { - String placeholderId = "1"; - int mode = PlaceholderExtractorConstants.PLACEHOLDER_MATCH_MODE_SORTED; - SignaturePlaceholderExtractor signaturePlaceholderExtractor = new SignaturePlaceholderExtractor( placeholderId, - mode, doc); - List<SignaturePlaceholderData> results = signaturePlaceholderExtractor.extractList(doc, placeholderId, - mode); - if (results == null) { - return null; - } - List<String> used = getExistingSignatureLocations(doc); - //return first not used - for(SignaturePlaceholderData result : results) { - if(!used.contains(result.getPlaceholderName())) - return result; - } - return null; - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - public static SignaturePlaceholderData getSignaturePlaceHolder(PDDocument doc, String placeholderId, - int mode) { - try { - SignaturePlaceholderExtractor signaturePlaceholderExtractor = new SignaturePlaceholderExtractor( placeholderId, - mode, doc); - return signaturePlaceholderExtractor.extract(doc, placeholderId, mode); - } catch (Exception e) { - e.printStackTrace(); - return null; + // Search for empty signature fields + public static List<String> findEmptySignatureFields(PDDocument doc) { + PDSignature signature; + List<PDField> signatureField; + final List<String> signatureFieldNames = new ArrayList<>(); + final PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm(); + if (acroForm != null) { + signatureField = acroForm.getFields(); + for (final PDField pdField : signatureField) { + if (pdField instanceof PDSignatureField && pdField.getPartialName() != null) { + signature = ((PDSignatureField) pdField).getSignature(); + if (signature == null) { + signatureFieldNames.add(pdField.getPartialName()); + } } + } } - - public static List<SignaturePlaceholderData> getSignaturePlaceHolderList(PDDocument doc, String placeholderId, int mode) { - try { - SignaturePlaceholderExtractor signaturePlaceholderExtractor = new SignaturePlaceholderExtractor( placeholderId, - mode, doc); - return signaturePlaceholderExtractor.extractList(doc, placeholderId, mode); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - public static List<String> getExistingSignatureLocations(PDDocument doc) { - List<String> existingLocations = new ArrayList<>(); - try { - List <PDSignature> pdSignatureList = doc.getSignatureDictionaries(); - if(pdSignatureList.size() != 0) { - for(PDSignature sig : pdSignatureList) { - existingLocations.add(sig.getLocation()); - } - } - } catch (IOException e) { - e.printStackTrace(); - } - return existingLocations; + return signatureFieldNames; + } + /* + * Needed by PDF-OVER + */ + + /** + * Returns the next unused signature placeholder + * + * @param doc The document to be searched for signature placeholders + * @return The next unused signature placeholder or null in case there is none + */ + public static SignaturePlaceholderData getNextUnusedSignaturePlaceHolder(PDDocument doc) { + try { + final String placeholderId = "1"; + final int mode = PlaceholderExtractorConstants.PLACEHOLDER_MATCH_MODE_SORTED; + final SignaturePlaceholderExtractor signaturePlaceholderExtractor = new SignaturePlaceholderExtractor(); + return signaturePlaceholderExtractor.extract(doc, placeholderId, mode); + + } catch (final Exception e) { + e.printStackTrace(); + return null; } + } } diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignaturePlaceholderExtractor.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignaturePlaceholderExtractor.java index 6df4f0b1..7cff90d6 100644 --- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignaturePlaceholderExtractor.java +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignaturePlaceholderExtractor.java @@ -3,19 +3,19 @@ * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a * joint initiative of the Federal Chancellery Austria and Graz University of * Technology. - * + * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. - * + * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works @@ -50,34 +50,27 @@ import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.image.BufferedImage; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Hashtable; +import java.util.List; import java.util.Map.Entry; - -import javassist.bytecode.stackmap.TypeData.ClassName; +import java.util.Properties; +import java.util.Set; +import java.util.Vector; +import java.util.stream.Collectors; import org.apache.pdfbox.contentstream.PDFStreamEngine; import org.apache.pdfbox.contentstream.operator.Operator; import org.apache.pdfbox.contentstream.operator.OperatorProcessor; import org.apache.pdfbox.cos.COSBase; -import org.apache.pdfbox.cos.COSDictionary; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; -import org.apache.pdfbox.pdmodel.font.PDFont; -import org.apache.pdfbox.pdmodel.font.PDFontFactory; import org.apache.pdfbox.pdmodel.graphics.PDXObject; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; import org.apache.pdfbox.util.Matrix; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import at.gv.egiz.pdfas.common.exceptions.PDFIOException; -import at.gv.egiz.pdfas.common.exceptions.PdfAsException; -import at.gv.egiz.pdfas.common.exceptions.PlaceholderExtractionException; -import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderExtractorConstants; -import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderContext; -import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData; -import at.knowcenter.wag.egov.egiz.pdf.TablePos; import com.google.zxing.BarcodeFormat; import com.google.zxing.BinaryBitmap; @@ -90,425 +83,432 @@ import com.google.zxing.Result; import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.common.HybridBinarizer; +import at.gv.egiz.pdfas.common.exceptions.PDFIOException; +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.exceptions.PlaceholderExtractionException; +import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderExtractorConstants; +import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData; +import at.knowcenter.wag.egov.egiz.pdf.TablePos; +import javassist.bytecode.stackmap.TypeData.ClassName; +import lombok.extern.slf4j.Slf4j; + /** * Extract all relevant information from a placeholder image. * * @author exthex * */ -public class SignaturePlaceholderExtractor extends PDFStreamEngine implements PlaceholderExtractorConstants{ - /** - * The log. - */ - private static Logger logger = LoggerFactory - .getLogger(SignaturePlaceholderExtractor.class); - - private List<SignaturePlaceholderData> placeholders = new ArrayList<>(); - private int currentPage = 0; - private PDDocument doc; - - protected SignaturePlaceholderExtractor(String placeholderId, - int placeholderMatchMode, PDDocument doc) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { - super(); - - final Properties properties = new Properties(); - properties.load(ClassName.class.getClassLoader().getResourceAsStream("placeholder/pdfbox-reader-2.properties")); - - Set<Entry<Object, Object>> entries = properties.entrySet(); - for(Entry<Object, Object> entry:entries){ - String processorClassName = (String)entry.getValue(); - Class<?> klass = Class.forName( processorClassName ); - org.apache.pdfbox.contentstream.operator.OperatorProcessor processor = - (OperatorProcessor) klass.newInstance(); - - addOperator( processor ); - } - this.doc = doc; - } - - /** - * Search the document for placeholder images and possibly included - * additional info.<br/> - * Searches only for the first placeholder page after page from top. - * - * @return all available info from the first found placeholder. - * @throws PdfAsException - * if the document could not be read. - * @throws PlaceholderExtractionException - * if STRICT matching mode was requested and no suitable - * placeholder could be found. - */ - public SignaturePlaceholderData extract(PDDocument doc, - String placeholderId, int matchMode) throws PdfAsException { - SignaturePlaceholderContext.setSignaturePlaceholderData(null); -// SignaturePlaceholderExtractor extractor; -// try { -// extractor = new SignaturePlaceholderExtractor(placeholderId, -// matchMode, doc); -// } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e2) { -// throw new PDFIOException("error.pdf.io.04", e2); -// } - - int pageNr = 0; - for(PDPage page : doc.getPages()){ - pageNr++; - - try { - setCurrentPage(pageNr); - if(page.getContents() != null && page.getResources() != null && page.getContentStreams() != null) { - processPage(page); //TODO: pdfbox2 - right? - - } - SignaturePlaceholderData ret = matchPlaceholderPage( - placeholders, placeholderId, matchMode); - if (ret != null) { - SignaturePlaceholderContext - .setSignaturePlaceholderData(ret); - return ret; - } - } catch (IOException e1) { - throw new PDFIOException("error.pdf.io.04", e1); - } catch(Throwable e) { - throw new PDFIOException("error.pdf.io.04", e); - } - } - if (placeholders.size() > 0) { - SignaturePlaceholderData ret = matchPlaceholderDocument( - placeholders, placeholderId, matchMode); - SignaturePlaceholderContext.setSignaturePlaceholderData(ret); - return ret; - } - // no placeholders found, apply strict mode if set - if (matchMode == PLACEHOLDER_MATCH_MODE_STRICT) { - throw new PlaceholderExtractionException("error.pdf.stamp.09"); - } - - return null; - } - - public List<SignaturePlaceholderData> extractList(PDDocument doc, - String placeholderId, int matchMode) throws PdfAsException { - SignaturePlaceholderContext.setSignaturePlaceholderData(null); - - int pageNr = 0; - for(PDPage page : doc.getPages()){ - pageNr++; - - try { - setCurrentPage(pageNr); - if(page.getContents() != null && page.getResources() != null && page.getContentStreams() != null) { - processPage(page); //TODO: pdfbox2 - right? - - } - SignaturePlaceholderData ret = matchPlaceholderPage( - placeholders, placeholderId, matchMode); - if (ret != null) { - SignaturePlaceholderContext - .setSignaturePlaceholderData(ret); - return placeholders; - } - } catch (IOException e1) { - throw new PDFIOException("error.pdf.io.04", e1); - } catch(Throwable e) { - throw new PDFIOException("error.pdf.io.04", e); - } - } - if (placeholders.size() > 0) { - SignaturePlaceholderData ret = matchPlaceholderDocument( - placeholders, placeholderId, matchMode); - SignaturePlaceholderContext.setSignaturePlaceholderData(ret); - return placeholders; - } - // no placeholders found, apply strict mode if set - if (matchMode == PLACEHOLDER_MATCH_MODE_STRICT) { - throw new PlaceholderExtractionException("error.pdf.stamp.09"); - } - return null; - } - - private SignaturePlaceholderData matchPlaceholderDocument( - List<SignaturePlaceholderData> placeholders, String placeholderId, - int matchMode) throws PlaceholderExtractionException { - - if (matchMode == PLACEHOLDER_MATCH_MODE_STRICT) - throw new PlaceholderExtractionException("error.pdf.stamp.09"); - - if (placeholders.size() == 0) - return null; - - if (matchMode == PLACEHOLDER_MATCH_MODE_SORTED) { - // sort all placeholders by the id string if all ids are null do nothing - SignaturePlaceholderData currentFirstSpd = null; - for (int i = 0; i < placeholders.size(); i++) { - SignaturePlaceholderData spd = placeholders.get(i); - if (spd.getId() != null) { - if(currentFirstSpd == null) { - currentFirstSpd = spd; - logger.debug("Setting new current ID: {}", - currentFirstSpd.getId()); - } else { - String currentID = currentFirstSpd.getId(); - String testID = spd.getId(); - logger.debug("Testing placeholder current: {} compare to {}", - currentID, testID); - if(testID.compareToIgnoreCase(currentID) < 0) { - currentFirstSpd = spd; - logger.debug("Setting new current ID: {}", - testID); - } - } - } - } - - if(currentFirstSpd != null) { - logger.info("Running Placeholder sorted mode: using id: {}", currentFirstSpd.getId()); - return currentFirstSpd; - } else { - logger.info("Running Placeholder sorted mode: no placeholder with id found, fallback to first placeholder"); - } - } - - for (int i = 0; i < placeholders.size(); i++) { - SignaturePlaceholderData spd = placeholders.get(i); - if (spd.getId() == null) - return spd; - } - - if (matchMode == PLACEHOLDER_MATCH_MODE_LENIENT) - return placeholders.get(0); - - return null; - } - - private SignaturePlaceholderData matchPlaceholderPage( - List<SignaturePlaceholderData> placeholders, String placeholderId, - int matchMode) { - - if(matchMode == PLACEHOLDER_MATCH_MODE_SORTED) - return null; - - if (placeholders.size() == 0) - return null; - for (int i = 0; i < placeholders.size(); i++) { - SignaturePlaceholderData data = placeholders.get(i); - if (placeholderId != null && placeholderId.equals(data.getId())) - return data; - if (placeholderId == null && data.getId() == null) - return data; - } - return null; - } - - - - private void setCurrentPage(int pageNr) { - this.currentPage = pageNr; - } - - @Override - protected void processOperator(Operator operator, List<COSBase> arguments) - throws IOException { - String operation = operator.getName(); - if (operation.equals("Do")) { - COSName objectName = (COSName) arguments.get(0); - PDXObject xobject = (PDXObject) getResources().getXObject(objectName); - if (xobject instanceof PDImageXObject) { - try { - PDImageXObject image = (PDImageXObject) xobject; - SignaturePlaceholderData data = checkImage(image); - if (data != null) { - PDPage page = getCurrentPage(); - Matrix ctm = getGraphicsState() - .getCurrentTransformationMatrix(); - int pageRotation = page.getRotation(); - pageRotation = pageRotation % 360; - double rotationInRadians = Math.toRadians(pageRotation);//(page.findRotation() * Math.PI) / 180; - - AffineTransform rotation = new AffineTransform(); - rotation.setToRotation(rotationInRadians); - AffineTransform rotationInverse = rotation - .createInverse(); - Matrix rotationInverseMatrix = new Matrix(); - rotationInverseMatrix - .setFromAffineTransform(rotationInverse); - Matrix rotationMatrix = new Matrix(); - rotationMatrix.setFromAffineTransform(rotation); - - Matrix unrotatedCTM = ctm - .multiply(rotationInverseMatrix); - - float x = unrotatedCTM.getXPosition(); - float yPos = unrotatedCTM.getYPosition(); - float yScale = unrotatedCTM.getScaleY(); - float y = yPos + yScale; - float w = unrotatedCTM.getScaleX(); - - logger.debug("Page height: {}", page.getCropBox().getHeight()); - logger.debug("Page width: {}", page.getCropBox().getWidth()); - - if(pageRotation == 90) { - y = page.getCropBox().getWidth() - (y * (-1)); - } else if(pageRotation == 180) { - x = page.getCropBox().getWidth() + x; - y = page.getCropBox().getHeight() - (y * (-1)); - } else if(pageRotation == 270) { - x = page.getCropBox().getHeight() + x; - } - - String posString = "p:" + currentPage + ";x:" + x - + ";y:" + y + ";w:" + w; - - logger.debug("Found Placeholder at: {}", posString); - try { - data.setTablePos(new TablePos(posString)); - data.setPlaceholderName(objectName.getName()); - placeholders.add(data); - } catch (PdfAsException e) { - throw new IOException(); - } - } - } catch (NoninvertibleTransformException e) { - throw new IOException(e); - } - } - } else { - super.processOperator(operator, arguments); - } - } - - private Map<String, PDFont> fonts; - - //TODO: pdfbox2 - was override - public Map<String, PDFont> getFonts() { - if (fonts == null) { - // at least an empty map will be returned - // TODO we should return null instead of an empty map - fonts = new HashMap<String, PDFont>(); - if(this.getResources() != null && this.getResources().getCOSObject() != null) { - COSDictionary fontsDictionary = (COSDictionary) this.getResources().getCOSObject().getDictionaryObject(COSName.FONT); - if (fontsDictionary == null) { - // ignore we do not want to set anything, never when creating a signature!!!!! - //fontsDictionary = new COSDictionary(); - //this.getResources().getCOSDictionary().setItem(COSName.FONT, fontsDictionary); +@Slf4j +public class SignaturePlaceholderExtractor extends PDFStreamEngine implements PlaceholderExtractorConstants { + + private final List<SignaturePlaceholderData> placeholders = new ArrayList<>(); + private int currentPage = 0; + + protected SignaturePlaceholderExtractor() throws IOException, ClassNotFoundException, + InstantiationException, IllegalAccessException { + super(); + + final Properties properties = new Properties(); + properties.load(ClassName.class.getClassLoader().getResourceAsStream( + "placeholder/pdfbox-reader-2.properties")); + + final Set<Entry<Object, Object>> entries = properties.entrySet(); + for (final Entry<Object, Object> entry : entries) { + final String processorClassName = (String) entry.getValue(); + final Class<?> klass = Class.forName(processorClassName); + final org.apache.pdfbox.contentstream.operator.OperatorProcessor processor = + (OperatorProcessor) klass.newInstance(); + + addOperator(processor); + + } + } + + /** + * Search the document for placeholder images and possibly included additional + * info.<br/> + * Searches only for the first placeholder page after page from top. + * + * @return available info from the first found placeholder. + * @throws PdfAsException if the document could not be read. + * @throws PlaceholderExtractionException if STRICT matching mode was requested + * and no suitable placeholder could be + * found. + */ + public SignaturePlaceholderData extract(PDDocument doc, + String placeholderId, int matchMode) throws PdfAsException { + + List<String> extistingSignatureNames = existingExistingSignatureNames(doc); + + + int pageNr = 0; + for (final PDPage page : doc.getPages()) { + pageNr++; + + try { + this.currentPage = pageNr; + if (page.getContents() != null && page.getResources() != null && page.getContentStreams() != null) { + processPage(page); // TODO: pdfbox2 - right? + + } + + final SignaturePlaceholderData ret = matchPlaceholderPage( + removeAlreadyUsePlaceholders(placeholders, extistingSignatureNames), placeholderId, matchMode); + if (ret != null) { + return ret; + + } + + } catch (final IOException e1) { + throw new PDFIOException("error.pdf.io.04", e1); + + } catch (final Throwable e) { + throw new PDFIOException("error.pdf.io.04", e); + + } + } + + if (placeholders.size() > 0) { + final SignaturePlaceholderData ret = matchPlaceholderDocument( + removeAlreadyUsePlaceholders(placeholders, extistingSignatureNames), placeholderId, matchMode); + return ret; + + } + // no placeholders found, apply strict mode if set + if (matchMode == PLACEHOLDER_MATCH_MODE_STRICT) { + throw new PlaceholderExtractionException("error.pdf.stamp.09"); + + } + return null; + } + + @Override + protected void processOperator(Operator operator, List<COSBase> arguments) + throws IOException { + final String operation = operator.getName(); + if (operation.equals("Do")) { + final COSName objectName = (COSName) arguments.get(0); + final PDXObject xobject = getResources().getXObject(objectName); + if (xobject instanceof PDImageXObject) { + try { + final PDImageXObject image = (PDImageXObject) xobject; + final SignaturePlaceholderData data = checkImage(image); + if (data != null) { + final PDPage page = getCurrentPage(); + final Matrix ctm = getGraphicsState() + .getCurrentTransformationMatrix(); + int pageRotation = page.getRotation(); + pageRotation = pageRotation % 360; + final double rotationInRadians = Math.toRadians(pageRotation);// (page.findRotation() * Math.PI) / + // 180; + + final AffineTransform rotation = new AffineTransform(); + rotation.setToRotation(rotationInRadians); + final AffineTransform rotationInverse = rotation + .createInverse(); + final Matrix rotationInverseMatrix = new Matrix(); + rotationInverseMatrix + .setFromAffineTransform(rotationInverse); + final Matrix rotationMatrix = new Matrix(); + rotationMatrix.setFromAffineTransform(rotation); + + final Matrix unrotatedCTM = ctm + .multiply(rotationInverseMatrix); + + float x = unrotatedCTM.getXPosition(); + final float yPos = unrotatedCTM.getYPosition(); + final float yScale = unrotatedCTM.getScaleY(); + float y = yPos + yScale; + final float w = unrotatedCTM.getScaleX(); + + log.debug("Page height: {}", page.getCropBox().getHeight()); + log.debug("Page width: {}", page.getCropBox().getWidth()); + + if (pageRotation == 90) { + y = page.getCropBox().getWidth() - y * -1; + } else if (pageRotation == 180) { + x = page.getCropBox().getWidth() + x; + y = page.getCropBox().getHeight() - y * -1; + } else if (pageRotation == 270) { + x = page.getCropBox().getHeight() + x; } - else { - for (COSName fontName : fontsDictionary.keySet()) { - COSBase font = fontsDictionary.getDictionaryObject(fontName); - // data-000174.pdf contains a font that is a COSArray, looks to be an error in the - // PDF, we will just ignore entries that are not dictionaries. - if (font instanceof COSDictionary) { - PDFont newFont = null; - try { - newFont = PDFontFactory.createFont((COSDictionary) font); - } - catch (IOException exception) { - logger.error("error while creating a font", exception); - } - if (newFont != null) { - fonts.put(fontName.getName(), newFont); - } - } - } + + final String posString = "p:" + currentPage + ";x:" + Math.floor(x) + + ";y:" + Math.ceil(y) + ";w:" + Math.ceil(w); + + log.debug("Found Placeholder at: {}", posString); + try { + data.setTablePos(new TablePos(posString)); + data.setPlaceholderName(objectName.getName()); + placeholders.add(data); + + } catch (final PdfAsException e) { + throw new IOException(); + } + } + } catch (final NoninvertibleTransformException e) { + throw new IOException(e); + } + } + } else { + super.processOperator(operator, arguments); + } + } + + private SignaturePlaceholderData matchPlaceholderPage( + List<SignaturePlaceholderData> placeholders, String placeholderId, int matchMode) { + log.debug("Searching requested placeholder:{} with matchMode:{} in single page ... ", placeholderId, matchMode); + + if (placeholders.size() == 0) { + return null; + + } + + // check if find a placeholder with that ID + for (final SignaturePlaceholderData data : placeholders) { + if (placeholderId != null && data.getId() != null + && matchPlaceHolderId(placeholderId, data.getId())) { + return data; + + } + + if (matchMode != PLACEHOLDER_MATCH_MODE_SORTED + && placeholderId == null && data.getId() == null) { + return data; + + } + } + + return null; + } + + private SignaturePlaceholderData matchPlaceholderDocument( + List<SignaturePlaceholderData> placeholders, String placeholderId, + int matchMode) throws PlaceholderExtractionException { + + log.debug("Searching requested placeholder:{} with matchMode:{} on any page ... ", placeholderId, matchMode); + + if (matchMode == PLACEHOLDER_MATCH_MODE_STRICT) { + throw new PlaceholderExtractionException("error.pdf.stamp.09"); + } + + if (placeholders.size() == 0) { + return null; + } + + if (matchMode == PLACEHOLDER_MATCH_MODE_SORTED) { + // sort all placeholders by the id string if all ids are null do nothing + SignaturePlaceholderData currentFirstSpd = null; + for (final SignaturePlaceholderData spd : placeholders) { + if (spd.getId() != null) { + if (currentFirstSpd == null) { + currentFirstSpd = spd; + log.debug("Setting new current ID: {}", + currentFirstSpd.getId()); + } else { + currentFirstSpd = placeHolderIdMatcher(currentFirstSpd, spd); + + } + } + } + + if (currentFirstSpd != null) { + log.info("Running Placeholder sorted mode: using id: {}", currentFirstSpd.getId()); + return currentFirstSpd; + + } else { + log.info( + "Running Placeholder sorted mode: no placeholder with id found, fallback to first placeholder"); + } + } + + for (final SignaturePlaceholderData spd : placeholders) { + if (spd.getId() == null) { + return spd; + } + } + + if (matchMode == PLACEHOLDER_MATCH_MODE_LENIENT) { + return placeholders.get(0); + } + + return null; + } + + private boolean matchPlaceHolderId(String first, String second) { + try { + Integer firstIdInt = Integer.valueOf(first); + Integer secondIdInt = Integer.valueOf(second); + return firstIdInt == secondIdInt; + + } catch (NumberFormatException e) { + log.trace("Can not compare placeholderId's on integer level. Using String compare ... "); + return first.equalsIgnoreCase(second); + + } + + } + + private SignaturePlaceholderData placeHolderIdMatcher(SignaturePlaceholderData currentFirstSpd, + SignaturePlaceholderData spd) { + try { + Integer currentIDInt = Integer.valueOf(currentFirstSpd.getId()); + Integer testIDInt = Integer.valueOf(spd.getId()); + + if (testIDInt < currentIDInt) { + log.debug("Setting new current ID: {}", testIDInt); + return spd; + + } else { + return currentFirstSpd; + + } + } catch (NumberFormatException e) { + log.trace("Can not compare placeholderId's on integer level. Using String compare ... "); + final String currentID = currentFirstSpd.getId(); + final String testID = spd.getId(); + log.debug("Testing placeholder current: {} compare to {}", + currentID, testID); + if (testID.compareToIgnoreCase(currentID) < 0) { + log.debug("Setting new current ID: {}", + testID); + return spd; + + } else { + return currentFirstSpd; + + } + } + } + + private List<String> existingExistingSignatureNames(PDDocument doc) { + final List<String> existingLocations = new ArrayList<>(); + try { + final List<PDSignature> pdSignatureList = doc.getSignatureDictionaries(); + if (pdSignatureList.size() != 0) { + for (final PDSignature sig : pdSignatureList) { + existingLocations.add(sig.getLocation()); + } + } + } catch (final IOException e) { + e.printStackTrace(); + } + return existingLocations; + } + + private List<SignaturePlaceholderData> removeAlreadyUsePlaceholders( + List<SignaturePlaceholderData> placeholders, List<String> existingPlaceholders) { + if (placeholders != null) { + List<SignaturePlaceholderData> result = placeholders.stream() + .filter(el -> !existingPlaceholders.contains(el.getPlaceholderName())) + .collect(Collectors.toList()); + log.debug("Initial found #{} placeholders, but #{} removed because already used.", + placeholders.size(), placeholders.size() - result.size()); + return result; + + } else { + return Collections.emptyList(); + + } + } + + /** + * Checks an image if it is a placeholder for a signature. + * + * @param image + * @return + * @throws IOException + */ + private SignaturePlaceholderData checkImage(PDImageXObject image) + throws IOException { + final BufferedImage bimg = image.getImage(); + if (bimg == null) { + String type = image.getSuffix(); + if (type != null) { + type = type.toUpperCase() + " images"; + } else { + type = "Image type"; + } + log.info("Unable to extract image for QRCode analysis. " + + type + + " not supported. Add additional JAI Image filters to your classpath. Refer to https://jai.dev.java.net. Skipping image."); + return null; + + } + + if (bimg.getHeight() < 10 || bimg.getWidth() < 10) { + log.debug("Image too small for QRCode. Skipping image."); + return null; + } + + final LuminanceSource source = new BufferedImageLuminanceSource(bimg); + final BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); + Result result; + final long before = System.currentTimeMillis(); + try { + final Hashtable<DecodeHintType, Object> hints = new Hashtable<>(); + final Vector<BarcodeFormat> formats = new Vector<>(); + formats.add(BarcodeFormat.QR_CODE); + hints.put(DecodeHintType.POSSIBLE_FORMATS, formats); + result = new MultiFormatReader().decode(bitmap, hints); + + final String text = result.getText(); + String profile = null; + String type = null; + String sigKey = null; + String id = null; + if (text != null) { + if (text.startsWith(QR_PLACEHOLDER_IDENTIFIER)) { + + final String[] data = text.split(";"); + if (data.length > 1) { + for (int i = 1; i < data.length; i++) { + final String kvPair = data[i]; + final String[] kv = kvPair.split("="); + if (kv.length != 2) { + log.debug("Invalid parameter in placeholder data: " + + kvPair); + } else { + if (kv[0] + .equalsIgnoreCase(SignaturePlaceholderData.ID_KEY)) { + id = kv[1]; + } else if (kv[0] + .equalsIgnoreCase(SignaturePlaceholderData.PROFILE_KEY)) { + profile = kv[1]; + } else if (kv[0] + .equalsIgnoreCase(SignaturePlaceholderData.SIG_KEY_KEY)) { + sigKey = kv[1]; + } else if (kv[0] + .equalsIgnoreCase(SignaturePlaceholderData.TYPE_KEY)) { + type = kv[1]; + } + } } + } + return new SignaturePlaceholderData(profile, type, sigKey, + id); + } else { + log.warn("QR-Code found but does not start with \"" + + QR_PLACEHOLDER_IDENTIFIER + + "\". Ignoring QR placeholder."); + } + } + } catch (final ReaderException re) { + if (log.isDebugEnabled()) { + log.debug("Could not decode - not a placeholder. needed: " + + (System.currentTimeMillis() - before)); + } + if (!(re instanceof NotFoundException)) { + if (log.isInfoEnabled()) { + log.info("Failed to decode image", re); } - return fonts; - } - - /** - * Checks an image if it is a placeholder for a signature. - * - * @param image - * @return - * @throws IOException - */ - private SignaturePlaceholderData checkImage(PDImageXObject image) - throws IOException { - BufferedImage bimg = image.getImage(); - if (bimg == null) { - String type = image.getSuffix(); - if (type != null) { - type = type.toUpperCase() + " images"; - } else { - type = "Image type"; - } - logger.info("Unable to extract image for QRCode analysis. " - + type - + " not supported. Add additional JAI Image filters to your classpath. Refer to https://jai.dev.java.net. Skipping image."); - return null; - } - if (bimg.getHeight() < 10 || bimg.getWidth() < 10) { - logger.debug("Image too small for QRCode. Skipping image."); - return null; - } - - LuminanceSource source = new BufferedImageLuminanceSource(bimg); - BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); - Result result; - long before = System.currentTimeMillis(); - try { - Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(); - Vector<BarcodeFormat> formats = new Vector<BarcodeFormat>(); - formats.add(BarcodeFormat.QR_CODE); - hints.put(DecodeHintType.POSSIBLE_FORMATS, formats); - result = new MultiFormatReader().decode(bitmap, hints); - - String text = result.getText(); - String profile = null; - String type = null; - String sigKey = null; - String id = null; - if (text != null) { - if (text.startsWith(QR_PLACEHOLDER_IDENTIFIER)) { - - String[] data = text.split(";"); - if (data.length > 1) { - for (int i = 1; i < data.length; i++) { - String kvPair = data[i]; - String[] kv = kvPair.split("="); - if (kv.length != 2) { - logger.debug("Invalid parameter in placeholder data: " - + kvPair); - } else { - if (kv[0] - .equalsIgnoreCase(SignaturePlaceholderData.ID_KEY)) { - id = kv[1]; - } else if (kv[0] - .equalsIgnoreCase(SignaturePlaceholderData.PROFILE_KEY)) { - profile = kv[1]; - } else if (kv[0] - .equalsIgnoreCase(SignaturePlaceholderData.SIG_KEY_KEY)) { - sigKey = kv[1]; - } else if (kv[0] - .equalsIgnoreCase(SignaturePlaceholderData.TYPE_KEY)) { - type = kv[1]; - } - } - } - } - return new SignaturePlaceholderData(profile, type, sigKey, - id); - } else { - logger.warn("QR-Code found but does not start with \"" - + QR_PLACEHOLDER_IDENTIFIER - + "\". Ignoring QR placeholder."); - } - } - } catch (ReaderException re) { - if (logger.isDebugEnabled()) { - logger.debug("Could not decode - not a placeholder. needed: " - + (System.currentTimeMillis() - before)); - } - if (!(re instanceof NotFoundException)) { - if (logger.isInfoEnabled()) { - logger.info("Failed to decode image", re); - } - } - } catch (ArrayIndexOutOfBoundsException e) { - if (logger.isInfoEnabled()) { - logger.info("Failed to decode image. Probably a zxing bug", e); - } - } - return null; - } + } + } catch (final ArrayIndexOutOfBoundsException e) { + if (log.isInfoEnabled()) { + log.info("Failed to decode image. Probably a zxing bug", e); + } + } + return null; + } } diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.java index 13d1ebe6..d1d097aa 100644 --- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.java +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.java @@ -32,7 +32,9 @@ import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.common.PDRectangle; import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.settings.IProfileConstants; import at.gv.egiz.pdfas.common.settings.ISettings; +import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings; import at.gv.egiz.pdfas.lib.api.IConfigurationConstants; import at.gv.egiz.pdfas.lib.impl.pdfbox2.utils.PdfBoxUtils; import at.gv.egiz.pdfas.lib.impl.stamping.IPDFVisualObject; @@ -64,18 +66,18 @@ public class Positioning { * * @param pos The absolute positioning parameter. If null it is * sought in the profile definition. - * @param signature_type The profile definition of the table to be written. * @param pdfDataSource The pdf. * @param pdf_table The pdf table to be written. * @param settings + * @param signatureProfileSettings Signature-block settings * @return Returns the PositioningInformation. * @throws PdfAsException F.e. */ public static PositioningInstruction determineTablePositioning( - TablePos pos, String signature_type, PDDocument pdfDataSource, - IPDFVisualObject pdf_table, ISettings settings) throws PdfAsException { + TablePos pos, PDDocument pdfDataSource, + IPDFVisualObject pdf_table, ISettings settings, SignatureProfileSettings signatureProfileSettings) throws PdfAsException { return adjustSignatureTableandCalculatePosition(pdfDataSource, - pdf_table, pos, settings); + pdf_table, pos, settings, signatureProfileSettings); } /** @@ -85,12 +87,13 @@ public class Positioning { * @param pdfDataSource The PDF document. * @param pdf_table The PDFPTable to be placed. * @param settings + * @param profilConfig Signature-profile configuration * @return Returns the position where the PDFPTable should be placed. * @throws PdfAsException F.e. */ public static PositioningInstruction adjustSignatureTableandCalculatePosition( final PDDocument pdfDataSource, IPDFVisualObject pdf_table, - TablePos pos, ISettings settings) throws PdfAsException { + TablePos pos, ISettings settings, SignatureProfileSettings profilConfig) throws PdfAsException { PdfBoxUtils.checkPDFPermissions(pdfDataSource); final long numberOfExistingSignatures = getNumberOfExistingSignatures(pdfDataSource); @@ -111,15 +114,9 @@ public class Positioning { } } - make_new_page = checkIfNewPageIsAllowed(make_new_page, numberOfExistingSignatures, settings); - - - if(make_new_page && numberOfExistingSignatures!=0) { - make_new_page = false; - - } - + make_new_page = checkIfNewPageIsAllowed(make_new_page, numberOfExistingSignatures, settings, profilConfig); + final PDPage pdPage = pdfDataSource.getPage(page - 1); PDRectangle cropBox = pdPage.getCropBox(); @@ -216,13 +213,13 @@ public class Positioning { // we do have an empty page or nothing in area above footerline pos_y = page_height - SIGNATURE_MARGIN_VERTICAL; return buildPostitionInfoOnSubpage(pdfDataSource, make_new_page, page, pos_x, pos_y, pos.rotation, - pos.getFooterLine(), table_height, pos, page_height, numberOfExistingSignatures, settings); + pos.getFooterLine(), table_height, pos, page_height, numberOfExistingSignatures, settings, profilConfig); } else { // we do have text take SIGNATURE_MARGIN pos_y = page_height - pre_page_length - SIGNATURE_MARGIN_VERTICAL; return buildPostitionInfoOnSubpage(pdfDataSource, make_new_page, page, pos_x, pos_y, pos.rotation, - pos.getFooterLine(), table_height, pos, page_height, numberOfExistingSignatures, settings); + pos.getFooterLine(), table_height, pos, page_height, numberOfExistingSignatures, settings, profilConfig); } } @@ -245,12 +242,23 @@ public class Positioning { } - private static boolean checkIfNewPageIsAllowed(boolean make_new_page, long numberOfExistingSignatures, ISettings settings) throws PdfAsException { - if(make_new_page && numberOfExistingSignatures!=0) { - log.info("Signature-block would be need a new page, but new pages are not allowed on already signed documents."); - if (isFailOnLessSpaceEnabled(settings)) { - throw new PdfAsException("error.pdf.stamp.12"); + private static boolean isNewPageOnSignedDocumentsEnabled(SignatureProfileSettings profilConfig) { + String value = profilConfig.getValue(IProfileConstants.SIG_NEWPAGE_FORCE); + return Boolean.valueOf(value); + + } + + private static boolean checkIfNewPageIsAllowed(boolean make_new_page, long numberOfExistingSignatures, ISettings settings, + SignatureProfileSettings profilConfig) throws PdfAsException { + if(make_new_page && numberOfExistingSignatures !=0 ) { + log.debug("Signature-block would be need a new page, but new pages are not allowed on already signed documents."); + if (isNewPageOnSignedDocumentsEnabled(profilConfig)) { + log.info("New pages not allowed on already signed documents, but force new page by configuration"); + return make_new_page; + } else if (isFailOnLessSpaceEnabled(settings)) { + throw new PdfAsException("error.pdf.stamp.12"); + } else { log.info("Placing signature-block on last page without free-space checks ... "); return false; @@ -265,10 +273,10 @@ public class Positioning { private static PositioningInstruction buildPostitionInfoOnSubpage(PDDocument pdfDataSource, boolean make_new_page, int page, float pos_x, float pos_y, float rotation, float footer_line, float table_height, TablePos pos, float page_height, - long numberOfExistingSignatures, ISettings settings) throws PdfAsException { + long numberOfExistingSignatures, ISettings settings, SignatureProfileSettings profilConfig) throws PdfAsException { if (pos_y - footer_line <= table_height) { - make_new_page = checkIfNewPageIsAllowed(true, numberOfExistingSignatures, settings); + make_new_page = checkIfNewPageIsAllowed(pos.isPauto(), numberOfExistingSignatures, settings, profilConfig); if (make_new_page) { page++; diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java index 42f16598..36d7dade 100644 --- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java @@ -70,8 +70,6 @@ import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.xmpbox.XMPMetadata; import org.apache.xmpbox.schema.PDFAIdentificationSchema; import org.apache.xmpbox.xml.DomXmpParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import at.gv.egiz.pdfas.common.exceptions.PDFASError; import at.gv.egiz.pdfas.common.exceptions.PdfAsException; @@ -83,7 +81,6 @@ import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner; import at.gv.egiz.pdfas.lib.api.sign.SignParameter; import at.gv.egiz.pdfas.lib.impl.ErrorExtractor; import at.gv.egiz.pdfas.lib.impl.SignaturePositionImpl; -import at.gv.egiz.pdfas.lib.impl.configuration.PlaceholderWebConfiguration; import at.gv.egiz.pdfas.lib.impl.configuration.SignatureProfileConfiguration; import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject; import at.gv.egiz.pdfas.lib.impl.pdfbox2.positioning.Positioning; @@ -107,231 +104,123 @@ import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction; import at.knowcenter.wag.egov.egiz.pdf.TablePos; import at.knowcenter.wag.egov.egiz.table.Table; import iaik.x509.X509Certificate; +import lombok.extern.slf4j.Slf4j; +@Slf4j public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { - private static final Logger logger = LoggerFactory.getLogger(PADESPDFBOXSigner.class); - private boolean isAdobeSigForm = false; + @Override public void signPDF(PDFObject genericPdfObject, RequestedSignature requestedSignature, PDFASSignatureInterface genericSigner) throws PdfAsException { - PDFAsVisualSignatureProperties properties = null; - List<SignaturePlaceholderData> placeholders; - List<SignaturePlaceholderData> availablePlaceholders; - SignaturePlaceholderData signaturePlaceholderData = null; - - String placeholder_id = ""; - - if (PlaceholderWebConfiguration.getValue(PLACEHOLDER_WEB_ID) != null && !PlaceholderWebConfiguration - .getValue(PLACEHOLDER_WEB_ID).equalsIgnoreCase("")) { - placeholder_id = PlaceholderWebConfiguration.getValue(PLACEHOLDER_WEB_ID); - } - + + boolean isAdobeSigForm = false; + if (!(genericPdfObject instanceof PDFBOXObject)) { - // tODO: - throw new PdfAsException(); + throw new PdfAsException("PDF to signObject is of wrong type: " + genericPdfObject.getClass().getName()); + } - final PDFBOXObject pdfObject = (PDFBOXObject) genericPdfObject; - if (!(genericSigner instanceof PDFASPDFBOXSignatureInterface)) { - // tODO: - throw new PdfAsException(); + throw new PdfAsException("PDF signerObject is of wrong type:" + genericSigner.getClass().getName()); + } - + + final PDFBOXObject pdfObject = (PDFBOXObject) genericPdfObject; final PDFASPDFBOXSignatureInterface signer = (PDFASPDFBOXSignatureInterface) genericSigner; - String pdfaVersion = null; - PDDocument doc = null; SignatureOptions options = new SignatureOptions(); try { - doc = pdfObject.getDocument(); - // if signature already exists dont create new page - final List<PDSignatureField> pdSignatureFieldList = doc.getSignatureFields(); - PDSignature signature; + // sign a PDF with an existing empty signature, as created by the // CreateEmptySignatureForm example. - String sigFieldName = pdfObject.getStatus().getSettings().getValue(SIGNATURE_FIELD_NAME); - signature = findExistingSignature(doc, sigFieldName); + PDSignature signature = findExistingSignature(doc, getSignatureFieldNameConfig(pdfObject)); if (signature == null) { // create signature dictionary signature = new PDSignature(); + } else { isAdobeSigForm = true; + } + // set basic signature parameters signature.setFilter(COSName.getPDFName(signer.getPDFFilter())); signature.setSubFilter(COSName.getPDFName(signer.getPDFSubFilter())); -// SignaturePlaceholderData signaturePlaceholderDataInit = - placeholders = PlaceholderFilter.checkPlaceholderSignatureLocationList(pdfObject.getStatus(), - pdfObject.getStatus().getSettings(), placeholder_id); - -// placeholders = SignaturePlaceholderExtractor.getPlaceholders(); - availablePlaceholders = listAvailablePlaceholders(placeholders, existingSignatureLocations(doc)); - - if (placeholder_id.equalsIgnoreCase("")) { - if (checkAvailablePlaceholders(placeholders, existingSignatureLocations(doc)) != null) { - placeholder_id = checkAvailablePlaceholders(placeholders, existingSignatureLocations(doc)).getId(); - } - } - - if (availablePlaceholders != null) { - signaturePlaceholderData = PlaceholderFilter - .checkPlaceholderSignatureLocation(pdfObject.getStatus(), pdfObject.getStatus().getSettings(), - placeholder_id); - } - - TablePos tablePos = null; - - if (signaturePlaceholderData != null) { - signature.setLocation(signaturePlaceholderData.getPlaceholderName()); - } - - if (signaturePlaceholderData != null) { - // Placeholder found! - placeholders.clear(); - logger.info("Placeholder data found."); - if (signaturePlaceholderData.getProfile() != null) { - logger.debug("Placeholder Profile set to: " + signaturePlaceholderData.getProfile()); - requestedSignature.setSignatureProfileID(signaturePlaceholderData.getProfile()); - } - - tablePos = signaturePlaceholderData.getTablePos(); - if (tablePos != null) { - - final SignatureProfileConfiguration signatureProfileConfiguration = pdfObject.getStatus() - .getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID()); - - final float minWidth = signatureProfileConfiguration.getMinWidth(); - - if (minWidth > 0) { - if (tablePos.getWidth() < minWidth) { - tablePos.width = minWidth; - logger.debug("Correcting placeholder with to minimum width {}", minWidth); - } + signature.setSignDate(Calendar.getInstance()); + log.debug("Signing @ " + signature.getSignDate().getTime().toString()); + + // extract next QR-code placeholder, if exists + SignaturePlaceholderData nextPlaceholderData = PlaceholderFilter.checkPlaceholderSignatureLocation( + pdfObject.getStatus(), pdfObject.getStatus().getSettings(), + pdfObject.getStatus().getSignParamter().getPlaceHolderId()); + + if (nextPlaceholderData != null) { + log.info("Placeholder data found."); + signature.setLocation(nextPlaceholderData.getPlaceholderName()); + + if (nextPlaceholderData.getProfile() != null) { + if (pdfObject.getStatus().getSettings().isValue(IConfigurationConstants.PLACEHOLDER_PROFILE_OVERWRITE, true)) { + log.debug("Placeholder Profile set to: {}", nextPlaceholderData.getProfile()); + requestedSignature.setSignatureProfileID(nextPlaceholderData.getProfile()); + + } else { + log.debug("Placeholder profile over-write is disabled. Using profile from request ... "); + } - logger.debug("Placeholder Position set to: " + tablePos.toString()); - } - } - final SignatureProfileSettings signatureProfileSettings = TableFactory.createProfile( - requestedSignature.getSignatureProfileID(), pdfObject.getStatus().getSettings()); - - // Check if input document is PDF-A conform - if (signatureProfileSettings.isPDFA()) { - final DataSource origDoc = pdfObject.getOriginalDocument(); - final InputStream stream = origDoc.getInputStream(); - // Run PreflightParser for checking conformity// - // runPDFAPreflight(origDoc); + } } + + + final SignatureProfileSettings signatureProfileSettings = + TableFactory.createProfile(requestedSignature.getSignatureProfileID(), pdfObject.getStatus().getSettings()); + + // set signature name final ValueResolver resolver = new ValueResolver(requestedSignature, pdfObject.getStatus()); - - final String signerName = resolver.resolve("SIG_SUBJECT", signatureProfileSettings.getValue( - "SIG_SUBJECT"), - signatureProfileSettings); - + final String signerName = resolver.resolve("SIG_SUBJECT", + signatureProfileSettings.getValue("SIG_SUBJECT"), signatureProfileSettings); signature.setName(signerName); - signature.setSignDate(Calendar.getInstance()); - String signerReason = signatureProfileSettings.getSigningReason(); - - if (signerReason == null) { - signerReason = "PAdES Signature"; - } + // set signature reason + String signerReason = signatureProfileSettings.getSigningReason() != null + ? signatureProfileSettings.getSigningReason() : "PAdES Signature"; signature.setReason(signerReason); - logger.debug("Signing reason: " + signerReason); - - logger.debug("Signing @ " + signer.getSigningDate().getTime().toString()); - // the signing date, needed for valid signature - // signature.setSignDate(signer.getSigningDate()); + log.debug("Signing reason: " + signerReason); signer.setPDSignature(signature); - int signatureSize = 0x1000; - try { - final String reservedSignatureSizeString = signatureProfileSettings.getValue(SIG_RESERVED_SIZE); - if (reservedSignatureSizeString != null) { - signatureSize = Integer.parseInt(reservedSignatureSizeString); - } - logger.debug("Reserving {} bytes for signature", signatureSize); - } catch (final NumberFormatException e) { - logger.warn("Invalid configuration value: {} should be a number using 0x1000", SIG_RESERVED_SIZE); - } - options.setPreferredSignatureSize(signatureSize); - + if (signatureProfileSettings.isPDFA() || signatureProfileSettings.isPDFA3()) { - pdfaVersion = getPDFAVersion(doc); - signatureProfileSettings.setPDFAVersion(pdfaVersion); + signatureProfileSettings.setPDFAVersion(getPDFAVersion(doc)); + } - // Is visible Signature - if (requestedSignature.isVisual()) { - logger.info("Creating visual siganture block"); - - final SignatureProfileConfiguration signatureProfileConfiguration = pdfObject.getStatus() - .getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID()); - - if (tablePos == null) { - // ================================================================ - // PositioningStage (visual) -> find position or use - // fixed - // position - - final String posString = pdfObject.getStatus().getSignParamter().getSignaturePosition(); - - TablePos signaturePos = null; - - final String signaturePosString = signatureProfileConfiguration.getDefaultPositioning(); - - if (signaturePosString != null) { - logger.debug("using signature Positioning: " + signaturePos); - signaturePos = new TablePos(signaturePosString); - } - - logger.debug("using Positioning: " + posString); - - if (posString != null) { - // Merge Signature Position - tablePos = new TablePos(posString, signaturePos); - } else { - // Fallback to signature Position! - tablePos = signaturePos; - } - - if (tablePos == null) { - // Last Fallback default position - tablePos = new TablePos(); - } - } - - // Legacy Modes not supported with pdfbox2 anymore -// boolean legacy32Position = signatureProfileConfiguration.getLegacy32Positioning(); -// boolean legacy40Position = signatureProfileConfiguration.getLegacy40Positioning(); - - // create Table describtion + // prepare basic signature options + options.setPreferredSignatureSize(calculateBlankAreaForSignature(signatureProfileSettings)); + + PDFAsVisualSignatureProperties properties = null; + if (requestedSignature.isVisual()) { + log.info("Creating visual siganture block"); + + final SignatureProfileConfiguration signatureProfileConfiguration = + pdfObject.getStatus().getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID()); + final TablePos tablePos = prepareTablePosition(nextPlaceholderData, signatureProfileConfiguration, + pdfObject.getStatus().getSignParamter().getSignaturePosition()); final Table main = TableFactory.createSigTable(signatureProfileSettings, MAIN, pdfObject.getStatus(), requestedSignature); final IPDFStamper stamper = StamperFactory.createDefaultStamper(pdfObject.getStatus().getSettings()); - final IPDFVisualObject visualObject = stamper.createVisualPDFObject(pdfObject, main); - /* - * PDDocument originalDocument = PDDocument .load(new - * ByteArrayInputStream(pdfObject.getStatus() - * .getPdfObject().getOriginalDocument())); - */ - final PositioningInstruction positioningInstruction = Positioning.determineTablePositioning(tablePos, - "", - doc, visualObject, pdfObject.getStatus().getSettings()); - - logger.debug("Positioning: {}", positioningInstruction.toString()); + doc, visualObject, pdfObject.getStatus().getSettings(), signatureProfileSettings); + log.debug("Positioning: {}", positioningInstruction.toString()); if (!isAdobeSigForm) { if (positioningInstruction.isMakeNewPage()) { @@ -347,11 +236,11 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { // handle rotated page final int targetPageNumber = positioningInstruction.getPage(); - logger.debug("Target Page: " + targetPageNumber); + log.debug("Target Page: " + targetPageNumber); final PDPage targetPage = doc.getPages().get(targetPageNumber - 1); final int rot = targetPage.getRotation(); - logger.debug("Page rotation: " + rot); - logger.debug("resulting Sign rotation: " + positioningInstruction.getRotation()); + log.debug("Page rotation: " + rot); + log.debug("resulting Sign rotation: " + positioningInstruction.getRotation()); final SignaturePositionImpl position = new SignaturePositionImpl(); position.setX(positioningInstruction.getX()); @@ -361,44 +250,11 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { position.setWidth(visualObject.getWidth()); requestedSignature.setSignaturePosition(position); } - + properties = new PDFAsVisualSignatureProperties(pdfObject.getStatus().getSettings(), pdfObject, (PdfBoxVisualObject) visualObject, positioningInstruction, signatureProfileSettings, false); - properties.buildSignature(); - /* - * ByteArrayOutputStream sigbos = new ByteArrayOutputStream(); - * sigbos.write(StreamUtils.inputStreamToByteArray (properties - * .getVisibleSignature())); sigbos.close(); - */ - - /* - * if (signaturePlaceholderData != null) { - * - * InputStream fis = - * PADESPDFBOXSigner.class.getResourceAsStream("/placeholder/empty.jpg"); - * PDImageXObject img = JPEGFactory.createFromStream(doc, fis); - * - * img.getCOSObject().setNeedToBeUpdated(true); // PDDocumentCatalog root = - * doc.getDocumentCatalog(); // PDPageNode rootPages = root.getPages(); // - * List<PDPage> kids = new ArrayList<PDPage>(); // rootPages.getAllKids(kids); - * int pageNumber = positioningInstruction.getPage(); PDPage page = - * doc.getPages().get(pageNumber - 1); - * - * logger.info("Placeholder name: " + - * signaturePlaceholderData.getPlaceholderName()); COSDictionary - * xobjectsDictionary = (COSDictionary) page.getResources().getCOSObject() - * .getDictionaryObject(COSName.XOBJECT); - * - * - * xobjectsDictionary.setItem(signaturePlaceholderData.getPlaceholderName(), - * img); xobjectsDictionary.setNeedToBeUpdated(true); - * page.getResources().getCOSObject().setNeedToBeUpdated(true); - * logger.info("Placeholder name: " + - * signaturePlaceholderData.getPlaceholderName()); } - */ - if (signatureProfileSettings.isPDFA() || signatureProfileSettings.isPDFA3()) { final PDDocumentCatalog root = doc.getDocumentCatalog(); @@ -418,39 +274,30 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { root.setOutputIntents(oi); root.getCOSObject().setNeedToBeUpdated(true); - logger.info("added Output Intent"); + log.info("added Output Intent"); } catch (final Throwable e) { e.printStackTrace(); throw new PdfAsException("Failed to add Output Intent", e); + } finally { IOUtils.closeQuietly(colorProfile); + } } + options.setPage(positioningInstruction.getPage() - 1); options.setVisualSignature(properties.getVisibleSignature()); + } doc.addSignature(signature, signer, options); - - if (sigFieldName == null) { - sigFieldName = "PDF-AS Signatur"; - } - final int count = PdfBoxUtils.countSignatures(doc, sigFieldName); - - sigFieldName = sigFieldName + count; - - final PDAcroForm acroFormm = doc.getDocumentCatalog().getAcroForm(); - - // PDStructureTreeRoot pdstRoot = - // doc.getDocumentCatalog().getStructureTreeRoot(); - // COSDictionary dic = - // doc.getDocumentCatalog().getCOSDictionary(); - // PDStructureElement el = new PDStructureElement("Widget", - // pdstRoot); - + + String sigFieldName = buildNextSignatureFieldName(doc, pdfObject); + // this is not used for Adobe signature fields if (!isAdobeSigForm) { PDSignatureField signatureField = null; + final PDAcroForm acroFormm = doc.getDocumentCatalog().getAcroForm(); if (acroFormm != null) { @SuppressWarnings("unchecked") final List<PDField> fields = acroFormm.getFields(); @@ -473,7 +320,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { } } } else { - logger.warn("Failed to name Signature Field! [Cannot find Field list in acroForm!]"); + log.warn("Failed to name Signature Field! [Cannot find Field list in acroForm!]"); } if (signatureField != null) { @@ -485,7 +332,8 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { signatureField.setAlternateFieldName(sigFieldName); } } else { - logger.warn("Failed to name Signature Field! [Cannot find acroForm!]"); + log.warn("Failed to name Signature Field! [Cannot find acroForm!]"); + } } @@ -493,194 +341,284 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { final PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm(); if (acroForm != null) { signatureField = (PDSignatureField) acroForm.getField(sigFieldName); + } - // PDF-UA - logger.info("Adding pdf/ua content."); - try { - final PDDocumentCatalog root = doc.getDocumentCatalog(); - final PDStructureTreeRoot structureTreeRoot = root.getStructureTreeRoot(); - if (structureTreeRoot != null) { - logger.info("Tree Root: {}", structureTreeRoot.toString()); - final List<Object> kids = structureTreeRoot.getKids(); - - if (kids == null) { - logger.info("No kid-elements in structure tree Root, maybe not PDF/UA document"); - } - PDStructureElement docElement = null; - for (final Object k : kids) { - if (k instanceof PDStructureElement) { - docElement = (PDStructureElement) k; - break; + injectPdfUaContent(doc, signatureField, sigFieldName, signatureProfileSettings); + + performTechnicalSignature(doc, pdfObject, signatureProfileSettings); + - } + log.debug("Signature done!"); + + } catch (final IOException e) { + log.warn(MessageResolver.resolveMessage("error.pdf.sig.01"), e); + throw new PdfAsException("error.pdf.sig.01", e); + + } catch (PDFASError e2) { + log.warn(e2.getInfo()); + throw new PdfAsException("error.pdf.sig.01", e2); + + } finally { + if (options != null) { + if (options.getVisualSignature() != null) { + try { + options.getVisualSignature().close(); + options.close(); + } catch (IOException e) { + log.debug("Failed to close VisualSignature!", e); } + } + } + + if (doc != null) { + try { + doc.close(); + // SignaturePlaceholderExtractor.getPlaceholders().clear(); + } catch (final IOException e) { + log.debug("Failed to close COS Doc!", e); + // Ignore + } + } + } + } - final PDStructureElement sigBlock = new PDStructureElement("Form", docElement); + private void performTechnicalSignature(PDDocument doc, PDFBOXObject pdfObject, + SignatureProfileSettings signatureProfileSettings) throws PdfAsException { + try { + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + synchronized (doc) { + doc.saveIncremental(bos); + final byte[] outputDocument = bos.toByteArray(); + pdfObject.setSignedDocument(outputDocument); + } + /* Check if resulting pdf is PDF-A conform */ + if (signatureProfileSettings.isPDFA()) { + runPDFAPreflight(new ByteArrayDataSource(pdfObject.getSignedDocument())); + } - // create object dictionary and add as child element - final COSDictionary objectDic = new COSDictionary(); - objectDic.setName("Type", "OBJR"); + } catch (final IOException e1) { + log.error("Can not save incremental update", e1); - objectDic.setItem("Pg", signatureField.getWidget().getPage()); - objectDic.setItem("Obj", signatureField.getWidget()); + } + + System.gc(); + + } - final List<Object> l = new ArrayList<>(); - l.add(objectDic); - sigBlock.setKids(l); - sigBlock.setPage(signatureField.getWidget().getPage()); + private void injectPdfUaContent(PDDocument doc, PDSignatureField signatureField, String sigFieldName, + SignatureProfileSettings signatureProfileSettings) throws PdfAsException { + try { + log.info("Adding pdf/ua content .... "); + final PDDocumentCatalog root = doc.getDocumentCatalog(); + final PDStructureTreeRoot structureTreeRoot = root.getStructureTreeRoot(); + if (structureTreeRoot != null) { + log.info("Tree Root: {}", structureTreeRoot.toString()); + final List<Object> kids = structureTreeRoot.getKids(); + + if (kids == null) { + log.info("No kid-elements in structure tree Root, maybe not PDF/UA document"); + } - sigBlock.setTitle("Signature Table"); - sigBlock.setParent(docElement); - docElement.appendKid(sigBlock); + PDStructureElement docElement = null; + for (final Object k : kids) { + if (k instanceof PDStructureElement) { + docElement = (PDStructureElement) k; + break; - // Create and add Attribute dictionary to mitigate PAC - // warning - final COSDictionary sigBlockDic = sigBlock.getCOSObject(); - final COSDictionary sub = new COSDictionary(); + } + } - sub.setName("O", "Layout"); - sub.setName("Placement", "Block"); - sigBlockDic.setItem(COSName.A, sub); - sigBlockDic.setNeedToBeUpdated(true); + final PDStructureElement sigBlock = new PDStructureElement("Form", docElement); - // Modify number tree - PDNumberTreeNode ntn = structureTreeRoot.getParentTree(); - if (ntn == null) { - ntn = new PDNumberTreeNode(objectDic, null); - logger.info("No number-tree-node found!"); - } + // create object dictionary and add as child element + final COSDictionary objectDic = new COSDictionary(); + objectDic.setName("Type", "OBJR"); - final COSArray ntnKids = (COSArray) ntn.getCOSObject().getDictionaryObject(COSName.KIDS); - final COSArray ntnNumbers = (COSArray) ntn.getCOSObject().getDictionaryObject(COSName.NUMS); + objectDic.setItem("Pg", signatureField.getWidget().getPage()); + objectDic.setItem("Obj", signatureField.getWidget()); - final int parentTreeNextKey = getParentTreeNextKey(structureTreeRoot); + final List<Object> l = new ArrayList<>(); + l.add(objectDic); + sigBlock.setKids(l); + sigBlock.setPage(signatureField.getWidget().getPage()); - if (ntnNumbers == null && ntnKids != null) {// no number array, so continue with the kids array - // create dictionary with limits and nums array - final COSDictionary pTreeEntry = new COSDictionary(); - final COSArray limitsArray = new COSArray(); - // limits for exact one entry - limitsArray.add(COSInteger.get(parentTreeNextKey)); - limitsArray.add(COSInteger.get(parentTreeNextKey)); + sigBlock.setTitle("Signature Table"); + sigBlock.setParent(docElement); + docElement.appendKid(sigBlock); - final COSArray numsArray = new COSArray(); - numsArray.add(COSInteger.get(parentTreeNextKey)); - numsArray.add(sigBlock); + // Create and add Attribute dictionary to mitigate PAC + // warning + final COSDictionary sigBlockDic = sigBlock.getCOSObject(); + final COSDictionary sub = new COSDictionary(); - pTreeEntry.setItem(COSName.NUMS, numsArray); - pTreeEntry.setItem(COSName.LIMITS, limitsArray); + sub.setName("O", "Layout"); + sub.setName("Placement", "Block"); + sigBlockDic.setItem(COSName.A, sub); + sigBlockDic.setNeedToBeUpdated(true); - final PDNumberTreeNode newKidsElement = new PDNumberTreeNode(pTreeEntry, PDNumberTreeNode.class); + // Modify number tree + PDNumberTreeNode ntn = structureTreeRoot.getParentTree(); + if (ntn == null) { + ntn = new PDNumberTreeNode(objectDic, null); + log.info("No number-tree-node found!"); + } - ntnKids.add(newKidsElement); - ntnKids.setNeedToBeUpdated(true); + final COSArray ntnKids = (COSArray) ntn.getCOSObject().getDictionaryObject(COSName.KIDS); + final COSArray ntnNumbers = (COSArray) ntn.getCOSObject().getDictionaryObject(COSName.NUMS); - } else if (ntnNumbers != null && ntnKids == null) { + final int parentTreeNextKey = getParentTreeNextKey(structureTreeRoot); - final int arrindex = ntnNumbers.size(); + if (ntnNumbers == null && ntnKids != null) {// no number array, so continue with the kids array + // create dictionary with limits and nums array + final COSDictionary pTreeEntry = new COSDictionary(); + final COSArray limitsArray = new COSArray(); + // limits for exact one entry + limitsArray.add(COSInteger.get(parentTreeNextKey)); + limitsArray.add(COSInteger.get(parentTreeNextKey)); - ntnNumbers.add(arrindex, COSInteger.get(parentTreeNextKey)); - ntnNumbers.add(arrindex + 1, sigBlock.getCOSObject()); + final COSArray numsArray = new COSArray(); + numsArray.add(COSInteger.get(parentTreeNextKey)); + numsArray.add(sigBlock); - ntnNumbers.setNeedToBeUpdated(true); + pTreeEntry.setItem(COSName.NUMS, numsArray); + pTreeEntry.setItem(COSName.LIMITS, limitsArray); - structureTreeRoot.setParentTree(ntn); + final PDNumberTreeNode newKidsElement = new PDNumberTreeNode(pTreeEntry, PDNumberTreeNode.class); - } else if (ntnNumbers == null && ntnKids == null) { - // document is not pdfua conform before signature creation - throw new PdfAsException("error.pdf.sig.pdfua.1"); - } else { - // this is not allowed - throw new PdfAsException("error.pdf.sig.pdfua.1"); - } + ntnKids.add(newKidsElement); + ntnKids.setNeedToBeUpdated(true); - // set StructureParent for signature field annotation - signatureField.getWidget().setStructParent(parentTreeNextKey); + } else if (ntnNumbers != null && ntnKids == null) { - // Increase the next Key value in the structure tree root - structureTreeRoot.setParentTreeNextKey(parentTreeNextKey + 1); + final int arrindex = ntnNumbers.size(); - // add the Tabs /S Element for Tabbing through annots - final PDPage p = signatureField.getWidget().getPage(); - p.getCOSObject().setName("Tabs", "S"); - p.getCOSObject().setNeedToBeUpdated(true); + ntnNumbers.add(arrindex, COSInteger.get(parentTreeNextKey)); + ntnNumbers.add(arrindex + 1, sigBlock.getCOSObject()); - // check alternative signature field name - if (signatureField != null) { - if (signatureField.getAlternateFieldName().equals("")) { - signatureField.setAlternateFieldName(sigFieldName); - } - } + ntnNumbers.setNeedToBeUpdated(true); - ntn.getCOSObject().setNeedToBeUpdated(true); - sigBlock.getCOSObject().setNeedToBeUpdated(true); - structureTreeRoot.getCOSObject().setNeedToBeUpdated(true); - objectDic.setNeedToBeUpdated(true); - docElement.getCOSObject().setNeedToBeUpdated(true); - } - } catch (final Throwable e) { - if (signatureProfileSettings.isPDFUA() == true) { - logger.error("Could not create PDF-UA conform document!"); - throw new PdfAsException("error.pdf.sig.pdfua.1", e); + structureTreeRoot.setParentTree(ntn); + + } else if (ntnNumbers == null && ntnKids == null) { + // document is not pdfua conform before signature creation + throw new PdfAsException("error.pdf.sig.pdfua.1"); } else { - logger.info("Could not create PDF-UA conform signature"); + // this is not allowed + throw new PdfAsException("error.pdf.sig.pdfua.1"); } - } - try { - final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - synchronized (doc) { - doc.saveIncremental(bos); - final byte[] outputDocument = bos.toByteArray(); - pdfObject.setSignedDocument(outputDocument); - } - /* Check if resulting pdf is PDF-A conform */ - if (signatureProfileSettings.isPDFA()) { - runPDFAPreflight(new ByteArrayDataSource(pdfObject.getSignedDocument())); + // set StructureParent for signature field annotation + signatureField.getWidget().setStructParent(parentTreeNextKey); + + // Increase the next Key value in the structure tree root + structureTreeRoot.setParentTreeNextKey(parentTreeNextKey + 1); + + // add the Tabs /S Element for Tabbing through annots + final PDPage p = signatureField.getWidget().getPage(); + p.getCOSObject().setName("Tabs", "S"); + p.getCOSObject().setNeedToBeUpdated(true); + + // check alternative signature field name + if (signatureField != null) { + if (signatureField.getAlternateFieldName().equals("")) { + signatureField.setAlternateFieldName(sigFieldName); + } } - } catch (final IOException e1) { - logger.error("Can not save incremental update", e1); + ntn.getCOSObject().setNeedToBeUpdated(true); + sigBlock.getCOSObject().setNeedToBeUpdated(true); + structureTreeRoot.getCOSObject().setNeedToBeUpdated(true); + objectDic.setNeedToBeUpdated(true); + docElement.getCOSObject().setNeedToBeUpdated(true); + } + } catch (final Throwable e) { + if (signatureProfileSettings.isPDFUA()) { + log.error("Could not create PDF-UA conform document!"); + throw new PdfAsException("error.pdf.sig.pdfua.1", e); + + } else { + log.info("Could not create PDF-UA conform signature"); + } + } + + } + private String buildNextSignatureFieldName(PDDocument doc, PDFBOXObject pdfObject) { + String sigFieldName = getSignatureFieldNameConfig(pdfObject); + if (sigFieldName == null) { + sigFieldName = "PDF-AS Signatur"; + + } + return sigFieldName + PdfBoxUtils.countSignatures(doc, sigFieldName); + + } + + private int calculateBlankAreaForSignature(SignatureProfileSettings signatureProfileSettings) { + int signatureSize = 0x1000; + try { + final String reservedSignatureSizeString = signatureProfileSettings.getValue(SIG_RESERVED_SIZE); + if (reservedSignatureSizeString != null) { + signatureSize = Integer.parseInt(reservedSignatureSizeString); } + log.debug("Reserving {} bytes for signature", signatureSize); - System.gc(); - logger.debug("Signature done!"); + } catch (final NumberFormatException e) { + log.warn("Invalid configuration value: {} should be a number using 0x1000", SIG_RESERVED_SIZE); - } catch (final IOException e) { - logger.warn(MessageResolver.resolveMessage("error.pdf.sig.01"), e); - throw new PdfAsException("error.pdf.sig.01", e); + } - } catch (PDFASError e2) { - logger.warn(e2.getInfo()); - throw new PdfAsException("error.pdf.sig.01", e2); - - } finally { - if (options != null) { - if (options.getVisualSignature() != null) { - try { - options.getVisualSignature().close(); - options.close(); - } catch (IOException e) { - logger.debug("Failed to close VisualSignature!", e); - } + return signatureSize; + } + + private TablePos prepareTablePosition(SignaturePlaceholderData nextPlaceholderData, + SignatureProfileConfiguration signatureProfileConfiguration, String profilePosParam) throws PdfAsException { + + + if (nextPlaceholderData != null && nextPlaceholderData.getTablePos() != null) { + final float minWidth = signatureProfileConfiguration.getMinWidth(); + TablePos tablePos = nextPlaceholderData.getTablePos(); + if (minWidth > 0) { + if (tablePos.getWidth() < minWidth) { + tablePos.width = minWidth; + log.debug("Correcting placeholder with to minimum width {}", minWidth); } } + log.debug("Placeholder Position set to: " + tablePos.toString()); + return tablePos; - if (doc != null) { - try { - doc.close(); - // SignaturePlaceholderExtractor.getPlaceholders().clear(); - } catch (final IOException e) { - logger.debug("Failed to close COS Doc!", e); - // Ignore - } + } else { + TablePos signaturePos = null; + final String signaturePosString = signatureProfileConfiguration.getDefaultPositioning(); + + if (signaturePosString != null) { + log.debug("using signature Positioning: " + signaturePosString); + signaturePos = new TablePos(signaturePosString); + + } + + log.debug("using Positioning: " + profilePosParam); + + if (profilePosParam != null) { + // Merge Signature Position + return new TablePos(profilePosParam, signaturePos); + + } else if (signaturePos != null){ + // Fallback to signature Position! + return signaturePos; + + } else { + return new TablePos(); + } } } + private String getSignatureFieldNameConfig(PDFBOXObject pdfObject) { + return pdfObject.getStatus().getSettings().getValue(SIGNATURE_FIELD_NAME); + } + private int getParentTreeNextKey(PDStructureTreeRoot structureTreeRoot) throws IOException { int nextKey = structureTreeRoot.getParentTreeNextKey(); if (nextKey < 0) { @@ -717,31 +655,31 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { document.close(); result = document.getResult(); - logger.info("PDF-A Validation Result: " + result.isValid()); + log.info("PDF-A Validation Result: " + result.isValid()); if (result.getErrorsList().size() > 0) { - logger.error("The following validation errors occured for PDF-A validation"); + log.error("The following validation errors occured for PDF-A validation"); } for (final ValidationResult.ValidationError ve : result.getErrorsList()) { - logger.error("\t" + ve.getErrorCode() + ": " + ve.getDetails()); + log.error("\t" + ve.getErrorCode() + ": " + ve.getDetails()); } if (!result.isValid()) { - logger.info("The file is not a valid PDF-A document"); + log.info("The file is not a valid PDF-A document"); } } catch (final SyntaxValidationException e) { - logger.error("The file is syntactically invalid.", e); + log.error("The file is syntactically invalid.", e); throw new PdfAsException("Resulting PDF Document is syntactically invalid."); } catch (final ValidationException e) { - logger.error("The file is not a valid PDF-A document.", e); + log.error("The file is not a valid PDF-A document.", e); } catch (final IOException e) { - logger.error("An IOException (" + e.getMessage() + log.error("An IOException (" + e.getMessage() + ") occurred, while validating the PDF-A conformance", e); throw new PdfAsException("Failed validating PDF Document IOException."); } catch (final RuntimeException e) { - logger.debug("An RuntimeException (" + e.getMessage() + log.debug("An RuntimeException (" + e.getMessage() + ") occurred, while validating the PDF-A conformance", e); throw new PdfAsException("Failed validating PDF Document RuntimeException."); } finally { @@ -819,11 +757,11 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { final String signaturePosString = signatureProfileConfiguration.getDefaultPositioning(); PositioningInstruction positioningInstruction; if (signaturePosString != null) { - positioningInstruction = Positioning.determineTablePositioning(new TablePos(signaturePosString), "", - origDoc, visualObject, pdfObject.getStatus().getSettings()); + positioningInstruction = Positioning.determineTablePositioning(new TablePos(signaturePosString), + origDoc, visualObject, pdfObject.getStatus().getSettings(), signatureProfileSettings); } else { - positioningInstruction = Positioning.determineTablePositioning(new TablePos(), "", origDoc, - visualObject, pdfObject.getStatus().getSettings()); + positioningInstruction = Positioning.determineTablePositioning(new TablePos(), origDoc, + visualObject, pdfObject.getStatus().getSettings(), signatureProfileSettings); } origDoc.close(); @@ -877,10 +815,10 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { return cutOut; } catch (final PdfAsException e) { - logger.warn("PDF-AS Exception", e); + log.warn("PDF-AS Exception", e); throw ErrorExtractor.searchPdfAsError(e, status); } catch (final Throwable e) { - logger.warn("Unexpected Throwable Exception", e); + log.warn("Unexpected Throwable Exception", e); throw ErrorExtractor.searchPdfAsError(e, status); } } @@ -898,7 +836,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { if (pdfaIdentificationSchema != null) { final Integer pdfaversion = pdfaIdentificationSchema.getPart(); final String conformance = pdfaIdentificationSchema.getConformance(); - logger.info("Detected PDF/A Version: {} - {}", pdfaversion, conformance); + log.info("Detected PDF/A Version: {} - {}", pdfaversion, conformance); if (pdfaversion != null) { return String.valueOf(pdfaversion); @@ -907,7 +845,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { } } } catch (final Throwable e) { - logger.warn("Failed to determine PDF/A Version!", e); + log.warn("Failed to determine PDF/A Version!", e); } return null; } @@ -931,75 +869,9 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { } } return signature; + } - - private List<String> existingSignatureLocations(PDDocument doc) { - final List<String> existingLocations = new ArrayList<>(); - try { - final List<PDSignature> pdSignatureList = doc.getSignatureDictionaries(); - if (pdSignatureList.size() != 0) { - for (final PDSignature sig : pdSignatureList) { - existingLocations.add(sig.getLocation()); - } - } - } catch (final IOException e) { - e.printStackTrace(); - } - return existingLocations; - } - - // find first placeholder_id - public SignaturePlaceholderData checkAvailablePlaceholders(List<SignaturePlaceholderData> placeholders, - List<String> existingPlaceholders) { - SignaturePlaceholderData result = null; - - if (placeholders != null) { - for (int i = 0; i < placeholders.size(); ++i) { - // take smallest id - if (!existingPlaceholders.contains(placeholders.get(i).getPlaceholderName())) { - final SignaturePlaceholderData spd = placeholders.get(i); - if (spd.getId() != null) { - if (result == null) { - result = spd; - } else { - try { - final int currentID = Integer.parseInt(result.getId()); - final int testID = Integer.parseInt(spd.getId()); - if (testID < currentID) { - result = spd; - } - } catch (final Exception e) { - // fallback to string compare - final String currentID = result.getId(); - final String testID = spd.getId(); - if (testID.compareToIgnoreCase(currentID) < 0) { - result = spd; - } - } - } - } - } - } - } - return result; - } - - // find first placeholder_id - public List<SignaturePlaceholderData> listAvailablePlaceholders(List<SignaturePlaceholderData> placeholders, - List<String> existingPlaceholders) { - final List<SignaturePlaceholderData> result = new ArrayList<>(); - - if (placeholders != null) { - for (int i = 0; i < placeholders.size(); ++i) { - // take smallest id - if (!existingPlaceholders.contains(placeholders.get(i).getPlaceholderName())) { - result.add(placeholders.get(i)); - } - } - } - return result; - } - + static Map<Integer, COSObjectable> getNumberTreeAsMap(PDNumberTreeNode tree) throws IOException { Map<Integer, COSObjectable> numbers = tree.getNumbers(); diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureBuilder.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureBuilder.java index d30604d3..a148b3ec 100644 --- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureBuilder.java +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureBuilder.java @@ -30,17 +30,25 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.MessageDigest; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; -import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings; import org.apache.commons.codec.binary.Hex; -import org.apache.commons.io.IOUtils; import org.apache.pdfbox.cos.COSArray; import org.apache.pdfbox.cos.COSDictionary; import org.apache.pdfbox.cos.COSName; -import org.apache.pdfbox.pdmodel.*; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentCatalog; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.PDResources; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDType0Font; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory; import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory; @@ -52,12 +60,12 @@ import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleS import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDField; import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField; -import org.apache.pdfbox.pdmodel.font.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import at.gv.egiz.pdfas.common.exceptions.PdfAsException; import at.gv.egiz.pdfas.common.settings.ISettings; +import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings; import at.gv.egiz.pdfas.common.utils.ImageUtils; import at.knowcenter.wag.egov.egiz.table.Entry; @@ -260,7 +268,7 @@ public class PDFAsVisualSignatureBuilder extends PDVisibleSigBuilder implements PDPageContentStream stream = new PDPageContentStream(template, getStructure().getPage()); // stream.setFont(PDType1Font.COURIER, 5); - TableDrawUtils.drawTable(getStructure().getPage(), stream, 1, 1, + TableDrawUtils.drawTable(getStructure().getPage(), stream, 0, 1, designer.getWidth(), designer.getHeight(), properties.getMainTable(), template, false, innerFormResources, images, settings, this, properties); diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureProperties.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureProperties.java index 34093a9d..623347ff 100644 --- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureProperties.java +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureProperties.java @@ -104,7 +104,7 @@ public class PDFAsVisualSignatureProperties extends PDVisibleSigProperties { logger.debug("ROT {}", rot); logger.debug("COORD X {} Y {}", posx, posy); designer.coordinates(posx, posy); - float[] form_rect = new float[] {0,0, main.getWidth() + 2, main.getHeight() + 2}; + float[] form_rect = new float[] {0,0, main.getWidth() + 1, main.getHeight() + 1}; logger.debug("AP Rect: {} {} {} {}", form_rect[0], form_rect[1], form_rect[2], form_rect[3]); designer.formaterRectangleParams(form_rect); //this.setPdVisibleSignature(designer); diff --git a/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/PDFBoxPlaceholderExtractorTest.java b/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/PDFBoxPlaceholderExtractorTest.java new file mode 100644 index 00000000..fbe3cdea --- /dev/null +++ b/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/PDFBoxPlaceholderExtractorTest.java @@ -0,0 +1,51 @@ +package at.gv.egiz.pdfas.lib.testpdfbox; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.junit.Test; + +import at.gv.egiz.pdfas.lib.impl.pdfbox2.placeholder.SignatureFieldsAndPlaceHolderExtractor; +import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData; +import lombok.SneakyThrows; + +public class PDFBoxPlaceholderExtractorTest { + + @Test + @SneakyThrows + public void nextPlaceholder() { + SignaturePlaceholderData result = getNextSignaturePlaceHolder("/data/platzhalter_en_de_test.pdf"); + assertEquals("Im48", result.getPlaceholderName()); + + } + + @Test + @SneakyThrows + public void allPlaceHolders() { + List<String> listOfPlaceHolders = getPlaceHolders("/data/platzhalter_en_de_test.pdf"); + assertNotNull(listOfPlaceHolders); + + } + + private static List<String> getPlaceHolders(String filePath) throws IOException { + final PDDocument doc = PDDocument.load(PDFBoxPlaceholderExtractorTest.class.getResourceAsStream( + filePath)); + final List<String> results = SignatureFieldsAndPlaceHolderExtractor.findEmptySignatureFields(doc); + return results; + + } + + private static SignaturePlaceholderData getNextSignaturePlaceHolder(String filePath) throws IOException { + final PDDocument doc = PDDocument.load(PDFBoxPlaceholderExtractorTest.class.getResourceAsStream( + filePath)); + final SignaturePlaceholderData result = + SignatureFieldsAndPlaceHolderExtractor.getNextUnusedSignaturePlaceHolder(doc); + return result; + + } + +} diff --git a/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/SignatureFieldsAndPlaceHolderExtractorTest.java b/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/SignatureFieldsAndPlaceHolderExtractorTest.java index 40522504..0ed05eb5 100644 --- a/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/SignatureFieldsAndPlaceHolderExtractorTest.java +++ b/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/SignatureFieldsAndPlaceHolderExtractorTest.java @@ -52,6 +52,14 @@ public class SignatureFieldsAndPlaceHolderExtractorTest { SignaturePlaceholderData result = getNextSignaturePlaceHolder(getPath("manySignFields.pdf")); Assert.assertEquals(null,result); } + + @Test + public void firstQrCodeOnUnsignedDoc() { + SignaturePlaceholderData result = getNextSignaturePlaceHolder(getPath("new_qr_2-2.pdf")); + Assert.assertEquals("Image5",result.getPlaceholderName()); + + } + @Test public void subsequentCalls(){ SignaturePlaceholderData result = getNextSignaturePlaceHolder(getPath("new_qr_2_signed_signed_signed.pdf")); diff --git a/pdf-as-pdfbox-2/src/test/resources/data/platzhalter_en_de_test.pdf b/pdf-as-pdfbox-2/src/test/resources/data/platzhalter_en_de_test.pdf Binary files differnew file mode 100644 index 00000000..06b9aa0e --- /dev/null +++ b/pdf-as-pdfbox-2/src/test/resources/data/platzhalter_en_de_test.pdf diff --git a/pdf-as-web-db/build.gradle b/pdf-as-web-db/build.gradle index e2ee78cf..d2b6fb02 100644 --- a/pdf-as-web-db/build.gradle +++ b/pdf-as-web-db/build.gradle @@ -17,9 +17,9 @@ dependencies { implementation project (':pdf-as-web') implementation project (':pdf-as-web-status') implementation project (':pdf-as-web-statistic-api') - api "org.hibernate:hibernate-core:5.6.14.Final" - api "org.hibernate:hibernate-entitymanager:5.6.14.Final" - implementation 'ch.qos.logback:logback-classic:1.2.11' + api "org.hibernate:hibernate-core:5.6.15.Final" + api "org.hibernate:hibernate-entitymanager:5.6.15.Final" + implementation 'ch.qos.logback:logback-classic:1.2.12' implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4jVersion } diff --git a/pdf-as-web-db/src/main/java/at/gv/egiz/pdfas/web/store/DBRequestStore.java b/pdf-as-web-db/src/main/java/at/gv/egiz/pdfas/web/store/DBRequestStore.java index 5afb28e4..e5a789d2 100644 --- a/pdf-as-web-db/src/main/java/at/gv/egiz/pdfas/web/store/DBRequestStore.java +++ b/pdf-as-web-db/src/main/java/at/gv/egiz/pdfas/web/store/DBRequestStore.java @@ -4,12 +4,12 @@ import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; -import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.Configuration; +import org.hibernate.query.Query; import org.hibernate.service.ServiceRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,6 +33,8 @@ public class DBRequestStore implements IRequestStore { public DBRequestStore() { final Configuration cfg = new Configuration(); cfg.addAnnotatedClass(Request.class); + cfg.addAnnotatedClass(Response.class); + cfg.addAnnotatedClass(StatisticRequest.class); cfg.setProperties(WebConfiguration.getHibernateProps()); serviceRegistry = new StandardServiceRegistryBuilder().applySettings( @@ -46,7 +48,7 @@ public class DBRequestStore implements IRequestStore { final Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.SECOND, -1 * seconds); final Date date = calendar.getTime(); - final SimpleDateFormat dt = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss"); + final SimpleDateFormat dt = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); logger.info("Clearing Entries before: " + dt.format(date)); Session session = null; Transaction tx = null; @@ -57,6 +59,7 @@ public class DBRequestStore implements IRequestStore { + " where req.created < :date"); query.setCalendar("date", calendar); query.executeUpdate(); + tx.commit(); } catch (final Throwable e) { logger.error("Failed to save Request", e); tx.rollback(); @@ -72,7 +75,7 @@ public class DBRequestStore implements IRequestStore { final Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.SECOND, -1 * seconds); final Date date = calendar.getTime(); - final SimpleDateFormat dt = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss"); + final SimpleDateFormat dt = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); logger.info("Clearing Entries before: " + dt.format(date)); Session session = null; try { @@ -91,7 +94,7 @@ public class DBRequestStore implements IRequestStore { + " where req.created < :date"); queryResponse.setCalendar("date", calendar); queryResponse.executeUpdate(); - + } finally { if (session != null) { session.close(); diff --git a/pdf-as-web/build.gradle b/pdf-as-web/build.gradle index f5843650..3123668d 100644 --- a/pdf-as-web/build.gradle +++ b/pdf-as-web/build.gradle @@ -56,8 +56,7 @@ dependencies { api project (':pdf-as-web-status') api project (':pdf-as-web-statistic-api') api project (':pdf-as-pdfbox-2') - api group: 'commons-fileupload', name: 'commons-fileupload', version: '1.4' - // Upgrade dependency of commons-fileupload from 2.2 to 2.8.0 to avoid CVE-2021-29425 + api group: 'commons-fileupload', name: 'commons-fileupload', version: '1.5' api group: 'commons-io', name: 'commons-io', version: '2.11.0' api group: 'opensymphony', name: 'sitemesh', version: '2.4.2' api group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1' @@ -69,12 +68,12 @@ dependencies { api 'org.apache.cxf:cxf-rt-transports-http:3.5.5' api 'org.apache.cxf:cxf-rt-frontend-jaxws:3.5.5' api 'com.thetransactioncompany:cors-filter:2.10' - api 'ch.qos.logback:logback-classic:1.2.11' - api 'ch.qos.logback:logback-core:1.2.11' - api 'org.json:json:20220924' + api 'ch.qos.logback:logback-classic:1.2.12' + api 'ch.qos.logback:logback-core:1.2.12' + api 'org.json:json:20230227' api group: 'javax.jws', name: 'javax.jws-api', version: '1.1' compileOnly 'javax.servlet:javax.servlet-api:3.0.1' - testRuntime 'org.springframework:spring-test:5.2.22.RELEASE' + testImplementation 'org.springframework:spring-test:5.3.28' } diff --git a/pdf-as-web/src/main/configuration/pdf-as-web.properties b/pdf-as-web/src/main/configuration/pdf-as-web.properties index 556fd667..fe6c9576 100644 --- a/pdf-as-web/src/main/configuration/pdf-as-web.properties +++ b/pdf-as-web/src/main/configuration/pdf-as-web.properties @@ -51,6 +51,7 @@ whitelist.enabled=true whitelist.url.01=^.*$ public.url=http://localhost:8080/pdf-as-web +#public.data.url=http://localhost:8088/pdf-as-web #Request Store # Default In Memory Store diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java index 1fffb17d..81b60131 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java @@ -39,6 +39,7 @@ import at.gv.egiz.pdfas.web.helper.PdfAsHelper; public class WebConfiguration implements IConfigurationConstants { public static final String PUBLIC_URL = "public.url"; + public static final String PUBLIC_DATA_URL = "public.data.url"; public static final String LOCAL_BKU_ENABLED = "bku.sign.enabled"; public static final String ONLINE_BKU_ENABLED = "moc.sign.enabled"; public static final String MOBILE_BKU_ENABLED = "mobile.sign.enabled"; @@ -226,6 +227,10 @@ public class WebConfiguration implements IConfigurationConstants { return properties.getProperty(PUBLIC_URL); } + public static String getPublicDataURL() { + return properties.getProperty(PUBLIC_DATA_URL); + } + public static String getLocalBKUURL() { if(getLocalBKUEnabled()) { String overwrite = properties.getProperty(CONFIG_BKU_URL); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/filter/ExceptionCatchFilter.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/filter/ExceptionCatchFilter.java index 675b1d6b..5d1abc15 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/filter/ExceptionCatchFilter.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/filter/ExceptionCatchFilter.java @@ -24,7 +24,9 @@ package at.gv.egiz.pdfas.web.filter; import java.io.IOException; +import java.util.Collections; import java.util.Enumeration; +import java.util.List; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -34,26 +36,47 @@ import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.apache.commons.lang3.StringUtils; import org.slf4j.MDC; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import at.gv.egiz.pdfas.web.helper.PdfAsHelper; +import com.beust.jcommander.Strings; +import com.beust.jcommander.internal.Lists; + +import lombok.extern.slf4j.Slf4j; /** * Servlet Filter implementation class ExceptionCatchFilter */ +@Slf4j public class ExceptionCatchFilter implements Filter { - private static final Logger logger = LoggerFactory.getLogger(ExceptionCatchFilter.class); - + List<String> statelessPaths; + /** * Default constructor. */ public ExceptionCatchFilter() { } + /** + * @see Filter#init(FilterConfig) + */ + public void init(FilterConfig fConfig) throws ServletException { + String statelessConfigStrings = fConfig.getInitParameter("statelessServlets"); + if (statelessConfigStrings != null) { + statelessPaths = Lists.newArrayList(StringUtils.split(statelessConfigStrings, ",")); + + } else { + statelessPaths = Collections.emptyList(); + + } + log.info("Stateless paths set to: {}", Strings.join(", ", statelessPaths)); + + } + + /** * @see Filter#destroy() */ @@ -68,12 +91,15 @@ public class ExceptionCatchFilter implements Filter { try { if (request instanceof HttpServletRequest) { - HttpServletRequest httpRequest = (HttpServletRequest) request; - MDC.put("SESSION_ID", httpRequest.getSession().getId()); - logger.debug("Processing Parameters into Attributes"); - logger.warn("Access from IP {}", getClientIpAddr(httpRequest)); - - PdfAsHelper.logAccess(httpRequest); + HttpServletRequest httpRequest = (HttpServletRequest) request; + + HttpSession session = httpRequest.getSession(isStatefull(httpRequest.getServletPath())); + String sessionId = session != null ? session.getId() : "-"; + MDC.put("SESSION_ID", sessionId); + log.info("Access from IP: {}", getClientIpAddr(httpRequest)); + log.info("Access to: {} in Session: {}", httpRequest.getServletPath(), sessionId); + + log.debug("Processing Parameters into Attributes"); @SuppressWarnings("unchecked") Enumeration<String> parameterNames = httpRequest.getParameterNames(); while (parameterNames.hasMoreElements()) { @@ -85,33 +111,41 @@ public class ExceptionCatchFilter implements Filter { try { chain.doFilter(request, response); + } finally { - if (response != null) { - if (response instanceof HttpServletResponse) { - HttpServletResponse resp = (HttpServletResponse) response; - logger.debug("Got response status: {}", resp.getStatus()); - } else { - logger.warn("Response is not a HttpServletResponse!"); - } - } else { - logger.warn("Response is not a HttpServletResponse!"); + if (response instanceof HttpServletResponse) { + HttpServletResponse resp = (HttpServletResponse) response; + log.debug("Got response status: {}", resp.getStatus()); + + } else { + log.warn("Response is not a HttpServletResponse!"); + } } } catch (Throwable e) { - logger.error("Unhandled exception found", e); + log.error("Unhandled exception found", e); throw new ServletException(e.getMessage()); + } finally { MDC.remove("SESSION_ID"); + } /* * } catch(Throwable e) { * System.err.println("Unhandled Exception found!"); * e.printStackTrace(System.err); - * logger.error("Unhandled Exception found!", e); } + * log.error("Unhandled Exception found!", e); } */ } - public static String getClientIpAddr(HttpServletRequest request) { + private boolean isStatefull(String contextPath) { + boolean statefull = !statelessPaths.contains(contextPath); + log.trace("ServletPath: {} is marked as {}", contextPath, statefull ? "statefull" : "stateless"); + return statefull; + + } + + public static String getClientIpAddr(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); @@ -131,10 +165,4 @@ public class ExceptionCatchFilter implements Filter { return ip; } - /** - * @see Filter#init(FilterConfig) - */ - public void init(FilterConfig fConfig) throws ServletException { - } - } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/filter/UserAgentFilter.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/filter/UserAgentFilter.java index 504cf472..ef7d391d 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/filter/UserAgentFilter.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/filter/UserAgentFilter.java @@ -10,14 +10,11 @@ import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; +@Slf4j public class UserAgentFilter implements Filter { - private static final Logger logger = LoggerFactory - .getLogger(UserAgentFilter.class); - @Override public void init(FilterConfig filterConfig) throws ServletException { // TODO Auto-generated method stub @@ -37,7 +34,7 @@ public class UserAgentFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if(request instanceof HttpServletRequest) { - logger.debug("Processing Parameters into Attributes"); + log.debug("Processing Parameters into Attributes"); HttpServletRequest httpRequest = (HttpServletRequest)request; requestUserAgent.set(httpRequest.getHeader("User-Agent")); } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java index 35b5a7ce..9900dda4 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java @@ -161,7 +161,9 @@ public class PdfAsHelper { reloadConfig(); } - public static void init() { + public static void init() { + JsonSecurityUtils.getInstance(); + log.info("PDF-AS Helper initialized"); } @@ -536,7 +538,9 @@ public class PdfAsHelper { // set Signature Position signParameter.setSignaturePosition(documentToSign.getPosition()); - + signParameter.setPlaceHolderId(documentToSign.getPlaceHolderId()); + signParameter.setPlaceHolderSearchEnabled(documentToSign.isPlaceholderSearchEnabled()); + // Set Preprocessor if (coreParams.getPreprocessor() != null) { signParameter.setPreprocessorArguments(coreParams.getPreprocessor()); @@ -549,8 +553,7 @@ public class PdfAsHelper { PDFASVerificationResponse verResponse = new PDFASVerificationResponse(); - verResponse.setSignerCertificate(signResult.getSignerCertificate() - .getEncoded()); + verResponse.setSignerCertificate(signResult.getSignerCertificate().getEncoded()); SignedDocument signPdfDoc = SignedDocument.builder() @@ -660,7 +663,8 @@ public class PdfAsHelper { // set Signature Position signParameter.setSignaturePosition(pdfToSign.getPosition()); - + signParameter.setPlaceHolderId(pdfToSign.getPlaceHolderId()); + signParameter.setPlaceHolderSearchEnabled(pdfToSign.isPlaceholderSearchEnabled()); signParameter.setDynamicSignatureBlockArguments(coreSignParams.getSignatureBlockParameters()); return pdfAs.startSign(signParameter); @@ -787,12 +791,6 @@ public class PdfAsHelper { PdfAsHelper.process(request, response, context); } - public static void logAccess(HttpServletRequest request) { - HttpSession session = request.getSession(); - log.info("Access to " + request.getServletPath() + " in Session: " - + session.getId()); - } - public static JSONStartResponse startJsonProcess(HttpServletRequest request, HttpServletResponse response, ServletContext context) throws Exception { @@ -907,6 +905,10 @@ public class PdfAsHelper { JsonObject sl20Req = null; String reqId = UUID.randomUUID().toString(); if (WebConfiguration.isSL20SigningEnabled()) { + if (joseTools == null) { + throw new PdfAsException("error.config.sl20.01"); + } + String signedCertCommand = SL20JSONBuilderUtils.createSignedCommand( SL20Constants.SL20_COMMAND_IDENTIFIER_GETCERTIFICATE, getCertParams, joseTools); sl20Req = SL20JSONBuilderUtils.createGenericRequest(reqId, null, null, signedCertCommand); @@ -1361,29 +1363,35 @@ public class PdfAsHelper { private static String generateURL(HttpServletRequest request, HttpServletResponse response, String Servlet) { - HttpSession session = request.getSession(); - String publicURL = WebConfiguration.getPublicURL(); - String dataURL = null; - if (publicURL != null) { - dataURL = publicURL + Servlet + ";jsessionid=" + session.getId(); - } else { - if ((request.getScheme().equals("http") && request.getServerPort() == 80) - || (request.getScheme().equals("https") && request - .getServerPort() == 443)) { - dataURL = request.getScheme() + "://" + request.getServerName() - + request.getContextPath() + Servlet + ";jsessionid=" - + session.getId(); - } else { - dataURL = request.getScheme() + "://" + request.getServerName() - + ":" + request.getServerPort() - + request.getContextPath() + Servlet + ";jsessionid=" - + session.getId(); - } - } - log.debug("Generated URL: " + dataURL); - return dataURL; + return generateURL(request, response, Servlet, WebConfiguration.getPublicURL()); + } + private static String generateURL(HttpServletRequest request, + HttpServletResponse response, String Servlet, String publicURL) { + HttpSession session = request.getSession(); + String dataURL = null; + if (publicURL != null) { + dataURL = publicURL + Servlet + ";jsessionid=" + session.getId(); + } else { + if ((request.getScheme().equals("http") && request.getServerPort() == 80) + || (request.getScheme().equals("https") && request + .getServerPort() == 443)) { + dataURL = request.getScheme() + "://" + request.getServerName() + + request.getContextPath() + Servlet + ";jsessionid=" + + session.getId(); + } else { + dataURL = request.getScheme() + "://" + request.getServerName() + + ":" + request.getServerPort() + + request.getContextPath() + Servlet + ";jsessionid=" + + session.getId(); + } + } + log.debug("Generated URL: " + dataURL); + return dataURL; + } + + public static void regenerateSession(HttpServletRequest request) { request.getSession(false).invalidate(); request.getSession(true); @@ -1391,12 +1399,16 @@ public class PdfAsHelper { public static String generateDataURLSL20(HttpServletRequest request, HttpServletResponse response) { - return generateURL(request, response, PDF_SL20_DATAURL_PAGE); + return generateURL(request, response, PDF_SL20_DATAURL_PAGE, + WebConfiguration.getPublicDataURL() != null + ? WebConfiguration.getPublicDataURL() : WebConfiguration.getPublicURL()); } public static String generateDataURL(HttpServletRequest request, HttpServletResponse response) { - return generateURL(request, response, PDF_DATAURL_PAGE); + return generateURL(request, response, PDF_DATAURL_PAGE, + WebConfiguration.getPublicDataURL() != null + ? WebConfiguration.getPublicDataURL() : WebConfiguration.getPublicURL()); } public static String generateProvideURL(HttpServletRequest request, diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsParameterExtractor.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsParameterExtractor.java index 31fbf46d..1ed85e98 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsParameterExtractor.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsParameterExtractor.java @@ -30,11 +30,13 @@ import java.util.Map; import javax.servlet.http.HttpServletRequest; -import at.gv.egiz.pdfas.lib.api.IConfigurationConstants; -import at.gv.egiz.pdfas.lib.api.verify.VerifyParameter.SignatureVerificationLevel; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import at.gv.egiz.pdfas.lib.api.IConfigurationConstants; +import at.gv.egiz.pdfas.lib.api.verify.VerifyParameter.SignatureVerificationLevel; + public class PdfAsParameterExtractor { public static final String PARAM_CONNECTOR = "connector"; @@ -118,6 +120,16 @@ public class PdfAsParameterExtractor { return (String)request.getAttribute(IConfigurationConstants.PLACEHOLDER_WEB_ID); } + public static boolean isPlaceholderSearchEnabled(HttpServletRequest request) { + String value = (String)request.getAttribute(IConfigurationConstants.PLACEHOLDER_WEB_ENABLED); + if (StringUtils.isNotEmpty(value)) { + return Boolean.valueOf(value); + + } else { + return true; + } + } + public static String getTransactionId(HttpServletRequest request) { String transactionId = (String)request.getAttribute(PARAM_TRANSACTION_ID); return transactionId; diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ErrorPage.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ErrorPage.java index 72128a9c..42236f5e 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ErrorPage.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ErrorPage.java @@ -42,8 +42,8 @@ import at.gv.egiz.pdfas.web.helper.HTMLFormater; import at.gv.egiz.pdfas.web.helper.PdfAsHelper; import at.gv.egiz.pdfas.web.helper.UrlParameterExtractor; import at.gv.egiz.pdfas.web.stats.StatisticEvent; -import at.gv.egiz.pdfas.web.stats.StatisticFrontend; import at.gv.egiz.pdfas.web.stats.StatisticEvent.Status; +import at.gv.egiz.pdfas.web.stats.StatisticFrontend; /** * Servlet implementation class ErrorPage @@ -116,11 +116,14 @@ public class ErrorPage extends HttpServlet { String template = PdfAsHelper.getErrorRedirectTemplateSL(); URL url = new URL(errorURL); - String errorURLProcessed = url.getProtocol() + "://" + // "http" + ":// - url.getHost() + // "myhost" - ":" + // ":" - url.getPort() + // "8080" - url.getPath(); + String errorURLProcessed = url.getProtocol() + "://" + url.getHost(); + if (url.getPort() != -1) { + errorURLProcessed += ":" + url.getPort(); + + } + + errorURLProcessed += url.getPath(); + template = template.replace("##ERROR_URL##", errorURLProcessed); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ExternSignServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ExternSignServlet.java index 898e44e2..957614b1 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ExternSignServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ExternSignServlet.java @@ -46,10 +46,8 @@ import at.gv.egiz.pdfas.common.exceptions.PDFASError; import at.gv.egiz.pdfas.common.exceptions.PdfAsException; import at.gv.egiz.pdfas.common.exceptions.PdfAsSettingsValidationException; import at.gv.egiz.pdfas.common.settings.ISettings; -import at.gv.egiz.pdfas.lib.api.IConfigurationConstants; import at.gv.egiz.pdfas.lib.api.PdfAsFactory; import at.gv.egiz.pdfas.lib.api.verify.VerifyParameter.SignatureVerificationLevel; -import at.gv.egiz.pdfas.lib.impl.configuration.PlaceholderWebConfiguration; import at.gv.egiz.pdfas.web.config.WebConfiguration; import at.gv.egiz.pdfas.web.exception.PdfAsWebException; import at.gv.egiz.pdfas.web.filter.UserAgentFilter; @@ -346,20 +344,6 @@ public class ExternSignServlet extends HttpServlet { String responseMode = PdfAsParameterExtractor.getResonseMode(request); PdfAsHelper.setResponseMode(request, response, responseMode); - - //read and set placholder web id - try{ - String placeholder_id = PdfAsParameterExtractor.getPlaceholderId(request); - if(org.apache.commons.lang3.StringUtils.isNotEmpty(placeholder_id)) { - PlaceholderWebConfiguration.setValue(IConfigurationConstants.PLACEHOLDER_WEB_ID, placeholder_id); - } else { - PlaceholderWebConfiguration.clear(); - } - - } catch(Exception e) { - log.error(e.getLocalizedMessage()); - } - String filename = PdfAsParameterExtractor.getFilename(request); if(filename != null) { log.debug("Setting Filename in session: " + filename); @@ -395,6 +379,9 @@ public class ExternSignServlet extends HttpServlet { document.setInputData(pdfData); document.setPosition(PdfAsHelper.buildPosString(request, response)); document.setProfile(PdfAsParameterExtractor.getSigType(request)); + document.setPlaceHolderId(PdfAsParameterExtractor.getPlaceholderId(request)); + log.debug("Add placeholderId: {} into process information", document.getPlaceHolderId()); + document.setPlaceholderSearchEnabled(PdfAsParameterExtractor.isPlaceholderSearchEnabled(request)); document.setQrCodeContent(qrcodeContent); document.setFileName(PdfAsHelper.getPDFFileName(request)); data.addDocumentToSign(document); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFData.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFData.java index bf45745d..96d02f16 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFData.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFData.java @@ -26,6 +26,8 @@ package at.gv.egiz.pdfas.web.servlets; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.Iterator; import java.util.List; import java.util.zip.Deflater; @@ -37,6 +39,7 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import at.gv.egiz.pdfas.api.processing.PdfasSignResponse; import at.gv.egiz.pdfas.api.processing.SignedDocument; import at.gv.egiz.pdfas.api.ws.PDFASVerificationResponse; import at.gv.egiz.pdfas.web.config.WebConfiguration; @@ -85,7 +88,15 @@ public class PDFData extends HttpServlet { protected void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - if (PdfAsHelper.getPdfSigningResponse(request).getSignedPdfs().isEmpty()) { + PdfasSignResponse resultObject = PdfAsHelper.getPdfSigningResponse(request); + + if (resultObject == null) { + log.warn("No data for session with Id: {}", request.getSession().getId()); + PdfAsHelper.setSessionException(request, response, + "No signed pdf document available.", null); + PdfAsHelper.gotoError(getServletContext(), request, response); + + } else if (resultObject.getSignedPdfs().isEmpty()) { log.info("No signed pdf document available."); PdfAsHelper.setSessionException(request, response, "No signed pdf document available.", null); @@ -136,7 +147,7 @@ public class PDFData extends HttpServlet { } // build response - response.setHeader("Content-Disposition", "inline;filename=multiple_documents.zip"); + response.setHeader("Content-Disposition", "inline;filename=\"multiple_documents.zip\""); response.setContentType("application/zip"); final OutputStream os = response.getOutputStream(); @@ -225,8 +236,13 @@ public class PDFData extends HttpServlet { return; } } - response.setHeader("Content-Disposition", "inline;filename=" - + PdfAsHelper.getPDFFileName(request)); + response.setHeader("Content-Disposition", "inline;filename=\"" + + PdfAsHelper.getPDFFileName(request) + "\""); + + response.setHeader("X-FILENAME-BASE64URL", + Base64.getUrlEncoder().encodeToString( + PdfAsHelper.getPDFFileName(request).getBytes(StandardCharsets.UTF_8))); + final String pdfCert = signedFile.getSignerCertificate(); if (pdfCert != null) { response.setHeader("Signer-Certificate", pdfCert); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PlaceholderGeneratorServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PlaceholderGeneratorServlet.java index f054db6a..b07293b1 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PlaceholderGeneratorServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PlaceholderGeneratorServlet.java @@ -32,6 +32,7 @@ public class PlaceholderGeneratorServlet extends HttpServlet implements Placehol public static final String PARAM_ID = "id"; public static final String PARAM_PROFILE = "profile"; + public static final String PARAM_LANG = "lang"; public static final String PARAM_WIDTH = "w"; public static final String PARAM_HEIGHT = "h"; public static final String PARAM_BORDER = "b"; @@ -62,6 +63,8 @@ public class PlaceholderGeneratorServlet extends HttpServlet implements Placehol String id = req.getParameter(PARAM_ID); String profile = req.getParameter(PARAM_PROFILE); + String lang = req.getParameter(PARAM_LANG) != null ? req.getParameter(PARAM_LANG) : "DE"; + String buildString = QR_PLACEHOLDER_IDENTIFIER; @@ -86,7 +89,7 @@ public class PlaceholderGeneratorServlet extends HttpServlet implements Placehol if(profile != null && !profile.isEmpty()) { buildString = buildString + ";" + SignaturePlaceholderData.PROFILE_KEY + "=" + profile; - if(profile.endsWith("_EN")) { + if(lang.equalsIgnoreCase("EN")) { baseImage = "/img/PLACEHOLDER-SIG_EN.png"; filename = filename + "_en"; } else { @@ -103,7 +106,7 @@ public class PlaceholderGeneratorServlet extends HttpServlet implements Placehol // default values set for pdf-as wai on buergerkarte.at int height = 60; int width = 300; - int border = 2; + int border = 1; if(req.getParameter(PARAM_HEIGHT) != null) { try { @@ -132,7 +135,7 @@ public class PlaceholderGeneratorServlet extends HttpServlet implements Placehol } } - int qrSize = height - ( 2 * border); + int qrSize = height - (border); InputStream is = this.getClass().getClassLoader().getResourceAsStream(baseImage); if(is == null) { @@ -146,6 +149,7 @@ public class PlaceholderGeneratorServlet extends HttpServlet implements Placehol // generate QR code try { QRCodeGenerator.generateQRCode(buildString, baos, qrSize); + } catch (WriterException e) { logger.warn("Failed to generate QR Code for placeholder generation", e); resp.sendError(HttpStatus.SC_INTERNAL_SERVER_ERROR); @@ -161,7 +165,7 @@ public class PlaceholderGeneratorServlet extends HttpServlet implements Placehol Graphics g = off_Image.getGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, width, height); - g.fillRect(border, border, width - (2 * border), height - (2 * border)); + //g.fillRect(border, border, width - (2 * border), height - (2 * border)); //g.drawImage(base, 0, 0, 250, 98, 0, 0, base.getWidth(), base.getHeight(), null); g.drawImage(qr, border, border, qrSize + border, qrSize + border, 0, 0, qr.getWidth(), qr.getHeight(), null); @@ -180,7 +184,7 @@ public class PlaceholderGeneratorServlet extends HttpServlet implements Placehol int start = (height - textHeight) / 2; - if(profile != null && profile.endsWith("_EN")) { + if(lang.equalsIgnoreCase("EN")) { g.drawString("placeholder for the", qrSize + ( 3 * border), start + lineSpace); g.drawString("electronic signature", qrSize + ( 3 * border), start + (2 * lineSpace)); } else { diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SoapServiceServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SoapServiceServlet.java index 71395304..ca005abe 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SoapServiceServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SoapServiceServlet.java @@ -5,20 +5,12 @@ import javax.xml.ws.Endpoint; import org.apache.cxf.Bus; import org.apache.cxf.BusFactory; -import org.apache.cxf.feature.LoggingFeature; -import org.apache.cxf.interceptor.LoggingInInterceptor; -import org.apache.cxf.interceptor.LoggingOutInterceptor; import org.apache.cxf.transport.servlet.CXFNonSpringServlet; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import at.gv.egiz.pdfas.web.ws.PDFASSigningImpl; import at.gv.egiz.pdfas.web.ws.PDFASVerificationImpl; public class SoapServiceServlet extends CXFNonSpringServlet { - - private static final Logger logger = LoggerFactory - .getLogger(SoapServiceServlet.class); /** * diff --git a/pdf-as-web/src/main/resources/META-INF/context.xml b/pdf-as-web/src/main/resources/META-INF/context.xml new file mode 100644 index 00000000..716b2233 --- /dev/null +++ b/pdf-as-web/src/main/resources/META-INF/context.xml @@ -0,0 +1,3 @@ +<Context> + <CookieProcessor sameSiteCookies="none" /> +</Context>
\ No newline at end of file diff --git a/pdf-as-web/src/main/webapp/WEB-INF/web.xml b/pdf-as-web/src/main/webapp/WEB-INF/web.xml index 7920ad91..46ae8272 100644 --- a/pdf-as-web/src/main/webapp/WEB-INF/web.xml +++ b/pdf-as-web/src/main/webapp/WEB-INF/web.xml @@ -12,6 +12,10 @@ <display-name>ExceptionCatchFilter</display-name> <description></description> <filter-class>at.gv.egiz.pdfas.web.filter.ExceptionCatchFilter</filter-class> + <init-param> + <param-name>statelessServlets</param-name> + <param-value>/placeholder,/visblock</param-value> + </init-param> </filter> <filter> <filter-name>UserAgentFilter</filter-name> diff --git a/signature-standards/sigs-pades/src/main/java/at/gv/egiz/pdfas/sigs/pades/PAdESSignerKeystore.java b/signature-standards/sigs-pades/src/main/java/at/gv/egiz/pdfas/sigs/pades/PAdESSignerKeystore.java index 6845adf2..4914833e 100644 --- a/signature-standards/sigs-pades/src/main/java/at/gv/egiz/pdfas/sigs/pades/PAdESSignerKeystore.java +++ b/signature-standards/sigs-pades/src/main/java/at/gv/egiz/pdfas/sigs/pades/PAdESSignerKeystore.java @@ -23,23 +23,6 @@ ******************************************************************************/ package at.gv.egiz.pdfas.sigs.pades; -import at.gv.egiz.pdfas.lib.api.IConfigurationConstants; -import iaik.asn1.ASN1Object; -import iaik.asn1.CodingException; -import iaik.asn1.ObjectID; -import iaik.asn1.SEQUENCE; -import iaik.asn1.UTF8String; -import iaik.asn1.structures.AlgorithmID; -import iaik.asn1.structures.Attribute; -import iaik.asn1.structures.ChoiceOfTime; -import iaik.cms.ContentInfo; -import iaik.cms.IssuerAndSerialNumber; -import iaik.cms.SignedData; -import iaik.cms.SignerInfo; -import iaik.smime.ess.ESSCertID; -import iaik.smime.ess.ESSCertIDv2; -import iaik.x509.X509Certificate; - import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -63,6 +46,7 @@ import at.gv.egiz.pdfas.common.exceptions.ErrorConstants; import at.gv.egiz.pdfas.common.exceptions.PDFASError; import at.gv.egiz.pdfas.common.exceptions.PdfAsException; import at.gv.egiz.pdfas.common.exceptions.PdfAsSignatureException; +import at.gv.egiz.pdfas.lib.api.IConfigurationConstants; import at.gv.egiz.pdfas.lib.api.PdfAsFactory; import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner; import at.gv.egiz.pdfas.lib.api.sign.SignParameter; @@ -70,6 +54,21 @@ import at.gv.egiz.pdfas.lib.api.verify.VerifyResult; import at.gv.egiz.pdfas.lib.impl.status.RequestedSignature; import at.gv.egiz.pdfas.lib.util.CertificateUtils; import at.gv.egiz.pdfas.lib.util.SignatureUtils; +import iaik.asn1.ASN1Object; +import iaik.asn1.CodingException; +import iaik.asn1.ObjectID; +import iaik.asn1.SEQUENCE; +import iaik.asn1.UTF8String; +import iaik.asn1.structures.AlgorithmID; +import iaik.asn1.structures.Attribute; +import iaik.asn1.structures.ChoiceOfTime; +import iaik.cms.ContentInfo; +import iaik.cms.IssuerAndSerialNumber; +import iaik.cms.SignedData; +import iaik.cms.SignerInfo; +import iaik.smime.ess.ESSCertID; +import iaik.smime.ess.ESSCertIDv2; +import iaik.x509.X509Certificate; public class PAdESSignerKeystore implements IPlainSigner, PAdESConstants { @@ -83,85 +82,6 @@ public class PAdESSignerKeystore implements IPlainSigner, PAdESConstants { PrivateKey privKey; X509Certificate cert; - private void readKeyStore(KeyStore ks, String alias, String keypassword) throws Throwable { - if (keypassword == null) { - throw new PdfAsException("error.pdf.sig.16"); - } - PasswordProtection pwdProt = new PasswordProtection( - keypassword.toCharArray()); - - logger.info("Opening Alias: [" + alias + "]"); - - Entry entry = ks.getEntry(alias, pwdProt); - - if (!(entry instanceof PrivateKeyEntry)) { - throw new PdfAsException("error.pdf.sig.18"); - } - - PrivateKeyEntry privateEntry = (PrivateKeyEntry) entry; - - privKey = privateEntry.getPrivateKey(); - - if (privKey == null) { - throw new PdfAsException("error.pdf.sig.13"); - } - - Certificate c = privateEntry.getCertificate(); - - if (c == null) { - if (privateEntry.getCertificateChain() != null) { - if (privateEntry.getCertificateChain().length > 0) { - c = privateEntry.getCertificateChain()[0]; - } - } - } - - if (c == null) { - throw new PdfAsException("error.pdf.sig.17"); - } - - cert = new X509Certificate(c.getEncoded()); - } - - private KeyStore buildKeyStoreFromFile(String file, String kspassword, - String type, String provider) throws Throwable { - String viusalProvider = (provider == null ? "IAIK" : provider); - logger.trace("Opening Keystore: " + file + " with [" + viusalProvider - + "]"); - - KeyStore ks = null; - if (provider == null) { - ks = KeyStore.getInstance(type); - } else { - ks = KeyStore.getInstance(type, provider); - } - - if (ks == null) { - throw new PdfAsException("error.pdf.sig.14"); - } - if (kspassword == null) { - throw new PdfAsException("error.pdf.sig.15"); - } - FileInputStream is = null; - try { - is = new FileInputStream(file); - ks.load(is, kspassword.toCharArray()); - } finally { - if (is != null) { - is.close(); - } - } - return ks; - } - - private void loadKeystore(String file, String alias, String kspassword, - String keypassword, String type, String provider) throws Throwable { - - KeyStore ks = buildKeyStoreFromFile(file, kspassword, type, provider); - - readKeyStore(ks, alias, keypassword); - } - public PAdESSignerKeystore(KeyStore ks, String alias, String keypassword) throws PDFASError { try { @@ -221,6 +141,160 @@ public class PAdESSignerKeystore implements IPlainSigner, PAdESConstants { return cert; } + public byte[] sign(byte[] input, int[] byteRange, SignParameter parameter, + RequestedSignature requestedSignature) throws PdfAsException { + try { + logger.info("Creating PAdES signature."); + + requestedSignature.getStatus().getMetaInformations() + .put(ErrorConstants.STATUS_INFO_SIGDEVICE, SIGNATURE_DEVICE); + requestedSignature.getStatus().getMetaInformations() + .put(ErrorConstants.STATUS_INFO_SIGDEVICEVERSION, PdfAsFactory.getVersion()); + + IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(cert); + + AlgorithmID[] algorithms = CertificateUtils.getAlgorithmIDs(cert); + + SignerInfo signer1 = new SignerInfo(issuer, algorithms[1], + algorithms[0], privKey); + + SignedData si = new SignedData(input, SignedData.EXPLICIT); + si.addCertificates(new Certificate[] { cert }); + + + //Check PAdES Flag + if (parameter.getConfiguration().hasValue(IConfigurationConstants.SIG_PADES_FORCE_FLAG)) + { + if (IConfigurationConstants.TRUE.equalsIgnoreCase(parameter.getConfiguration().getValue(IConfigurationConstants.SIG_PADES_FORCE_FLAG))) + { + setAttributes(cert, signer1); + } + else + { + setAttributes("application/pdf", cert, new Date(), signer1); + } + } + else + { + setAttributes("application/pdf", cert, new Date(), signer1); + } + + si.addSignerInfo(signer1); + InputStream dataIs = si.getInputStream(); + byte[] buf = new byte[1024]; + @SuppressWarnings("unused") + int r; + while ((r = dataIs.read(buf)) > 0) + ; // skip data + ContentInfo ci = new ContentInfo(si); + byte[] signature = ci.getEncoded(); + + VerifyResult verifyResult = SignatureUtils.verifySignature( + signature, input); + + return signature; + } catch (NoSuchAlgorithmException e) { + throw new PdfAsSignatureException("error.pdf.sig.01", e); + } catch (iaik.cms.CMSException e) { + throw new PdfAsSignatureException("error.pdf.sig.01", e); + } catch (IOException e) { + throw new PdfAsSignatureException("error.pdf.sig.01", e); + } catch (CertificateException e) { + throw new PdfAsSignatureException("error.pdf.sig.01", e); + } catch (CodingException e) { + throw new PdfAsSignatureException("error.pdf.sig.01", e); + } catch (PDFASError e) { + throw new PdfAsSignatureException("error.pdf.sig.01", e); + } + } + + public String getPDFSubFilter() { + return SUBFILTER_ETSI_CADES_DETACHED; + } + + public String getPDFFilter() { + return FILTER_ADOBE_PPKLITE; + } + + private void readKeyStore(KeyStore ks, String alias, String keypassword) throws Throwable { + if (keypassword == null) { + throw new PdfAsException("error.pdf.sig.16"); + } + PasswordProtection pwdProt = new PasswordProtection( + keypassword.toCharArray()); + + logger.info("Opening Alias: [" + alias + "]"); + + Entry entry = ks.getEntry(alias, pwdProt); + + if (!(entry instanceof PrivateKeyEntry)) { + throw new PdfAsException("error.pdf.sig.18"); + } + + PrivateKeyEntry privateEntry = (PrivateKeyEntry) entry; + + privKey = privateEntry.getPrivateKey(); + + if (privKey == null) { + throw new PdfAsException("error.pdf.sig.13"); + } + + Certificate c = privateEntry.getCertificate(); + + if (c == null) { + if (privateEntry.getCertificateChain() != null) { + if (privateEntry.getCertificateChain().length > 0) { + c = privateEntry.getCertificateChain()[0]; + } + } + } + + if (c == null) { + throw new PdfAsException("error.pdf.sig.17"); + } + + cert = new X509Certificate(c.getEncoded()); + } + + private KeyStore buildKeyStoreFromFile(String file, String kspassword, + String type, String provider) throws Throwable { + String viusalProvider = (provider == null ? "IAIK" : provider); + logger.trace("Opening Keystore: " + file + " with [" + viusalProvider + + "]"); + + KeyStore ks = null; + if (provider == null) { + ks = KeyStore.getInstance(type); + } else { + ks = KeyStore.getInstance(type, provider); + } + + if (ks == null) { + throw new PdfAsException("error.pdf.sig.14"); + } + if (kspassword == null) { + throw new PdfAsException("error.pdf.sig.15"); + } + FileInputStream is = null; + try { + is = new FileInputStream(file); + ks.load(is, kspassword.toCharArray()); + } finally { + if (is != null) { + is.close(); + } + } + return ks; + } + + private void loadKeystore(String file, String alias, String kspassword, + String keypassword, String type, String provider) throws Throwable { + + KeyStore ks = buildKeyStoreFromFile(file, kspassword, type, provider); + + readKeyStore(ks, alias, keypassword); + } + private void setMimeTypeAttrib(List<Attribute> attributes, String mimeType) { String oidStr = "0.4.0.1733.2.1"; String name = "mime-type"; @@ -292,79 +366,4 @@ public class PAdESSignerKeystore implements IPlainSigner, PAdESConstants { signerInfo.setSignedAttributes(attributeArray); } - public byte[] sign(byte[] input, int[] byteRange, SignParameter parameter, - RequestedSignature requestedSignature) throws PdfAsException { - try { - logger.info("Creating PAdES signature."); - - requestedSignature.getStatus().getMetaInformations() - .put(ErrorConstants.STATUS_INFO_SIGDEVICE, SIGNATURE_DEVICE); - requestedSignature.getStatus().getMetaInformations() - .put(ErrorConstants.STATUS_INFO_SIGDEVICEVERSION, PdfAsFactory.getVersion()); - - IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(cert); - - AlgorithmID[] algorithms = CertificateUtils.getAlgorithmIDs(cert); - - SignerInfo signer1 = new SignerInfo(issuer, algorithms[1], - algorithms[0], privKey); - - SignedData si = new SignedData(input, SignedData.EXPLICIT); - si.addCertificates(new Certificate[] { cert }); - - - //Check PAdES Flag - if (parameter.getConfiguration().hasValue(IConfigurationConstants.SIG_PADES_FORCE_FLAG)) - { - if (IConfigurationConstants.TRUE.equalsIgnoreCase(parameter.getConfiguration().getValue(IConfigurationConstants.SIG_PADES_FORCE_FLAG))) - { - setAttributes(cert, signer1); - } - else - { - setAttributes("application/pdf", cert, new Date(), signer1); - } - } - else - { - setAttributes("application/pdf", cert, new Date(), signer1); - } - - si.addSignerInfo(signer1); - InputStream dataIs = si.getInputStream(); - byte[] buf = new byte[1024]; - @SuppressWarnings("unused") - int r; - while ((r = dataIs.read(buf)) > 0) - ; // skip data - ContentInfo ci = new ContentInfo(si); - byte[] signature = ci.getEncoded(); - - VerifyResult verifyResult = SignatureUtils.verifySignature( - signature, input); - - return signature; - } catch (NoSuchAlgorithmException e) { - throw new PdfAsSignatureException("error.pdf.sig.01", e); - } catch (iaik.cms.CMSException e) { - throw new PdfAsSignatureException("error.pdf.sig.01", e); - } catch (IOException e) { - throw new PdfAsSignatureException("error.pdf.sig.01", e); - } catch (CertificateException e) { - throw new PdfAsSignatureException("error.pdf.sig.01", e); - } catch (CodingException e) { - throw new PdfAsSignatureException("error.pdf.sig.01", e); - } catch (PDFASError e) { - throw new PdfAsSignatureException("error.pdf.sig.01", e); - } - } - - public String getPDFSubFilter() { - return SUBFILTER_ETSI_CADES_DETACHED; - } - - public String getPDFFilter() { - return FILTER_ADOBE_PPKLITE; - } - } |