diff options
| author | Thomas Lenz <thomas.lenz@egiz.gv.at> | 2018-07-09 10:11:25 +0200 | 
|---|---|---|
| committer | Thomas Lenz <thomas.lenz@egiz.gv.at> | 2018-07-09 10:11:25 +0200 | 
| commit | 9acf3c2e8aca9016daf76785747d838cdc5b0330 (patch) | |
| tree | f97d392a8eff4906c961128e231926a76829a4c8 /pdf-as-web | |
| parent | 797634c687c6f44d314e4baa3fed220d142eed73 (diff) | |
| download | pdf-as-4-9acf3c2e8aca9016daf76785747d838cdc5b0330.tar.gz pdf-as-4-9acf3c2e8aca9016daf76785747d838cdc5b0330.tar.bz2 pdf-as-4-9acf3c2e8aca9016daf76785747d838cdc5b0330.zip | |
add SL20 connecter-backend in a first beta version (getCertificate looks good, create signature is untested)
Diffstat (limited to 'pdf-as-web')
13 files changed, 1427 insertions, 69 deletions
| diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java index d63f698f..c6b27eb3 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java @@ -42,9 +42,12 @@ public class WebConfiguration implements IConfigurationConstants {  	public static final String LOCAL_BKU_ENABLED = "bku.sign.enabled";  	public static final String ONLINE_BKU_ENABLED = "moc.sign.enabled";  	public static final String MOBILE_BKU_ENABLED = "mobile.sign.enabled"; +	public static final String SL20_BKU_ENABLED = "sl20.sign.enabled";  	public static final String LOCAL_BKU_URL = "bku.local.url";  	public static final String ONLINE_BKU_URL = "bku.online.url";  	public static final String MOBILE_BKU_URL = "bku.mobile.url"; +	public static final String SL20_BKU_URL = "sl20.mobile.url"; +	  	public static final String ERROR_DETAILS = "error.showdetails";  	public static final String PDF_AS_WORK_DIR = "pdfas.dir";  	public static final String STATISTIC_BACKEND_LIST = "statistic.backends"; @@ -82,6 +85,23 @@ public class WebConfiguration implements IConfigurationConstants {  	public static final String KEYSTORE_DEFAULT_ALIAS = KEYSTORE_DEFAULT + "." + KEYSTORE_ALIAS;  	public static final String KEYSTORE_DEFAULT_KEY_PASS = KEYSTORE_DEFAULT + "." + KEYSTORE_KEY_PASS; +	//SL20 stuff +	public static final String SL20_PREFIX = "sl20"; +	public static final String SL20_KEYSTORE_PREFIX = SL20_PREFIX + ".keystore";  +	public static final String SL20_KEYSTORE_FILE = SL20_KEYSTORE_PREFIX + "." + "file"; +	public static final String SL20_KEYSTORE_TYPE = SL20_KEYSTORE_PREFIX + "." + "type"; +	public static final String SL20_KEYSTORE_PASS = SL20_KEYSTORE_PREFIX + "." + "pass"; +	public static final String SL20_KEYSTORE_KEY_SIGN_ALIAS = SL20_KEYSTORE_PREFIX + "." + "sign.key.alias"; +	public static final String SL20_KEYSTORE_KEY_SIGN_PASS = SL20_KEYSTORE_PREFIX + "." + "sign.key.pass"; +	public static final String SL20_KEYSTORE_KEY_ENCRYPTION_ALIAS = SL20_KEYSTORE_PREFIX + "." + "enc.key.alias"; +	public static final String SL20_KEYSTORE_KEY_ENCRYPTION_PASS = SL20_KEYSTORE_PREFIX + "." + "enc.key.pass"; +	public static final String SL20_DEBUG_VALIDATION_DISABLED = SL20_PREFIX + ".debug.validation.disable"; +	public static final String SL20_DEBUG_SIGNING_ENABLED = SL20_PREFIX + ".debug.signed.result.enabled"; +	public static final String SL20_DEBUG_SIGNING_REQUIRED = SL20_PREFIX + ".debug.signed.result.required"; +	public static final String SL20_DEBUG_ENCRYPTION_ENABLED = SL20_PREFIX + ".debug.encryption.enabled"; +	public static final String SL20_DEBUG_ENCRYPTION_REQUIRED = SL20_PREFIX + ".debug.encryption.required"; +	 +	  	public static final String WHITELIST_ENABLED = "whitelist.enabled";  	public static final String WHITELIST_VALUE_PRE = "whitelist.url."; @@ -248,6 +268,20 @@ public class WebConfiguration implements IConfigurationConstants {  		return null;  	} +	public static String getSecurityLayer20URL() { +		if(getSL20Enabled()) { +			String overwrite = properties.getProperty(SL20_SIGN_URL); +			if(overwrite == null) { +				overwrite = properties.getProperty(SL20_BKU_URL); +				if(overwrite == null) { +					overwrite = PdfAsHelper.getPdfAsConfig().getValue(SL20_SIGN_URL); +				} +			} +			return overwrite; +		} +		return null; +	} +	  	public static String getPdfASDir() {  		return properties.getProperty(PDF_AS_WORK_DIR);  	} @@ -447,6 +481,16 @@ public class WebConfiguration implements IConfigurationConstants {  		return false;  	} +	public static boolean getSL20Enabled() { +		String value = properties.getProperty(SL20_BKU_ENABLED); +		if (value != null) { +			if (value.equals("true")) { +				return true; +			} +		} +		return false; +	} +	  	public static boolean getSoapSignEnabled() {  		String value = properties.getProperty(SOAP_SIGN_ENABLED);  		if (value != null) { @@ -598,5 +642,66 @@ public class WebConfiguration implements IConfigurationConstants {  		}  		return false;  	} +	 +	public static String getSL20KeyStorePath() { +		return properties.getProperty(SL20_KEYSTORE_FILE); +		 +	} +	 +	public static String getSL20KeyStoreType() { +		return properties.getProperty(SL20_KEYSTORE_TYPE); +		 +	} +	 +	public static String getSL20KeyStorePassword() { +		return properties.getProperty(SL20_KEYSTORE_PASS); +		 +	} +	 +	public static String getSL20KeySigningAlias() { +		return properties.getProperty(SL20_KEYSTORE_KEY_SIGN_ALIAS); +		 +	} +	 +	public static String getSL20KeySigningPassword() { +		return properties.getProperty(SL20_KEYSTORE_KEY_SIGN_PASS); +		 +	} +	 +	public static String getSL20KeyEncryptionAlias() { +		return properties.getProperty(SL20_KEYSTORE_KEY_ENCRYPTION_ALIAS); +		 +	} +	 +	public static String getSL20KeyEncryptionPassword() { +		return properties.getProperty(SL20_KEYSTORE_KEY_ENCRYPTION_PASS); +		 +	} +	 +	public static boolean isSL20ValidationDisabled( ) { +		return Boolean.parseBoolean(properties.getProperty(SL20_DEBUG_VALIDATION_DISABLED, String.valueOf(false))); +		 +	} +	 +	public static boolean isSL20SigningEnabled( ) { +		return Boolean.parseBoolean(properties.getProperty(SL20_DEBUG_SIGNING_ENABLED, String.valueOf(false))); +		 +	} +	 +	public static boolean isSL20SigningRequired( ) { +		return Boolean.parseBoolean(properties.getProperty(SL20_DEBUG_SIGNING_REQUIRED, String.valueOf(false))); +		 +	} +	 +	public static boolean isSL20EncryptionEnabled( ) { +		return Boolean.parseBoolean(properties.getProperty(SL20_DEBUG_ENCRYPTION_ENABLED, String.valueOf(false))); +		 +	} +	 +	public static boolean isSL20EncryptionRequired( ) { +		return Boolean.parseBoolean(properties.getProperty(SL20_DEBUG_ENCRYPTION_REQUIRED, String.valueOf(false))); +		 +	} +	  } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java index 3aad831d..4b776cb3 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java @@ -30,12 +30,17 @@ import java.awt.image.RenderedImage;  import java.io.ByteArrayOutputStream;  import java.io.File;  import java.io.IOException; +import java.io.StringWriter;  import java.io.UnsupportedEncodingException; +import java.net.URL;  import java.net.URLEncoder;  import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.HashMap;  import java.util.Iterator;  import java.util.List;  import java.util.Map; +import java.util.UUID;  import javax.imageio.ImageIO;  import javax.servlet.RequestDispatcher; @@ -51,9 +56,12 @@ import org.apache.commons.codec.binary.Base64;  import org.apache.commons.io.FileUtils;  import org.apache.commons.io.IOUtils;  import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.http.entity.ContentType;  import org.slf4j.Logger;  import org.slf4j.LoggerFactory; +import com.google.gson.JsonObject; +  import at.gv.egiz.pdfas.api.ws.PDFASSignParameters;  import at.gv.egiz.pdfas.api.ws.PDFASSignParameters.Connector;  import at.gv.egiz.pdfas.api.ws.PDFASSignResponse; @@ -77,6 +85,8 @@ import at.gv.egiz.pdfas.sigs.pades.PAdESSignerKeystore;  import at.gv.egiz.pdfas.web.config.WebConfiguration;  import at.gv.egiz.pdfas.web.exception.PdfAsWebException;  import at.gv.egiz.pdfas.web.servlets.UIEntryPointServlet; +import at.gv.egiz.pdfas.web.sl20.JsonSecurityUtils; +import at.gv.egiz.pdfas.web.sl20.SL20HttpBindingUtils;  import at.gv.egiz.pdfas.web.stats.StatisticEvent;  import at.gv.egiz.sl.schema.CreateCMSSignatureResponseType;  import at.gv.egiz.sl.schema.InfoboxAssocArrayPairType; @@ -84,8 +94,16 @@ import at.gv.egiz.sl.schema.InfoboxReadRequestType;  import at.gv.egiz.sl.schema.InfoboxReadResponseType;  import at.gv.egiz.sl.schema.ObjectFactory;  import at.gv.egiz.sl.util.BKUSLConnector; +import at.gv.egiz.sl.util.BaseSLConnector;  import at.gv.egiz.sl.util.RequestPackage;  import at.gv.egiz.sl.util.SLMarschaller; +import at.gv.egiz.sl20.SL20Connector; +import at.gv.egiz.sl20.data.VerificationResult; +import at.gv.egiz.sl20.exceptions.SL20Exception; +import at.gv.egiz.sl20.exceptions.SLCommandoParserException; +import at.gv.egiz.sl20.utils.SL20Constants; +import at.gv.egiz.sl20.utils.SL20JSONBuilderUtils; +import at.gv.egiz.sl20.utils.SL20JSONExtractorUtils;  public class PdfAsHelper { @@ -105,6 +123,7 @@ public class PdfAsHelper {  	private static final String PDF_PROVIDE_PAGE = "/ProvidePDF";  	private static final String PDF_PDFDATA_PAGE = "/PDFData";  	private static final String PDF_DATAURL_PAGE = "/DataURL"; +	private static final String PDF_SL20_DATAURL_PAGE = "/DataURLSL20";  	private static final String PDF_USERENTRY_PAGE = "/userentry";  	private static final String PDF_ERR_URL = "PDF_ERR_URL";  	private static final String PDF_FILE_NAME = "PDF_FILE_NAME"; @@ -118,6 +137,7 @@ public class PdfAsHelper {  	private static final String SIGNATURE_ACTIVE = "SIGNATURE_ACTIVE";  	private static final String VERIFICATION_RESULT = "VERIFICATION_RESULT";  	private static final String QRCODE_CONTENT = "QR_CONT"; +	public static final String PDF_SESSION_PREFIX = "PDF_SESSION_";  	private static final Logger logger = LoggerFactory  			.getLogger(PdfAsHelper.class); @@ -707,6 +727,12 @@ public class PdfAsHelper {  			// conn.setBase64(true);  			signer = new PAdESSigner(conn);  			session.setAttribute(PDF_SL_CONNECTOR, conn); +			 +		} else if (connector.equals("sl20")) { +			SL20Connector conn = new SL20Connector(config); +			signer = new PAdESSigner(conn); +			session.setAttribute(PDF_SL_CONNECTOR, conn); +			  		} else {  			throw new PdfAsWebException(  					"Invalid connector (bku | onlinebku | mobilebku | moa | jks)"); @@ -794,9 +820,15 @@ public class PdfAsHelper {  			// conn.setBase64(true);  			signer = new PAdESSigner(conn);  			session.setAttribute(PDF_SL_CONNECTOR, conn); + +		} else if (connector.equals("sl20")) { +			SL20Connector conn = new SL20Connector(config); +			signer = new PAdESSigner(conn); +			session.setAttribute(PDF_SL_CONNECTOR, conn); +			  		} else {  			throw new PdfAsWebException( -					"Invalid connector (bku | onlinebku | mobilebku | moa | jks)"); +					"Invalid connector (bku | onlinebku | mobilebku | moa | jks | sl20)");  		}  		signParameter.setPreprocessorArguments(preProcessor);  		signParameter.setPlainSigner(signer); @@ -839,7 +871,7 @@ public class PdfAsHelper {  		PdfAsHelper.process(request, response, context);  	} -	private static byte[] getCertificate( +	public static byte[] getCertificate(  			InfoboxReadResponseType infoboxReadResponseType) {  		byte[] data = null;  		if (infoboxReadResponseType.getAssocArrayData() != null) { @@ -898,7 +930,7 @@ public class PdfAsHelper {  	public static void injectCertificate(HttpServletRequest request,  			HttpServletResponse response, -			InfoboxReadResponseType infoboxReadResponseType, +			byte[] certificate,  			ServletContext context) throws Exception {  		HttpSession session = request.getSession(); @@ -910,7 +942,7 @@ public class PdfAsHelper {  					+ session.getId());  		} -		statusRequest.setCertificate(getCertificate(infoboxReadResponseType)); +		statusRequest.setCertificate(certificate);  		statusRequest = pdfAs.process(statusRequest);  		session.setAttribute(PDF_STATUS, statusRequest); @@ -919,7 +951,7 @@ public class PdfAsHelper {  	public static void injectSignature(HttpServletRequest request,  			HttpServletResponse response, -			CreateCMSSignatureResponseType createCMSSignatureResponseType, +			byte[] cmsSginature,  			ServletContext context) throws Exception {  		logger.debug("Got CMS Signature Response"); @@ -933,8 +965,7 @@ public class PdfAsHelper {  					+ session.getId());  		} -		statusRequest.setSigature(createCMSSignatureResponseType -				.getCMSSignature()); +		statusRequest.setSigature(cmsSginature);  		statusRequest = pdfAs.process(statusRequest);  		session.setAttribute(PDF_STATUS, statusRequest); @@ -996,21 +1027,35 @@ public class PdfAsHelper {  		String connector = (String) session.getAttribute(PDF_SL_INTERACTIVE); +		//load connector +		BaseSLConnector slConnector = null;  		if (connector.equals("bku") || connector.equals("onlinebku") -				|| connector.equals("mobilebku")) { -			BKUSLConnector bkuSLConnector = (BKUSLConnector) session +				|| connector.equals("mobilebku")) +			slConnector = (BKUSLConnector) session  					.getAttribute(PDF_SL_CONNECTOR); - -			if (statusRequest.needCertificate()) { -				logger.debug("Needing Certificate from BKU"); -				// build SL Request to read certificate -				InfoboxReadRequestType readCertificateRequest = bkuSLConnector -						.createInfoboxReadRequest(statusRequest -								.getSignParameter()); - +		 +		else if (connector.equals("sl20")) +			slConnector = (SL20Connector) session +					.getAttribute(PDF_SL_CONNECTOR); +		 +		else +			throw new PdfAsWebException("Invalid connector: " + connector); +		 +		JsonSecurityUtils joseTools = JsonSecurityUtils.getInstance(); +		if (!joseTools.isInitialized()) +			joseTools = null; +		 +		if (statusRequest.needCertificate()) { +			logger.debug("Needing Certificate from BKU"); +			// build SL Request to read certificate +			InfoboxReadRequestType readCertificateRequest = slConnector +					.createInfoboxReadRequest(statusRequest +							.getSignParameter()); + +			if (slConnector instanceof BKUSLConnector) {  				JAXBElement<InfoboxReadRequestType> readRequest = of  						.createInfoboxReadRequest(readCertificateRequest); - +	  				String url = generateDataURL(request, response);  				String slRequest = SLMarschaller.marshalToString(readRequest);  				String template = getTemplateSL(); @@ -1021,7 +1066,7 @@ public class PdfAsHelper {  						StringEscapeUtils.escapeHtml4(slRequest));  				template = template.replace("##DataURL##", url);  				template = template.replace("##LOCALE##", locale); - +	  				if (statusRequest.getSignParameter().getTransactionId() != null) {  					template = template.replace(  							"##ADDITIONAL##", @@ -1034,70 +1079,220 @@ public class PdfAsHelper {  				} else {  					template = template.replace("##ADDITIONAL##", "");  				} - +	  				response.getWriter().write(template);  				// TODO: set content type of response!!  				response.setContentType("text/html");  				response.getWriter().close(); -			} else if (statusRequest.needSignature()) { -				logger.debug("Needing Signature from BKU"); -				// build SL Request for cms signature -				RequestPackage pack = bkuSLConnector.createCMSRequest( -						statusRequest.getSignatureData(), -						statusRequest.getSignatureDataByteRange(), -						statusRequest.getSignParameter()); - +				 +			} else if (slConnector instanceof SL20Connector) { +				//generate request for getCertificate command  +				SL20Connector sl20Connector = (SL20Connector)slConnector; +				 +				//use 'SecureSigningKeypair' per default +				String keyId = SL20Connector.SecureSignatureKeypair; +				 +				java.security.cert.X509Certificate x5cEnc = null; +				if (WebConfiguration.isSL20EncryptionEnabled() && joseTools != null) +					x5cEnc = joseTools.getEncryptionCertificate(); +				JsonObject getCertParams =  +						SL20JSONBuilderUtils.createGetCertificateCommandParameters( +								keyId, generateDataURLSL20(request, response), x5cEnc); +				 +				JsonObject sl20Req = null; +				String reqId = UUID.randomUUID().toString(); +				if (WebConfiguration.isSL20SigningEnabled()) { +					String signedCertCommand = SL20JSONBuilderUtils.createSignedCommand( +							SL20Constants.SL20_COMMAND_IDENTIFIER_GETCERTIFICATE, getCertParams, joseTools); +					sl20Req = SL20JSONBuilderUtils.createGenericRequest(reqId, null, null, signedCertCommand); +					 +				} else { +					JsonObject getCertCommand = SL20JSONBuilderUtils.createCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_GETCERTIFICATE, getCertParams); +					sl20Req = SL20JSONBuilderUtils.createGenericRequest(reqId, null, getCertCommand, null); +					 +				}	 +								 +				//send SL20 request via Backend connection +				JsonObject sl20Resp = sl20Connector.sendSL20Request(sl20Req, null, generateBKUURL(connector)); +				if (sl20Resp == null) { +					logger.info("Receive NO responce from SL2.0 connection. Process stops ... "); +					throw new SLCommandoParserException(); +					 +				} +				 +				VerificationResult respPayloadContainer = SL20JSONExtractorUtils.extractSL20PayLoad( +						sl20Resp, joseTools, WebConfiguration.isSL20SigningRequired()); +				 +				if (respPayloadContainer.isValidSigned() == null) +					logger.debug("Receive unsigned payLoad from VDA"); +					 +				JsonObject respPayload = respPayloadContainer.getPayload(); +				if (respPayload.get(SL20Constants.SL20_COMMAND_CONTAINER_NAME).getAsString() +						.equals(SL20Constants.SL20_COMMAND_IDENTIFIER_REDIRECT)) { +					logger.debug("Find 'redirect' command in VDA response ... ");									 +					JsonObject params = SL20JSONExtractorUtils.getJSONObjectValue(respPayload, SL20Constants.SL20_COMMAND_CONTAINER_PARAMS, true);					 +					String redirectURL = SL20JSONExtractorUtils.getStringValue(params, SL20Constants.SL20_COMMAND_PARAM_GENERAL_REDIRECT_URL, true);									 +					JsonObject command = SL20JSONExtractorUtils.getJSONObjectValue(params, SL20Constants.SL20_COMMAND_PARAM_GENERAL_REDIRECT_COMMAND, false); +					String signedCommand = SL20JSONExtractorUtils.getStringValue(params, SL20Constants.SL20_COMMAND_PARAM_GENERAL_REDIRECT_SIGNEDCOMMAND, false);					 + +					//create forward SL2.0 command +					JsonObject sl20Forward = sl20Resp.deepCopy().getAsJsonObject(); +					SL20JSONBuilderUtils.addOnlyOnceOfTwo(sl20Forward,  +							SL20Constants.SL20_PAYLOAD, SL20Constants.SL20_SIGNEDPAYLOAD,  +							command, signedCommand); +										 +					//store requestId +					 +					request.getSession(false).setAttribute(PDF_SESSION_PREFIX + SL20Constants.SL20_REQID, reqId); + +					//forward SL2.0 command +					SL20HttpBindingUtils.writeIntoResponse(request, response, sl20Forward, redirectURL); +													 +				} else if (respPayload.get(SL20Constants.SL20_COMMAND_CONTAINER_NAME).getAsString() +						.equals(SL20Constants.SL20_COMMAND_IDENTIFIER_ERROR)) {  +					JsonObject result = SL20JSONExtractorUtils.getJSONObjectValue(respPayload, SL20Constants.SL20_COMMAND_CONTAINER_RESULT, false); +					if (result  == null) +						result = SL20JSONExtractorUtils.getJSONObjectValue(respPayload, SL20Constants.SL20_COMMAND_CONTAINER_PARAMS, false); +					 +					String errorCode = SL20JSONExtractorUtils.getStringValue(result, SL20Constants.SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERRORCODE, true); +					String errorMsg = SL20JSONExtractorUtils.getStringValue(result, SL20Constants.SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERRORMESSAGE, true); +					 +					logger.info("Receive SL2.0 error. Code:" + errorCode + " Msg:" + errorMsg); +					throw new SL20Exception("sl20.08"); +					 +				} else { +					logger.warn("Received an unrecognized command: " + respPayload.get(SL20Constants.SL20_COMMAND_CONTAINER_NAME).getAsString()); +					throw new SLCommandoParserException(); +					 +				} +				 +			} else +				throw new PdfAsWebException("Invalid connector: " + slConnector.getClass().getName()); +			 +		} else if (statusRequest.needSignature()) { +			logger.debug("Needing Signature from BKU"); +			// build SL Request for cms signature +			RequestPackage pack = slConnector.createCMSRequest( +					statusRequest.getSignatureData(), +					statusRequest.getSignatureDataByteRange(), +					statusRequest.getSignParameter()); + +			if (slConnector instanceof BKUSLConnector) {						  				String slRequest = SLMarschaller  						.marshalToString(of  								.createCreateCMSSignatureRequest(pack  										.getRequestType()));  				logger.trace("SL Request: " + slRequest); - +				  				response.setContentType("text/xml");  				response.getWriter().write(slRequest);  				response.getWriter().close(); +				 +			} else if (slConnector instanceof SL20Connector) {				 +				//convert byte range +				List<String> byteRanges = new ArrayList<String>(); +				for (int el : statusRequest.getSignatureDataByteRange()) +					byteRanges.add(String.valueOf(el)); +				 +				java.security.cert.X509Certificate x5cEnc = null; +				if (WebConfiguration.isSL20EncryptionEnabled() && joseTools != null) +					x5cEnc = joseTools.getEncryptionCertificate(); + +				//set 'true' as default +				boolean padesCompatibel = true; +				if (pack.getRequestType().getPAdESFlag() != null) +					padesCompatibel = pack.getRequestType().getPAdESFlag(); +				 +				JsonObject createCAdESSigParams =  +						SL20JSONBuilderUtils.createCreateCAdESCommandParameters( +								pack.getRequestType().getKeyboxIdentifier(),  +								statusRequest.getSignatureData(),  +								pack.getRequestType().getDataObject().getMetaInfo().getMimeType(),  +								padesCompatibel ,  +								byteRanges,  +								SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_CADESLEVEL_BASIC,  +								generateDataURLSL20(request, response),  +								x5cEnc) ; +				 +				JsonObject sl20CreateCAdES = null; +				String reqId = UUID.randomUUID().toString(); +				if (WebConfiguration.isSL20SigningEnabled()) { +					String signedCertCommand = SL20JSONBuilderUtils.createSignedCommand( +							SL20Constants.SL20_COMMAND_IDENTIFIER_CREATE_SIG_CADES, createCAdESSigParams, joseTools); +					sl20CreateCAdES = SL20JSONBuilderUtils.createGenericRequest(reqId, null, null, signedCertCommand); +					 +				} else { +					JsonObject getCertCommand = SL20JSONBuilderUtils.createCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_CREATE_SIG_CADES, createCAdESSigParams); +					sl20CreateCAdES = SL20JSONBuilderUtils.createGenericRequest(UUID.randomUUID().toString(), null, getCertCommand, null); +					 +				}	 +				 +				request.getSession(false).setAttribute(PDF_SESSION_PREFIX + SL20Constants.SL20_REQID, reqId); + +				//forward SL2.0 command +				logger.trace("Write 'createCAdES' command to VDA: " + sl20CreateCAdES.toString()); +				StringWriter writer = new StringWriter(); +				writer.write(sl20CreateCAdES.toString());						 +				final byte[] content = writer.toString().getBytes("UTF-8"); +				response.setStatus(HttpServletResponse.SC_OK); +				response.setContentLength(content.length); +				response.setContentType(ContentType.APPLICATION_JSON.toString());						 +				response.getOutputStream().write(content); +				 +			} else +				throw new PdfAsWebException("Invalid connector: " + slConnector.getClass().getName()); +				 +				 + +		} else if (statusRequest.isReady()) { +			// TODO: store pdf document redirect to Finish URL +			logger.debug("Document ready!"); + +			SignResult result = pdfAs.finishSign(statusRequest); + +			ByteArrayOutputStream baos = (ByteArrayOutputStream) session +					.getAttribute(PDF_OUTPUT); +			baos.close(); + +			PDFASVerificationResponse verResponse = new PDFASVerificationResponse(); +			List<VerifyResult> verResults = PdfAsHelper.synchornousVerify( +					baos.toByteArray(), -2, +					PdfAsHelper.getVerificationLevel(request), null); + +			if (verResults.size() != 1) { +				throw new WebServiceException( +						"Document verification failed!"); +			} +			VerifyResult verifyResult = verResults.get(0); -			} else if (statusRequest.isReady()) { -				// TODO: store pdf document redirect to Finish URL -				logger.debug("Document ready!"); - -				SignResult result = pdfAs.finishSign(statusRequest); - -				ByteArrayOutputStream baos = (ByteArrayOutputStream) session -						.getAttribute(PDF_OUTPUT); -				baos.close(); - -				PDFASVerificationResponse verResponse = new PDFASVerificationResponse(); -				List<VerifyResult> verResults = PdfAsHelper.synchornousVerify( -						baos.toByteArray(), -2, -						PdfAsHelper.getVerificationLevel(request), null); +			verResponse.setCertificateCode(verifyResult +					.getCertificateCheck().getCode()); +			verResponse.setValueCode(verifyResult.getValueCheckCode() +					.getCode()); -				if (verResults.size() != 1) { -					throw new WebServiceException( -							"Document verification failed!"); -				} -				VerifyResult verifyResult = verResults.get(0); +			PdfAsHelper.setPDFASVerificationResponse(request, verResponse); +			PdfAsHelper.setSignedPdf(request, response, baos.toByteArray()); -				verResponse.setCertificateCode(verifyResult -						.getCertificateCheck().getCode()); -				verResponse.setValueCode(verifyResult.getValueCheckCode() -						.getCode()); +			String signerCert = Base64.encodeBase64String(result +					.getSignerCertificate().getEncoded()); -				PdfAsHelper.setPDFASVerificationResponse(request, verResponse); -				PdfAsHelper.setSignedPdf(request, response, baos.toByteArray()); +			PdfAsHelper.setSignerCertificate(request, signerCert); +			 +			if (slConnector instanceof BKUSLConnector) {  				PdfAsHelper.gotoProvidePdf(context, request, response); - -				String signerCert = Base64.encodeBase64String(result -						.getSignerCertificate().getEncoded()); - -				PdfAsHelper.setSignerCertificate(request, signerCert); - -			} else { -				throw new PdfAsWebException("Invalid state!"); -			} +				 +			} else if (slConnector instanceof SL20Connector) { +				//TODO: add code to send SL20 redirect command to redirect the user from DataURL connection to App Front-End connection +				String callUrl = generateProvideURL(request, response); +				String transactionId = (String) request.getAttribute(PdfAsHelper.PDF_SESSION_PREFIX + SL20Constants.SL20_TRANSACTIONID); +				buildSL20RedirectResponse(request, response, transactionId, callUrl); +				 +			} else +				throw new PdfAsWebException("Invalid connector: " + slConnector.getClass().getName()); +			  		} else { -			throw new PdfAsWebException("Invalid connector: " + connector); +			throw new PdfAsWebException("Invalid state!");  		}  	} @@ -1338,6 +1533,11 @@ public class PdfAsHelper {  		request.getSession(true);  	} +	public static String generateDataURLSL20(HttpServletRequest request, +			HttpServletResponse response) { +		return generateURL(request, response, PDF_SL20_DATAURL_PAGE); +	} +	  	public static String generateDataURL(HttpServletRequest request,  			HttpServletResponse response) {  		return generateURL(request, response, PDF_DATAURL_PAGE); @@ -1385,6 +1585,8 @@ public class PdfAsHelper {  			return WebConfiguration.getOnlineBKUURL();  		} else if (connector.equals("mobilebku")) {  			return WebConfiguration.getHandyBKUURL(); +		} else if (connector.equals("sl20")) { +			return WebConfiguration.getSecurityLayer20URL();  		}  		return WebConfiguration.getLocalBKUURL();  	} @@ -1542,4 +1744,64 @@ public class PdfAsHelper {  	public static String getSCMRevision() {  		return PdfAsFactory.getSCMRevision();  	} +	 +	public static void buildSL20RedirectResponse(HttpServletRequest request, HttpServletResponse response, String transactionId, String callURL) throws IOException, SL20Exception {		 +		//create response  +		Map<String, String> reqParameters = UrlParameterExtractor.splitQuery(new URL(callURL)); +		 +		//extract URL without parameters +		String url; +		int paramIndex = callURL.indexOf("?"); +		if (paramIndex == -1) +			url = callURL; +		else +			url = callURL.substring(0, paramIndex); +		 +		JsonObject callReqParams = SL20JSONBuilderUtils.createCallCommandParameters( +				url,  +				SL20Constants.SL20_COMMAND_PARAM_GENERAL_CALL_METHOD_GET,  +				false,  +				reqParameters); +		JsonObject callCommand = SL20JSONBuilderUtils.createCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_CALL, callReqParams); +		 +		//build first redirect command for app +		JsonObject redirectOneParams = SL20JSONBuilderUtils.createRedirectCommandParameters( +				null,  +				callCommand, null, true); +		JsonObject redirectOneCommand = SL20JSONBuilderUtils.createCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_REDIRECT, redirectOneParams); +						 +		//build second redirect command for IDP +		JsonObject redirectTwoParams = SL20JSONBuilderUtils.createRedirectCommandParameters( +				callURL,  +				redirectOneCommand, null, false); +		JsonObject redirectTwoCommand = SL20JSONBuilderUtils.createCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_REDIRECT, redirectTwoParams); +		 +		//build generic SL2.0 response container								 +		JsonObject respContainer = SL20JSONBuilderUtils.createGenericRequest( +				UUID.randomUUID().toString(),  +				transactionId,  +				redirectTwoCommand,  +				null);  +		 +		//workaround for A-Trust +		if (request.getHeader(SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE) != null &&  +				request.getHeader(SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE).equals(SL20Constants.HTTP_HEADER_VALUE_NATIVE) +					|| true) {					 +			logger.debug("Client request containts 'native client' header ... "); +			logger.trace("SL20 response to VDA: " + respContainer); +			StringWriter writer = new StringWriter(); +			writer.write(respContainer.toString());						 +			final byte[] content = writer.toString().getBytes("UTF-8"); +			response.setStatus(HttpServletResponse.SC_OK); +			response.setContentLength(content.length); +			response.setContentType(ContentType.APPLICATION_JSON.toString());						 +			response.getOutputStream().write(content); +			 +			 +		} else { +			logger.info("SL2.0 DataURL communication needs http header: '" + SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE + "'"); +			throw new SL20Exception("sl20.06"); +			 +		} +	}  } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/DataURLServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/DataURLServlet.java index 45861953..50c3b063 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/DataURLServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/DataURLServlet.java @@ -93,11 +93,11 @@ public class DataURLServlet extends HttpServlet {  			if(jaxbObject.getValue() instanceof InfoboxReadResponseType) {  				InfoboxReadResponseType infoboxReadResponseType = (InfoboxReadResponseType)jaxbObject.getValue();  				logger.info("Got InfoboxReadResponseType"); -				PdfAsHelper.injectCertificate(request, response, infoboxReadResponseType, getServletContext()); +				PdfAsHelper.injectCertificate(request, response, PdfAsHelper.getCertificate(infoboxReadResponseType), getServletContext());  			} else if(jaxbObject.getValue() instanceof CreateCMSSignatureResponseType) {  				CreateCMSSignatureResponseType createCMSSignatureResponseType = (CreateCMSSignatureResponseType)jaxbObject.getValue();  				logger.info("Got CreateCMSSignatureResponseType"); -				PdfAsHelper.injectSignature(request, response, createCMSSignatureResponseType, getServletContext()); +				PdfAsHelper.injectSignature(request, response, createCMSSignatureResponseType.getCMSSignature(), getServletContext());  			} else if(jaxbObject.getValue() instanceof ErrorResponseType) {  				ErrorResponseType errorResponseType = (ErrorResponseType)jaxbObject.getValue();  				logger.warn("SecurityLayer: " + errorResponseType.getErrorCode() + " " + errorResponseType.getInfo()); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ExternSignServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ExternSignServlet.java index 3cea5247..1d2ab14e 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ExternSignServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ExternSignServlet.java @@ -354,7 +354,8 @@ public class ExternSignServlet extends HttpServlet {  		logger.debug("Starting signature creation with: " + connector);  		//IPlainSigner signer; -		if (connector.equals("bku") || connector.equals("onlinebku") || connector.equals("mobilebku")) { +		if (connector.equals("bku") || connector.equals("onlinebku") || connector.equals("mobilebku") +				|| connector.equals("sl20")) {  			// start asynchronous signature creation  			if(connector.equals("bku")) { @@ -372,6 +373,11 @@ public class ExternSignServlet extends HttpServlet {  					throw new PdfAsWebException("Invalid connector bku is not supported");  				}  			} +			if (connector.equals("sl20")) { +				if(WebConfiguration.getSecurityLayer20URL() == null) { +					throw new PdfAsWebException("Invalid connector bku is not supported"); +				} +			}  			PdfAsHelper.setStatisticEvent(request, response, statisticEvent); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/JSONAPIServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/JSONAPIServlet.java index 0cee185a..13d874e8 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/JSONAPIServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/JSONAPIServlet.java @@ -119,7 +119,9 @@ public class JSONAPIServlet extends HttpServlet {                  connectorEnum = PDFASSignParameters.Connector.MOBILEBKU;              } else if(PDFASSignParameters.Connector.ONLINEBKU.equalsName(connector)) {                  connectorEnum = PDFASSignParameters.Connector.ONLINEBKU; -            } +            } else if(PDFASSignParameters.Connector.SECLAYER20.equalsName(connector)) { +                connectorEnum = PDFASSignParameters.Connector.SECLAYER20; +            }               if(connectorEnum == null) {                  throw new ServletException( @@ -212,6 +214,13 @@ public class JSONAPIServlet extends HttpServlet {                                      "Invalid connector mobilebku is not supported");                          }                      } +                     +                    if (PDFASSignParameters.Connector.SECLAYER20.equals(connectorEnum)) { +                        if (WebConfiguration.getSecurityLayer20URL() == null) { +                            throw new PdfAsWebException( +                                    "Invalid connector mobilebku is not supported"); +                        } +                    }                      PdfAsHelper.startSignatureJson(request, response, getServletContext(), diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SLDataURLServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SLDataURLServlet.java new file mode 100644 index 00000000..7ddf0a55 --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SLDataURLServlet.java @@ -0,0 +1,234 @@ +package at.gv.egiz.pdfas.web.servlets; + +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.annotation.MultipartConfig; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.jose4j.base64url.Base64Url; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; + +import at.gv.egiz.pdfas.lib.util.StreamUtils; +import at.gv.egiz.pdfas.web.config.WebConfiguration; +import at.gv.egiz.pdfas.web.helper.PdfAsHelper; +import at.gv.egiz.pdfas.web.sl20.JsonSecurityUtils; +import at.gv.egiz.pdfas.web.sl20.X509Utils; +import at.gv.egiz.sl20.data.VerificationResult; +import at.gv.egiz.sl20.exceptions.SL20Exception; +import at.gv.egiz.sl20.exceptions.SL20SecurityException; +import at.gv.egiz.sl20.exceptions.SLCommandoParserException; +import at.gv.egiz.sl20.utils.SL20Constants; +import at.gv.egiz.sl20.utils.SL20JSONExtractorUtils; + +@MultipartConfig +public class SLDataURLServlet extends HttpServlet { + +	private static final Logger logger = LoggerFactory +			.getLogger(SLDataURLServlet.class); +	 +	private static final long serialVersionUID = 1L; + +	/** +	 * @see HttpServlet#HttpServlet() +	 */ +	public SLDataURLServlet() { +		super(); +	} + +	/** +	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse +	 *      response) +	 */ +	protected void doGet(HttpServletRequest request, +			HttpServletResponse response) throws ServletException, IOException { +		this.process(request, response); +	} + +	/** +	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse +	 *      response) +	 */ +	protected void doPost(HttpServletRequest request, +			HttpServletResponse response) throws ServletException, IOException { +		this.process(request, response); +	} + +	protected void process(HttpServletRequest request, +			HttpServletResponse response) throws ServletException, IOException { +		 +		JsonObject sl20ReqObj = null;	 +		try { +			if(!PdfAsHelper.checkDataUrlAccess(request)) { +				throw new Exception("No valid dataURL access"); +			} +			 +			PdfAsHelper.setFromDataUrl(request); +						 +			String sl20Result = request.getParameter(SL20Constants.PARAM_SL20_REQ_COMMAND_PARAM); +			if (StringUtils.isEmpty(sl20Result)) { +				//Workaround for SIC Handy-Signature, because it sends result in InputStream +				String isReqInput = StreamUtils.readStream(request.getInputStream(), "UTF-8");					 +				if (StringUtils.isNotEmpty(isReqInput)) { +					logger.info("Use SIC Handy-Signature work-around!"); +					sl20Result = isReqInput.substring("slcommand=".length()); +					 +				} else {					 +					logger.info("NO SL2.0 commando or result FOUND."); +					throw new SL20Exception("sl20.04", null); +				} +				 +			} + +			logger.trace("Received SL2.0 result: " + sl20Result);		 +			 +			//parse SL2.0 command/result into JSON				 +			try { +				JsonParser jsonParser = new JsonParser(); +				JsonElement sl20Req = jsonParser.parse(Base64Url.decodeToUtf8String(sl20Result)); +				sl20ReqObj = sl20Req.getAsJsonObject(); +				 +			} catch (JsonSyntaxException e) { +				logger.warn("SL2.0 command or result is NOT valid JSON.", e); +				logger.debug("SL2.0 msg: " + sl20Result); +				throw new SL20Exception("sl20.02", e); +				 +			} +			 +			//extract transactionId +			String transactionId = SL20JSONExtractorUtils.getStringValue(sl20ReqObj, SL20Constants.SL20_TRANSACTIONID, false); +			if (StringUtils.isNotEmpty(transactionId)) +					request.setAttribute(PdfAsHelper.PDF_SESSION_PREFIX + SL20Constants.SL20_TRANSACTIONID, transactionId); +			 +			 +			//validate reqId with inResponseTo  +			String sl20ReqId = (String) request.getSession(false).getAttribute(PdfAsHelper.PDF_SESSION_PREFIX + SL20Constants.SL20_REQID); +			String inRespTo = SL20JSONExtractorUtils.getStringValue(sl20ReqObj, SL20Constants.SL20_INRESPTO, true); +			if (sl20ReqId == null || !sl20ReqId.equals(inRespTo)) { +				logger.info("SL20 'reqId': " + sl20ReqId + " does NOT match to 'inResponseTo':" + inRespTo); +				throw new SL20SecurityException("SL20 'reqId': " + sl20ReqId + " does NOT match to 'inResponseTo':" + inRespTo); +			} +			 +			JsonSecurityUtils joseTools = JsonSecurityUtils.getInstance(); +			if (!joseTools.isInitialized()) +				joseTools = null; +			 +			//validate signature +			VerificationResult payLoadContainer = SL20JSONExtractorUtils.extractSL20PayLoad(sl20ReqObj, joseTools,  +					WebConfiguration.isSL20SigningRequired()); +			 +			if ( (payLoadContainer.isValidSigned() == null || !payLoadContainer.isValidSigned())) { +				if (WebConfiguration.isSL20SigningRequired()) { +					logger.info("SL20 result from VDA was not valid signed"); +					throw new SL20SecurityException("Signature on SL20 result NOT valid."); +					 +				} else { +					logger.warn("SL20 result from VDA is NOT valid signed, but signatures-verification is DISABLED by configuration!"); +					 +				}				 +			} +						 +			//extract payloaf +			JsonObject payLoad = payLoadContainer.getPayload(); +			 +			//check response type +			if (SL20JSONExtractorUtils.getStringValue( +					payLoad, SL20Constants.SL20_COMMAND_CONTAINER_NAME, true) +						.equals(SL20Constants.SL20_COMMAND_IDENTIFIER_GETCERTIFICATE)) { +				logger.debug("Find " + SL20Constants.SL20_COMMAND_IDENTIFIER_GETCERTIFICATE + " result .... "); +								 +				JsonElement getCertificateResult = SL20JSONExtractorUtils.extractSL20Result( +						payLoad, joseTools,  +						WebConfiguration.isSL20EncryptionRequired()); + +				//extract certificates												 +				List<String> certsB64 = SL20JSONExtractorUtils.getListOfStringElements(getCertificateResult.getAsJsonObject(),  +						SL20Constants.SL20_COMMAND_PARAM_GETCERTIFICATE_RESULT_CERTIFICATE,  +						true); +				 +				if (certsB64.isEmpty()) { +					logger.warn("SL20 'getCertificate' result contains NO certificate"); +					throw new SLCommandoParserException(); +					 +				} else if (certsB64.size() == 1) { +					logger.debug("SL20 'getCertificate' result contains only one certificate"); +					PdfAsHelper.injectCertificate(request, response, Base64.getDecoder().decode(certsB64.get(0)), getServletContext());	 +					 +				} else { +					logger.debug("SL20 'getCertificate' result contains more than one certificate. Certificates must be sorted ... "); +					List<X509Certificate> certs = new ArrayList<X509Certificate>(); +					for (String certB64 : certsB64) +						certs.add(new iaik.x509.X509Certificate(Base64.getDecoder().decode(certB64))); +												 +					List<X509Certificate> sortedCerts = X509Utils.sortCertificates(certs); +					logger.debug("Sorting of certificate completed. Select end-user certificate ... "); +					PdfAsHelper.injectCertificate(request, response, Base64.getDecoder().decode(sortedCerts.get(0).getEncoded()), getServletContext()); +					 +				} +				 +			} else if (SL20JSONExtractorUtils.getStringValue( +					payLoad, SL20Constants.SL20_COMMAND_CONTAINER_NAME, true) +					.equals(SL20Constants.SL20_COMMAND_IDENTIFIER_CREATE_SIG_CADES)) { +				logger.debug("Find " + SL20Constants.SL20_COMMAND_IDENTIFIER_CREATE_SIG_CADES + " result .... "); +				 +				JsonElement getCertificateResult = SL20JSONExtractorUtils.extractSL20Result( +						payLoad, joseTools,  +						WebConfiguration.isSL20EncryptionRequired()); + +				//extract CAdES signature +				String cadesSigB64 = SL20JSONExtractorUtils.getStringValue( +						getCertificateResult.getAsJsonObject(),  +						SL20Constants.SL20_COMMAND_PARAM_CREATE_SIG_CADES_RESULT_SIGNATURE,  +						true); +				 +				if (StringUtils.isEmpty(cadesSigB64)) { +					logger.warn("SL20 'createCAdES' result contains NO signature"); +					throw new SLCommandoParserException(); +				} +				 +				PdfAsHelper.injectSignature(request, response, Base64.getDecoder().decode(cadesSigB64), getServletContext()); +				 +			} else { +				logger.info("SL20 response is NOT a " + SL20Constants.SL20_COMMAND_IDENTIFIER_QUALIFIEDEID + " result"); +				throw new SLCommandoParserException(); +				 +			}	 +			 +		} catch (Exception e) { +			logger.warn("Error in DataURL Servlet. " , e); +			PdfAsHelper.setSessionException(request, response, e.getMessage(), +					e); +			 +			if (PdfAsHelper.getFromDataUrl(request)) { +				String errorUrl = PdfAsHelper.generateErrorURL(request, response); +				try { +					String transactionId = null; +					if (sl20ReqObj != null) +						transactionId = SL20JSONExtractorUtils.getStringValue(sl20ReqObj, SL20Constants.SL20_TRANSACTIONID, false); +					 +					PdfAsHelper.buildSL20RedirectResponse(request, response, transactionId, errorUrl); +					 +				} catch (SL20Exception e1) { +					logger.error("SL20 error-handling FAILED", e); +					response.sendError(500, "Internal Server Error."); +					 +				} +				 +			} else			 +				PdfAsHelper.gotoError(getServletContext(), request, response); +		} +	} +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java index e8ac3658..73f8299c 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java @@ -131,7 +131,8 @@ public class UIEntryPointServlet extends HttpServlet {  			// IPlainSigner signer;  			if (connector.equals(Connector.BKU)  					|| connector.equals(Connector.ONLINEBKU) -					|| connector.equals(Connector.MOBILEBKU)) { +					|| connector.equals(Connector.MOBILEBKU) +					|| connector.equals(Connector.SECLAYER20)) {  				// start asynchronous signature creation  				if (connector.equals(Connector.BKU)) { @@ -154,6 +155,14 @@ public class UIEntryPointServlet extends HttpServlet {  								"Invalid connector mobilebku is not supported");  					}  				} +				 +				if (connector.equals(Connector.SECLAYER20)) { +					if (WebConfiguration.getSecurityLayer20URL() == null) { +						throw new PdfAsWebException( +								"Invalid connector mobilebku is not supported"); +					} +				} +				  				Map<String, String> map = null;  				if (pdfAsRequest.getParameters().getPreprocessor() != null) {  					map = pdfAsRequest.getParameters().getPreprocessor() diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/JsonSecurityUtils.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/JsonSecurityUtils.java new file mode 100644 index 00000000..141808de --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/JsonSecurityUtils.java @@ -0,0 +1,381 @@ +package at.gv.egiz.pdfas.web.sl20; + +import java.io.IOException; +import java.security.Key; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.apache.commons.lang3.StringUtils; +import org.bouncycastle.util.encoders.Base64Encoder; +import org.jose4j.jwa.AlgorithmConstraints; +import org.jose4j.jwa.AlgorithmConstraints.ConstraintType; +import org.jose4j.jwe.JsonWebEncryption; +import org.jose4j.jws.AlgorithmIdentifiers; +import org.jose4j.jws.JsonWebSignature; +import org.jose4j.jwx.JsonWebStructure; +import org.jose4j.keys.X509Util; +import org.jose4j.keys.resolvers.X509VerificationKeyResolver; +import org.jose4j.lang.JoseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; + +import at.gv.egiz.pdfas.web.config.WebConfiguration; +import at.gv.egiz.sl20.data.VerificationResult; +import at.gv.egiz.sl20.exceptions.SL20Exception; +import at.gv.egiz.sl20.exceptions.SL20SecurityException; +import at.gv.egiz.sl20.exceptions.SLCommandoBuildException; +import at.gv.egiz.sl20.exceptions.SLCommandoParserException; +import at.gv.egiz.sl20.utils.IJOSETools; +import at.gv.egiz.sl20.utils.SL20Constants; + +public class JsonSecurityUtils implements IJOSETools{ + +	private static final Logger logger = LoggerFactory.getLogger(JsonSecurityUtils.class); +	 +	private Key signPrivKey = null; +	private X509Certificate[] signCertChain = null;	 +	private Key encPrivKey = null; +	private X509Certificate[] encCertChain = null;	 +	private List<X509Certificate> trustedCerts = new ArrayList<X509Certificate>(); +	 +	private boolean isInitialized = false; +	 +	private static JsonSecurityUtils instance = null; +		 +	public static JsonSecurityUtils getInstance() { +		if (instance == null) { +			instance = new JsonSecurityUtils(); +			instance.initalize(); +		} +		 +		return instance; +	} +	 +	private JsonSecurityUtils() { +		 +		 +	} +	 +	protected synchronized void initalize() { +		logger.info("Initialize SL2.0 authentication security constrains ... "); +		try { +			String keyStorePath = getKeyStoreFilePath(); +			 +			if (StringUtils.isNotEmpty(keyStorePath)) { +				KeyStore keyStore = KeyStoreUtils.loadKeyStore(getKeyStoreFilePath(),  +						getKeyStorePassword()); +				 +				//load signing key +				signPrivKey = keyStore.getKey(getSigningKeyAlias(), getSigningKeyPassword().toCharArray()); +				Certificate[] certChainSigning = keyStore.getCertificateChain(getSigningKeyAlias()); +				signCertChain = new X509Certificate[certChainSigning.length]; +				for (int i=0; i<certChainSigning.length; i++) { +					if (certChainSigning[i] instanceof X509Certificate) { +						signCertChain[i] = (X509Certificate)certChainSigning[i]; +					} else +						logger.warn("NO X509 certificate for signing: " + certChainSigning[i].getType()); +					 +				} +				 +				//load encryption key +				try { +					encPrivKey = keyStore.getKey(getEncryptionKeyAlias(), getEncryptionKeyPassword().toCharArray()); +					if (encPrivKey != null) { +						Certificate[] certChainEncryption = keyStore.getCertificateChain(getEncryptionKeyAlias()); +						encCertChain = new X509Certificate[certChainEncryption.length]; +						for (int i=0; i<certChainEncryption.length; i++) { +							if (certChainEncryption[i] instanceof X509Certificate) { +								encCertChain[i] = (X509Certificate)certChainEncryption[i]; +							} else +								logger.warn("NO X509 certificate for encryption: " + certChainEncryption[i].getType()); +						}									 +					} else +						logger.info("No encryption key for SL2.0 found. End-to-End encryption is not used."); +					 +				} catch (Exception e) { +					logger.warn("No encryption key for SL2.0 found. End-to-End encryption is not used. Reason: " + e.getMessage(), e); +			 +				} +				 +				//load trusted certificates +				Enumeration<String> aliases = keyStore.aliases(); +				while(aliases.hasMoreElements()) { +					String el = aliases.nextElement(); +					logger.trace("Process TrustStoreEntry: " + el); +					if (keyStore.isCertificateEntry(el)) { +						Certificate cert = keyStore.getCertificate(el);  +						if (cert != null && cert instanceof X509Certificate) +							trustedCerts.add((X509Certificate) cert); +						else +							logger.info("Can not process entry: " + el + ". Reason: " + cert.toString()); +						 +					} +				} +	 +				//some short validation +				if (signPrivKey == null || !(signPrivKey instanceof PrivateKey)) { +					logger.info("Can NOT open privateKey for SL2.0 signing. KeyStore=" + getKeyStoreFilePath()); +					throw new SL20Exception("sl20.03"); +					 +				} +				 +				if (signCertChain == null || signCertChain.length == 0) { +					logger.info("NO certificate for SL2.0 signing. KeyStore=" + getKeyStoreFilePath()); +					throw new SL20Exception("sl20.03"); +					 +				} +				 +				isInitialized = true; +				logger.info("SL2.0 authentication security constrains initialized."); +				 +			} else +				logger.info("SL2.0 security constrains not configurated!"); +			 +		} catch ( Exception e) { +			logger.error("SL2.0 security constrains initialization FAILED.", e); +			 +		} +		 +	} +	 +	 +	@Override +	public String createSignature(String payLoad) throws SLCommandoBuildException { +		try { +			JsonWebSignature jws = new JsonWebSignature(); +			 +			//set payload +			jws.setPayload(payLoad); +		 +			//set basic header		 +			jws.setContentTypeHeaderValue(SL20Constants.SL20_CONTENTTYPE_SIGNED_COMMAND); +		 +			//set signing information +			jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); +			jws.setKey(signPrivKey); +			 +			//TODO: +			jws.setCertificateChainHeaderValue(signCertChain); +			jws.setX509CertSha256ThumbprintHeaderValue(signCertChain[0]); +						 +			return jws.getCompactSerialization(); +			 +		} catch (JoseException e) { +			logger.warn("Can NOT sign SL2.0 command.", e); +			throw new SLCommandoBuildException(e); +			 +		} +		 +	} +	 +	@Override +	public VerificationResult validateSignature(String serializedContent) throws SL20Exception { +		try { +			JsonWebSignature jws = new JsonWebSignature(); +			//set payload +			jws.setCompactSerialization(serializedContent); +				 +			//set security constrains +			jws.setAlgorithmConstraints(new AlgorithmConstraints(ConstraintType.WHITELIST,    +					SL20Constants.SL20_ALGORITHM_WHITELIST_SIGNING.toArray(new String[SL20Constants.SL20_ALGORITHM_WHITELIST_SIGNING.size()]))); +			 +			//load signinc certs +			Key selectedKey = null; +			List<X509Certificate> x5cCerts = jws.getCertificateChainHeaderValue(); +			String x5t256 = jws.getX509CertSha256ThumbprintHeaderValue(); +			if (x5cCerts != null) { +				logger.debug("Found x509 certificate in JOSE header ... "); +				logger.trace("Sorting received X509 certificates ... "); +				List<X509Certificate> sortedX5cCerts  = X509Utils.sortCertificates(x5cCerts); +				 +				if (trustedCerts.contains(sortedX5cCerts.get(0))) { +					selectedKey = sortedX5cCerts.get(0).getPublicKey(); +					 +				} else { +					logger.info("Can NOT find JOSE certificate in truststore."); +					logger.debug("JOSE certificate: " + sortedX5cCerts.get(0).toString()); +					try {						 +						logger.debug("Cert: " + Base64.getEncoder().encodeToString(sortedX5cCerts.get(0).getEncoded())); +						 +					} catch (CertificateEncodingException e) { +						e.printStackTrace(); +						 +					} +					 +				} +				 +			} else if (StringUtils.isNotEmpty(x5t256)) { +				logger.debug("Found x5t256 fingerprint in JOSE header .... "); +				X509VerificationKeyResolver x509VerificationKeyResolver = new X509VerificationKeyResolver(trustedCerts); +				selectedKey = x509VerificationKeyResolver.resolveKey(jws, Collections.<JsonWebStructure>emptyList()); +				 +			} else { +				logger.info("Signed SL2.0 response contains NO signature certificate or NO certificate fingerprint"); +				throw new SLCommandoParserException(); +				 +			} +					 +			if (selectedKey == null) { +				logger.info("Can NOT select verification key for JWS. Signature verification FAILED."); +				throw new SLCommandoParserException(); +				 +			} +			 +			//set verification key +			jws.setKey(selectedKey); +			 +			//validate signature +			boolean valid = jws.verifySignature(); +			if (!valid) { +				logger.info("JWS signature invalide. Stopping authentication process ..."); +				logger.debug("Received JWS msg: " + serializedContent); +				throw new SL20SecurityException(); +				 +			} +				 +			 +			//load payLoad +			logger.debug("SL2.0 commando signature validation sucessfull"); +			JsonElement sl20Req = new JsonParser().parse(jws.getPayload()); +			 +			return new VerificationResult(sl20Req.getAsJsonObject(), null, valid) ; +			 +		} catch (JoseException e) { +			logger.warn("SL2.0 commando signature validation FAILED", e); +			throw new SL20SecurityException(e); +			 +		} +					 +	} +	 + +	@Override +	public JsonElement decryptPayload(String compactSerialization) throws SL20Exception { +		try {			 +			JsonWebEncryption receiverJwe = new JsonWebEncryption(); +					 +			//set security constrains +			receiverJwe.setAlgorithmConstraints( +					new AlgorithmConstraints(ConstraintType.WHITELIST, +							SL20Constants.SL20_ALGORITHM_WHITELIST_KEYENCRYPTION.toArray(new String[SL20Constants.SL20_ALGORITHM_WHITELIST_KEYENCRYPTION.size()]))); +			receiverJwe.setContentEncryptionAlgorithmConstraints( +					new AlgorithmConstraints(ConstraintType.WHITELIST, +							SL20Constants.SL20_ALGORITHM_WHITELIST_ENCRYPTION.toArray(new String[SL20Constants.SL20_ALGORITHM_WHITELIST_ENCRYPTION.size()]))); +		 +			//set payload +			receiverJwe.setCompactSerialization(compactSerialization); + +			 +			//validate key from header against key from config +			List<X509Certificate> x5cCerts = receiverJwe.getCertificateChainHeaderValue(); +			String x5t256 = receiverJwe.getX509CertSha256ThumbprintHeaderValue(); +			if (x5cCerts != null) { +				logger.debug("Found x509 certificate in JOSE header ... "); +				logger.trace("Sorting received X509 certificates ... "); +				List<X509Certificate> sortedX5cCerts  = X509Utils.sortCertificates(x5cCerts); +				 +				if (!sortedX5cCerts.get(0).equals(encCertChain[0])) { +					logger.info("Certificate from JOSE header does NOT match encryption certificate"); +					logger.debug("JOSE certificate: " + sortedX5cCerts.get(0).toString()); +					 +					try { +						logger.debug("Cert: " + Base64.getEncoder().encodeToString(sortedX5cCerts.get(0).getEncoded())); +					} catch (CertificateEncodingException e) { +						e.printStackTrace(); +					} +					throw new SL20Exception("sl20.05"); +				} +				 +			} else if (StringUtils.isNotEmpty(x5t256)) { +				logger.debug("Found x5t256 fingerprint in JOSE header .... "); +				String certFingerPrint = X509Util.x5tS256(encCertChain[0]); +				if (!certFingerPrint.equals(x5t256)) { +					logger.info("X5t256 from JOSE header does NOT match encryption certificate"); +					logger.debug("X5t256 from JOSE header: " + x5t256 + " Encrytption cert: " + certFingerPrint); +					throw new SL20Exception("sl20.05"); +					 +				} +				 +			} else { +				logger.info("Signed SL2.0 response contains NO signature certificate or NO certificate fingerprint"); +				throw new SLCommandoParserException(); +				 +			} +						 +			//set key +			receiverJwe.setKey(encPrivKey); +			 +						 +			//decrypt payload			 +			return new JsonParser().parse(receiverJwe.getPlaintextString()); +			 +		} catch (JoseException e) { +			logger.warn("SL2.0 result decryption FAILED", e); +			throw new SL20SecurityException(e); +			 +		} catch ( JsonSyntaxException e) { +			logger.warn("Decrypted SL2.0 result is NOT a valid JSON.", e); +			throw new SLCommandoParserException(e); +			 +		} +		 +	} +	 +	 +	 +	@Override +	public X509Certificate getEncryptionCertificate() { +		//TODO: maybe update after SL2.0 update on encryption certificate parts +		if (encCertChain !=null && encCertChain.length > 0) +			return encCertChain[0]; +		else +			return null; +	} +	 +	@Override +	public boolean isInitialized() { +		return isInitialized; +		 +	} +	 +	private String getKeyStoreFilePath() { +		return WebConfiguration.getSL20KeyStorePath(); +	} +	 +	private String getKeyStorePassword() { +		return WebConfiguration.getSL20KeyStorePassword(); + +	} +	 +	private String getSigningKeyAlias() { +		return WebConfiguration.getSL20KeySigningAlias(); +	} +	 +	private String getSigningKeyPassword() { +		return WebConfiguration.getSL20KeySigningPassword(); +		 +	} + +	private String getEncryptionKeyAlias() { +		return WebConfiguration.getSL20KeyEncryptionAlias(); +	} +	 +	private String getEncryptionKeyPassword() { +		return WebConfiguration.getSL20KeyEncryptionPassword(); +	} +	 +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/KeyStoreUtils.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/KeyStoreUtils.java new file mode 100644 index 00000000..c7472a22 --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/KeyStoreUtils.java @@ -0,0 +1,222 @@ +/* + * Copyright 2003 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ + + +package at.gv.egiz.pdfas.web.sl20; + +import iaik.x509.X509Certificate; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.cert.Certificate; + +/** + * Utility for creating and loading key stores. + *  + * @author Paul Ivancsics + * @version $Id$ + */ +public class KeyStoreUtils { +	 +	/** +	 * JAVA KeyStore +	 */ +	private static final String KEYSTORE_TYPE_JKS = "JKS"; +	 +	/** +	 * PKCS12 KeyStore +	 */ +	private static final String KEYSTORE_TYPE_PKCS12 = "PKCS12"; +	 +	 + +  /** +   * Loads a key store from file. +   *  +   * @param keystoreType key store type +   * @param urlString URL of key store +   * @param password password protecting the key store +   * @return key store loaded +   * @throws IOException thrown while reading the key store from file +   * @throws GeneralSecurityException thrown while creating the key store +   */ +  public static KeyStore loadKeyStore( +    String keystoreType, +    String urlString, +    String password) +    throws IOException, GeneralSecurityException { + +    URL keystoreURL = new URL(urlString); +    InputStream in = keystoreURL.openStream(); +    return loadKeyStore(keystoreType, in, password); +  } +  /** +   * Loads a key store from an <code>InputStream</code>, and +   * closes the <code>InputStream</code>. +   *  +   * @param keystoreType key store type +   * @param in input stream +   * @param password password protecting the key store +   * @return key store loaded +   * @throws IOException thrown while reading the key store from the stream +   * @throws GeneralSecurityException thrown while creating the key store +   */ +  public static KeyStore loadKeyStore( +    String keystoreType, +    InputStream in, +    String password) +    throws IOException, GeneralSecurityException { + +    char[] chPassword = null; +    if (password != null) +      chPassword = password.toCharArray(); +    KeyStore ks = KeyStore.getInstance(keystoreType); +    ks.load(in, chPassword); +    in.close(); +    return ks; +  } +  /** +   * Creates a key store from X509 certificate files, aliasing them with +   * the index in the <code>String[]</code>, starting with <code>"0"</code>. +   *  +   * @param keyStoreType key store type +   * @param certFilenames certificate filenames +   * @return key store created +   * @throws IOException thrown while reading the certificates from file +   * @throws GeneralSecurityException thrown while creating the key store +   */ +  public static KeyStore createKeyStore( +    String keyStoreType, +    String[] certFilenames) +    throws IOException, GeneralSecurityException { + +    KeyStore ks = KeyStore.getInstance(keyStoreType); +    ks.load(null, null); +    for (int i = 0; i < certFilenames.length; i++) { +      Certificate cert = loadCertificate(certFilenames[i]); +      ks.setCertificateEntry("" + i, cert); +    } +    return ks; +  } +//  /** +//   * Creates a key store from a directory containg X509 certificate files,  +//   * aliasing them with the index in the <code>String[]</code>, starting with <code>"0"</code>. +//   * All the files in the directory are considered to be certificates. +//   *  +//   * @param keyStoreType key store type +//   * @param certDirURLString file URL of directory containing certificate filenames +//   * @return key store created +//   * @throws IOException thrown while reading the certificates from file +//   * @throws GeneralSecurityException thrown while creating the key store +//   */ +//  public static KeyStore createKeyStoreFromCertificateDirectory( +//    String keyStoreType, +//    String certDirURLString) +//    throws IOException, GeneralSecurityException { +// +//    URL certDirURL = new URL(certDirURLString); +//    String certDirname = certDirURL.getFile(); +//    File certDir = new File(certDirname); +//    String[] certFilenames = certDir.list(); +//    String separator = +//      (certDirname.endsWith(File.separator) ? "" : File.separator); +//    for (int i = 0; i < certFilenames.length; i++) { +//      certFilenames[i] = certDirname + separator + certFilenames[i]; +//    } +//    return createKeyStore(keyStoreType, certFilenames); +//  } + +  /** +   * Loads an X509 certificate from file. +   * @param certFilename filename +   * @return the certificate loaded +   * @throws IOException thrown while reading the certificate from file +   * @throws GeneralSecurityException thrown while creating the certificate +   */ +  private static Certificate loadCertificate(String certFilename) +    throws IOException, GeneralSecurityException { + +    FileInputStream in = new FileInputStream(certFilename); +    Certificate cert = new X509Certificate(in); +    in.close(); +    return cert; +  } +   +  +	/** +	 * Loads a keyStore without knowing the keyStore type +	 * @param keyStorePath URL to the keyStore +	 * @param password Password protecting the keyStore +	 * @return keyStore loaded +	 * @throws KeyStoreException thrown if keyStore cannot be loaded +	 * @throws FileNotFoundException  +	 * @throws IOException  +	 */ +  public static KeyStore loadKeyStore(String keyStorePath, String password) throws KeyStoreException, IOException{ +		 +		//InputStream is = new FileInputStream(keyStorePath); +	  	URL keystoreURL = new URL(keyStorePath); +	    InputStream in = keystoreURL.openStream(); +		InputStream isBuffered = new BufferedInputStream(in);				 +		return loadKeyStore(isBuffered, password); +		 +	} +	 +	/** +	 * Loads a keyStore without knowing the keyStore type +	 * @param in input stream +	 * @param password Password protecting the keyStore +	 * @return keyStore loaded +	 * @throws KeyStoreException thrown if keyStore cannot be loaded +	 * @throws FileNotFoundException  +	 * @throws IOException  +	 */ +public static KeyStore loadKeyStore(InputStream is, String password) throws KeyStoreException, IOException{		 +		is.mark(1024*1024); +		KeyStore ks = null; +		try { +			try {				 +				ks = loadKeyStore(KEYSTORE_TYPE_PKCS12, is, password); +			} catch (IOException e2) { +				is.reset();				 +				ks = loadKeyStore(KEYSTORE_TYPE_JKS, is, password); +			} +		} catch(Exception e) {			 +			e.printStackTrace(); +			//throw new KeyStoreException(e); +		} +		return ks;	 +						 +	} +   +	 + + +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/SL20HttpBindingUtils.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/SL20HttpBindingUtils.java new file mode 100644 index 00000000..f5d6ff55 --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/SL20HttpBindingUtils.java @@ -0,0 +1,47 @@ +package at.gv.egiz.pdfas.web.sl20; + +import java.io.IOException; +import java.io.StringWriter; +import java.net.URISyntaxException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.ContentType; +import org.jose4j.base64url.Base64Url; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonObject; + +import at.gv.egiz.sl20.utils.SL20Constants; + +public class SL20HttpBindingUtils { +	private static final org.slf4j.Logger log = LoggerFactory.getLogger(SL20HttpBindingUtils.class); +	 +	public static void writeIntoResponse(HttpServletRequest request, HttpServletResponse response, JsonObject sl20Forward, String redirectURL) throws IOException, URISyntaxException { +		//forward SL2.0 command +		if (request.getHeader(SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE) != null &&  +				request.getHeader(SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE).equals(SL20Constants.HTTP_HEADER_VALUE_NATIVE)) { +			log.debug("Client request containts 'native client' header ... ");												 +			StringWriter writer = new StringWriter(); +			writer.write(sl20Forward.toString());						 +			final byte[] content = writer.toString().getBytes("UTF-8"); +			response.setStatus(HttpServletResponse.SC_OK); +			response.setContentLength(content.length); +			response.setContentType(ContentType.APPLICATION_JSON.toString());						 +			response.getOutputStream().write(content); +											 +		} else { +			log.debug("Client request containts is no native client ... "); +			URIBuilder clientRedirectURI = new URIBuilder(redirectURL); +			clientRedirectURI.addParameter( +					SL20Constants.PARAM_SL20_REQ_COMMAND_PARAM,  +					Base64Url.encode(sl20Forward.toString().getBytes())); +			response.setStatus(307); +			response.setHeader("Location", clientRedirectURI.build().toString()); +			 +		} +		 +	} +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/X509Utils.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/X509Utils.java new file mode 100644 index 00000000..391b8271 --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/X509Utils.java @@ -0,0 +1,62 @@ +package at.gv.egiz.pdfas.web.sl20; + +import java.security.cert.X509Certificate; +import java.util.List; + +import javax.security.auth.x500.X500Principal; + +public class X509Utils { + +	  /** +	   * Sorts the Certificate Chain by IssuerDN and SubjectDN. The [0]-Element should be the Hostname, +	   * the last Element should be the Root Certificate. +	   *  +	   * @param certs +	   *          The first element must be the correct one. +	   * @return sorted Certificate Chain +	   */ +	  public static List<X509Certificate> sortCertificates( +		      List<X509Certificate> certs) +		  { +		    int length = certs.size(); +		    if (certs.size() <= 1) +		    { +		      return certs; +		    } + +		    for (X509Certificate cert : certs) +		    { +		      if (cert == null) +		      { +		        throw new NullPointerException(); +		      } +		    } + +		    for (int i = 0; i < length; i++) +		    { +		      boolean found = false; +		      X500Principal issuer = certs.get(i).getIssuerX500Principal(); +		      for (int j = i + 1; j < length; j++) +		      { +		        X500Principal subject = certs.get(j).getSubjectX500Principal(); +		        if (issuer.equals(subject)) +		        { +		          // sorting necessary? +		          if (i + 1 != j) +		          { +		            X509Certificate tmp = certs.get(i + 1); +		            certs.set(i + 1, certs.get(j)); +		            certs.set(j, tmp); +		          } +		          found = true; +		        } +		      } +		      if (!found) +		      { +		        break; +		      } +		    } + +		    return certs; +		} +} diff --git a/pdf-as-web/src/main/webapp/WEB-INF/web.xml b/pdf-as-web/src/main/webapp/WEB-INF/web.xml index a7bb5f74..9b69c983 100644 --- a/pdf-as-web/src/main/webapp/WEB-INF/web.xml +++ b/pdf-as-web/src/main/webapp/WEB-INF/web.xml @@ -85,6 +85,12 @@  		<description></description>  		<servlet-class>at.gv.egiz.pdfas.web.servlets.DataURLServlet</servlet-class>  	</servlet> + 	<servlet> +		<servlet-name>SLDataURLServlet</servlet-name> +		<display-name>SLDataURLServlet</display-name> +		<description></description> +		<servlet-class>at.gv.egiz.pdfas.web.servlets.SLDataURLServlet</servlet-class> +	</servlet>  	<servlet>  		<servlet-name>VisBlockServlet</servlet-name>  		<display-name>VisBlockServlet</display-name> @@ -185,6 +191,10 @@  		<url-pattern>/DataURL</url-pattern>  	</servlet-mapping>  	<servlet-mapping> +		<servlet-name>SLDataURLServlet</servlet-name> +		<url-pattern>/DataURLSL20</url-pattern> +	</servlet-mapping> +	<servlet-mapping>  		<servlet-name>VerifyServlet</servlet-name>  		<url-pattern>/Verify</url-pattern>  	</servlet-mapping> diff --git a/pdf-as-web/src/main/webapp/index.jsp b/pdf-as-web/src/main/webapp/index.jsp index de41028b..c07b2cc0 100644 --- a/pdf-as-web/src/main/webapp/index.jsp +++ b/pdf-as-web/src/main/webapp/index.jsp @@ -37,6 +37,17 @@  		<%  			}  		%> +		 +		<% +			if (WebConfiguration.getSecurityLayer20URL() != null) { +		%> +		<button type="submit" value="sl20" name="connector" id="sl20backend">SL2.0 Interface</button> +		<label for="placeholder_web_id">Placeholder ID</label> +          <input type="text" id="placeholder_web_id" name="placeholder_web_id"> +		<% +			} +		%> +		  		<%  			if (WebConfiguration.getKeystoreDefaultEnabled()) {  		%> | 
