/* * Copyright 2014 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.egovernment.moa.id.auth.modules.ssotransfer.servlet; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.math.BigInteger; import java.net.URL; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.SecureRandom; import java.security.Security; import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidParameterSpecException; import java.util.Date; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.DHParameterSpec; import javax.crypto.spec.DHPublicKeySpec; import javax.security.cert.CertificateException; import javax.security.cert.X509Certificate; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringEscapeUtils; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.ContentVerifierProvider; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder; import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.bouncycastle.pkcs.PKCSException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import at.gv.egiz.eaaf.core.api.gui.IGUIFormBuilder; import at.gv.egiz.eaaf.core.api.storage.ITransactionStorage; import at.gv.egiz.eaaf.core.exceptions.EAAFException; import at.gv.egiz.eaaf.core.exceptions.EAAFStorageException; import at.gv.egiz.eaaf.core.impl.utils.FileUtils; import at.gv.egiz.eaaf.core.impl.utils.HTTPUtils; import at.gv.egiz.eaaf.core.impl.utils.Random; import at.gv.egiz.eaaf.modules.pvp2.exception.CredentialsNotAvailableException; import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; import at.gv.egovernment.moa.id.auth.exception.AuthenticationException; import at.gv.egovernment.moa.id.auth.exception.ParseException; import at.gv.egovernment.moa.id.auth.frontend.builder.DefaultGUIFormBuilderConfiguration; import at.gv.egovernment.moa.id.auth.modules.ssotransfer.SSOTransferConstants; import at.gv.egovernment.moa.id.auth.modules.ssotransfer.data.Pair; import at.gv.egovernment.moa.id.auth.modules.ssotransfer.data.SSOTransferContainer; import at.gv.egovernment.moa.id.auth.modules.ssotransfer.utils.SSOContainerUtils; import at.gv.egovernment.moa.id.auth.parser.IdentityLinkAssertionParser; import at.gv.egovernment.moa.id.commons.api.AuthConfiguration; import at.gv.egovernment.moa.id.commons.api.data.IAuthenticationSession; import at.gv.egovernment.moa.id.commons.api.exceptions.ConfigurationException; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.commons.api.exceptions.SessionDataStorageException; import at.gv.egovernment.moa.id.commons.db.ex.MOADatabaseException; import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; import at.gv.egovernment.moa.id.moduls.SSOManager; import at.gv.egovernment.moa.id.protocols.pvp2x.signer.IDPCredentialProvider; import at.gv.egovernment.moa.id.storage.IAuthenticationSessionStoreage; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.Base64Utils; import at.gv.egovernment.moa.util.MiscUtil; import net.glxn.qrgen.QRCode; import net.glxn.qrgen.image.ImageType; /** * @author tlenz * */ //@WebServlet(name = "SSOTransferGUI", value = "/TransferSSOSession") @Controller public class SSOTransferServlet{ private static final long transmisionTimeOut = 90 * 1000; // default 90 secundes @Autowired SSOManager ssomanager; @Autowired IAuthenticationSessionStoreage authenticationSessionStorage; @Autowired SSOContainerUtils ssoTransferUtils; @Autowired ITransactionStorage transactionStorage; @Autowired IDPCredentialProvider idpCredentials; @Autowired AuthConfiguration authConfig; @Autowired IGUIFormBuilder guiBuilder; public SSOTransferServlet() { super(); Logger.debug("Registering servlet " + getClass().getName() + " with mapping {'/TransferSSOSession','/TransmitSSOSession'}" + " Development-EndPoints: {'/TestTransferSSOSession','/TestTransmitSSOSession'}."); } /** * Only for development and debugging * This methode create template QR and for the template service * * @param req * @param resp * @throws IOException */ @RequestMapping(value = { "/TestTransferSSOSession" }, method = {RequestMethod.GET}) public void testTransferSSOSessionGUIWithoutAuthentication(HttpServletRequest req, HttpServletResponse resp) throws IOException { try { //create first step of SSO Transfer GUI String authURL = HTTPUtils.extractAuthURLFromRequest(req); if (!AuthConfigurationProviderFactory.getInstance().getPublicURLPrefix().contains(authURL)) { Logger.warn("Requested URL is not allowed.");; resp.sendError(500, "Requested URL is not allowed."); } DefaultGUIFormBuilderConfiguration config = new DefaultGUIFormBuilderConfiguration( authURL, DefaultGUIFormBuilderConfiguration.VIEW_SSO_SESSION_TRANSFER, null); internalCreateQRCodeForTransfer(resp, authURL, "123456", "/TestTransmitSSOSession", config); } catch (MOAIDException | MOADatabaseException e) { e.printStackTrace(); resp.sendError(500, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (NoSuchAlgorithmException | InvalidParameterSpecException e) { e.printStackTrace(); resp.sendError(500, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (Exception e) { e.printStackTrace(); resp.sendError(500, StringEscapeUtils.escapeHtml(e.getMessage())); } } /** * Only for development and debugging * This methode transfer personal information to smartphone * * @param req * @param resp * @throws IOException */ @RequestMapping(value = { "/TestTransmitSSOSession" }, method = {RequestMethod.GET, RequestMethod.POST}) public void testTransferToPhone(HttpServletRequest req, HttpServletResponse resp) throws IOException { Logger.debug("Receive " + this.getClass().getName() + " request"); Object tokenObj = req.getParameter(SSOTransferConstants.REQ_PARAM_TOKEN); if (tokenObj != null && tokenObj instanceof String) { String token = StringEscapeUtils.escapeHtml((String)tokenObj); try { Logger.debug("Load token:" + token + " from storage."); SSOTransferContainer container = transactionStorage.get(token, SSOTransferContainer.class, transmisionTimeOut * 1000); if (container != null) { IAuthenticationSession moaSession = new AuthenticationSession("123456", new Date()); URL idlURL = new URL(FileUtils.makeAbsoluteURL( authConfig.getMonitoringTestIdentityLinkURL(), authConfig.getRootConfigFileDir())); InputStream idlstream = idlURL.openStream(); moaSession.setIdentityLink(new IdentityLinkAssertionParser(idlstream).parseIdentityLink()); internalTransferPersonalInformation(req, resp, container, moaSession, true); } else { Logger.info("Servlet " + getClass().getName() + " receive a token:" + token + ", which references an empty data object."); resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Empty data object."); } } catch (MOADatabaseException e) { Logger.info("Servlet " + getClass().getName() + " receive a token:" + token + ", which is UNKNOWN."); resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Transfer token is UNKOWN:"); } catch (AuthenticationException e) { Logger.info("Servlet " + getClass().getName() + " receive a token:" + token + ", which has a timeout."); resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Single Sign-On session transfer token is not valid any more."); } catch (OperatorCreationException e) { Logger.warn("Device inpersonisation FAILED: " + e.getMessage(), e); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (CredentialsNotAvailableException e) { Logger.warn("Device inpersonisation FAILED: " + e.getMessage(), e); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (PKCSException e) { Logger.warn("Device inpersonisation FAILED: " + e.getMessage(), e); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (CertificateException e) { Logger.warn("Device inpersonisation FAILED: " + e.getMessage(), e); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (InvalidKeyException e) { Logger.warn("Device inpersonisation FAILED: " + e.getMessage(), e); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (NoSuchAlgorithmException e) { Logger.warn("Device inpersonisation FAILED: " + e.getMessage(), e); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (InvalidKeySpecException e) { Logger.warn("Device inpersonisation FAILED: " + e.getMessage(), e); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (SessionDataStorageException e) { Logger.warn("Device inpersonisation FAILED: " + e.getMessage(), e); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (ParseException e) { Logger.warn("Device inpersonisation FAILED: " + e.getMessage(), e); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (IllegalBlockSizeException e) { Logger.warn("Device inpersonisation FAILED: " + e.getMessage(), e); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (BadPaddingException e) { Logger.warn("Device inpersonisation FAILED: " + e.getMessage(), e); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (NoSuchPaddingException e) { Logger.warn("Device inpersonisation FAILED: " + e.getMessage(), e); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (ConfigurationException e) { Logger.warn("Device inpersonisation FAILED: " + e.getMessage(), e); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (EAAFException e) { Logger.warn("Device inpersonisation FAILED: " + e.getMessage(), e); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } } else { Logger.info("Servlet " + getClass().getName() + " receive a NOT valid request."); resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Request not valid."); } } @RequestMapping(value = { "/TransmitSSOSession" }, method = {RequestMethod.GET, RequestMethod.POST}) public void transferToPhone(HttpServletRequest req, HttpServletResponse resp) throws IOException { Logger.debug("Receive " + this.getClass().getName() + " request"); Object tokenObj = req.getParameter(SSOTransferConstants.REQ_PARAM_TOKEN); if (tokenObj != null && tokenObj instanceof String) { String token = StringEscapeUtils.escapeHtml((String)tokenObj); try { SSOTransferContainer container = transactionStorage.get(token, SSOTransferContainer.class, transmisionTimeOut); if (container != null) { IAuthenticationSession moaSession = authenticationSessionStorage.getInternalSSOSession(container.getMoaSessionID()); if (moaSession != null) { internalTransferPersonalInformation(req, resp, container, moaSession, false); } else { Logger.info("Servlet " + getClass().getName() + " receive a token:" + token + ", but the corresponding MOASession is empty"); resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "No MOASession."); } } else { Logger.info("Servlet " + getClass().getName() + " receive a token:" + token + ", which references an empty data object."); resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Empty data object."); } } catch (MOADatabaseException e) { Logger.info("Servlet " + getClass().getName() + " receive a token:" + token + ", which is UNKNOWN."); resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Transfer token is UNKOWN:"); } catch (AuthenticationException e) { Logger.info("Servlet " + getClass().getName() + " receive a token:" + token + ", which has a timeout."); resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Single Sign-On session transfer token is not valid any more."); } catch (OperatorCreationException e) { // TODO Auto-generated catch block e.printStackTrace(); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (CredentialsNotAvailableException e) { // TODO Auto-generated catch block e.printStackTrace(); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (PKCSException e) { // TODO Auto-generated catch block e.printStackTrace(); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (CertificateException e) { // TODO Auto-generated catch block e.printStackTrace(); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (InvalidKeyException e) { // TODO Auto-generated catch block e.printStackTrace(); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (InvalidKeySpecException e) { // TODO Auto-generated catch block e.printStackTrace(); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (SessionDataStorageException e) { e.printStackTrace(); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (IllegalBlockSizeException e) { e.printStackTrace(); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (BadPaddingException e) { e.printStackTrace(); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (NoSuchPaddingException e) { e.printStackTrace(); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (EAAFException e) { e.printStackTrace(); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, StringEscapeUtils.escapeHtml(e.getMessage())); } } else { Logger.info("Servlet " + getClass().getName() + " receive a NOT valid request."); resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Request not valid."); } } @RequestMapping(value = { "/TransferSSOSession" }, method = {RequestMethod.GET, RequestMethod.POST}) public void transferSSOSessionGUI(HttpServletRequest req, HttpServletResponse resp) throws IOException { //search SSO session String ssoid = ssomanager.getSSOSessionID(req); try { String authURL = HTTPUtils.extractAuthURLFromRequest(req); if (!AuthConfigurationProviderFactory.getInstance().getPublicURLPrefix(). contains(authURL)) { Logger.warn("Requested URL is not allowed.");; resp.sendError(500, "Requested URL is not allowed."); } DefaultGUIFormBuilderConfiguration config = new DefaultGUIFormBuilderConfiguration( authURL, DefaultGUIFormBuilderConfiguration.VIEW_SSO_SESSION_TRANSFER, null); if (ssomanager.isValidSSOSession(ssoid, null)) { //create first step of SSO Transfer GUI String ssoSessionId = authenticationSessionStorage.getInternalSSOSessionWithSSOID(ssoid); if(ssoSessionId != null) { internalCreateQRCodeForTransfer(resp, authURL, ssoSessionId, SSOTransferConstants.SERVLET_SSOTRANSFER_TO_SMARTPHONE, config); return; } } config.putCustomParameter("errorMsg", "No active Single Sign-On session found! SSO Session transfer is not possible."); guiBuilder.build(resp, config, "SSO-Transfer-Module"); } catch (MOAIDException | MOADatabaseException e) { e.printStackTrace(); resp.sendError(500, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (NoSuchAlgorithmException | InvalidParameterSpecException e) { e.printStackTrace(); resp.sendError(500, StringEscapeUtils.escapeHtml(e.getMessage())); } catch (Exception e) { e.printStackTrace(); resp.sendError(500, StringEscapeUtils.escapeHtml(e.getMessage())); } } private void internalTransferPersonalInformation(HttpServletRequest req, HttpServletResponse resp, SSOTransferContainer container, IAuthenticationSession moaSession, boolean developmentMode) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, OperatorCreationException, CredentialsNotAvailableException, PKCSException, CertificateException, SessionDataStorageException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException, EAAFStorageException { Logger.debug(""); JsonObject receivedData = getJSONObjectFromPostMessage(req, developmentMode); if (receivedData == null) { Logger.warn("No data received"); throw new IOException("No data received"); } String mobilePubKeyBase64 = receivedData.get( SSOTransferConstants.SSOCONTAINER_KEY_DH_PUBKEY).getAsString(); String mobileCSRBase64 = receivedData.get( SSOTransferConstants.SSOCONTAINER_KEY_CSR).getAsString(); Logger.debug("Receive PubKey:" +mobilePubKeyBase64 + " | CSR:" + mobileCSRBase64); //finish DH key agreement BigInteger mobilePubKey = new BigInteger(Base64Utils.decode(mobilePubKeyBase64, true)); DHPublicKeySpec mobilePubKeySpec = new DHPublicKeySpec(mobilePubKey, container.getDhParams().getF().getP(), container.getDhParams().getF().getG()); byte[] sharedSecret = ssoTransferUtils.getSecret(mobilePubKeySpec, container.getDhParams().getS()); //build ASE256 key MessageDigest digest = MessageDigest.getInstance("SHA-256"); digest.reset(); byte[] hashedSecret = digest.digest(sharedSecret); //decrypt CSR Logger.debug("Finished Diffie-Hellman key exchange. --> Starting CSR decryption ..."); byte[] encryptedCSR = Base64Utils.decode(mobileCSRBase64, true); Logger.debug("EncCSR:" + Base64Utils.encode(encryptedCSR) + " | Key:" + Base64Utils.encode(hashedSecret)); byte[] decryptedCSR = ssoTransferUtils.enOrDeCryptCSR(encryptedCSR, hashedSecret, Cipher.DECRYPT_MODE); Logger.debug("DecCSR:" + Base64Utils.encode(decryptedCSR)); Logger.debug("CSR decryption finished. --> Starting CSR validation and signing ..."); //generate certificate from CSR X509Certificate mobileCert = signCSRWithMOAKey(decryptedCSR); Logger.debug("CSR validation finished. --> Starting personData generation ... "); moaSession.setGenericDataToSession( SSOTransferConstants.MOASESSION_DATA_HOLDEROFKEY_CERTIFICATE, mobileCert.getEncoded()); //generate assertion Date now = new Date(); String personInformationToTransfer = ssoTransferUtils.generateSignedAndEncryptedSSOContainer( container.getAuthURL(), moaSession, now, hashedSecret); Logger.debug("PersonData:" + personInformationToTransfer); //encrypt personal information Logger.debug("PersonData generation finished. --> Starting personData encryption ... "); Logger.debug("Encrypt personData finished. --> Send token to device."); resp.setContentType("text/html;charset=UTF-8"); PrintWriter out = new PrintWriter(resp.getOutputStream()); out.print(personInformationToTransfer); out.flush(); return; } private void internalCreateQRCodeForTransfer(HttpServletResponse resp, String authURL, String moaSessionID, String servletEndPoint, DefaultGUIFormBuilderConfiguration config) throws Exception { SSOTransferContainer container = new SSOTransferContainer(); String token = Random.nextRandom(); container.setAuthURL(authURL); container.setTokkenID(token); container.setMoaSessionID(moaSessionID); //build Diffie-Hellman parameter for Data transfer Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); //TODO: implement worker-thread to generate new parameters every day //generate new DH parameters //SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG-SP80090", "IAIK"); //AlgorithmParameterGenerator paramGen = AlgorithmParameterGenerator.getInstance("DiffieHellman", "BC"); //paramGen.init(1024, secureRandom ); // number of bits //AlgorithmParameters params = paramGen.generateParameters(); //DHParameterSpec dhSpec = (DHParameterSpec)params.getParameterSpec(DHParameterSpec.class); //DHParameterSpec dhSpec = (DHParameterSpec)params.getParameterSpec(DHParameterSpec.class); //use predefined parameters BigInteger prime = new BigInteger(Base64Utils.decode(SSOTransferConstants.DH_PRIME_BASE64, false)); BigInteger generator = new BigInteger(Base64Utils.decode(SSOTransferConstants.DH_GENERATOR_BASE64, false)); DHParameterSpec dhSpec = new DHParameterSpec(prime, generator, 1024); Pair dhKeyIDP = ssoTransferUtils.createSpecificKey(dhSpec.getP(), dhSpec.getG()); container.setDhParams(dhKeyIDP); //store container transactionStorage.put(token, container,(int)transmisionTimeOut); //build QR code String containerURL = authURL + servletEndPoint + "?"+ SSOTransferConstants.REQ_PARAM_TOKEN + "=" + token; JsonObject qrResult = new JsonObject(); qrResult.addProperty(SSOTransferConstants.SSOCONTAINER_KEY_TYPE, SSOTransferConstants.SSOCONTAINER_VALUE_TYPE_PERSIST); qrResult.addProperty(SSOTransferConstants.SSOCONTAINER_KEY_URL, containerURL); //add DH parameters qrResult.addProperty(SSOTransferConstants.SSOCONTAINER_KEY_DH_PUBKEY, Base64Utils.encode(dhKeyIDP.getF().getY().toByteArray())); qrResult.addProperty(SSOTransferConstants.SSOCONTAINER_KEY_DH_PRIME, Base64Utils.encode(dhKeyIDP.getF().getP().toByteArray())); qrResult.addProperty(SSOTransferConstants.SSOCONTAINER_KEY_DH_GENERATOR, Base64Utils.encode(dhKeyIDP.getF().getG().toByteArray())); ByteArrayOutputStream qrStream = QRCode.from(qrResult.toString()).to(ImageType.GIF).withSize(350, 350).stream(); String base64EncodedImage = Base64Utils.encode(qrStream.toByteArray()); config.putCustomParameter("QRImage", base64EncodedImage); config.putCustomParameterWithOutEscaption("successMsg", "Scan the QR-Code with your SSO-Transfer App to start the transfer operation."); guiBuilder.build(resp, config, "SSO-Session Transfer-Module"); } private X509Certificate signCSRWithMOAKey(byte[] inputCSR) throws IOException, OperatorCreationException, PKCSException, CredentialsNotAvailableException, CertificateException { PKCS10CertificationRequest csr = new PKCS10CertificationRequest(inputCSR); //validate CSR request ContentVerifierProvider verifier = new JcaContentVerifierProviderBuilder().setProvider( new BouncyCastleProvider()).build(csr.getSubjectPublicKeyInfo()); csr.isSignatureValid(verifier); //build certificate with CSR X500Name issuer = new X500Name("CN=IDP"); BigInteger serial = new BigInteger(32, new SecureRandom()); Date from = new Date(); Date to = new Date(System.currentTimeMillis() + (SSOTransferConstants.CERT_VALIDITY * 86400000L)); X509v3CertificateBuilder certgen = new X509v3CertificateBuilder(issuer, serial, from, to, csr.getSubject(), csr.getSubjectPublicKeyInfo()); certgen.addExtension(Extension.basicConstraints, false, new BasicConstraints(false)); //certgen.addExtension(Extension.subjectKeyIdentifier, false, SubjectKeyIdentifier.getInstance(csr.getSubjectPublicKeyInfo())); //build signer ContentSigner sigGen = new JcaContentSignerBuilder("SHA1withRSA").build(idpCredentials.getIDPAssertionSigningCredential().getPrivateKey()); //sign certificate X509CertificateHolder x509CertificateHolder = certgen.build(sigGen); return X509Certificate.getInstance(x509CertificateHolder.getEncoded()); } private JsonObject getJSONObjectFromPostMessage(HttpServletRequest req, boolean developmentMode) { //read POST request StringBuffer sb = new StringBuffer(); String receivedPostMessage = null; try { BufferedReader reader = req.getReader(); String line = null; while ((line = reader.readLine()) != null) { sb.append(line); } receivedPostMessage = sb.toString(); } catch (IOException e) { Logger.warn("Received POST-message produce an ERROR.", e); Logger.info("Msg: " + receivedPostMessage); } JsonParser parser = new JsonParser(); JsonObject receivedData = null; Logger.debug("JSON POST msg: " + sb.toString()); if (MiscUtil.isNotEmpty(receivedPostMessage)) { receivedData = (JsonObject) parser.parse(sb.toString()); } else if (developmentMode && MiscUtil.isNotEmpty(req.getParameter("blob"))) { receivedData = (JsonObject) parser.parse(req.getParameter("blob")); } return receivedData; } }