aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lenz <thomas.lenz@egiz.gv.at>2018-07-09 10:11:25 +0200
committerThomas Lenz <thomas.lenz@egiz.gv.at>2018-07-09 10:11:25 +0200
commit9acf3c2e8aca9016daf76785747d838cdc5b0330 (patch)
treef97d392a8eff4906c961128e231926a76829a4c8
parent797634c687c6f44d314e4baa3fed220d142eed73 (diff)
downloadpdf-as-4-9acf3c2e8aca9016daf76785747d838cdc5b0330.tar.gz
pdf-as-4-9acf3c2e8aca9016daf76785747d838cdc5b0330.tar.bz2
pdf-as-4-9acf3c2e8aca9016daf76785747d838cdc5b0330.zip
add SL20 connecter-backend in a first beta version (getCertificate looks good, create signature is untested)
-rw-r--r--build.gradle2
-rw-r--r--pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASSignParameters.java5
-rw-r--r--pdf-as-lib/build.gradle3
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/IConfigurationConstants.java1
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/StreamUtils.java168
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/sl/schema/CreateCMSSignatureRequestType.java2
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/sl20/SL20Connector.java98
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/sl20/data/VerificationResult.java39
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SL20Exception.java19
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SL20SecurityException.java20
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SLCommandoBuildException.java17
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SLCommandoParserException.java17
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/IJOSETools.java54
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/SL20Constants.java232
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/SL20JSONBuilderUtils.java604
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/SL20JSONExtractorUtils.java422
-rw-r--r--pdf-as-moa/src/main/java/META-INF/MANIFEST.MF3
-rw-r--r--pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java105
-rw-r--r--pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java390
-rw-r--r--pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/DataURLServlet.java4
-rw-r--r--pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ExternSignServlet.java8
-rw-r--r--pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/JSONAPIServlet.java11
-rw-r--r--pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SLDataURLServlet.java234
-rw-r--r--pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java11
-rw-r--r--pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/JsonSecurityUtils.java381
-rw-r--r--pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/KeyStoreUtils.java222
-rw-r--r--pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/SL20HttpBindingUtils.java47
-rw-r--r--pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/X509Utils.java62
-rw-r--r--pdf-as-web/src/main/webapp/WEB-INF/web.xml10
-rw-r--r--pdf-as-web/src/main/webapp/index.jsp11
30 files changed, 3130 insertions, 72 deletions
diff --git a/build.gradle b/build.gradle
index cb63098e..7c4bf510 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@ task wrapper(type: Wrapper) {
allprojects {
apply plugin: 'com.github.ben-manes.versions'
repositories { mavenCentral() }
- version = '4.1.3'
+ version = '4.1.4-Snapshot'
}
configurations {
diff --git a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASSignParameters.java b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASSignParameters.java
index 35c057a3..94906112 100644
--- a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASSignParameters.java
+++ b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASSignParameters.java
@@ -52,7 +52,10 @@ public class PDFASSignParameters implements Serializable {
@XmlEnumValue("mobilebku")
MOBILEBKU("mobilebku"),
@XmlEnumValue("onlinebku")
- ONLINEBKU("onlinebku");
+ ONLINEBKU("onlinebku"),
+ @XmlEnumValue("sl20")
+ SECLAYER20("sl20");
+
private final String name;
diff --git a/pdf-as-lib/build.gradle b/pdf-as-lib/build.gradle
index d6e813ac..bdb96949 100644
--- a/pdf-as-lib/build.gradle
+++ b/pdf-as-lib/build.gradle
@@ -61,6 +61,9 @@ dependencies {
compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.59'
+ compile group: 'com.google.code.gson', name: 'gson', version: '2.8.5'
+ compile group: 'org.bitbucket.b_c', name: 'jose4j', version: '0.6.3'
+
compile group: 'commons-io', name: 'commons-io', version: '2.4'
compile 'org.apache.commons:commons-collections4:4.0'
compile group: 'ognl', name: 'ognl', version: '3.0.8'
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 e9e7e0f4..f043ff21 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
@@ -91,6 +91,7 @@ public interface IConfigurationConstants {
public static final String MOC_SIGN_URL = "moc.sign.url";
public static final String MOBILE_SIGN_URL = "mobile.sign.url";
+ public static final String SL20_SIGN_URL = "sl20.sign.url";
public static final String REGISTER_PROVIDER = "registerSecurityProvider";
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/StreamUtils.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/StreamUtils.java
new file mode 100644
index 00000000..1590bef7
--- /dev/null
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/StreamUtils.java
@@ -0,0 +1,168 @@
+
+package at.gv.egiz.pdfas.lib.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+public class StreamUtils {
+
+ /**
+ * Compare the contents of two <code>InputStream</code>s.
+ *
+ * @param is1 The 1st <code>InputStream</code> to compare.
+ * @param is2 The 2nd <code>InputStream</code> to compare.
+ * @return boolean <code>true</code>, if both streams contain the exactly the
+ * same content, <code>false</code> otherwise.
+ * @throws IOException An error occurred reading one of the streams.
+ */
+ public static boolean compareStreams(InputStream is1, InputStream is2)
+ throws IOException {
+
+ byte[] buf1 = new byte[256];
+ byte[] buf2 = new byte[256];
+ int length1;
+ int length2;
+
+ try {
+ while (true) {
+ length1 = is1.read(buf1);
+ length2 = is2.read(buf2);
+
+ if (length1 != length2) {
+ return false;
+ }
+ if (length1 <= 0) {
+ return true;
+ }
+ if (!compareBytes(buf1, buf2, length1)) {
+ return false;
+ }
+ }
+ } catch (IOException e) {
+ throw e;
+ } finally {
+ // close both streams
+ try {
+ is1.close();
+ is2.close();
+ } catch (IOException e) {
+ // ignore this
+ }
+ }
+ }
+
+ /**
+ * Compare two byte arrays, up to a given maximum length.
+ *
+ * @param b1 1st byte array to compare.
+ * @param b2 2nd byte array to compare.
+ * @param length The maximum number of bytes to compare.
+ * @return <code>true</code>, if the byte arrays are equal, <code>false</code>
+ * otherwise.
+ */
+ private static boolean compareBytes(byte[] b1, byte[] b2, int length) {
+ if (b1.length != b2.length) {
+ return false;
+ }
+
+ for (int i = 0; i < b1.length && i < length; i++) {
+ if (b1[i] != b2[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Reads a byte array from a stream.
+ * @param in The <code>InputStream</code> to read.
+ * @return The bytes contained in the given <code>InputStream</code>.
+ * @throws IOException on any exception thrown
+ */
+ public static byte[] readStream(InputStream in) throws IOException {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ copyStream(in, out, null);
+
+ /*
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int b;
+ while ((b = in.read()) >= 0)
+ out.write(b);
+
+ */
+ in.close();
+ return out.toByteArray();
+ }
+
+ /**
+ * Reads a <code>String</code> from a stream, using given encoding.
+ * @param in The <code>InputStream</code> to read.
+ * @param encoding The character encoding to use for converting the bytes
+ * of the <code>InputStream</code> into a <code>String</code>.
+ * @return The content of the given <code>InputStream</code> converted into
+ * a <code>String</code>.
+ * @throws IOException on any exception thrown
+ */
+ public static String readStream(InputStream in, String encoding) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ copyStream(in, out, null);
+
+ /*
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int b;
+ while ((b = in.read()) >= 0)
+ out.write(b);
+ */
+ in.close();
+ return out.toString(encoding);
+ }
+
+ /**
+ * Reads all data (until EOF is reached) from the given source to the
+ * destination stream. If the destination stream is null, all data is dropped.
+ * It uses the given buffer to read data and forward it. If the buffer is
+ * null, this method allocates a buffer.
+ *
+ * @param source The stream providing the data.
+ * @param destination The stream that takes the data. If this is null, all
+ * data from source will be read and discarded.
+ * @param buffer The buffer to use for forwarding. If it is null, the method
+ * allocates a buffer.
+ * @exception IOException If reading from the source or writing to the
+ * destination fails.
+ */
+ private static void copyStream(InputStream source, OutputStream destination, byte[] buffer) throws IOException {
+ if (source == null) {
+ throw new NullPointerException("Argument \"source\" must not be null.");
+ }
+ if (buffer == null) {
+ buffer = new byte[8192];
+ }
+
+ if (destination != null) {
+ int bytesRead;
+ while ((bytesRead = source.read(buffer)) >= 0) {
+ destination.write(buffer, 0, bytesRead);
+ }
+ } else {
+ while (source.read(buffer) >= 0);
+ }
+ }
+
+ /**
+ * Gets the stack trace of the <code>Throwable</code> passed in as a string.
+ * @param t The <code>Throwable</code>.
+ * @return a String representing the stack trace of the <code>Throwable</code>.
+ */
+ public static String getStackTraceAsString(Throwable t)
+ {
+ ByteArrayOutputStream stackTraceBIS = new ByteArrayOutputStream();
+ t.printStackTrace(new PrintStream(stackTraceBIS));
+ return new String(stackTraceBIS.toByteArray());
+ }
+}
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/sl/schema/CreateCMSSignatureRequestType.java b/pdf-as-lib/src/main/java/at/gv/egiz/sl/schema/CreateCMSSignatureRequestType.java
index 5d565d9d..3046b109 100644
--- a/pdf-as-lib/src/main/java/at/gv/egiz/sl/schema/CreateCMSSignatureRequestType.java
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/sl/schema/CreateCMSSignatureRequestType.java
@@ -31,7 +31,7 @@
package at.gv.egiz.sl.schema;
-import com.sun.org.apache.xpath.internal.operations.Bool;
+//import com.sun.org.apache.xpath.internal.operations.Bool;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/sl20/SL20Connector.java b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/SL20Connector.java
new file mode 100644
index 00000000..a82771bd
--- /dev/null
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/SL20Connector.java
@@ -0,0 +1,98 @@
+package at.gv.egiz.sl20;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.message.BasicNameValuePair;
+import org.jose4j.base64url.Base64Url;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonObject;
+
+import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
+import at.gv.egiz.pdfas.lib.api.Configuration;
+import at.gv.egiz.pdfas.lib.api.sign.SignParameter;
+import at.gv.egiz.sl.schema.CreateCMSSignatureResponseType;
+import at.gv.egiz.sl.schema.InfoboxReadRequestType;
+import at.gv.egiz.sl.schema.InfoboxReadResponseType;
+import at.gv.egiz.sl.util.BaseSLConnector;
+import at.gv.egiz.sl.util.RequestPackage;
+import at.gv.egiz.sl20.exceptions.SLCommandoParserException;
+import at.gv.egiz.sl20.utils.SL20Constants;
+import at.gv.egiz.sl20.utils.SL20JSONExtractorUtils;
+
+public class SL20Connector extends BaseSLConnector {
+ private static final Logger log = LoggerFactory.getLogger(SL20Connector.class);
+
+ private String bkuUrl;
+
+ public SL20Connector(Configuration config) {
+ this.bkuUrl = config.getValue(CONFIG_BKU_URL);
+
+ }
+
+ public JsonObject sendSL20Request(JsonObject sl20Req, SignParameter parameter, String vdaURL) {
+ try {
+ log.trace("Request VDA via SL20 with: " + org.bouncycastle.util.encoders.Base64.toBase64String(sl20Req.toString().getBytes()));
+ //build http client
+ CloseableHttpClient httpClient = buildHttpClient();
+
+ //build http POST request
+ HttpPost httpReq = new HttpPost(new URIBuilder(vdaURL).build());
+ List<NameValuePair> parameters = new ArrayList<NameValuePair>();;
+ parameters.add(new BasicNameValuePair(SL20Constants.PARAM_SL20_REQ_COMMAND_PARAM, Base64Url.encode(sl20Req.toString().getBytes())));
+ httpReq.setEntity(new UrlEncodedFormEntity(parameters ));
+
+ //set native client header
+ httpReq.addHeader(SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE, SL20Constants.HTTP_HEADER_VALUE_NATIVE);
+
+ //request VDA
+ log.trace("Requesting VDA ... ");
+ HttpResponse httpResp = httpClient.execute(httpReq);
+ log.debug("Response from VDA received ");
+
+ return SL20JSONExtractorUtils.getSL20ContainerFromResponse(httpResp);
+
+ } catch (URISyntaxException | IOException e) {
+ log.warn("Can NOT build SL20 http requst. Reason:" + e.getMessage(), e);
+ return null;
+
+ } catch (SLCommandoParserException e) {
+ log.warn("Can NOT parse SL20 response from VDA: Reason: " + e.getMessage(), e);
+ return null;
+
+ }
+
+ }
+
+ @Override
+ public InfoboxReadResponseType sendInfoboxReadRequest(InfoboxReadRequestType request, SignParameter parameter)
+ throws PdfAsException {
+ log.error("'sendInfoboxReadRequest' is NOT supported by " + SL20Connector.class.getName());
+ return null;
+ }
+
+ @Override
+ public CreateCMSSignatureResponseType sendCMSRequest(RequestPackage pack, SignParameter parameter)
+ throws PdfAsException {
+ log.error("'sendCMSReques' is NOT supported by " + SL20Connector.class.getName());
+ return null;
+ }
+
+ private CloseableHttpClient buildHttpClient() {
+ HttpClientBuilder builder = HttpClientBuilder.create();
+ return builder.build();
+ }
+
+}
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/sl20/data/VerificationResult.java b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/data/VerificationResult.java
new file mode 100644
index 00000000..acb0977e
--- /dev/null
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/data/VerificationResult.java
@@ -0,0 +1,39 @@
+package at.gv.egiz.sl20.data;
+
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+import com.google.gson.JsonObject;
+
+public class VerificationResult {
+
+ private Boolean validSigned = null;
+ private List<X509Certificate> certs = null;
+ private JsonObject payload = null;
+
+ public VerificationResult(JsonObject payload) {
+ this.payload = payload;
+
+ }
+
+ public VerificationResult(JsonObject string, List<X509Certificate> certs, boolean wasValidSigned) {
+ this.payload = string;
+ this.certs = certs;
+ this.validSigned = wasValidSigned;
+
+ }
+
+ public Boolean isValidSigned() {
+ return validSigned;
+ }
+ public List<X509Certificate> getCertChain() {
+ return certs;
+ }
+ public JsonObject getPayload() {
+ return payload;
+ }
+
+
+
+
+}
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SL20Exception.java b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SL20Exception.java
new file mode 100644
index 00000000..108081bf
--- /dev/null
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SL20Exception.java
@@ -0,0 +1,19 @@
+package at.gv.egiz.sl20.exceptions;
+
+import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
+
+public class SL20Exception extends PdfAsException {
+
+ private static final long serialVersionUID = 1L;
+
+ public SL20Exception(String messageId) {
+ super(messageId);
+
+ }
+
+ public SL20Exception(String messageId, Throwable wrapped) {
+ super(messageId, wrapped);
+
+ }
+
+}
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SL20SecurityException.java b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SL20SecurityException.java
new file mode 100644
index 00000000..e1562b14
--- /dev/null
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SL20SecurityException.java
@@ -0,0 +1,20 @@
+package at.gv.egiz.sl20.exceptions;
+
+public class SL20SecurityException extends SL20Exception {
+
+ private static final long serialVersionUID = 3281385988027147449L;
+
+ public SL20SecurityException() {
+ super("sl20.05");
+ }
+
+ public SL20SecurityException(Throwable wrapped) {
+ super("sl20.05", wrapped);
+
+ }
+
+ public SL20SecurityException(String string) {
+ super(string);
+ }
+
+}
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SLCommandoBuildException.java b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SLCommandoBuildException.java
new file mode 100644
index 00000000..31ec2451
--- /dev/null
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SLCommandoBuildException.java
@@ -0,0 +1,17 @@
+package at.gv.egiz.sl20.exceptions;
+
+public class SLCommandoBuildException extends SL20Exception {
+
+ private static final long serialVersionUID = 1L;
+
+
+ public SLCommandoBuildException() {
+ super("sl20.01");
+
+ }
+
+ public SLCommandoBuildException(Throwable e) {
+ super("sl20.01", e);
+
+ }
+}
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SLCommandoParserException.java b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SLCommandoParserException.java
new file mode 100644
index 00000000..6c49ca66
--- /dev/null
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SLCommandoParserException.java
@@ -0,0 +1,17 @@
+package at.gv.egiz.sl20.exceptions;
+
+public class SLCommandoParserException extends SL20Exception {
+
+ private static final long serialVersionUID = 1L;
+
+
+ public SLCommandoParserException() {
+ super("sl20.02");
+
+ }
+
+ public SLCommandoParserException(Throwable e) {
+ super("sl20.02", e);
+
+ }
+}
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/IJOSETools.java b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/IJOSETools.java
new file mode 100644
index 00000000..a0f369d8
--- /dev/null
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/IJOSETools.java
@@ -0,0 +1,54 @@
+package at.gv.egiz.sl20.utils;
+
+import java.security.cert.X509Certificate;
+
+import com.google.gson.JsonElement;
+
+import at.gv.egiz.sl20.data.VerificationResult;
+import at.gv.egiz.sl20.exceptions.SL20Exception;
+import at.gv.egiz.sl20.exceptions.SLCommandoBuildException;
+
+public interface IJOSETools {
+
+ /**
+ * Check if the JOSE tools are initialized
+ *
+ * @return
+ */
+ public boolean isInitialized();
+
+ /**
+ * Create a JWS signature
+ *
+ * @param payLoad Payload to sign
+ * @throws SLCommandoBuildException
+ */
+ public String createSignature(String payLoad) throws SLCommandoBuildException;
+
+ /**
+ * Validates a JWS signature
+ *
+ * @param serializedContent
+ * @return
+ * @throws SLCommandoParserException
+ * @throws SL20Exception
+ */
+ public VerificationResult validateSignature(String serializedContent) throws SL20Exception;
+
+ /**
+ * Get the encryption certificate for SL2.0 End-to-End encryption
+ *
+ * @return
+ */
+ public X509Certificate getEncryptionCertificate();
+
+ /**
+ * Decrypt a serialized JWE token
+ *
+ * @param compactSerialization Serialized JWE token
+ * @return decrypted payload
+ * @throws SL20Exception
+ */
+ public JsonElement decryptPayload(String compactSerialization) throws SL20Exception;
+
+}
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/SL20Constants.java b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/SL20Constants.java
new file mode 100644
index 00000000..59c3079d
--- /dev/null
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/SL20Constants.java
@@ -0,0 +1,232 @@
+package at.gv.egiz.sl20.utils;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.jose4j.jwe.ContentEncryptionAlgorithmIdentifiers;
+import org.jose4j.jwe.KeyManagementAlgorithmIdentifiers;
+import org.jose4j.jws.AlgorithmIdentifiers;
+
+public class SL20Constants {
+ public static final int CURRENT_SL20_VERSION = 10;
+
+ //http binding parameters
+ public static final String PARAM_SL20_REQ_COMMAND_PARAM = "slcommand";
+ public static final String PARAM_SL20_REQ_COMMAND_PARAM_OLD = "sl2command";
+
+ public static final String PARAM_SL20_REQ_ICP_RETURN_URL_PARAM = "slIPCReturnUrl";
+ public static final String PARAM_SL20_REQ_TRANSACTIONID = "slTransactionID";
+
+ public static final String HTTP_HEADER_SL20_CLIENT_TYPE = "SL2ClientType";
+ public static final String HTTP_HEADER_SL20_VDA_TYPE = "X-MOA-VDA";
+ public static final String HTTP_HEADER_VALUE_NATIVE = "nativeApp";
+
+
+ //*******************************************************************************************
+ //JSON signing and encryption headers
+ public static final String JSON_ALGORITHM = "alg";
+ public static final String JSON_CONTENTTYPE = "cty";
+ public static final String JSON_X509_CERTIFICATE = "x5c";
+ public static final String JSON_X509_FINGERPRINT = "x5t#S256";
+ public static final String JSON_ENCRYPTION_PAYLOAD = "enc";
+
+ public static final String JSON_ALGORITHM_SIGNING_RS256 = AlgorithmIdentifiers.RSA_USING_SHA256;
+ public static final String JSON_ALGORITHM_SIGNING_RS512 = AlgorithmIdentifiers.RSA_USING_SHA512;
+ public static final String JSON_ALGORITHM_SIGNING_ES256 = AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256;
+ public static final String JSON_ALGORITHM_SIGNING_ES512 = AlgorithmIdentifiers.ECDSA_USING_P521_CURVE_AND_SHA512;
+ public static final String JSON_ALGORITHM_SIGNING_PS256 = AlgorithmIdentifiers.RSA_PSS_USING_SHA256;
+ public static final String JSON_ALGORITHM_SIGNING_PS512 = AlgorithmIdentifiers.RSA_PSS_USING_SHA512;
+
+ public static final List<String> SL20_ALGORITHM_WHITELIST_SIGNING = Arrays.asList(
+ JSON_ALGORITHM_SIGNING_RS256,
+ JSON_ALGORITHM_SIGNING_RS512,
+ JSON_ALGORITHM_SIGNING_ES256,
+ JSON_ALGORITHM_SIGNING_ES512,
+ JSON_ALGORITHM_SIGNING_PS256,
+ JSON_ALGORITHM_SIGNING_PS512
+ );
+
+ public static final String JSON_ALGORITHM_ENC_KEY_RSAOAEP = KeyManagementAlgorithmIdentifiers.RSA_OAEP;
+ public static final String JSON_ALGORITHM_ENC_KEY_RSAOAEP256 = KeyManagementAlgorithmIdentifiers.RSA_OAEP_256;
+
+ public static final List<String> SL20_ALGORITHM_WHITELIST_KEYENCRYPTION = Arrays.asList(
+ JSON_ALGORITHM_ENC_KEY_RSAOAEP,
+ JSON_ALGORITHM_ENC_KEY_RSAOAEP256
+ );
+
+ public static final String JSON_ALGORITHM_ENC_PAYLOAD_A128CBCHS256 = ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256;
+ public static final String JSON_ALGORITHM_ENC_PAYLOAD_A256CBCHS512 = ContentEncryptionAlgorithmIdentifiers.AES_256_CBC_HMAC_SHA_512;
+ public static final String JSON_ALGORITHM_ENC_PAYLOAD_A128GCM = ContentEncryptionAlgorithmIdentifiers.AES_128_GCM;
+ public static final String JSON_ALGORITHM_ENC_PAYLOAD_A256GCM = ContentEncryptionAlgorithmIdentifiers.AES_256_GCM;
+
+ public static final List<String> SL20_ALGORITHM_WHITELIST_ENCRYPTION = Arrays.asList(
+ JSON_ALGORITHM_ENC_PAYLOAD_A128CBCHS256,
+ JSON_ALGORITHM_ENC_PAYLOAD_A256CBCHS512,
+ JSON_ALGORITHM_ENC_PAYLOAD_A128GCM,
+ JSON_ALGORITHM_ENC_PAYLOAD_A256GCM
+ );
+
+
+ //*********************************************************************************************
+ //Object identifier for generic transport container
+ public static final String SL20_CONTENTTYPE_SIGNED_COMMAND ="application/sl2.0;command";
+ public static final String SL20_CONTENTTYPE_ENCRYPTED_RESULT ="application/sl2.0;result";
+
+ public static final String SL20_VERSION = "v";
+ public static final String SL20_REQID = "reqID";
+ public static final String SL20_RESPID = "respID";
+ public static final String SL20_INRESPTO = "inResponseTo";
+ public static final String SL20_TRANSACTIONID = "transactionID";
+ public static final String SL20_PAYLOAD = "payload";
+ public static final String SL20_SIGNEDPAYLOAD = "signedPayload";
+
+ //Generic Object identifier for commands
+ public static final String SL20_COMMAND_CONTAINER_NAME = "name";
+ public static final String SL20_COMMAND_CONTAINER_PARAMS = "params";
+ public static final String SL20_COMMAND_CONTAINER_RESULT = "result";
+ public static final String SL20_COMMAND_CONTAINER_ENCRYPTEDRESULT = "encryptedResult";
+
+ //COMMAND Object identifier
+ public static final String SL20_COMMAND_IDENTIFIER_REDIRECT = "redirect";
+ public static final String SL20_COMMAND_IDENTIFIER_CALL = "call";
+ public static final String SL20_COMMAND_IDENTIFIER_ERROR = "error";
+ public static final String SL20_COMMAND_IDENTIFIER_QUALIFIEDEID = "qualifiedeID";
+ //public static final String SL20_COMMAND_IDENTIFIER_QUALIFIEDSIG = "qualifiedSig";
+
+ public static final String SL20_COMMAND_IDENTIFIER_GETCERTIFICATE = "getCertificate";
+ public static final String SL20_COMMAND_IDENTIFIER_CREATE_SIG_CADES = "createCAdES";
+
+
+ public static final String SL20_COMMAND_IDENTIFIER_BINDING_CREATE_KEY = "createBindingKey";
+ public static final String SL20_COMMAND_IDENTIFIER_BINDING_STORE_CERT = "storeBindingCert";
+
+ public static final String SL20_COMMAND_IDENTIFIER_AUTH_IDANDPASSWORD = "idAndPassword";
+ public static final String SL20_COMMAND_IDENTIFIER_AUTH_JWSTOKENFACTOR = "jwsTokenAuth";
+ public static final String SL20_COMMAND_IDENTIFIER_AUTH_QRCODEFACTOR = "qrCodeFactor";
+
+ //*****COMMAND parameter identifier******
+ //general Identifier
+ public static final String SL20_COMMAND_PARAM_GENERAL_REQPARAMETER_VALUE = "value";
+ public static final String SL20_COMMAND_PARAM_GENERAL_REQPARAMETER_KEY = "key";
+ public static final String SL20_COMMAND_PARAM_GENERAL_DATAURL = "dataUrl";
+ public static final String SL20_COMMAND_PARAM_GENERAL_RESPONSEENCRYPTIONCERTIFICATE = "x5cEnc";
+ public static final String SL20_COMMAND_PARAM_GENERAL_RESPONSEENCRYPTIONJWK = "jwkEnc";
+
+ //Redirect command
+ public static final String SL20_COMMAND_PARAM_GENERAL_REDIRECT_URL = "url";
+ public static final String SL20_COMMAND_PARAM_GENERAL_REDIRECT_COMMAND = "command";
+ public static final String SL20_COMMAND_PARAM_GENERAL_REDIRECT_SIGNEDCOMMAND = "signedCommand";
+ public static final String SL20_COMMAND_PARAM_GENERAL_REDIRECT_IPCREDIRECT = "IPCRedirect";
+
+ //Call command
+ public static final String SL20_COMMAND_PARAM_GENERAL_CALL_URL = SL20_COMMAND_PARAM_GENERAL_REDIRECT_URL;
+ public static final String SL20_COMMAND_PARAM_GENERAL_CALL_METHOD = "method";
+ public static final String SL20_COMMAND_PARAM_GENERAL_CALL_METHOD_GET = "get";
+ public static final String SL20_COMMAND_PARAM_GENERAL_CALL_METHOD_POST = "post";
+ public static final String SL20_COMMAND_PARAM_GENERAL_CALL_INCLUDETRANSACTIONID = "includeTransactionID";
+ public static final String SL20_COMMAND_PARAM_GENERAL_CALL_REQPARAMETER = "reqParams";
+
+ //error command
+ public static final String SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERRORCODE = "errorCode";
+ public static final String SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERRORMESSAGE = "errorMessage";
+
+ //qualified eID command
+ public static final String SL20_COMMAND_PARAM_EID_AUTHBLOCKID = "authBlockTemplateID";
+ public static final String SL20_COMMAND_PARAM_EID_DATAURL = SL20_COMMAND_PARAM_GENERAL_DATAURL;
+ public static final String SL20_COMMAND_PARAM_EID_ATTRIBUTES = "attributes";
+ public static final String SL20_COMMAND_PARAM_EID_ATTRIBUTES_MANDATEREFVALUE = "MANDATE-REFERENCE-VALUE";
+ public static final String SL20_COMMAND_PARAM_EID_ATTRIBUTES_SPUNIQUEID = "SP-UNIQUEID";
+ public static final String SL20_COMMAND_PARAM_EID_ATTRIBUTES_SPFRIENDLYNAME = "SP-FRIENDLYNAME";
+ public static final String SL20_COMMAND_PARAM_EID_ATTRIBUTES_SPCOUNTRYCODE = "SP-COUNTRYCODE";
+ public static final String SL20_COMMAND_PARAM_EID_X5CENC = SL20_COMMAND_PARAM_GENERAL_RESPONSEENCRYPTIONCERTIFICATE;
+ public static final String SL20_COMMAND_PARAM_EID_JWKCENC = SL20_COMMAND_PARAM_GENERAL_RESPONSEENCRYPTIONJWK;
+ public static final String SL20_COMMAND_PARAM_EID_RESULT_IDL = "EID-IDENTITY-LINK";
+ public static final String SL20_COMMAND_PARAM_EID_RESULT_AUTHBLOCK = "EID-AUTH-BLOCK";
+ public static final String SL20_COMMAND_PARAM_EID_RESULT_CCSURL = "EID-CCS-URL";
+ public static final String SL20_COMMAND_PARAM_EID_RESULT_LOA = "EID-CITIZEN-QAA-LEVEL";
+
+ //qualified Signature comamnd
+// public static final String SL20_COMMAND_PARAM_QUALSIG_DATAURL = SL20_COMMAND_PARAM_GENERAL_DATAURL;
+// public static final String SL20_COMMAND_PARAM_QUALSIG_X5CENC = SL20_COMMAND_PARAM_GENERAL_RESPONSEENCRYPTIONCERTIFICATE;
+
+
+ //getCertificate
+ public static final String SL20_COMMAND_PARAM_GETCERTIFICATE_KEYID = "keyId";
+ public static final String SL20_COMMAND_PARAM_GETCERTIFICATE_DATAURL = SL20_COMMAND_PARAM_GENERAL_DATAURL;
+ public static final String SL20_COMMAND_PARAM_GETCERTIFICATE_X5CENC = SL20_COMMAND_PARAM_GENERAL_RESPONSEENCRYPTIONCERTIFICATE;
+ public static final String SL20_COMMAND_PARAM_GETCERTIFICATE_JWKCENC = SL20_COMMAND_PARAM_GENERAL_RESPONSEENCRYPTIONJWK;
+ public static final String SL20_COMMAND_PARAM_GETCERTIFICATE_RESULT_CERTIFICATE = "x5c";
+
+ //createCAdES Signture
+ public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_KEYID = "keyId";
+ public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_CONTENT = "content";
+ public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_MIMETYPE = "mimeType";
+ public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_PADES_COMBATIBILTY = "padesComatibility";
+ public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_EXCLUDEBYTERANGE = "excludedByteRange";
+ public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_CADESLEVEL = "cadesLevel";
+ public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_DATAURL = SL20_COMMAND_PARAM_GENERAL_DATAURL;
+ public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_X5CENC = SL20_COMMAND_PARAM_GENERAL_RESPONSEENCRYPTIONCERTIFICATE;
+ public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_JWKCENC = SL20_COMMAND_PARAM_GENERAL_RESPONSEENCRYPTIONJWK;
+ public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_RESULT_SIGNATURE = "signature";
+
+ public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_CADESLEVEL_BASIC = "cAdES";
+ public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_CADESLEVEL_T = "cAdES-T";
+ public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_CADESLEVEL_C = "cAdES-C";
+ public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_CADESLEVEL_X = "cAdES-X";
+ public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_CADESLEVEL_XL = "cAdES-X-L";
+ public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_CADESLEVEL_A = "cAdES-A";
+
+
+
+ //create binding key command
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_KONTOID = "kontoID";
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_SN = "SN";
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_KEYLENGTH = "keyLength";
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_KEYALG = "keyAlg";
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_POLICIES = "policies";
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_DATAURL = SL20_COMMAND_PARAM_GENERAL_DATAURL;
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_X5CVDATRUST = "x5cVdaTrust";
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_REQUESTUSERPASSWORD = "reqUserPassword";
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_X5CENC = SL20_COMMAND_PARAM_GENERAL_RESPONSEENCRYPTIONCERTIFICATE;
+
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_KEYALG_RSA = "RSA";
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_KEYALG_SECPR256R1 = "secp256r1";
+
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_POLICIES_LIFETIME = "lifeTime";
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_POLICIES_USESECUREELEMENT = "useSecureElement";
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_POLICIES_KEYTIMEOUT = "keyTimeout";
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_POLICIES_NEEDUSERAUTH = "needUserAuth";
+
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_RESULT_APPID = "appID";
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_RESULT_CSR = "csr";
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_RESULT_KEYATTESTATIONZERTIFICATE = "attCert";
+ public static final String SL20_COMMAND_PARAM_BINDING_CREATE_RESULT_USERPASSWORD = "encodedPass";
+
+
+ //store binding certificate command
+ public static final String SL20_COMMAND_PARAM_BINDING_STORE_CERTIFICATE = "x5c";
+ public static final String SL20_COMMAND_PARAM_BINDING_STORE_DATAURL = SL20_COMMAND_PARAM_GENERAL_DATAURL;
+ public static final String SL20_COMMAND_PARAM_BINDING_STORE_RESULT_SUCESS = "success";
+ public static final String SL20_COMMAND_PARAM_BINDING_STORE_RESULT_SUCESS_VALUE = "OK";
+
+ // Username and password authentication
+ public static final String SL20_COMMAND_PARAM_AUTH_IDANDPASSWORD_KEYALG = "keyAlg";
+ public static final String SL20_COMMAND_PARAM_AUTH_IDANDPASSWORD_KEYALG_VALUE_PLAIN = "plain";
+ public static final String SL20_COMMAND_PARAM_AUTH_IDANDPASSWORD_KEYALG_VALUE_PBKDF2 = "PBKDF2";
+ public static final String SL20_COMMAND_PARAM_AUTH_IDANDPASSWORD_DATAURL = SL20_COMMAND_PARAM_GENERAL_DATAURL;
+ public static final String SL20_COMMAND_PARAM_AUTH_IDANDPASSWORD_X5CENC = SL20_COMMAND_PARAM_GENERAL_RESPONSEENCRYPTIONCERTIFICATE;
+ public static final String SL20_COMMAND_PARAM_AUTH_IDANDPASSWORD_RESULT_KONTOID = SL20_COMMAND_PARAM_BINDING_CREATE_KONTOID;
+ public static final String SL20_COMMAND_PARAM_AUTH_IDANDPASSWORD_RESULT_USERPASSWORD = SL20_COMMAND_PARAM_BINDING_CREATE_RESULT_USERPASSWORD;
+
+ //JWS Token authentication
+ public static final String SL20_COMMAND_PARAM_AUTH_JWSTOKEN_NONCE = "nonce";
+ public static final String SL20_COMMAND_PARAM_AUTH_JWSTOKEN_DISPLAYDATA = "displayData";
+ public static final String SL20_COMMAND_PARAM_AUTH_JWSTOKEN_DISPLAYURL = "displayUrl";
+ public static final String SL20_COMMAND_PARAM_AUTH_JWSTOKEN_DATAURL = SL20_COMMAND_PARAM_GENERAL_DATAURL;
+ public static final String SL20_COMMAND_PARAM_AUTH_JWSTOKEN_RESULT_NONCE = SL20_COMMAND_PARAM_AUTH_JWSTOKEN_NONCE;
+
+ //QR-Code authentication
+ public static final String SL20_COMMAND_PARAM_AUTH_QRCODE_QRCODE = "qrCode";
+ public static final String SL20_COMMAND_PARAM_AUTH_QRCODE_DATAURL = SL20_COMMAND_PARAM_GENERAL_DATAURL;
+
+}
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/SL20JSONBuilderUtils.java b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/SL20JSONBuilderUtils.java
new file mode 100644
index 00000000..40edb74b
--- /dev/null
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/SL20JSONBuilderUtils.java
@@ -0,0 +1,604 @@
+package at.gv.egiz.sl20.utils;
+
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.commons.codec.binary.Base64;
+import org.bouncycastle.util.encoders.Base64Encoder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import at.gv.egiz.sl20.exceptions.SLCommandoBuildException;
+
+public class SL20JSONBuilderUtils {
+ private static final Logger log = LoggerFactory.getLogger(SL20JSONBuilderUtils.class);
+
+ /**
+ * Create command request
+ * @param name
+ * @param params
+ * @throws SLCommandoBuildException
+ * @return
+ */
+ public static JsonObject createCommand(String name, JsonElement params) throws SLCommandoBuildException {
+ JsonObject command = new JsonObject();
+ addSingleStringElement(command, SL20Constants.SL20_COMMAND_CONTAINER_NAME, name, true);
+ addSingleJSONElement(command, SL20Constants.SL20_COMMAND_CONTAINER_PARAMS, params, true);
+ return command;
+
+ }
+
+ /**
+ * Create signed command request
+ *
+ * @param name
+ * @param params
+ * @param signer
+ * @return
+ * @throws SLCommandoBuildException
+ */
+ public static String createSignedCommand(String name, JsonElement params, IJOSETools signer) throws SLCommandoBuildException {
+ JsonObject command = new JsonObject();
+ addSingleStringElement(command, SL20Constants.SL20_COMMAND_CONTAINER_NAME, name, true);
+ addSingleJSONElement(command, SL20Constants.SL20_COMMAND_CONTAINER_PARAMS, params, true);
+ return signer.createSignature(command.toString());
+
+ }
+
+ /**
+ * Create command result
+ *
+ * @param name
+ * @param result
+ * @param encryptedResult
+ * @throws SLCommandoBuildException
+ * @return
+ */
+ public static JsonObject createCommandResponse(String name, JsonElement result, String encryptedResult) throws SLCommandoBuildException {
+ JsonObject command = new JsonObject();
+ addSingleStringElement(command, SL20Constants.SL20_COMMAND_CONTAINER_NAME, name, true);
+ addOnlyOnceOfTwo(command,
+ SL20Constants.SL20_COMMAND_CONTAINER_RESULT, SL20Constants.SL20_COMMAND_CONTAINER_ENCRYPTEDRESULT,
+ result, encryptedResult);
+ return command;
+
+ }
+
+ /**
+ * Create command result
+ *
+ * @param name
+ * @param result
+ * @param encryptedResult
+ * @throws SLCommandoBuildException
+ * @return
+ */
+ public static String createSignedCommandResponse(String name, JsonElement result, String encryptedResult, IJOSETools signer) throws SLCommandoBuildException {
+ JsonObject command = new JsonObject();
+ addSingleStringElement(command, SL20Constants.SL20_COMMAND_CONTAINER_NAME, name, true);
+ addOnlyOnceOfTwo(command,
+ SL20Constants.SL20_COMMAND_CONTAINER_RESULT, SL20Constants.SL20_COMMAND_CONTAINER_ENCRYPTEDRESULT,
+ result, encryptedResult);
+ return signer.createSignature(command.toString());
+
+ }
+
+ /**
+ * Create parameters for Redirect command
+ *
+ * @param url
+ * @param command
+ * @param signedCommand
+ * @param ipcRedirect
+ * @return
+ * @throws SLCommandoBuildException
+ */
+ public static JsonObject createRedirectCommandParameters(String url, JsonElement command, JsonElement signedCommand, Boolean ipcRedirect) throws SLCommandoBuildException{
+ JsonObject redirectReqParams = new JsonObject();
+ addOnlyOnceOfTwo(redirectReqParams,
+ SL20Constants.SL20_COMMAND_PARAM_GENERAL_REDIRECT_COMMAND, SL20Constants.SL20_COMMAND_PARAM_GENERAL_REDIRECT_SIGNEDCOMMAND,
+ command, signedCommand);
+ addSingleStringElement(redirectReqParams, SL20Constants.SL20_COMMAND_PARAM_GENERAL_REDIRECT_URL, url, false);
+ addSingleBooleanElement(redirectReqParams, SL20Constants.SL20_COMMAND_PARAM_GENERAL_REDIRECT_IPCREDIRECT, ipcRedirect, false);
+ return redirectReqParams;
+
+ }
+
+ /**
+ * Create parameters for Call command
+ *
+ * @param url
+ * @param method
+ * @param includeTransactionId
+ * @param reqParameters
+ * @return
+ * @throws SLCommandoBuildException
+ */
+ public static JsonObject createCallCommandParameters(String url, String method, Boolean includeTransactionId, Map<String, String> reqParameters) throws SLCommandoBuildException {
+ JsonObject callReqParams = new JsonObject();
+ addSingleStringElement(callReqParams, SL20Constants.SL20_COMMAND_PARAM_GENERAL_CALL_URL, url, true);
+ addSingleStringElement(callReqParams, SL20Constants.SL20_COMMAND_PARAM_GENERAL_CALL_METHOD, method, true);
+ addSingleBooleanElement(callReqParams, SL20Constants.SL20_COMMAND_PARAM_GENERAL_CALL_INCLUDETRANSACTIONID, includeTransactionId, false);
+ addArrayOfStringElements(callReqParams, SL20Constants.SL20_COMMAND_PARAM_GENERAL_CALL_REQPARAMETER, reqParameters);
+ return callReqParams;
+
+ }
+
+ /**
+ * Create result for Error command
+ *
+ * @param errorCode
+ * @param errorMsg
+ * @return
+ * @throws SLCommandoBuildException
+ */
+ public static JsonObject createErrorCommandResult(String errorCode, String errorMsg) throws SLCommandoBuildException {
+ JsonObject result = new JsonObject();
+ addSingleStringElement(result, SL20Constants.SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERRORCODE, errorCode, true);
+ addSingleStringElement(result, SL20Constants.SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERRORMESSAGE, errorMsg, true);
+ return result;
+
+ }
+
+
+ /**
+ * Create parameters for qualifiedeID command
+ *
+ * @param authBlockId
+ * @param dataUrl
+ * @param additionalReqParameters
+ * @param x5cEnc
+ * @return
+ * @throws CertificateEncodingException
+ * @throws SLCommandoBuildException
+ */
+ public static JsonObject createQualifiedeIDCommandParameters(String authBlockId, String dataUrl,
+ Map<String, String> additionalReqParameters, X509Certificate x5cEnc) throws CertificateEncodingException, SLCommandoBuildException {
+ JsonObject params = new JsonObject();
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_EID_AUTHBLOCKID, authBlockId, true);
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_EID_DATAURL, dataUrl, true);
+ addArrayOfStringElements(params, SL20Constants.SL20_COMMAND_PARAM_EID_ATTRIBUTES, additionalReqParameters);
+ addSingleCertificateElement(params, SL20Constants.SL20_COMMAND_PARAM_EID_X5CENC, x5cEnc, false);
+ return params;
+
+ }
+
+ public static JsonObject createGetCertificateCommandParameters(String keyId, String dataUrl,
+ X509Certificate x5cEnc) throws CertificateEncodingException, SLCommandoBuildException {
+ JsonObject params = new JsonObject();
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_GETCERTIFICATE_KEYID, keyId, true);
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_GETCERTIFICATE_DATAURL, dataUrl, true);
+ addSingleCertificateElement(params, SL20Constants.SL20_COMMAND_PARAM_GETCERTIFICATE_X5CENC, x5cEnc, false);
+ return params;
+
+ }
+
+ public static JsonObject createCreateCAdESCommandParameters(String keyId,
+ byte[] content, String mimeType, boolean padesCompatiblem, List<String> byteRanges, String cadesLevel,
+ String dataUrl, X509Certificate x5cEnc) throws CertificateEncodingException, SLCommandoBuildException {
+ JsonObject params = new JsonObject();
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_KEYID, keyId, true);
+ addSingleByteElement(params, SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_CONTENT, content, true);
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_MIMETYPE, mimeType, true);
+ addSingleBooleanElement(params, SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_PADES_COMBATIBILTY, padesCompatiblem, false);
+ addArrayOfStrings(params, SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_EXCLUDEBYTERANGE, byteRanges);
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_CADESLEVEL, cadesLevel, false);
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_GETCERTIFICATE_DATAURL, dataUrl, true);
+ addSingleCertificateElement(params, SL20Constants.SL20_COMMAND_PARAM_GETCERTIFICATE_X5CENC, x5cEnc, false);
+ return params;
+
+ }
+
+ /**
+ * Create result for qualifiedeID command
+ *
+ * @param idl
+ * @param authBlock
+ * @param ccsURL
+ * @param LoA
+ * @return
+ * @throws SLCommandoBuildException
+ */
+ public static JsonObject createQualifiedeIDCommandResult(byte[] idl, byte[] authBlock, String ccsURL, String LoA) throws SLCommandoBuildException {
+ JsonObject result = new JsonObject();
+ addSingleByteElement(result, SL20Constants.SL20_COMMAND_PARAM_EID_RESULT_IDL, idl, true);
+ addSingleByteElement(result, SL20Constants.SL20_COMMAND_PARAM_EID_RESULT_AUTHBLOCK, authBlock, true);
+ addSingleStringElement(result, SL20Constants.SL20_COMMAND_PARAM_EID_RESULT_CCSURL, ccsURL, true);
+ addSingleStringElement(result, SL20Constants.SL20_COMMAND_PARAM_EID_RESULT_LOA, LoA, true);
+ return result;
+
+ }
+
+
+ /**
+ * Create Binding-Key command parameters
+ *
+ * @param kontoId
+ * @param subjectName
+ * @param keySize
+ * @param keyAlg
+ * @param policies
+ * @param dataUrl
+ * @param x5cVdaTrust
+ * @param reqUserPassword
+ * @param x5cEnc
+ * @return
+ * @throws SLCommandoBuildException
+ * @throws CertificateEncodingException
+ */
+ public static JsonObject createBindingKeyCommandParams(String kontoId, String subjectName, int keySize, String keyAlg,
+ Map<String, String> policies, String dataUrl, X509Certificate x5cVdaTrust, Boolean reqUserPassword, X509Certificate x5cEnc) throws SLCommandoBuildException, CertificateEncodingException {
+ JsonObject params = new JsonObject();
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_BINDING_CREATE_KONTOID, kontoId, true);
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_BINDING_CREATE_SN, subjectName, true);
+ addSingleNumberElement(params, SL20Constants.SL20_COMMAND_PARAM_BINDING_CREATE_KEYLENGTH, keySize, true);
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_BINDING_CREATE_KEYALG, keyAlg, true);
+ addArrayOfStringElements(params, SL20Constants.SL20_COMMAND_PARAM_BINDING_CREATE_POLICIES, policies);
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_BINDING_CREATE_DATAURL, dataUrl, true);
+ addSingleCertificateElement(params, SL20Constants.SL20_COMMAND_PARAM_BINDING_CREATE_X5CVDATRUST, x5cVdaTrust, false);
+ addSingleBooleanElement(params, SL20Constants.SL20_COMMAND_PARAM_BINDING_CREATE_REQUESTUSERPASSWORD, reqUserPassword, false);
+ addSingleCertificateElement(params, SL20Constants.SL20_COMMAND_PARAM_BINDING_CREATE_X5CENC, x5cEnc, false);
+ return params;
+
+ }
+
+ /**
+ * Create Binding-Key command result
+ *
+ * @param appId
+ * @param csr
+ * @param attCert
+ * @param password
+ * @return
+ * @throws SLCommandoBuildException
+ * @throws CertificateEncodingException
+ */
+ public static JsonObject createBindingKeyCommandResult(String appId, byte[] csr, X509Certificate attCert, byte[] password) throws SLCommandoBuildException, CertificateEncodingException {
+ JsonObject result = new JsonObject();
+ addSingleStringElement(result, SL20Constants.SL20_COMMAND_PARAM_BINDING_CREATE_RESULT_APPID, appId, true);
+ addSingleByteElement(result, SL20Constants.SL20_COMMAND_PARAM_BINDING_CREATE_RESULT_CSR, csr, true);
+ addSingleCertificateElement(result, SL20Constants.SL20_COMMAND_PARAM_BINDING_CREATE_RESULT_KEYATTESTATIONZERTIFICATE, attCert, false);
+ addSingleByteElement(result, SL20Constants.SL20_COMMAND_PARAM_BINDING_CREATE_RESULT_USERPASSWORD, password, false);
+ return result;
+
+ }
+
+ /**
+ * Create Store Binding-Certificate command parameters
+ *
+ * @param cert
+ * @param dataUrl
+ * @return
+ * @throws CertificateEncodingException
+ * @throws SLCommandoBuildException
+ */
+ public static JsonObject createStoreBindingCertCommandParams(X509Certificate cert, String dataUrl) throws CertificateEncodingException, SLCommandoBuildException {
+ JsonObject params = new JsonObject();
+ addSingleCertificateElement(params, SL20Constants.SL20_COMMAND_PARAM_BINDING_STORE_CERTIFICATE, cert, true);
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_BINDING_STORE_DATAURL, dataUrl, true);
+ return params;
+
+ }
+
+ /**
+ * Create Store Binding-Certificate command result
+ *
+ * @return
+ * @throws SLCommandoBuildException
+ */
+ public static JsonObject createStoreBindingCertCommandSuccessResult() throws SLCommandoBuildException {
+ JsonObject result = new JsonObject();
+ addSingleStringElement(result, SL20Constants.SL20_COMMAND_PARAM_BINDING_STORE_RESULT_SUCESS,
+ SL20Constants.SL20_COMMAND_PARAM_BINDING_STORE_RESULT_SUCESS_VALUE, true);
+ return result;
+
+ }
+
+
+ /**
+ * Create idAndPassword command parameters
+ *
+ * @param keyAlg
+ * @param dataUrl
+ * @param x5cEnc
+ * @return
+ * @throws SLCommandoBuildException
+ * @throws CertificateEncodingException
+ */
+ public static JsonObject createIdAndPasswordCommandParameters(String keyAlg, String dataUrl, X509Certificate x5cEnc) throws SLCommandoBuildException, CertificateEncodingException {
+ JsonObject params = new JsonObject();
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_AUTH_IDANDPASSWORD_KEYALG, keyAlg, true);
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_AUTH_IDANDPASSWORD_DATAURL, dataUrl, true);
+ addSingleCertificateElement(params, SL20Constants.SL20_COMMAND_PARAM_AUTH_IDANDPASSWORD_X5CENC, x5cEnc, false);
+ return params;
+
+ }
+
+ /**
+ * Create idAndPassword command result
+ *
+ * @param kontoId
+ * @param password
+ * @return
+ * @throws SLCommandoBuildException
+ */
+ public static JsonObject createIdAndPasswordCommandResult(String kontoId, byte[] password) throws SLCommandoBuildException {
+ JsonObject result = new JsonObject();
+ addSingleStringElement(result, SL20Constants.SL20_COMMAND_PARAM_AUTH_IDANDPASSWORD_RESULT_KONTOID, kontoId, true);
+ addSingleByteElement(result, SL20Constants.SL20_COMMAND_PARAM_AUTH_IDANDPASSWORD_RESULT_USERPASSWORD, password, true);
+ return result;
+
+ }
+
+ /**
+ * Create JWS Token Authentication command
+ *
+ * @param nonce
+ * @param dataUrl
+ * @param displayData
+ * @param displayUrl
+ * @return
+ * @throws SLCommandoBuildException
+ */
+ public static JsonObject createJwsTokenAuthCommandParams(String nonce, String dataUrl, List<String> displayData, List<String> displayUrl) throws SLCommandoBuildException {
+ JsonObject params = new JsonObject();
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_AUTH_JWSTOKEN_NONCE, nonce, true);
+ addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_AUTH_JWSTOKEN_DATAURL, dataUrl, true);
+ addArrayOfStrings(params, SL20Constants.SL20_COMMAND_PARAM_AUTH_JWSTOKEN_DISPLAYDATA, displayData);
+ addArrayOfStrings(params, SL20Constants.SL20_COMMAND_PARAM_AUTH_JWSTOKEN_DISPLAYURL, displayUrl);
+ return params;
+
+ }
+
+ /**
+ * Create JWS Token Authentication command result
+ *
+ * @param nonce
+ * @return
+ * @throws SLCommandoBuildException
+ */
+ public static JsonObject createJwsTokenAuthCommandResult(String nonce) throws SLCommandoBuildException {
+ JsonObject result = new JsonObject();
+ addSingleStringElement(result, SL20Constants.SL20_COMMAND_PARAM_AUTH_JWSTOKEN_RESULT_NONCE, nonce, true);
+ return result;
+
+ }
+
+
+ /**
+ * Create Generic Request Container
+ *
+ * @param reqId
+ * @param transactionId
+ * @param payLoad
+ * @param signedPayload
+ * @return
+ * @throws SLCommandoBuildException
+ */
+ public static JsonObject createGenericRequest(String reqId, String transactionId, JsonElement payLoad, String signedPayload) throws SLCommandoBuildException {
+ JsonObject req = new JsonObject();
+ addSingleIntegerElement(req, SL20Constants.SL20_VERSION, SL20Constants.CURRENT_SL20_VERSION, true);
+ addSingleStringElement(req, SL20Constants.SL20_REQID, reqId, true);
+ addSingleStringElement(req, SL20Constants.SL20_TRANSACTIONID, transactionId, false);
+ addOnlyOnceOfTwo(req, SL20Constants.SL20_PAYLOAD, SL20Constants.SL20_SIGNEDPAYLOAD,
+ payLoad, signedPayload);
+ return req;
+
+ }
+
+ /**
+ * Create Generic Response Container
+ *
+ * @param respId
+ * @param inResponseTo
+ * @param transactionId
+ * @param payLoad
+ * @param signedPayload
+ * @return
+ * @throws SLCommandoBuildException
+ */
+ public static final JsonObject createGenericResponse(String respId, String inResponseTo, String transactionId,
+ JsonElement payLoad, String signedPayload) throws SLCommandoBuildException {
+
+ JsonObject req = new JsonObject();
+ addSingleIntegerElement(req, SL20Constants.SL20_VERSION, SL20Constants.CURRENT_SL20_VERSION, true);
+ addSingleStringElement(req, SL20Constants.SL20_RESPID, respId, true);
+ addSingleStringElement(req, SL20Constants.SL20_INRESPTO, inResponseTo, true);
+ addSingleStringElement(req, SL20Constants.SL20_TRANSACTIONID, transactionId, false);
+ addOnlyOnceOfTwo(req, SL20Constants.SL20_PAYLOAD, SL20Constants.SL20_SIGNEDPAYLOAD,
+ payLoad, signedPayload);
+ return req;
+
+ }
+
+ /**
+ * Add one element of two possible elements <br>
+ * This method adds either the first element or the second element to parent JSON, but never both.
+ *
+ * @param parent Parent JSON element
+ * @param firstKeyId first element Id
+ * @param secondKeyId second element Id
+ * @param first first element
+ * @param second second element
+ * @throws SLCommandoBuildException
+ */
+ public static void addOnlyOnceOfTwo(JsonObject parent, String firstKeyId, String secondKeyId, JsonElement first, String second) throws SLCommandoBuildException {
+ if (first == null && (second == null || second.isEmpty())) {
+ log.warn(firstKeyId + " and " + secondKeyId + " is NULL");
+ throw new SLCommandoBuildException();
+
+ } else if (first != null && second != null) {
+ log.warn(firstKeyId + " and " + secondKeyId + " can not SET TWICE");
+ throw new SLCommandoBuildException();
+
+ } else if (first != null)
+ parent.add(firstKeyId, first);
+
+ else if (second != null && !second.isEmpty())
+ parent.addProperty(secondKeyId, second);
+
+ else {
+ log.warn("Internal build error");
+ throw new SLCommandoBuildException();
+
+ }
+ }
+
+ private static void addArrayOfStrings(JsonObject parent, String keyId, List<String> values) throws SLCommandoBuildException {
+ validateParentAndKey(parent, keyId);
+ if (values != null) {
+ JsonArray callReqParamsArray = new JsonArray();
+ parent.add(keyId, callReqParamsArray );
+ for(String el : values)
+ callReqParamsArray.add(el);
+
+ }
+ }
+
+
+ private static void addArrayOfStringElements(JsonObject parent, String keyId, Map<String, String> keyValuePairs) throws SLCommandoBuildException {
+ validateParentAndKey(parent, keyId);
+ if (keyValuePairs != null) {
+ JsonArray callReqParamsArray = new JsonArray();
+ parent.add(keyId, callReqParamsArray );
+
+ for(Entry<String, String> el : keyValuePairs.entrySet()) {
+ JsonObject callReqParams = new JsonObject();
+ //callReqParams.addProperty(SL20Constants.SL20_COMMAND_PARAM_GENERAL_REQPARAMETER_KEY, el.getKey());
+ //callReqParams.addProperty(SL20Constants.SL20_COMMAND_PARAM_GENERAL_REQPARAMETER_VALUE, el.getValue());
+ callReqParams.addProperty(el.getKey(), el.getValue());
+ callReqParamsArray.add(callReqParams);
+
+ }
+ }
+ }
+
+ private static void addSingleCertificateElement(JsonObject parent, String keyId, X509Certificate cert, boolean isRequired) throws CertificateEncodingException, SLCommandoBuildException {
+ if (cert != null)
+ addSingleByteElement(parent, keyId, cert.getEncoded(), isRequired);
+
+ else if (isRequired) {
+ log.warn(keyId + " is marked as REQUIRED");
+ throw new SLCommandoBuildException();
+
+ }
+
+ }
+
+
+
+ private static void addSingleByteElement(JsonObject parent, String keyId, byte[] value, boolean isRequired) throws SLCommandoBuildException {
+ validateParentAndKey(parent, keyId);
+
+ if (isRequired && value == null) {
+ log.warn(keyId + " has NULL value");
+ throw new SLCommandoBuildException();
+
+ } else if (value != null)
+ parent.addProperty(keyId, org.bouncycastle.util.encoders.Base64.toBase64String(value));
+
+ }
+
+ private static void addSingleBooleanElement(JsonObject parent, String keyId, Boolean value, boolean isRequired) throws SLCommandoBuildException {
+ validateParentAndKey(parent, keyId);
+
+ if (isRequired && value == null) {
+ log.warn(keyId + " has a NULL value");
+ throw new SLCommandoBuildException();
+
+ } else if (value != null)
+ parent.addProperty(keyId, value);
+
+ }
+
+ private static void addSingleNumberElement(JsonObject parent, String keyId, Integer value, boolean isRequired) throws SLCommandoBuildException {
+ validateParentAndKey(parent, keyId);
+
+ if (isRequired && value == null) {
+ log.warn(keyId + " has a NULL value");
+ throw new SLCommandoBuildException();
+
+ } else if (value != null)
+ parent.addProperty(keyId, value);;
+
+ }
+
+ private static void addSingleStringElement(JsonObject parent, String keyId, String value, boolean isRequired) throws SLCommandoBuildException {
+ validateParentAndKey(parent, keyId);
+
+ if (isRequired && (value == null || value.isEmpty())) {
+ log.warn(keyId + " has an empty value");
+ throw new SLCommandoBuildException();
+
+ } else if (value != null && !value.isEmpty())
+ parent.addProperty(keyId, value);
+
+ }
+
+ private static void addSingleIntegerElement(JsonObject parent, String keyId, Integer value, boolean isRequired) throws SLCommandoBuildException {
+ validateParentAndKey(parent, keyId);
+
+ if (isRequired && value == null) {
+ log.warn(keyId + " has an empty value");
+ throw new SLCommandoBuildException();
+
+ } else if (value != null)
+ parent.addProperty(keyId, value);
+
+ }
+
+ private static void addSingleJSONElement(JsonObject parent, String keyId, JsonElement element, boolean isRequired) throws SLCommandoBuildException {
+ validateParentAndKey(parent, keyId);
+
+ if (isRequired && element == null) {
+ log.warn("No commando name included");
+ throw new SLCommandoBuildException();
+
+ } else if (element != null)
+ parent.add(keyId, element);
+
+ }
+
+ private static void addOnlyOnceOfTwo(JsonObject parent, String firstKeyId, String secondKeyId, JsonElement first, JsonElement second) throws SLCommandoBuildException {
+ if (first == null && second == null) {
+ log.warn(firstKeyId + " and " + secondKeyId + " is NULL");
+ throw new SLCommandoBuildException();
+
+ } else if (first != null && second != null) {
+ log.warn(firstKeyId + " and " + secondKeyId + " can not SET TWICE");
+ throw new SLCommandoBuildException();
+
+ } else if (first != null)
+ parent.add(firstKeyId, first);
+
+ else if (second != null)
+ parent.add(secondKeyId, second);
+
+ else {
+ log.warn("Internal build error");
+ throw new SLCommandoBuildException();
+
+ }
+ }
+
+ private static void validateParentAndKey(JsonObject parent, String keyId) throws SLCommandoBuildException {
+ if (parent == null) {
+ log.warn("NO parent JSON element");
+ throw new SLCommandoBuildException();
+
+ }
+ if (keyId == null || keyId.isEmpty()) {
+ log.warn("NO JSON element identifier");
+ throw new SLCommandoBuildException();
+
+ }
+ }
+}
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/SL20JSONExtractorUtils.java b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/SL20JSONExtractorUtils.java
new file mode 100644
index 00000000..5a438e16
--- /dev/null
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/SL20JSONExtractorUtils.java
@@ -0,0 +1,422 @@
+package at.gv.egiz.sl20.utils;
+
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.utils.URIBuilder;
+import org.jose4j.base64url.Base64Url;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+import at.gv.egiz.sl20.data.VerificationResult;
+import at.gv.egiz.sl20.exceptions.SL20Exception;
+import at.gv.egiz.sl20.exceptions.SLCommandoParserException;
+
+public class SL20JSONExtractorUtils {
+ private static final Logger log = LoggerFactory.getLogger(SL20JSONExtractorUtils.class);
+
+ /**
+ * Extract String value from JSON
+ *
+ * @param input
+ * @param keyID
+ * @param isRequired
+ * @return
+ * @throws SLCommandoParserException
+ */
+ public static String getStringValue(JsonObject input, String keyID, boolean isRequired) throws SLCommandoParserException {
+ try {
+ JsonElement internal = getAndCheck(input, keyID, isRequired);
+
+ if (internal != null)
+ return internal.getAsString();
+ else
+ return null;
+
+ } catch (SLCommandoParserException e) {
+ throw e;
+
+ } catch (Exception e) {
+ log.warn("Can not extract String value with keyId: " + keyID);
+ throw new SLCommandoParserException(e);
+
+ }
+ }
+
+ /**
+ * Extract Boolean value from JSON
+ *
+ * @param input
+ * @param keyID
+ * @param isRequired
+ * @return
+ * @throws SLCommandoParserException
+ */
+ public static boolean getBooleanValue(JsonObject input, String keyID, boolean isRequired, boolean defaultValue) throws SLCommandoParserException {
+ try {
+ JsonElement internal = getAndCheck(input, keyID, isRequired);
+
+ if (internal != null)
+ return internal.getAsBoolean();
+ else
+ return defaultValue;
+
+ } catch (SLCommandoParserException e) {
+ throw e;
+
+ } catch (Exception e) {
+ log.warn("Can not extract Boolean value with keyId: " + keyID);
+ throw new SLCommandoParserException(e);
+
+ }
+ }
+
+ /**
+ * Extract JSONObject value from JSON
+ *
+ * @param input
+ * @param keyID
+ * @param isRequired
+ * @return
+ * @throws SLCommandoParserException
+ */
+ public static JsonObject getJSONObjectValue(JsonObject input, String keyID, boolean isRequired) throws SLCommandoParserException {
+ try {
+ JsonElement internal = getAndCheck(input, keyID, isRequired);
+
+ if (internal != null)
+ return internal.getAsJsonObject();
+ else
+ return null;
+
+ } catch (SLCommandoParserException e) {
+ throw e;
+
+ } catch (Exception e) {
+ log.warn("Can not extract Boolean value with keyId: \" + keyID");
+ throw new SLCommandoParserException(e);
+
+ }
+ }
+
+ /**
+ * Extract Map of Key/Value pairs from a JSON Element
+ *
+ * @param input parent JSON object
+ * @param keyID KeyId of the child that should be parsed
+ * @param isRequired
+ * @return
+ * @throws SLCommandoParserException
+ */
+ public static Map<String, String> getMapOfStringElements(JsonObject input, String keyID, boolean isRequired) throws SLCommandoParserException {
+ JsonElement internal = getAndCheck(input, keyID, isRequired);
+ return getMapOfStringElements(internal);
+
+ }
+
+ /**
+ * Extract a List of String elements from a JSON element
+ *
+ * @param input
+ * @param isRequired
+ * @param keyID
+ * @return
+ * @throws SLCommandoParserException
+ */
+ public static List<String> getListOfStringElements(JsonObject input, String keyID, boolean isRequired) throws SLCommandoParserException {
+ JsonElement internal = getAndCheck(input, keyID, isRequired);
+
+ List<String> result = new ArrayList<String>();
+ if (internal != null) {
+ if (internal.isJsonArray()) {
+ Iterator<JsonElement> arrayIterator = internal.getAsJsonArray().iterator();
+ while(arrayIterator.hasNext()) {
+ JsonElement next = arrayIterator.next();
+ if (next.isJsonPrimitive())
+ result.add(next.getAsString());
+ }
+
+ } else if (internal.isJsonPrimitive()) {
+ result.add(internal.getAsString());
+
+ } else {
+ log.warn("JSON Element IS NOT a JSON array or a JSON Primitive");
+ throw new SLCommandoParserException();
+ }
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Extract Map of Key/Value pairs from a JSON Element
+ *
+ * @param input
+ * @return
+ * @throws SLCommandoParserException
+ */
+ public static Map<String, String> getMapOfStringElements(JsonElement input) throws SLCommandoParserException {
+ Map<String, String> result = new HashMap<String, String>();
+
+ if (input != null) {
+ if (input.isJsonArray()) {
+ Iterator<JsonElement> arrayIterator = input.getAsJsonArray().iterator();
+ while(arrayIterator.hasNext()) {
+ JsonElement next = arrayIterator.next();
+ Iterator<Entry<String, JsonElement>> entry = next.getAsJsonObject().entrySet().iterator();
+ entitySetToMap(result, entry);
+
+ }
+
+ } else if (input.isJsonObject()) {
+ Iterator<Entry<String, JsonElement>> objectKeys = input.getAsJsonObject().entrySet().iterator();
+ entitySetToMap(result, objectKeys);
+
+ } else {
+ log.warn("JSON Element IS NOT a JSON array or a JSON object");
+ throw new SLCommandoParserException();
+ }
+
+ }
+
+ return result;
+ }
+
+ private static void entitySetToMap(Map<String, String> result, Iterator<Entry<String, JsonElement>> entry) {
+ while (entry.hasNext()) {
+ Entry<String, JsonElement> el = entry.next();
+ if (result.containsKey(el.getKey()))
+ log.info("Attr. Map already contains Element with Key: " + el.getKey() + ". Overwrite element ... ");
+
+ result.put(el.getKey(), el.getValue().getAsString());
+
+ }
+
+ }
+
+
+ public static JsonElement extractSL20Result(JsonObject command, IJOSETools decrypter, boolean mustBeEncrypted) throws SL20Exception {
+ JsonElement result = command.get(SL20Constants.SL20_COMMAND_CONTAINER_RESULT);
+ JsonElement encryptedResult = command.get(SL20Constants.SL20_COMMAND_CONTAINER_ENCRYPTEDRESULT);
+
+ if (result == null && encryptedResult == null) {
+ log.warn("NO result OR encryptedResult FOUND.");
+ throw new SLCommandoParserException();
+
+ } else if (encryptedResult == null && mustBeEncrypted) {
+ log.warn("result MUST be signed.");
+ throw new SLCommandoParserException();
+
+ } else if (encryptedResult != null && encryptedResult.isJsonPrimitive()) {
+ try {
+ return decrypter.decryptPayload(encryptedResult.getAsString());
+
+ } catch (Exception e) {
+ log.info("Can NOT decrypt SL20 result. Reason:" + e.getMessage());
+ if (!mustBeEncrypted) {
+ log.warn("Decrypted results are disabled by configuration. Parse result in plain if it is possible");
+
+ //dummy code
+ try {
+ String[] signedPayload = encryptedResult.toString().split("\\.");
+ JsonElement payLoad = new JsonParser().parse(new String(Base64Url.decodeToUtf8String(signedPayload[1])));
+ return payLoad;
+
+ } catch (Exception e1) {
+ log.debug("DummyCode FAILED, Reason: " + e1.getMessage() + " Ignore it ...");
+ throw new SL20Exception(e.getMessage(), e);
+
+ }
+
+ } else
+ throw e;
+
+ }
+
+ } else if (result != null) {
+ return result;
+
+ } else {
+ log.error("Internal build error");
+ throw new SLCommandoParserException();
+
+ }
+
+
+ }
+
+ /**
+ * Extract payLoad from generic transport container
+ *
+ * @param container
+ * @param joseTools
+ * @return
+ * @throws SLCommandoParserException
+ */
+ public static VerificationResult extractSL20PayLoad(JsonObject container, IJOSETools joseTools, boolean mustBeSigned) throws SL20Exception {
+
+ JsonElement sl20Payload = container.get(SL20Constants.SL20_PAYLOAD);
+ JsonElement sl20SignedPayload = container.get(SL20Constants.SL20_SIGNEDPAYLOAD);
+
+ if (mustBeSigned && joseTools == null) {
+ log.warn("'joseTools' MUST be set if 'mustBeSigned' is 'true'");
+ throw new SLCommandoParserException();
+
+ }
+
+ if (sl20Payload == null && sl20SignedPayload == null) {
+ log.warn("NO payLoad OR signedPayload FOUND.");
+ throw new SLCommandoParserException();
+
+ } else if (sl20SignedPayload == null && mustBeSigned) {
+ log.warn("payLoad MUST be signed.");
+ throw new SLCommandoParserException();
+
+ } else if (joseTools != null && sl20SignedPayload != null && sl20SignedPayload.isJsonPrimitive()) {
+ try {
+ return joseTools.validateSignature(sl20SignedPayload.getAsString());
+
+ } catch (SL20Exception e) {
+ if (!mustBeSigned && sl20Payload == null) {
+ log.debug("Signature verification FAILED with reason: " + e.getMessage()
+ + " but response MUST NOT be signed by configuration"
+ + " Starting backup process ... ");
+ String[] split = sl20SignedPayload.getAsString().split("\\.");
+ if (split.length == 3) {
+ JsonElement payLoad = new JsonParser().parse(new String(Base64Url.decodeToUtf8String(split[1])));
+ log.info("Signature verification FAILED with reason: " + e.getMessage() + " Use plain result as it is");
+ return new VerificationResult(payLoad.getAsJsonObject());
+
+ }
+ }
+
+ throw e;
+
+ }
+
+ } else if (sl20Payload != null)
+ return new VerificationResult(sl20Payload.getAsJsonObject());
+
+ else if (joseTools == null && !mustBeSigned && sl20SignedPayload != null && sl20SignedPayload.isJsonPrimitive()) {
+ log.info("Received signed SL20 response, but verification IS NOT required and NOT CONFIGURATED. Skip signature verification ... ");
+ String[] split = sl20SignedPayload.getAsString().split("\\.");
+ if (split.length == 3) {
+ JsonElement payLoad = new JsonParser().parse(new String(Base64Url.decodeToUtf8String(split[1])));
+ return new VerificationResult(payLoad.getAsJsonObject());
+
+ } else {
+ log.warn("Can NOT skip signature verification, because signed result has an unsupported format!");
+ throw new SLCommandoParserException();
+
+ }
+
+ } else {
+ log.warn("Internal build error");
+ throw new SLCommandoParserException();
+
+ }
+
+
+ }
+
+
+ /**
+ * Extract generic transport container from httpResponse
+ *
+ * @param httpResp
+ * @return
+ * @throws SLCommandoParserException
+ */
+ public static JsonObject getSL20ContainerFromResponse(HttpResponse httpResp) throws SLCommandoParserException {
+ try {
+ JsonObject sl20Resp = null;
+ if (httpResp.getStatusLine().getStatusCode() == 307) {
+ Header[] locationHeader = httpResp.getHeaders("Location");
+ if (locationHeader == null) {
+ log.warn("Find Redirect statuscode but not Location header");
+ throw new SLCommandoParserException();
+
+ }
+ String sl20RespString = new URIBuilder(locationHeader[0].getValue()).getQueryParams().get(0).getValue();
+ sl20Resp = new JsonParser().parse(Base64Url.encode((sl20RespString.getBytes()))).getAsJsonObject();
+
+ } else if (httpResp.getStatusLine().getStatusCode() == 200) {
+ if (!httpResp.getEntity().getContentType().getValue().startsWith("application/json")) {
+ log.warn("SL20 response with a wrong ContentType: " + httpResp.getEntity().getContentType().getValue());
+ throw new SLCommandoParserException();
+
+ }
+ sl20Resp = parseSL20ResultFromResponse(httpResp.getEntity());
+
+ } else if ( (httpResp.getStatusLine().getStatusCode() == 500) ||
+ (httpResp.getStatusLine().getStatusCode() == 401) ||
+ (httpResp.getStatusLine().getStatusCode() == 400) ) {
+ log.info("SL20 response with http-code: " + httpResp.getStatusLine().getStatusCode()
+ + ". Search for error message");
+ sl20Resp = parseSL20ResultFromResponse(httpResp.getEntity());
+
+
+ } else {
+ log.warn("SL20 response with http-code: " + httpResp.getStatusLine().getStatusCode());
+ throw new SLCommandoParserException();
+
+ }
+
+ log.info("Find JSON object in http response");
+ return sl20Resp;
+
+ } catch (Exception e) {
+ log.warn("SL20 response parsing FAILED! Reason: " + e.getMessage(), e);
+ throw new SLCommandoParserException(e);
+
+ }
+ }
+
+ private static JsonObject parseSL20ResultFromResponse(HttpEntity resp) throws Exception {
+ if (resp != null && resp.getContent() != null) {
+ JsonElement sl20Resp = new JsonParser().parse(new InputStreamReader(resp.getContent()));
+ if (sl20Resp != null && sl20Resp.isJsonObject()) {
+ return sl20Resp.getAsJsonObject();
+
+ } else {
+ log.warn("SL2.0 can NOT parse to a JSON object");
+ throw new SLCommandoParserException();
+
+ }
+
+
+ } else {
+ log.warn("Can NOT find content in http response");
+ throw new SLCommandoParserException();
+
+ }
+
+ }
+
+
+ private static JsonElement getAndCheck(JsonObject input, String keyID, boolean isRequired) throws SLCommandoParserException {
+ JsonElement internal = input.get(keyID);
+
+ if (internal == null && isRequired) {
+ log.warn("REQUIRED Element with keyId: " + keyID + " does not exist");
+ throw new SLCommandoParserException();
+
+ }
+
+ return internal;
+
+ }
+}
diff --git a/pdf-as-moa/src/main/java/META-INF/MANIFEST.MF b/pdf-as-moa/src/main/java/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..254272e1
--- /dev/null
+++ b/pdf-as-moa/src/main/java/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path:
+
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 d63f698f..c6b27eb3 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
@@ -42,9 +42,12 @@ public class WebConfiguration implements IConfigurationConstants {
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";
+ public static final String SL20_BKU_ENABLED = "sl20.sign.enabled";
public static final String LOCAL_BKU_URL = "bku.local.url";
public static final String ONLINE_BKU_URL = "bku.online.url";
public static final String MOBILE_BKU_URL = "bku.mobile.url";
+ public static final String SL20_BKU_URL = "sl20.mobile.url";
+
public static final String ERROR_DETAILS = "error.showdetails";
public static final String PDF_AS_WORK_DIR = "pdfas.dir";
public static final String STATISTIC_BACKEND_LIST = "statistic.backends";
@@ -82,6 +85,23 @@ public class WebConfiguration implements IConfigurationConstants {
public static final String KEYSTORE_DEFAULT_ALIAS = KEYSTORE_DEFAULT + "." + KEYSTORE_ALIAS;
public static final String KEYSTORE_DEFAULT_KEY_PASS = KEYSTORE_DEFAULT + "." + KEYSTORE_KEY_PASS;
+ //SL20 stuff
+ public static final String SL20_PREFIX = "sl20";
+ public static final String SL20_KEYSTORE_PREFIX = SL20_PREFIX + ".keystore";
+ public static final String SL20_KEYSTORE_FILE = SL20_KEYSTORE_PREFIX + "." + "file";
+ public static final String SL20_KEYSTORE_TYPE = SL20_KEYSTORE_PREFIX + "." + "type";
+ public static final String SL20_KEYSTORE_PASS = SL20_KEYSTORE_PREFIX + "." + "pass";
+ public static final String SL20_KEYSTORE_KEY_SIGN_ALIAS = SL20_KEYSTORE_PREFIX + "." + "sign.key.alias";
+ public static final String SL20_KEYSTORE_KEY_SIGN_PASS = SL20_KEYSTORE_PREFIX + "." + "sign.key.pass";
+ public static final String SL20_KEYSTORE_KEY_ENCRYPTION_ALIAS = SL20_KEYSTORE_PREFIX + "." + "enc.key.alias";
+ public static final String SL20_KEYSTORE_KEY_ENCRYPTION_PASS = SL20_KEYSTORE_PREFIX + "." + "enc.key.pass";
+ public static final String SL20_DEBUG_VALIDATION_DISABLED = SL20_PREFIX + ".debug.validation.disable";
+ public static final String SL20_DEBUG_SIGNING_ENABLED = SL20_PREFIX + ".debug.signed.result.enabled";
+ public static final String SL20_DEBUG_SIGNING_REQUIRED = SL20_PREFIX + ".debug.signed.result.required";
+ public static final String SL20_DEBUG_ENCRYPTION_ENABLED = SL20_PREFIX + ".debug.encryption.enabled";
+ public static final String SL20_DEBUG_ENCRYPTION_REQUIRED = SL20_PREFIX + ".debug.encryption.required";
+
+
public static final String WHITELIST_ENABLED = "whitelist.enabled";
public static final String WHITELIST_VALUE_PRE = "whitelist.url.";
@@ -248,6 +268,20 @@ public class WebConfiguration implements IConfigurationConstants {
return null;
}
+ public static String getSecurityLayer20URL() {
+ if(getSL20Enabled()) {
+ String overwrite = properties.getProperty(SL20_SIGN_URL);
+ if(overwrite == null) {
+ overwrite = properties.getProperty(SL20_BKU_URL);
+ if(overwrite == null) {
+ overwrite = PdfAsHelper.getPdfAsConfig().getValue(SL20_SIGN_URL);
+ }
+ }
+ return overwrite;
+ }
+ return null;
+ }
+
public static String getPdfASDir() {
return properties.getProperty(PDF_AS_WORK_DIR);
}
@@ -447,6 +481,16 @@ public class WebConfiguration implements IConfigurationConstants {
return false;
}
+ public static boolean getSL20Enabled() {
+ String value = properties.getProperty(SL20_BKU_ENABLED);
+ if (value != null) {
+ if (value.equals("true")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public static boolean getSoapSignEnabled() {
String value = properties.getProperty(SOAP_SIGN_ENABLED);
if (value != null) {
@@ -598,5 +642,66 @@ public class WebConfiguration implements IConfigurationConstants {
}
return false;
}
+
+ public static String getSL20KeyStorePath() {
+ return properties.getProperty(SL20_KEYSTORE_FILE);
+
+ }
+
+ public static String getSL20KeyStoreType() {
+ return properties.getProperty(SL20_KEYSTORE_TYPE);
+
+ }
+
+ public static String getSL20KeyStorePassword() {
+ return properties.getProperty(SL20_KEYSTORE_PASS);
+
+ }
+
+ public static String getSL20KeySigningAlias() {
+ return properties.getProperty(SL20_KEYSTORE_KEY_SIGN_ALIAS);
+
+ }
+
+ public static String getSL20KeySigningPassword() {
+ return properties.getProperty(SL20_KEYSTORE_KEY_SIGN_PASS);
+
+ }
+
+ public static String getSL20KeyEncryptionAlias() {
+ return properties.getProperty(SL20_KEYSTORE_KEY_ENCRYPTION_ALIAS);
+
+ }
+
+ public static String getSL20KeyEncryptionPassword() {
+ return properties.getProperty(SL20_KEYSTORE_KEY_ENCRYPTION_PASS);
+
+ }
+
+ public static boolean isSL20ValidationDisabled( ) {
+ return Boolean.parseBoolean(properties.getProperty(SL20_DEBUG_VALIDATION_DISABLED, String.valueOf(false)));
+
+ }
+
+ public static boolean isSL20SigningEnabled( ) {
+ return Boolean.parseBoolean(properties.getProperty(SL20_DEBUG_SIGNING_ENABLED, String.valueOf(false)));
+
+ }
+
+ public static boolean isSL20SigningRequired( ) {
+ return Boolean.parseBoolean(properties.getProperty(SL20_DEBUG_SIGNING_REQUIRED, String.valueOf(false)));
+
+ }
+
+ public static boolean isSL20EncryptionEnabled( ) {
+ return Boolean.parseBoolean(properties.getProperty(SL20_DEBUG_ENCRYPTION_ENABLED, String.valueOf(false)));
+
+ }
+
+ public static boolean isSL20EncryptionRequired( ) {
+ return Boolean.parseBoolean(properties.getProperty(SL20_DEBUG_ENCRYPTION_REQUIRED, String.valueOf(false)));
+
+ }
+
}
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 3aad831d..4b776cb3 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
@@ -30,12 +30,17 @@ import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
+import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
+import java.net.URL;
import java.net.URLEncoder;
import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.UUID;
import javax.imageio.ImageIO;
import javax.servlet.RequestDispatcher;
@@ -51,9 +56,12 @@ import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.http.entity.ContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.gson.JsonObject;
+
import at.gv.egiz.pdfas.api.ws.PDFASSignParameters;
import at.gv.egiz.pdfas.api.ws.PDFASSignParameters.Connector;
import at.gv.egiz.pdfas.api.ws.PDFASSignResponse;
@@ -77,6 +85,8 @@ import at.gv.egiz.pdfas.sigs.pades.PAdESSignerKeystore;
import at.gv.egiz.pdfas.web.config.WebConfiguration;
import at.gv.egiz.pdfas.web.exception.PdfAsWebException;
import at.gv.egiz.pdfas.web.servlets.UIEntryPointServlet;
+import at.gv.egiz.pdfas.web.sl20.JsonSecurityUtils;
+import at.gv.egiz.pdfas.web.sl20.SL20HttpBindingUtils;
import at.gv.egiz.pdfas.web.stats.StatisticEvent;
import at.gv.egiz.sl.schema.CreateCMSSignatureResponseType;
import at.gv.egiz.sl.schema.InfoboxAssocArrayPairType;
@@ -84,8 +94,16 @@ import at.gv.egiz.sl.schema.InfoboxReadRequestType;
import at.gv.egiz.sl.schema.InfoboxReadResponseType;
import at.gv.egiz.sl.schema.ObjectFactory;
import at.gv.egiz.sl.util.BKUSLConnector;
+import at.gv.egiz.sl.util.BaseSLConnector;
import at.gv.egiz.sl.util.RequestPackage;
import at.gv.egiz.sl.util.SLMarschaller;
+import at.gv.egiz.sl20.SL20Connector;
+import at.gv.egiz.sl20.data.VerificationResult;
+import at.gv.egiz.sl20.exceptions.SL20Exception;
+import at.gv.egiz.sl20.exceptions.SLCommandoParserException;
+import at.gv.egiz.sl20.utils.SL20Constants;
+import at.gv.egiz.sl20.utils.SL20JSONBuilderUtils;
+import at.gv.egiz.sl20.utils.SL20JSONExtractorUtils;
public class PdfAsHelper {
@@ -105,6 +123,7 @@ public class PdfAsHelper {
private static final String PDF_PROVIDE_PAGE = "/ProvidePDF";
private static final String PDF_PDFDATA_PAGE = "/PDFData";
private static final String PDF_DATAURL_PAGE = "/DataURL";
+ private static final String PDF_SL20_DATAURL_PAGE = "/DataURLSL20";
private static final String PDF_USERENTRY_PAGE = "/userentry";
private static final String PDF_ERR_URL = "PDF_ERR_URL";
private static final String PDF_FILE_NAME = "PDF_FILE_NAME";
@@ -118,6 +137,7 @@ public class PdfAsHelper {
private static final String SIGNATURE_ACTIVE = "SIGNATURE_ACTIVE";
private static final String VERIFICATION_RESULT = "VERIFICATION_RESULT";
private static final String QRCODE_CONTENT = "QR_CONT";
+ public static final String PDF_SESSION_PREFIX = "PDF_SESSION_";
private static final Logger logger = LoggerFactory
.getLogger(PdfAsHelper.class);
@@ -707,6 +727,12 @@ public class PdfAsHelper {
// conn.setBase64(true);
signer = new PAdESSigner(conn);
session.setAttribute(PDF_SL_CONNECTOR, conn);
+
+ } else if (connector.equals("sl20")) {
+ SL20Connector conn = new SL20Connector(config);
+ signer = new PAdESSigner(conn);
+ session.setAttribute(PDF_SL_CONNECTOR, conn);
+
} else {
throw new PdfAsWebException(
"Invalid connector (bku | onlinebku | mobilebku | moa | jks)");
@@ -794,9 +820,15 @@ public class PdfAsHelper {
// conn.setBase64(true);
signer = new PAdESSigner(conn);
session.setAttribute(PDF_SL_CONNECTOR, conn);
+
+ } else if (connector.equals("sl20")) {
+ SL20Connector conn = new SL20Connector(config);
+ signer = new PAdESSigner(conn);
+ session.setAttribute(PDF_SL_CONNECTOR, conn);
+
} else {
throw new PdfAsWebException(
- "Invalid connector (bku | onlinebku | mobilebku | moa | jks)");
+ "Invalid connector (bku | onlinebku | mobilebku | moa | jks | sl20)");
}
signParameter.setPreprocessorArguments(preProcessor);
signParameter.setPlainSigner(signer);
@@ -839,7 +871,7 @@ public class PdfAsHelper {
PdfAsHelper.process(request, response, context);
}
- private static byte[] getCertificate(
+ public static byte[] getCertificate(
InfoboxReadResponseType infoboxReadResponseType) {
byte[] data = null;
if (infoboxReadResponseType.getAssocArrayData() != null) {
@@ -898,7 +930,7 @@ public class PdfAsHelper {
public static void injectCertificate(HttpServletRequest request,
HttpServletResponse response,
- InfoboxReadResponseType infoboxReadResponseType,
+ byte[] certificate,
ServletContext context) throws Exception {
HttpSession session = request.getSession();
@@ -910,7 +942,7 @@ public class PdfAsHelper {
+ session.getId());
}
- statusRequest.setCertificate(getCertificate(infoboxReadResponseType));
+ statusRequest.setCertificate(certificate);
statusRequest = pdfAs.process(statusRequest);
session.setAttribute(PDF_STATUS, statusRequest);
@@ -919,7 +951,7 @@ public class PdfAsHelper {
public static void injectSignature(HttpServletRequest request,
HttpServletResponse response,
- CreateCMSSignatureResponseType createCMSSignatureResponseType,
+ byte[] cmsSginature,
ServletContext context) throws Exception {
logger.debug("Got CMS Signature Response");
@@ -933,8 +965,7 @@ public class PdfAsHelper {
+ session.getId());
}
- statusRequest.setSigature(createCMSSignatureResponseType
- .getCMSSignature());
+ statusRequest.setSigature(cmsSginature);
statusRequest = pdfAs.process(statusRequest);
session.setAttribute(PDF_STATUS, statusRequest);
@@ -996,21 +1027,35 @@ public class PdfAsHelper {
String connector = (String) session.getAttribute(PDF_SL_INTERACTIVE);
+ //load connector
+ BaseSLConnector slConnector = null;
if (connector.equals("bku") || connector.equals("onlinebku")
- || connector.equals("mobilebku")) {
- BKUSLConnector bkuSLConnector = (BKUSLConnector) session
+ || connector.equals("mobilebku"))
+ slConnector = (BKUSLConnector) session
.getAttribute(PDF_SL_CONNECTOR);
-
- if (statusRequest.needCertificate()) {
- logger.debug("Needing Certificate from BKU");
- // build SL Request to read certificate
- InfoboxReadRequestType readCertificateRequest = bkuSLConnector
- .createInfoboxReadRequest(statusRequest
- .getSignParameter());
-
+
+ else if (connector.equals("sl20"))
+ slConnector = (SL20Connector) session
+ .getAttribute(PDF_SL_CONNECTOR);
+
+ else
+ throw new PdfAsWebException("Invalid connector: " + connector);
+
+ JsonSecurityUtils joseTools = JsonSecurityUtils.getInstance();
+ if (!joseTools.isInitialized())
+ joseTools = null;
+
+ if (statusRequest.needCertificate()) {
+ logger.debug("Needing Certificate from BKU");
+ // build SL Request to read certificate
+ InfoboxReadRequestType readCertificateRequest = slConnector
+ .createInfoboxReadRequest(statusRequest
+ .getSignParameter());
+
+ if (slConnector instanceof BKUSLConnector) {
JAXBElement<InfoboxReadRequestType> readRequest = of
.createInfoboxReadRequest(readCertificateRequest);
-
+
String url = generateDataURL(request, response);
String slRequest = SLMarschaller.marshalToString(readRequest);
String template = getTemplateSL();
@@ -1021,7 +1066,7 @@ public class PdfAsHelper {
StringEscapeUtils.escapeHtml4(slRequest));
template = template.replace("##DataURL##", url);
template = template.replace("##LOCALE##", locale);
-
+
if (statusRequest.getSignParameter().getTransactionId() != null) {
template = template.replace(
"##ADDITIONAL##",
@@ -1034,70 +1079,220 @@ public class PdfAsHelper {
} else {
template = template.replace("##ADDITIONAL##", "");
}
-
+
response.getWriter().write(template);
// TODO: set content type of response!!
response.setContentType("text/html");
response.getWriter().close();
- } else if (statusRequest.needSignature()) {
- logger.debug("Needing Signature from BKU");
- // build SL Request for cms signature
- RequestPackage pack = bkuSLConnector.createCMSRequest(
- statusRequest.getSignatureData(),
- statusRequest.getSignatureDataByteRange(),
- statusRequest.getSignParameter());
-
+
+ } else if (slConnector instanceof SL20Connector) {
+ //generate request for getCertificate command
+ SL20Connector sl20Connector = (SL20Connector)slConnector;
+
+ //use 'SecureSigningKeypair' per default
+ String keyId = SL20Connector.SecureSignatureKeypair;
+
+ java.security.cert.X509Certificate x5cEnc = null;
+ if (WebConfiguration.isSL20EncryptionEnabled() && joseTools != null)
+ x5cEnc = joseTools.getEncryptionCertificate();
+ JsonObject getCertParams =
+ SL20JSONBuilderUtils.createGetCertificateCommandParameters(
+ keyId, generateDataURLSL20(request, response), x5cEnc);
+
+ JsonObject sl20Req = null;
+ String reqId = UUID.randomUUID().toString();
+ if (WebConfiguration.isSL20SigningEnabled()) {
+ String signedCertCommand = SL20JSONBuilderUtils.createSignedCommand(
+ SL20Constants.SL20_COMMAND_IDENTIFIER_GETCERTIFICATE, getCertParams, joseTools);
+ sl20Req = SL20JSONBuilderUtils.createGenericRequest(reqId, null, null, signedCertCommand);
+
+ } else {
+ JsonObject getCertCommand = SL20JSONBuilderUtils.createCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_GETCERTIFICATE, getCertParams);
+ sl20Req = SL20JSONBuilderUtils.createGenericRequest(reqId, null, getCertCommand, null);
+
+ }
+
+ //send SL20 request via Backend connection
+ JsonObject sl20Resp = sl20Connector.sendSL20Request(sl20Req, null, generateBKUURL(connector));
+ if (sl20Resp == null) {
+ logger.info("Receive NO responce from SL2.0 connection. Process stops ... ");
+ throw new SLCommandoParserException();
+
+ }
+
+ VerificationResult respPayloadContainer = SL20JSONExtractorUtils.extractSL20PayLoad(
+ sl20Resp, joseTools, WebConfiguration.isSL20SigningRequired());
+
+ if (respPayloadContainer.isValidSigned() == null)
+ logger.debug("Receive unsigned payLoad from VDA");
+
+ JsonObject respPayload = respPayloadContainer.getPayload();
+ if (respPayload.get(SL20Constants.SL20_COMMAND_CONTAINER_NAME).getAsString()
+ .equals(SL20Constants.SL20_COMMAND_IDENTIFIER_REDIRECT)) {
+ logger.debug("Find 'redirect' command in VDA response ... ");
+ JsonObject params = SL20JSONExtractorUtils.getJSONObjectValue(respPayload, SL20Constants.SL20_COMMAND_CONTAINER_PARAMS, true);
+ String redirectURL = SL20JSONExtractorUtils.getStringValue(params, SL20Constants.SL20_COMMAND_PARAM_GENERAL_REDIRECT_URL, true);
+ JsonObject command = SL20JSONExtractorUtils.getJSONObjectValue(params, SL20Constants.SL20_COMMAND_PARAM_GENERAL_REDIRECT_COMMAND, false);
+ String signedCommand = SL20JSONExtractorUtils.getStringValue(params, SL20Constants.SL20_COMMAND_PARAM_GENERAL_REDIRECT_SIGNEDCOMMAND, false);
+
+ //create forward SL2.0 command
+ JsonObject sl20Forward = sl20Resp.deepCopy().getAsJsonObject();
+ SL20JSONBuilderUtils.addOnlyOnceOfTwo(sl20Forward,
+ SL20Constants.SL20_PAYLOAD, SL20Constants.SL20_SIGNEDPAYLOAD,
+ command, signedCommand);
+
+ //store requestId
+
+ request.getSession(false).setAttribute(PDF_SESSION_PREFIX + SL20Constants.SL20_REQID, reqId);
+
+ //forward SL2.0 command
+ SL20HttpBindingUtils.writeIntoResponse(request, response, sl20Forward, redirectURL);
+
+ } else if (respPayload.get(SL20Constants.SL20_COMMAND_CONTAINER_NAME).getAsString()
+ .equals(SL20Constants.SL20_COMMAND_IDENTIFIER_ERROR)) {
+ JsonObject result = SL20JSONExtractorUtils.getJSONObjectValue(respPayload, SL20Constants.SL20_COMMAND_CONTAINER_RESULT, false);
+ if (result == null)
+ result = SL20JSONExtractorUtils.getJSONObjectValue(respPayload, SL20Constants.SL20_COMMAND_CONTAINER_PARAMS, false);
+
+ String errorCode = SL20JSONExtractorUtils.getStringValue(result, SL20Constants.SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERRORCODE, true);
+ String errorMsg = SL20JSONExtractorUtils.getStringValue(result, SL20Constants.SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERRORMESSAGE, true);
+
+ logger.info("Receive SL2.0 error. Code:" + errorCode + " Msg:" + errorMsg);
+ throw new SL20Exception("sl20.08");
+
+ } else {
+ logger.warn("Received an unrecognized command: " + respPayload.get(SL20Constants.SL20_COMMAND_CONTAINER_NAME).getAsString());
+ throw new SLCommandoParserException();
+
+ }
+
+ } else
+ throw new PdfAsWebException("Invalid connector: " + slConnector.getClass().getName());
+
+ } else if (statusRequest.needSignature()) {
+ logger.debug("Needing Signature from BKU");
+ // build SL Request for cms signature
+ RequestPackage pack = slConnector.createCMSRequest(
+ statusRequest.getSignatureData(),
+ statusRequest.getSignatureDataByteRange(),
+ statusRequest.getSignParameter());
+
+ if (slConnector instanceof BKUSLConnector) {
String slRequest = SLMarschaller
.marshalToString(of
.createCreateCMSSignatureRequest(pack
.getRequestType()));
logger.trace("SL Request: " + slRequest);
-
+
response.setContentType("text/xml");
response.getWriter().write(slRequest);
response.getWriter().close();
+
+ } else if (slConnector instanceof SL20Connector) {
+ //convert byte range
+ List<String> byteRanges = new ArrayList<String>();
+ for (int el : statusRequest.getSignatureDataByteRange())
+ byteRanges.add(String.valueOf(el));
+
+ java.security.cert.X509Certificate x5cEnc = null;
+ if (WebConfiguration.isSL20EncryptionEnabled() && joseTools != null)
+ x5cEnc = joseTools.getEncryptionCertificate();
+
+ //set 'true' as default
+ boolean padesCompatibel = true;
+ if (pack.getRequestType().getPAdESFlag() != null)
+ padesCompatibel = pack.getRequestType().getPAdESFlag();
+
+ JsonObject createCAdESSigParams =
+ SL20JSONBuilderUtils.createCreateCAdESCommandParameters(
+ pack.getRequestType().getKeyboxIdentifier(),
+ statusRequest.getSignatureData(),
+ pack.getRequestType().getDataObject().getMetaInfo().getMimeType(),
+ padesCompatibel ,
+ byteRanges,
+ SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_CADESLEVEL_BASIC,
+ generateDataURLSL20(request, response),
+ x5cEnc) ;
+
+ JsonObject sl20CreateCAdES = null;
+ String reqId = UUID.randomUUID().toString();
+ if (WebConfiguration.isSL20SigningEnabled()) {
+ String signedCertCommand = SL20JSONBuilderUtils.createSignedCommand(
+ SL20Constants.SL20_COMMAND_IDENTIFIER_CREATE_SIG_CADES, createCAdESSigParams, joseTools);
+ sl20CreateCAdES = SL20JSONBuilderUtils.createGenericRequest(reqId, null, null, signedCertCommand);
+
+ } else {
+ JsonObject getCertCommand = SL20JSONBuilderUtils.createCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_CREATE_SIG_CADES, createCAdESSigParams);
+ sl20CreateCAdES = SL20JSONBuilderUtils.createGenericRequest(UUID.randomUUID().toString(), null, getCertCommand, null);
+
+ }
+
+ request.getSession(false).setAttribute(PDF_SESSION_PREFIX + SL20Constants.SL20_REQID, reqId);
+
+ //forward SL2.0 command
+ logger.trace("Write 'createCAdES' command to VDA: " + sl20CreateCAdES.toString());
+ StringWriter writer = new StringWriter();
+ writer.write(sl20CreateCAdES.toString());
+ final byte[] content = writer.toString().getBytes("UTF-8");
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setContentLength(content.length);
+ response.setContentType(ContentType.APPLICATION_JSON.toString());
+ response.getOutputStream().write(content);
+
+ } else
+ throw new PdfAsWebException("Invalid connector: " + slConnector.getClass().getName());
+
+
+
+ } else if (statusRequest.isReady()) {
+ // TODO: store pdf document redirect to Finish URL
+ logger.debug("Document ready!");
+
+ SignResult result = pdfAs.finishSign(statusRequest);
+
+ ByteArrayOutputStream baos = (ByteArrayOutputStream) session
+ .getAttribute(PDF_OUTPUT);
+ baos.close();
+
+ PDFASVerificationResponse verResponse = new PDFASVerificationResponse();
+ List<VerifyResult> verResults = PdfAsHelper.synchornousVerify(
+ baos.toByteArray(), -2,
+ PdfAsHelper.getVerificationLevel(request), null);
+
+ if (verResults.size() != 1) {
+ throw new WebServiceException(
+ "Document verification failed!");
+ }
+ VerifyResult verifyResult = verResults.get(0);
- } else if (statusRequest.isReady()) {
- // TODO: store pdf document redirect to Finish URL
- logger.debug("Document ready!");
-
- SignResult result = pdfAs.finishSign(statusRequest);
-
- ByteArrayOutputStream baos = (ByteArrayOutputStream) session
- .getAttribute(PDF_OUTPUT);
- baos.close();
-
- PDFASVerificationResponse verResponse = new PDFASVerificationResponse();
- List<VerifyResult> verResults = PdfAsHelper.synchornousVerify(
- baos.toByteArray(), -2,
- PdfAsHelper.getVerificationLevel(request), null);
+ verResponse.setCertificateCode(verifyResult
+ .getCertificateCheck().getCode());
+ verResponse.setValueCode(verifyResult.getValueCheckCode()
+ .getCode());
- if (verResults.size() != 1) {
- throw new WebServiceException(
- "Document verification failed!");
- }
- VerifyResult verifyResult = verResults.get(0);
+ PdfAsHelper.setPDFASVerificationResponse(request, verResponse);
+ PdfAsHelper.setSignedPdf(request, response, baos.toByteArray());
- verResponse.setCertificateCode(verifyResult
- .getCertificateCheck().getCode());
- verResponse.setValueCode(verifyResult.getValueCheckCode()
- .getCode());
+ String signerCert = Base64.encodeBase64String(result
+ .getSignerCertificate().getEncoded());
- PdfAsHelper.setPDFASVerificationResponse(request, verResponse);
- PdfAsHelper.setSignedPdf(request, response, baos.toByteArray());
+ PdfAsHelper.setSignerCertificate(request, signerCert);
+
+ if (slConnector instanceof BKUSLConnector) {
PdfAsHelper.gotoProvidePdf(context, request, response);
-
- String signerCert = Base64.encodeBase64String(result
- .getSignerCertificate().getEncoded());
-
- PdfAsHelper.setSignerCertificate(request, signerCert);
-
- } else {
- throw new PdfAsWebException("Invalid state!");
- }
+
+ } else if (slConnector instanceof SL20Connector) {
+ //TODO: add code to send SL20 redirect command to redirect the user from DataURL connection to App Front-End connection
+ String callUrl = generateProvideURL(request, response);
+ String transactionId = (String) request.getAttribute(PdfAsHelper.PDF_SESSION_PREFIX + SL20Constants.SL20_TRANSACTIONID);
+ buildSL20RedirectResponse(request, response, transactionId, callUrl);
+
+ } else
+ throw new PdfAsWebException("Invalid connector: " + slConnector.getClass().getName());
+
} else {
- throw new PdfAsWebException("Invalid connector: " + connector);
+ throw new PdfAsWebException("Invalid state!");
}
}
@@ -1338,6 +1533,11 @@ public class PdfAsHelper {
request.getSession(true);
}
+ public static String generateDataURLSL20(HttpServletRequest request,
+ HttpServletResponse response) {
+ return generateURL(request, response, PDF_SL20_DATAURL_PAGE);
+ }
+
public static String generateDataURL(HttpServletRequest request,
HttpServletResponse response) {
return generateURL(request, response, PDF_DATAURL_PAGE);
@@ -1385,6 +1585,8 @@ public class PdfAsHelper {
return WebConfiguration.getOnlineBKUURL();
} else if (connector.equals("mobilebku")) {
return WebConfiguration.getHandyBKUURL();
+ } else if (connector.equals("sl20")) {
+ return WebConfiguration.getSecurityLayer20URL();
}
return WebConfiguration.getLocalBKUURL();
}
@@ -1542,4 +1744,64 @@ public class PdfAsHelper {
public static String getSCMRevision() {
return PdfAsFactory.getSCMRevision();
}
+
+ public static void buildSL20RedirectResponse(HttpServletRequest request, HttpServletResponse response, String transactionId, String callURL) throws IOException, SL20Exception {
+ //create response
+ Map<String, String> reqParameters = UrlParameterExtractor.splitQuery(new URL(callURL));
+
+ //extract URL without parameters
+ String url;
+ int paramIndex = callURL.indexOf("?");
+ if (paramIndex == -1)
+ url = callURL;
+ else
+ url = callURL.substring(0, paramIndex);
+
+ JsonObject callReqParams = SL20JSONBuilderUtils.createCallCommandParameters(
+ url,
+ SL20Constants.SL20_COMMAND_PARAM_GENERAL_CALL_METHOD_GET,
+ false,
+ reqParameters);
+ JsonObject callCommand = SL20JSONBuilderUtils.createCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_CALL, callReqParams);
+
+ //build first redirect command for app
+ JsonObject redirectOneParams = SL20JSONBuilderUtils.createRedirectCommandParameters(
+ null,
+ callCommand, null, true);
+ JsonObject redirectOneCommand = SL20JSONBuilderUtils.createCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_REDIRECT, redirectOneParams);
+
+ //build second redirect command for IDP
+ JsonObject redirectTwoParams = SL20JSONBuilderUtils.createRedirectCommandParameters(
+ callURL,
+ redirectOneCommand, null, false);
+ JsonObject redirectTwoCommand = SL20JSONBuilderUtils.createCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_REDIRECT, redirectTwoParams);
+
+ //build generic SL2.0 response container
+ JsonObject respContainer = SL20JSONBuilderUtils.createGenericRequest(
+ UUID.randomUUID().toString(),
+ transactionId,
+ redirectTwoCommand,
+ null);
+
+ //workaround for A-Trust
+ if (request.getHeader(SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE) != null &&
+ request.getHeader(SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE).equals(SL20Constants.HTTP_HEADER_VALUE_NATIVE)
+ || true) {
+ logger.debug("Client request containts 'native client' header ... ");
+ logger.trace("SL20 response to VDA: " + respContainer);
+ StringWriter writer = new StringWriter();
+ writer.write(respContainer.toString());
+ final byte[] content = writer.toString().getBytes("UTF-8");
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setContentLength(content.length);
+ response.setContentType(ContentType.APPLICATION_JSON.toString());
+ response.getOutputStream().write(content);
+
+
+ } else {
+ logger.info("SL2.0 DataURL communication needs http header: '" + SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE + "'");
+ throw new SL20Exception("sl20.06");
+
+ }
+ }
}
diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/DataURLServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/DataURLServlet.java
index 45861953..50c3b063 100644
--- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/DataURLServlet.java
+++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/DataURLServlet.java
@@ -93,11 +93,11 @@ public class DataURLServlet extends HttpServlet {
if(jaxbObject.getValue() instanceof InfoboxReadResponseType) {
InfoboxReadResponseType infoboxReadResponseType = (InfoboxReadResponseType)jaxbObject.getValue();
logger.info("Got InfoboxReadResponseType");
- PdfAsHelper.injectCertificate(request, response, infoboxReadResponseType, getServletContext());
+ PdfAsHelper.injectCertificate(request, response, PdfAsHelper.getCertificate(infoboxReadResponseType), getServletContext());
} else if(jaxbObject.getValue() instanceof CreateCMSSignatureResponseType) {
CreateCMSSignatureResponseType createCMSSignatureResponseType = (CreateCMSSignatureResponseType)jaxbObject.getValue();
logger.info("Got CreateCMSSignatureResponseType");
- PdfAsHelper.injectSignature(request, response, createCMSSignatureResponseType, getServletContext());
+ PdfAsHelper.injectSignature(request, response, createCMSSignatureResponseType.getCMSSignature(), getServletContext());
} else if(jaxbObject.getValue() instanceof ErrorResponseType) {
ErrorResponseType errorResponseType = (ErrorResponseType)jaxbObject.getValue();
logger.warn("SecurityLayer: " + errorResponseType.getErrorCode() + " " + errorResponseType.getInfo());
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 3cea5247..1d2ab14e 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
@@ -354,7 +354,8 @@ public class ExternSignServlet extends HttpServlet {
logger.debug("Starting signature creation with: " + connector);
//IPlainSigner signer;
- if (connector.equals("bku") || connector.equals("onlinebku") || connector.equals("mobilebku")) {
+ if (connector.equals("bku") || connector.equals("onlinebku") || connector.equals("mobilebku")
+ || connector.equals("sl20")) {
// start asynchronous signature creation
if(connector.equals("bku")) {
@@ -372,6 +373,11 @@ public class ExternSignServlet extends HttpServlet {
throw new PdfAsWebException("Invalid connector bku is not supported");
}
}
+ if (connector.equals("sl20")) {
+ if(WebConfiguration.getSecurityLayer20URL() == null) {
+ throw new PdfAsWebException("Invalid connector bku is not supported");
+ }
+ }
PdfAsHelper.setStatisticEvent(request, response, statisticEvent);
diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/JSONAPIServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/JSONAPIServlet.java
index 0cee185a..13d874e8 100644
--- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/JSONAPIServlet.java
+++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/JSONAPIServlet.java
@@ -119,7 +119,9 @@ public class JSONAPIServlet extends HttpServlet {
connectorEnum = PDFASSignParameters.Connector.MOBILEBKU;
} else if(PDFASSignParameters.Connector.ONLINEBKU.equalsName(connector)) {
connectorEnum = PDFASSignParameters.Connector.ONLINEBKU;
- }
+ } else if(PDFASSignParameters.Connector.SECLAYER20.equalsName(connector)) {
+ connectorEnum = PDFASSignParameters.Connector.SECLAYER20;
+ }
if(connectorEnum == null) {
throw new ServletException(
@@ -212,6 +214,13 @@ public class JSONAPIServlet extends HttpServlet {
"Invalid connector mobilebku is not supported");
}
}
+
+ if (PDFASSignParameters.Connector.SECLAYER20.equals(connectorEnum)) {
+ if (WebConfiguration.getSecurityLayer20URL() == null) {
+ throw new PdfAsWebException(
+ "Invalid connector mobilebku is not supported");
+ }
+ }
PdfAsHelper.startSignatureJson(request, response, getServletContext(),
diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SLDataURLServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SLDataURLServlet.java
new file mode 100644
index 00000000..7ddf0a55
--- /dev/null
+++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SLDataURLServlet.java
@@ -0,0 +1,234 @@
+package at.gv.egiz.pdfas.web.servlets;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.annotation.MultipartConfig;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.StringUtils;
+import org.jose4j.base64url.Base64Url;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+
+import at.gv.egiz.pdfas.lib.util.StreamUtils;
+import at.gv.egiz.pdfas.web.config.WebConfiguration;
+import at.gv.egiz.pdfas.web.helper.PdfAsHelper;
+import at.gv.egiz.pdfas.web.sl20.JsonSecurityUtils;
+import at.gv.egiz.pdfas.web.sl20.X509Utils;
+import at.gv.egiz.sl20.data.VerificationResult;
+import at.gv.egiz.sl20.exceptions.SL20Exception;
+import at.gv.egiz.sl20.exceptions.SL20SecurityException;
+import at.gv.egiz.sl20.exceptions.SLCommandoParserException;
+import at.gv.egiz.sl20.utils.SL20Constants;
+import at.gv.egiz.sl20.utils.SL20JSONExtractorUtils;
+
+@MultipartConfig
+public class SLDataURLServlet extends HttpServlet {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(SLDataURLServlet.class);
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @see HttpServlet#HttpServlet()
+ */
+ public SLDataURLServlet() {
+ super();
+ }
+
+ /**
+ * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
+ * response)
+ */
+ protected void doGet(HttpServletRequest request,
+ HttpServletResponse response) throws ServletException, IOException {
+ this.process(request, response);
+ }
+
+ /**
+ * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
+ * response)
+ */
+ protected void doPost(HttpServletRequest request,
+ HttpServletResponse response) throws ServletException, IOException {
+ this.process(request, response);
+ }
+
+ protected void process(HttpServletRequest request,
+ HttpServletResponse response) throws ServletException, IOException {
+
+ JsonObject sl20ReqObj = null;
+ try {
+ if(!PdfAsHelper.checkDataUrlAccess(request)) {
+ throw new Exception("No valid dataURL access");
+ }
+
+ PdfAsHelper.setFromDataUrl(request);
+
+ String sl20Result = request.getParameter(SL20Constants.PARAM_SL20_REQ_COMMAND_PARAM);
+ if (StringUtils.isEmpty(sl20Result)) {
+ //Workaround for SIC Handy-Signature, because it sends result in InputStream
+ String isReqInput = StreamUtils.readStream(request.getInputStream(), "UTF-8");
+ if (StringUtils.isNotEmpty(isReqInput)) {
+ logger.info("Use SIC Handy-Signature work-around!");
+ sl20Result = isReqInput.substring("slcommand=".length());
+
+ } else {
+ logger.info("NO SL2.0 commando or result FOUND.");
+ throw new SL20Exception("sl20.04", null);
+ }
+
+ }
+
+ logger.trace("Received SL2.0 result: " + sl20Result);
+
+ //parse SL2.0 command/result into JSON
+ try {
+ JsonParser jsonParser = new JsonParser();
+ JsonElement sl20Req = jsonParser.parse(Base64Url.decodeToUtf8String(sl20Result));
+ sl20ReqObj = sl20Req.getAsJsonObject();
+
+ } catch (JsonSyntaxException e) {
+ logger.warn("SL2.0 command or result is NOT valid JSON.", e);
+ logger.debug("SL2.0 msg: " + sl20Result);
+ throw new SL20Exception("sl20.02", e);
+
+ }
+
+ //extract transactionId
+ String transactionId = SL20JSONExtractorUtils.getStringValue(sl20ReqObj, SL20Constants.SL20_TRANSACTIONID, false);
+ if (StringUtils.isNotEmpty(transactionId))
+ request.setAttribute(PdfAsHelper.PDF_SESSION_PREFIX + SL20Constants.SL20_TRANSACTIONID, transactionId);
+
+
+ //validate reqId with inResponseTo
+ String sl20ReqId = (String) request.getSession(false).getAttribute(PdfAsHelper.PDF_SESSION_PREFIX + SL20Constants.SL20_REQID);
+ String inRespTo = SL20JSONExtractorUtils.getStringValue(sl20ReqObj, SL20Constants.SL20_INRESPTO, true);
+ if (sl20ReqId == null || !sl20ReqId.equals(inRespTo)) {
+ logger.info("SL20 'reqId': " + sl20ReqId + " does NOT match to 'inResponseTo':" + inRespTo);
+ throw new SL20SecurityException("SL20 'reqId': " + sl20ReqId + " does NOT match to 'inResponseTo':" + inRespTo);
+ }
+
+ JsonSecurityUtils joseTools = JsonSecurityUtils.getInstance();
+ if (!joseTools.isInitialized())
+ joseTools = null;
+
+ //validate signature
+ VerificationResult payLoadContainer = SL20JSONExtractorUtils.extractSL20PayLoad(sl20ReqObj, joseTools,
+ WebConfiguration.isSL20SigningRequired());
+
+ if ( (payLoadContainer.isValidSigned() == null || !payLoadContainer.isValidSigned())) {
+ if (WebConfiguration.isSL20SigningRequired()) {
+ logger.info("SL20 result from VDA was not valid signed");
+ throw new SL20SecurityException("Signature on SL20 result NOT valid.");
+
+ } else {
+ logger.warn("SL20 result from VDA is NOT valid signed, but signatures-verification is DISABLED by configuration!");
+
+ }
+ }
+
+ //extract payloaf
+ JsonObject payLoad = payLoadContainer.getPayload();
+
+ //check response type
+ if (SL20JSONExtractorUtils.getStringValue(
+ payLoad, SL20Constants.SL20_COMMAND_CONTAINER_NAME, true)
+ .equals(SL20Constants.SL20_COMMAND_IDENTIFIER_GETCERTIFICATE)) {
+ logger.debug("Find " + SL20Constants.SL20_COMMAND_IDENTIFIER_GETCERTIFICATE + " result .... ");
+
+ JsonElement getCertificateResult = SL20JSONExtractorUtils.extractSL20Result(
+ payLoad, joseTools,
+ WebConfiguration.isSL20EncryptionRequired());
+
+ //extract certificates
+ List<String> certsB64 = SL20JSONExtractorUtils.getListOfStringElements(getCertificateResult.getAsJsonObject(),
+ SL20Constants.SL20_COMMAND_PARAM_GETCERTIFICATE_RESULT_CERTIFICATE,
+ true);
+
+ if (certsB64.isEmpty()) {
+ logger.warn("SL20 'getCertificate' result contains NO certificate");
+ throw new SLCommandoParserException();
+
+ } else if (certsB64.size() == 1) {
+ logger.debug("SL20 'getCertificate' result contains only one certificate");
+ PdfAsHelper.injectCertificate(request, response, Base64.getDecoder().decode(certsB64.get(0)), getServletContext());
+
+ } else {
+ logger.debug("SL20 'getCertificate' result contains more than one certificate. Certificates must be sorted ... ");
+ List<X509Certificate> certs = new ArrayList<X509Certificate>();
+ for (String certB64 : certsB64)
+ certs.add(new iaik.x509.X509Certificate(Base64.getDecoder().decode(certB64)));
+
+ List<X509Certificate> sortedCerts = X509Utils.sortCertificates(certs);
+ logger.debug("Sorting of certificate completed. Select end-user certificate ... ");
+ PdfAsHelper.injectCertificate(request, response, Base64.getDecoder().decode(sortedCerts.get(0).getEncoded()), getServletContext());
+
+ }
+
+ } else if (SL20JSONExtractorUtils.getStringValue(
+ payLoad, SL20Constants.SL20_COMMAND_CONTAINER_NAME, true)
+ .equals(SL20Constants.SL20_COMMAND_IDENTIFIER_CREATE_SIG_CADES)) {
+ logger.debug("Find " + SL20Constants.SL20_COMMAND_IDENTIFIER_CREATE_SIG_CADES + " result .... ");
+
+ JsonElement getCertificateResult = SL20JSONExtractorUtils.extractSL20Result(
+ payLoad, joseTools,
+ WebConfiguration.isSL20EncryptionRequired());
+
+ //extract CAdES signature
+ String cadesSigB64 = SL20JSONExtractorUtils.getStringValue(
+ getCertificateResult.getAsJsonObject(),
+ SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_RESULT_SIGNATURE,
+ true);
+
+ if (StringUtils.isEmpty(cadesSigB64)) {
+ logger.warn("SL20 'createCAdES' result contains NO signature");
+ throw new SLCommandoParserException();
+ }
+
+ PdfAsHelper.injectSignature(request, response, Base64.getDecoder().decode(cadesSigB64), getServletContext());
+
+ } else {
+ logger.info("SL20 response is NOT a " + SL20Constants.SL20_COMMAND_IDENTIFIER_QUALIFIEDEID + " result");
+ throw new SLCommandoParserException();
+
+ }
+
+ } catch (Exception e) {
+ logger.warn("Error in DataURL Servlet. " , e);
+ PdfAsHelper.setSessionException(request, response, e.getMessage(),
+ e);
+
+ if (PdfAsHelper.getFromDataUrl(request)) {
+ String errorUrl = PdfAsHelper.generateErrorURL(request, response);
+ try {
+ String transactionId = null;
+ if (sl20ReqObj != null)
+ transactionId = SL20JSONExtractorUtils.getStringValue(sl20ReqObj, SL20Constants.SL20_TRANSACTIONID, false);
+
+ PdfAsHelper.buildSL20RedirectResponse(request, response, transactionId, errorUrl);
+
+ } catch (SL20Exception e1) {
+ logger.error("SL20 error-handling FAILED", e);
+ response.sendError(500, "Internal Server Error.");
+
+ }
+
+ } else
+ PdfAsHelper.gotoError(getServletContext(), request, response);
+ }
+ }
+}
diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java
index e8ac3658..73f8299c 100644
--- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java
+++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java
@@ -131,7 +131,8 @@ public class UIEntryPointServlet extends HttpServlet {
// IPlainSigner signer;
if (connector.equals(Connector.BKU)
|| connector.equals(Connector.ONLINEBKU)
- || connector.equals(Connector.MOBILEBKU)) {
+ || connector.equals(Connector.MOBILEBKU)
+ || connector.equals(Connector.SECLAYER20)) {
// start asynchronous signature creation
if (connector.equals(Connector.BKU)) {
@@ -154,6 +155,14 @@ public class UIEntryPointServlet extends HttpServlet {
"Invalid connector mobilebku is not supported");
}
}
+
+ if (connector.equals(Connector.SECLAYER20)) {
+ if (WebConfiguration.getSecurityLayer20URL() == null) {
+ throw new PdfAsWebException(
+ "Invalid connector mobilebku is not supported");
+ }
+ }
+
Map<String, String> map = null;
if (pdfAsRequest.getParameters().getPreprocessor() != null) {
map = pdfAsRequest.getParameters().getPreprocessor()
diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/JsonSecurityUtils.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/JsonSecurityUtils.java
new file mode 100644
index 00000000..141808de
--- /dev/null
+++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/JsonSecurityUtils.java
@@ -0,0 +1,381 @@
+package at.gv.egiz.pdfas.web.sl20;
+
+import java.io.IOException;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.commons.lang3.StringUtils;
+import org.bouncycastle.util.encoders.Base64Encoder;
+import org.jose4j.jwa.AlgorithmConstraints;
+import org.jose4j.jwa.AlgorithmConstraints.ConstraintType;
+import org.jose4j.jwe.JsonWebEncryption;
+import org.jose4j.jws.AlgorithmIdentifiers;
+import org.jose4j.jws.JsonWebSignature;
+import org.jose4j.jwx.JsonWebStructure;
+import org.jose4j.keys.X509Util;
+import org.jose4j.keys.resolvers.X509VerificationKeyResolver;
+import org.jose4j.lang.JoseException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+
+import at.gv.egiz.pdfas.web.config.WebConfiguration;
+import at.gv.egiz.sl20.data.VerificationResult;
+import at.gv.egiz.sl20.exceptions.SL20Exception;
+import at.gv.egiz.sl20.exceptions.SL20SecurityException;
+import at.gv.egiz.sl20.exceptions.SLCommandoBuildException;
+import at.gv.egiz.sl20.exceptions.SLCommandoParserException;
+import at.gv.egiz.sl20.utils.IJOSETools;
+import at.gv.egiz.sl20.utils.SL20Constants;
+
+public class JsonSecurityUtils implements IJOSETools{
+
+ private static final Logger logger = LoggerFactory.getLogger(JsonSecurityUtils.class);
+
+ private Key signPrivKey = null;
+ private X509Certificate[] signCertChain = null;
+ private Key encPrivKey = null;
+ private X509Certificate[] encCertChain = null;
+ private List<X509Certificate> trustedCerts = new ArrayList<X509Certificate>();
+
+ private boolean isInitialized = false;
+
+ private static JsonSecurityUtils instance = null;
+
+ public static JsonSecurityUtils getInstance() {
+ if (instance == null) {
+ instance = new JsonSecurityUtils();
+ instance.initalize();
+ }
+
+ return instance;
+ }
+
+ private JsonSecurityUtils() {
+
+
+ }
+
+ protected synchronized void initalize() {
+ logger.info("Initialize SL2.0 authentication security constrains ... ");
+ try {
+ String keyStorePath = getKeyStoreFilePath();
+
+ if (StringUtils.isNotEmpty(keyStorePath)) {
+ KeyStore keyStore = KeyStoreUtils.loadKeyStore(getKeyStoreFilePath(),
+ getKeyStorePassword());
+
+ //load signing key
+ signPrivKey = keyStore.getKey(getSigningKeyAlias(), getSigningKeyPassword().toCharArray());
+ Certificate[] certChainSigning = keyStore.getCertificateChain(getSigningKeyAlias());
+ signCertChain = new X509Certificate[certChainSigning.length];
+ for (int i=0; i<certChainSigning.length; i++) {
+ if (certChainSigning[i] instanceof X509Certificate) {
+ signCertChain[i] = (X509Certificate)certChainSigning[i];
+ } else
+ logger.warn("NO X509 certificate for signing: " + certChainSigning[i].getType());
+
+ }
+
+ //load encryption key
+ try {
+ encPrivKey = keyStore.getKey(getEncryptionKeyAlias(), getEncryptionKeyPassword().toCharArray());
+ if (encPrivKey != null) {
+ Certificate[] certChainEncryption = keyStore.getCertificateChain(getEncryptionKeyAlias());
+ encCertChain = new X509Certificate[certChainEncryption.length];
+ for (int i=0; i<certChainEncryption.length; i++) {
+ if (certChainEncryption[i] instanceof X509Certificate) {
+ encCertChain[i] = (X509Certificate)certChainEncryption[i];
+ } else
+ logger.warn("NO X509 certificate for encryption: " + certChainEncryption[i].getType());
+ }
+ } else
+ logger.info("No encryption key for SL2.0 found. End-to-End encryption is not used.");
+
+ } catch (Exception e) {
+ logger.warn("No encryption key for SL2.0 found. End-to-End encryption is not used. Reason: " + e.getMessage(), e);
+
+ }
+
+ //load trusted certificates
+ Enumeration<String> aliases = keyStore.aliases();
+ while(aliases.hasMoreElements()) {
+ String el = aliases.nextElement();
+ logger.trace("Process TrustStoreEntry: " + el);
+ if (keyStore.isCertificateEntry(el)) {
+ Certificate cert = keyStore.getCertificate(el);
+ if (cert != null && cert instanceof X509Certificate)
+ trustedCerts.add((X509Certificate) cert);
+ else
+ logger.info("Can not process entry: " + el + ". Reason: " + cert.toString());
+
+ }
+ }
+
+ //some short validation
+ if (signPrivKey == null || !(signPrivKey instanceof PrivateKey)) {
+ logger.info("Can NOT open privateKey for SL2.0 signing. KeyStore=" + getKeyStoreFilePath());
+ throw new SL20Exception("sl20.03");
+
+ }
+
+ if (signCertChain == null || signCertChain.length == 0) {
+ logger.info("NO certificate for SL2.0 signing. KeyStore=" + getKeyStoreFilePath());
+ throw new SL20Exception("sl20.03");
+
+ }
+
+ isInitialized = true;
+ logger.info("SL2.0 authentication security constrains initialized.");
+
+ } else
+ logger.info("SL2.0 security constrains not configurated!");
+
+ } catch ( Exception e) {
+ logger.error("SL2.0 security constrains initialization FAILED.", e);
+
+ }
+
+ }
+
+
+ @Override
+ public String createSignature(String payLoad) throws SLCommandoBuildException {
+ try {
+ JsonWebSignature jws = new JsonWebSignature();
+
+ //set payload
+ jws.setPayload(payLoad);
+
+ //set basic header
+ jws.setContentTypeHeaderValue(SL20Constants.SL20_CONTENTTYPE_SIGNED_COMMAND);
+
+ //set signing information
+ jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
+ jws.setKey(signPrivKey);
+
+ //TODO:
+ jws.setCertificateChainHeaderValue(signCertChain);
+ jws.setX509CertSha256ThumbprintHeaderValue(signCertChain[0]);
+
+ return jws.getCompactSerialization();
+
+ } catch (JoseException e) {
+ logger.warn("Can NOT sign SL2.0 command.", e);
+ throw new SLCommandoBuildException(e);
+
+ }
+
+ }
+
+ @Override
+ public VerificationResult validateSignature(String serializedContent) throws SL20Exception {
+ try {
+ JsonWebSignature jws = new JsonWebSignature();
+ //set payload
+ jws.setCompactSerialization(serializedContent);
+
+ //set security constrains
+ jws.setAlgorithmConstraints(new AlgorithmConstraints(ConstraintType.WHITELIST,
+ SL20Constants.SL20_ALGORITHM_WHITELIST_SIGNING.toArray(new String[SL20Constants.SL20_ALGORITHM_WHITELIST_SIGNING.size()])));
+
+ //load signinc certs
+ Key selectedKey = null;
+ List<X509Certificate> x5cCerts = jws.getCertificateChainHeaderValue();
+ String x5t256 = jws.getX509CertSha256ThumbprintHeaderValue();
+ if (x5cCerts != null) {
+ logger.debug("Found x509 certificate in JOSE header ... ");
+ logger.trace("Sorting received X509 certificates ... ");
+ List<X509Certificate> sortedX5cCerts = X509Utils.sortCertificates(x5cCerts);
+
+ if (trustedCerts.contains(sortedX5cCerts.get(0))) {
+ selectedKey = sortedX5cCerts.get(0).getPublicKey();
+
+ } else {
+ logger.info("Can NOT find JOSE certificate in truststore.");
+ logger.debug("JOSE certificate: " + sortedX5cCerts.get(0).toString());
+ try {
+ logger.debug("Cert: " + Base64.getEncoder().encodeToString(sortedX5cCerts.get(0).getEncoded()));
+
+ } catch (CertificateEncodingException e) {
+ e.printStackTrace();
+
+ }
+
+ }
+
+ } else if (StringUtils.isNotEmpty(x5t256)) {
+ logger.debug("Found x5t256 fingerprint in JOSE header .... ");
+ X509VerificationKeyResolver x509VerificationKeyResolver = new X509VerificationKeyResolver(trustedCerts);
+ selectedKey = x509VerificationKeyResolver.resolveKey(jws, Collections.<JsonWebStructure>emptyList());
+
+ } else {
+ logger.info("Signed SL2.0 response contains NO signature certificate or NO certificate fingerprint");
+ throw new SLCommandoParserException();
+
+ }
+
+ if (selectedKey == null) {
+ logger.info("Can NOT select verification key for JWS. Signature verification FAILED.");
+ throw new SLCommandoParserException();
+
+ }
+
+ //set verification key
+ jws.setKey(selectedKey);
+
+ //validate signature
+ boolean valid = jws.verifySignature();
+ if (!valid) {
+ logger.info("JWS signature invalide. Stopping authentication process ...");
+ logger.debug("Received JWS msg: " + serializedContent);
+ throw new SL20SecurityException();
+
+ }
+
+
+ //load payLoad
+ logger.debug("SL2.0 commando signature validation sucessfull");
+ JsonElement sl20Req = new JsonParser().parse(jws.getPayload());
+
+ return new VerificationResult(sl20Req.getAsJsonObject(), null, valid) ;
+
+ } catch (JoseException e) {
+ logger.warn("SL2.0 commando signature validation FAILED", e);
+ throw new SL20SecurityException(e);
+
+ }
+
+ }
+
+
+ @Override
+ public JsonElement decryptPayload(String compactSerialization) throws SL20Exception {
+ try {
+ JsonWebEncryption receiverJwe = new JsonWebEncryption();
+
+ //set security constrains
+ receiverJwe.setAlgorithmConstraints(
+ new AlgorithmConstraints(ConstraintType.WHITELIST,
+ SL20Constants.SL20_ALGORITHM_WHITELIST_KEYENCRYPTION.toArray(new String[SL20Constants.SL20_ALGORITHM_WHITELIST_KEYENCRYPTION.size()])));
+ receiverJwe.setContentEncryptionAlgorithmConstraints(
+ new AlgorithmConstraints(ConstraintType.WHITELIST,
+ SL20Constants.SL20_ALGORITHM_WHITELIST_ENCRYPTION.toArray(new String[SL20Constants.SL20_ALGORITHM_WHITELIST_ENCRYPTION.size()])));
+
+ //set payload
+ receiverJwe.setCompactSerialization(compactSerialization);
+
+
+ //validate key from header against key from config
+ List<X509Certificate> x5cCerts = receiverJwe.getCertificateChainHeaderValue();
+ String x5t256 = receiverJwe.getX509CertSha256ThumbprintHeaderValue();
+ if (x5cCerts != null) {
+ logger.debug("Found x509 certificate in JOSE header ... ");
+ logger.trace("Sorting received X509 certificates ... ");
+ List<X509Certificate> sortedX5cCerts = X509Utils.sortCertificates(x5cCerts);
+
+ if (!sortedX5cCerts.get(0).equals(encCertChain[0])) {
+ logger.info("Certificate from JOSE header does NOT match encryption certificate");
+ logger.debug("JOSE certificate: " + sortedX5cCerts.get(0).toString());
+
+ try {
+ logger.debug("Cert: " + Base64.getEncoder().encodeToString(sortedX5cCerts.get(0).getEncoded()));
+ } catch (CertificateEncodingException e) {
+ e.printStackTrace();
+ }
+ throw new SL20Exception("sl20.05");
+ }
+
+ } else if (StringUtils.isNotEmpty(x5t256)) {
+ logger.debug("Found x5t256 fingerprint in JOSE header .... ");
+ String certFingerPrint = X509Util.x5tS256(encCertChain[0]);
+ if (!certFingerPrint.equals(x5t256)) {
+ logger.info("X5t256 from JOSE header does NOT match encryption certificate");
+ logger.debug("X5t256 from JOSE header: " + x5t256 + " Encrytption cert: " + certFingerPrint);
+ throw new SL20Exception("sl20.05");
+
+ }
+
+ } else {
+ logger.info("Signed SL2.0 response contains NO signature certificate or NO certificate fingerprint");
+ throw new SLCommandoParserException();
+
+ }
+
+ //set key
+ receiverJwe.setKey(encPrivKey);
+
+
+ //decrypt payload
+ return new JsonParser().parse(receiverJwe.getPlaintextString());
+
+ } catch (JoseException e) {
+ logger.warn("SL2.0 result decryption FAILED", e);
+ throw new SL20SecurityException(e);
+
+ } catch ( JsonSyntaxException e) {
+ logger.warn("Decrypted SL2.0 result is NOT a valid JSON.", e);
+ throw new SLCommandoParserException(e);
+
+ }
+
+ }
+
+
+
+ @Override
+ public X509Certificate getEncryptionCertificate() {
+ //TODO: maybe update after SL2.0 update on encryption certificate parts
+ if (encCertChain !=null && encCertChain.length > 0)
+ return encCertChain[0];
+ else
+ return null;
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return isInitialized;
+
+ }
+
+ private String getKeyStoreFilePath() {
+ return WebConfiguration.getSL20KeyStorePath();
+ }
+
+ private String getKeyStorePassword() {
+ return WebConfiguration.getSL20KeyStorePassword();
+
+ }
+
+ private String getSigningKeyAlias() {
+ return WebConfiguration.getSL20KeySigningAlias();
+ }
+
+ private String getSigningKeyPassword() {
+ return WebConfiguration.getSL20KeySigningPassword();
+
+ }
+
+ private String getEncryptionKeyAlias() {
+ return WebConfiguration.getSL20KeyEncryptionAlias();
+ }
+
+ private String getEncryptionKeyPassword() {
+ return WebConfiguration.getSL20KeyEncryptionPassword();
+ }
+
+}
diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/KeyStoreUtils.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/KeyStoreUtils.java
new file mode 100644
index 00000000..c7472a22
--- /dev/null
+++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/KeyStoreUtils.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2003 Federal Chancellery Austria
+ * MOA-ID has been developed in a cooperation between BRZ, the Federal
+ * Chancellery Austria - ICT staff unit, 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.web.sl20;
+
+import iaik.x509.X509Certificate;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.cert.Certificate;
+
+/**
+ * Utility for creating and loading key stores.
+ *
+ * @author Paul Ivancsics
+ * @version $Id$
+ */
+public class KeyStoreUtils {
+
+ /**
+ * JAVA KeyStore
+ */
+ private static final String KEYSTORE_TYPE_JKS = "JKS";
+
+ /**
+ * PKCS12 KeyStore
+ */
+ private static final String KEYSTORE_TYPE_PKCS12 = "PKCS12";
+
+
+
+ /**
+ * Loads a key store from file.
+ *
+ * @param keystoreType key store type
+ * @param urlString URL of key store
+ * @param password password protecting the key store
+ * @return key store loaded
+ * @throws IOException thrown while reading the key store from file
+ * @throws GeneralSecurityException thrown while creating the key store
+ */
+ public static KeyStore loadKeyStore(
+ String keystoreType,
+ String urlString,
+ String password)
+ throws IOException, GeneralSecurityException {
+
+ URL keystoreURL = new URL(urlString);
+ InputStream in = keystoreURL.openStream();
+ return loadKeyStore(keystoreType, in, password);
+ }
+ /**
+ * Loads a key store from an <code>InputStream</code>, and
+ * closes the <code>InputStream</code>.
+ *
+ * @param keystoreType key store type
+ * @param in input stream
+ * @param password password protecting the key store
+ * @return key store loaded
+ * @throws IOException thrown while reading the key store from the stream
+ * @throws GeneralSecurityException thrown while creating the key store
+ */
+ public static KeyStore loadKeyStore(
+ String keystoreType,
+ InputStream in,
+ String password)
+ throws IOException, GeneralSecurityException {
+
+ char[] chPassword = null;
+ if (password != null)
+ chPassword = password.toCharArray();
+ KeyStore ks = KeyStore.getInstance(keystoreType);
+ ks.load(in, chPassword);
+ in.close();
+ return ks;
+ }
+ /**
+ * Creates a key store from X509 certificate files, aliasing them with
+ * the index in the <code>String[]</code>, starting with <code>"0"</code>.
+ *
+ * @param keyStoreType key store type
+ * @param certFilenames certificate filenames
+ * @return key store created
+ * @throws IOException thrown while reading the certificates from file
+ * @throws GeneralSecurityException thrown while creating the key store
+ */
+ public static KeyStore createKeyStore(
+ String keyStoreType,
+ String[] certFilenames)
+ throws IOException, GeneralSecurityException {
+
+ KeyStore ks = KeyStore.getInstance(keyStoreType);
+ ks.load(null, null);
+ for (int i = 0; i < certFilenames.length; i++) {
+ Certificate cert = loadCertificate(certFilenames[i]);
+ ks.setCertificateEntry("" + i, cert);
+ }
+ return ks;
+ }
+// /**
+// * Creates a key store from a directory containg X509 certificate files,
+// * aliasing them with the index in the <code>String[]</code>, starting with <code>"0"</code>.
+// * All the files in the directory are considered to be certificates.
+// *
+// * @param keyStoreType key store type
+// * @param certDirURLString file URL of directory containing certificate filenames
+// * @return key store created
+// * @throws IOException thrown while reading the certificates from file
+// * @throws GeneralSecurityException thrown while creating the key store
+// */
+// public static KeyStore createKeyStoreFromCertificateDirectory(
+// String keyStoreType,
+// String certDirURLString)
+// throws IOException, GeneralSecurityException {
+//
+// URL certDirURL = new URL(certDirURLString);
+// String certDirname = certDirURL.getFile();
+// File certDir = new File(certDirname);
+// String[] certFilenames = certDir.list();
+// String separator =
+// (certDirname.endsWith(File.separator) ? "" : File.separator);
+// for (int i = 0; i < certFilenames.length; i++) {
+// certFilenames[i] = certDirname + separator + certFilenames[i];
+// }
+// return createKeyStore(keyStoreType, certFilenames);
+// }
+
+ /**
+ * Loads an X509 certificate from file.
+ * @param certFilename filename
+ * @return the certificate loaded
+ * @throws IOException thrown while reading the certificate from file
+ * @throws GeneralSecurityException thrown while creating the certificate
+ */
+ private static Certificate loadCertificate(String certFilename)
+ throws IOException, GeneralSecurityException {
+
+ FileInputStream in = new FileInputStream(certFilename);
+ Certificate cert = new X509Certificate(in);
+ in.close();
+ return cert;
+ }
+
+
+ /**
+ * Loads a keyStore without knowing the keyStore type
+ * @param keyStorePath URL to the keyStore
+ * @param password Password protecting the keyStore
+ * @return keyStore loaded
+ * @throws KeyStoreException thrown if keyStore cannot be loaded
+ * @throws FileNotFoundException
+ * @throws IOException
+ */
+ public static KeyStore loadKeyStore(String keyStorePath, String password) throws KeyStoreException, IOException{
+
+ //InputStream is = new FileInputStream(keyStorePath);
+ URL keystoreURL = new URL(keyStorePath);
+ InputStream in = keystoreURL.openStream();
+ InputStream isBuffered = new BufferedInputStream(in);
+ return loadKeyStore(isBuffered, password);
+
+ }
+
+ /**
+ * Loads a keyStore without knowing the keyStore type
+ * @param in input stream
+ * @param password Password protecting the keyStore
+ * @return keyStore loaded
+ * @throws KeyStoreException thrown if keyStore cannot be loaded
+ * @throws FileNotFoundException
+ * @throws IOException
+ */
+public static KeyStore loadKeyStore(InputStream is, String password) throws KeyStoreException, IOException{
+ is.mark(1024*1024);
+ KeyStore ks = null;
+ try {
+ try {
+ ks = loadKeyStore(KEYSTORE_TYPE_PKCS12, is, password);
+ } catch (IOException e2) {
+ is.reset();
+ ks = loadKeyStore(KEYSTORE_TYPE_JKS, is, password);
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ //throw new KeyStoreException(e);
+ }
+ return ks;
+
+ }
+
+
+
+
+}
diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/SL20HttpBindingUtils.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/SL20HttpBindingUtils.java
new file mode 100644
index 00000000..f5d6ff55
--- /dev/null
+++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/SL20HttpBindingUtils.java
@@ -0,0 +1,47 @@
+package at.gv.egiz.pdfas.web.sl20;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.URISyntaxException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.entity.ContentType;
+import org.jose4j.base64url.Base64Url;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonObject;
+
+import at.gv.egiz.sl20.utils.SL20Constants;
+
+public class SL20HttpBindingUtils {
+ private static final org.slf4j.Logger log = LoggerFactory.getLogger(SL20HttpBindingUtils.class);
+
+ public static void writeIntoResponse(HttpServletRequest request, HttpServletResponse response, JsonObject sl20Forward, String redirectURL) throws IOException, URISyntaxException {
+ //forward SL2.0 command
+ if (request.getHeader(SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE) != null &&
+ request.getHeader(SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE).equals(SL20Constants.HTTP_HEADER_VALUE_NATIVE)) {
+ log.debug("Client request containts 'native client' header ... ");
+ StringWriter writer = new StringWriter();
+ writer.write(sl20Forward.toString());
+ final byte[] content = writer.toString().getBytes("UTF-8");
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setContentLength(content.length);
+ response.setContentType(ContentType.APPLICATION_JSON.toString());
+ response.getOutputStream().write(content);
+
+ } else {
+ log.debug("Client request containts is no native client ... ");
+ URIBuilder clientRedirectURI = new URIBuilder(redirectURL);
+ clientRedirectURI.addParameter(
+ SL20Constants.PARAM_SL20_REQ_COMMAND_PARAM,
+ Base64Url.encode(sl20Forward.toString().getBytes()));
+ response.setStatus(307);
+ response.setHeader("Location", clientRedirectURI.build().toString());
+
+ }
+
+ }
+}
diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/X509Utils.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/X509Utils.java
new file mode 100644
index 00000000..391b8271
--- /dev/null
+++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/X509Utils.java
@@ -0,0 +1,62 @@
+package at.gv.egiz.pdfas.web.sl20;
+
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+import javax.security.auth.x500.X500Principal;
+
+public class X509Utils {
+
+ /**
+ * Sorts the Certificate Chain by IssuerDN and SubjectDN. The [0]-Element should be the Hostname,
+ * the last Element should be the Root Certificate.
+ *
+ * @param certs
+ * The first element must be the correct one.
+ * @return sorted Certificate Chain
+ */
+ public static List<X509Certificate> sortCertificates(
+ List<X509Certificate> certs)
+ {
+ int length = certs.size();
+ if (certs.size() <= 1)
+ {
+ return certs;
+ }
+
+ for (X509Certificate cert : certs)
+ {
+ if (cert == null)
+ {
+ throw new NullPointerException();
+ }
+ }
+
+ for (int i = 0; i < length; i++)
+ {
+ boolean found = false;
+ X500Principal issuer = certs.get(i).getIssuerX500Principal();
+ for (int j = i + 1; j < length; j++)
+ {
+ X500Principal subject = certs.get(j).getSubjectX500Principal();
+ if (issuer.equals(subject))
+ {
+ // sorting necessary?
+ if (i + 1 != j)
+ {
+ X509Certificate tmp = certs.get(i + 1);
+ certs.set(i + 1, certs.get(j));
+ certs.set(j, tmp);
+ }
+ found = true;
+ }
+ }
+ if (!found)
+ {
+ break;
+ }
+ }
+
+ return certs;
+ }
+}
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 a7bb5f74..9b69c983 100644
--- a/pdf-as-web/src/main/webapp/WEB-INF/web.xml
+++ b/pdf-as-web/src/main/webapp/WEB-INF/web.xml
@@ -85,6 +85,12 @@
<description></description>
<servlet-class>at.gv.egiz.pdfas.web.servlets.DataURLServlet</servlet-class>
</servlet>
+ <servlet>
+ <servlet-name>SLDataURLServlet</servlet-name>
+ <display-name>SLDataURLServlet</display-name>
+ <description></description>
+ <servlet-class>at.gv.egiz.pdfas.web.servlets.SLDataURLServlet</servlet-class>
+ </servlet>
<servlet>
<servlet-name>VisBlockServlet</servlet-name>
<display-name>VisBlockServlet</display-name>
@@ -185,6 +191,10 @@
<url-pattern>/DataURL</url-pattern>
</servlet-mapping>
<servlet-mapping>
+ <servlet-name>SLDataURLServlet</servlet-name>
+ <url-pattern>/DataURLSL20</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
<servlet-name>VerifyServlet</servlet-name>
<url-pattern>/Verify</url-pattern>
</servlet-mapping>
diff --git a/pdf-as-web/src/main/webapp/index.jsp b/pdf-as-web/src/main/webapp/index.jsp
index de41028b..c07b2cc0 100644
--- a/pdf-as-web/src/main/webapp/index.jsp
+++ b/pdf-as-web/src/main/webapp/index.jsp
@@ -37,6 +37,17 @@
<%
}
%>
+
+ <%
+ if (WebConfiguration.getSecurityLayer20URL() != null) {
+ %>
+ <button type="submit" value="sl20" name="connector" id="sl20backend">SL2.0 Interface</button>
+ <label for="placeholder_web_id">Placeholder ID</label>
+ <input type="text" id="placeholder_web_id" name="placeholder_web_id">
+ <%
+ }
+ %>
+
<%
if (WebConfiguration.getKeystoreDefaultEnabled()) {
%>