aboutsummaryrefslogtreecommitdiff
path: root/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20
diff options
context:
space:
mode:
Diffstat (limited to 'pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20')
-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
4 files changed, 712 insertions, 0 deletions
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;
+ }
+}