From 9acf3c2e8aca9016daf76785747d838cdc5b0330 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Mon, 9 Jul 2018 10:11:25 +0200 Subject: add SL20 connecter-backend in a first beta version (getCertificate looks good, create signature is untested) --- pdf-as-lib/build.gradle | 3 + .../pdfas/lib/api/IConfigurationConstants.java | 1 + .../at/gv/egiz/pdfas/lib/util/StreamUtils.java | 168 ++++++ .../sl/schema/CreateCMSSignatureRequestType.java | 2 +- .../main/java/at/gv/egiz/sl20/SL20Connector.java | 98 ++++ .../at/gv/egiz/sl20/data/VerificationResult.java | 39 ++ .../at/gv/egiz/sl20/exceptions/SL20Exception.java | 19 + .../sl20/exceptions/SL20SecurityException.java | 20 + .../sl20/exceptions/SLCommandoBuildException.java | 17 + .../sl20/exceptions/SLCommandoParserException.java | 17 + .../java/at/gv/egiz/sl20/utils/IJOSETools.java | 54 ++ .../java/at/gv/egiz/sl20/utils/SL20Constants.java | 232 ++++++++ .../gv/egiz/sl20/utils/SL20JSONBuilderUtils.java | 604 +++++++++++++++++++++ .../gv/egiz/sl20/utils/SL20JSONExtractorUtils.java | 422 ++++++++++++++ 14 files changed, 1695 insertions(+), 1 deletion(-) create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/StreamUtils.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/sl20/SL20Connector.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/sl20/data/VerificationResult.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SL20Exception.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SL20SecurityException.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SLCommandoBuildException.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/sl20/exceptions/SLCommandoParserException.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/IJOSETools.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/SL20Constants.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/SL20JSONBuilderUtils.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/sl20/utils/SL20JSONExtractorUtils.java (limited to 'pdf-as-lib') 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 InputStreams. + * + * @param is1 The 1st InputStream to compare. + * @param is2 The 2nd InputStream to compare. + * @return boolean true, if both streams contain the exactly the + * same content, false 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 true, if the byte arrays are equal, false + * 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 InputStream to read. + * @return The bytes contained in the given InputStream. + * @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 String from a stream, using given encoding. + * @param in The InputStream to read. + * @param encoding The character encoding to use for converting the bytes + * of the InputStream into a String. + * @return The content of the given InputStream converted into + * a String. + * @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 Throwable passed in as a string. + * @param t The Throwable. + * @return a String representing the stack trace of the Throwable. + */ + 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 parameters = new ArrayList();; + 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 certs = null; + private JsonObject payload = null; + + public VerificationResult(JsonObject payload) { + this.payload = payload; + + } + + public VerificationResult(JsonObject string, List certs, boolean wasValidSigned) { + this.payload = string; + this.certs = certs; + this.validSigned = wasValidSigned; + + } + + public Boolean isValidSigned() { + return validSigned; + } + public List 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 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 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 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 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 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 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 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 displayData, List 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
+ * 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 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 keyValuePairs) throws SLCommandoBuildException { + validateParentAndKey(parent, keyId); + if (keyValuePairs != null) { + JsonArray callReqParamsArray = new JsonArray(); + parent.add(keyId, callReqParamsArray ); + + for(Entry 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 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 getListOfStringElements(JsonObject input, String keyID, boolean isRequired) throws SLCommandoParserException { + JsonElement internal = getAndCheck(input, keyID, isRequired); + + List result = new ArrayList(); + if (internal != null) { + if (internal.isJsonArray()) { + Iterator 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 getMapOfStringElements(JsonElement input) throws SLCommandoParserException { + Map result = new HashMap(); + + if (input != null) { + if (input.isJsonArray()) { + Iterator arrayIterator = input.getAsJsonArray().iterator(); + while(arrayIterator.hasNext()) { + JsonElement next = arrayIterator.next(); + Iterator> entry = next.getAsJsonObject().entrySet().iterator(); + entitySetToMap(result, entry); + + } + + } else if (input.isJsonObject()) { + Iterator> 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 result, Iterator> entry) { + while (entry.hasNext()) { + Entry 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; + + } +} -- cgit v1.2.3 From e5f2601e931594572f88bb402343e39023dab939 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Wed, 25 Jul 2018 08:06:36 +0200 Subject: update to SL2.0 v0.61 fix a bug in SL2.0 impl for createCAdES --- .../java/at/gv/egiz/sl20/utils/SL20Constants.java | 5 ++- .../gv/egiz/sl20/utils/SL20JSONBuilderUtils.java | 44 +++++++++++++++++++--- 2 files changed, 42 insertions(+), 7 deletions(-) (limited to 'pdf-as-lib') 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 index 59c3079d..fdefa1d9 100644 --- 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 @@ -160,6 +160,8 @@ public class SL20Constants { //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_CONTENTURL = "contentUrl"; + public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_CONTENTMODE = "contentMode"; 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"; @@ -176,7 +178,8 @@ public class SL20Constants { 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"; - + public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_CONTENTMODE_DETACHED = "detached"; + public static final String SL20_COMMAND_PARAM_CREATE_SIG_CADES_CONTENTMODE_ENVELOPING = "enveloping"; //create binding key command public static final String SL20_COMMAND_PARAM_BINDING_CREATE_KONTOID = "kontoID"; 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 index 40edb74b..7cbb7800 100644 --- 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 @@ -2,13 +2,10 @@ 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; @@ -182,14 +179,37 @@ public class SL20JSONBuilderUtils { } public static JsonObject createCreateCAdESCommandParameters(String keyId, - byte[] content, String mimeType, boolean padesCompatiblem, List byteRanges, String cadesLevel, + byte[] content, String contentUrl, String contentMode, String mimeType, boolean padesCompatiblem, List 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); + + if (content != null && contentUrl != null) { + log.warn(SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_CONTENT + " and " + + SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_CONTENTURL + " can not SET TWICE"); + throw new SLCommandoBuildException(); + + } + + if (content != null) + addSingleByteElement(params, SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_CONTENT, content, true); + + else if (contentUrl != null ) + addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_CONTENTURL, contentUrl, true); + + else { + log.warn(SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_CONTENT + " and " + + SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_CONTENTURL + " is NULL"); + throw new SLCommandoBuildException(); + + } + + addSingleStringElement(params, SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_CONTENTMODE, contentMode, 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); + + //addArrayOfStrings(params, SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_EXCLUDEBYTERANGE, byteRanges); + addArrayOfElements(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); @@ -452,6 +472,18 @@ public class SL20JSONBuilderUtils { } } + private static void addArrayOfElements(JsonObject parent, String keyId, List values) throws SLCommandoBuildException { + validateParentAndKey(parent, keyId); + if (values != null) { + JsonArray callReqParamsArray = new JsonArray(); + parent.add(keyId, callReqParamsArray ); + for(JsonElement el : values) + callReqParamsArray.add(el); + + } + + } + private static void addArrayOfStrings(JsonObject parent, String keyId, List values) throws SLCommandoBuildException { validateParentAndKey(parent, keyId); if (values != null) { -- cgit v1.2.3 From a5541ab96512cb78d493516ecf0dfb94a6544f52 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Thu, 26 Jul 2018 11:23:47 +0200 Subject: add logger on trace level for request/response debugging --- pdf-as-lib/src/main/java/at/gv/egiz/sl20/SL20Connector.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'pdf-as-lib') 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 index a82771bd..3088a564 100644 --- 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 @@ -62,7 +62,9 @@ public class SL20Connector extends BaseSLConnector { HttpResponse httpResp = httpClient.execute(httpReq); log.debug("Response from VDA received "); - return SL20JSONExtractorUtils.getSL20ContainerFromResponse(httpResp); + JsonObject sl20Resp = SL20JSONExtractorUtils.getSL20ContainerFromResponse(httpResp); + log.trace("SL20 command: " + sl20Resp.toString()); + return sl20Resp; } catch (URISyntaxException | IOException e) { log.warn("Can NOT build SL20 http requst. Reason:" + e.getMessage(), e); -- cgit v1.2.3 From 236cd00a49b04523a325e06fdc8839be9049f892 Mon Sep 17 00:00:00 2001 From: emusic Date: Fri, 27 Jul 2018 12:28:01 +0200 Subject: adding additional data transfer type --- .../main/java/at/gv/egiz/sl20/utils/SL20JSONBuilderUtils.java | 4 ++-- .../java/at/gv/egiz/sl20/utils/SL20JSONExtractorUtils.java | 10 ++-------- 2 files changed, 4 insertions(+), 10 deletions(-) (limited to 'pdf-as-lib') 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 index 40edb74b..efbc2890 100644 --- 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 @@ -182,11 +182,11 @@ public class SL20JSONBuilderUtils { } public static JsonObject createCreateCAdESCommandParameters(String keyId, - byte[] content, String mimeType, boolean padesCompatiblem, List byteRanges, String cadesLevel, + String pdfUrl, String mimeType, boolean padesCompatiblem, List 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_CONTENT, pdfUrl, 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); 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 index 5a438e16..5fbce83b 100644 --- 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 @@ -206,8 +206,7 @@ public class SL20JSONExtractorUtils { } } - - + 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); @@ -242,20 +241,15 @@ public class SL20JSONExtractorUtils { } } else - throw e; - + throw e; } - } else if (result != null) { return result; } else { log.error("Internal build error"); throw new SLCommandoParserException(); - } - - } /** -- cgit v1.2.3 From 3a1eaed4175bb1ec5b2dc2d2d13e125af997f286 Mon Sep 17 00:00:00 2001 From: emusic Date: Mon, 3 Sep 2018 16:19:18 +0200 Subject: delete unused statements --- pdf-as-lib/src/main/resources/config/config.zip | Bin 1276569 -> 1276569 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'pdf-as-lib') diff --git a/pdf-as-lib/src/main/resources/config/config.zip b/pdf-as-lib/src/main/resources/config/config.zip index 0b61bfdd..17fe5c6b 100644 Binary files a/pdf-as-lib/src/main/resources/config/config.zip and b/pdf-as-lib/src/main/resources/config/config.zip differ -- cgit v1.2.3 From be27da36e9b1e319bf8910228cdbcdd502afbc9d Mon Sep 17 00:00:00 2001 From: emusic Date: Tue, 18 Sep 2018 10:16:56 +0200 Subject: changes in pdfurl --- pdf-as-lib/src/configuration/cfg/advancedconfig.properties | 6 ------ 1 file changed, 6 deletions(-) (limited to 'pdf-as-lib') diff --git a/pdf-as-lib/src/configuration/cfg/advancedconfig.properties b/pdf-as-lib/src/configuration/cfg/advancedconfig.properties index 858b652f..e2d39c1c 100644 --- a/pdf-as-lib/src/configuration/cfg/advancedconfig.properties +++ b/pdf-as-lib/src/configuration/cfg/advancedconfig.properties @@ -105,9 +105,3 @@ default.verifier.01=at.gv.egiz.pdfas.sigs.pades.PAdESVerifier #sigblock.placement.bgcolor.detection.enabled=true #sigblock.placement.debug.file=/home/user/temp/debugImg.png #runtime.backend=PDFBOX_2_BACKEND - -################## -# Protect PDF files from copying and extractiong content -# Set values to be true|false|unchanged - -default.protectPDF = true -- cgit v1.2.3 From c87c82009fb07e285d6c9acfd4686efc2cbf21c9 Mon Sep 17 00:00:00 2001 From: emusic Date: Wed, 21 Nov 2018 14:23:07 +0100 Subject: remove unused statements --- .../main/java/at/gv/egiz/pdfas/lib/api/IConfigurationConstants.java | 3 --- 1 file changed, 3 deletions(-) (limited to 'pdf-as-lib') 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 f043ff21..a921bcd3 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 @@ -53,9 +53,6 @@ public interface IConfigurationConstants { public static final String DEFAULT_SIGNATURE_PROFILE = SIG_OBJECT + SEPERATOR + TYPE + SEPERATOR + DEFAULT; public static final String CONFIG_BKU_URL = "bku.sign.url"; - - /*Protect PDF content*/ - public static final String DEFAULT_CONFIG_PROTECT_PDF = DEFAULT+SEPERATOR+"protectPDF"; /** * MOA SS Signing Key Identifier -- cgit v1.2.3