/** * Validates that the message context is a {@link SAMLMessageContext} and that its outbound transport is HTTP. * * @param messageContext current message context * * @throws MessageEncodingException thrown if the message context conditions are not met */ protected void validateMessageContent(MessageContext messageContext) throws MessageEncodingException { if (!(messageContext instanceof SAMLMessageContext)) { log.error("Invalid message context type, this encoder only support SAMLMessageContext"); throw new MessageEncodingException( "Invalid message context type, this encoder only support SAMLMessageContext"); } if (!(messageContext.getOutboundMessageTransport() instanceof HTTPOutTransport)) { log.error("Invalid outbound message transport type, this encoder only support HTTPOutTransport"); throw new MessageEncodingException( "Invalid outbound message transport type, this encoder only support HTTPOutTransport"); } } }
/** {@inheritDoc} */ protected void doEncode(MessageContext messageContext) throws MessageEncodingException { if (!(messageContext.getOutboundMessageTransport() instanceof HTTPOutTransport)) { log.error("Invalid outbound message transport type, this encoder only support HTTPOutTransport"); throw new MessageEncodingException( "Invalid outbound message transport type, this encoder only support HTTPOutTransport"); } super.doEncode(messageContext); }
/** {@inheritDoc} */ protected void doEncode(MessageContext messageContext) throws MessageEncodingException { if (!(messageContext instanceof SAMLMessageContext)) { log.error("Invalid message context type, this encoder only support SAMLMessageContext"); throw new MessageEncodingException( "Invalid message context type, this encoder only support SAMLMessageContext"); } if (!(messageContext.getOutboundMessageTransport() instanceof HTTPOutTransport)) { log.error("Invalid outbound message transport type, this encoder only support HTTPOutTransport"); throw new MessageEncodingException( "Invalid outbound message transport type, this encoder only support HTTPOutTransport"); } prepareMessageContext(messageContext); processOutboundHandlerChain(messageContext); encodeToTransport(messageContext); }
/** * DEFLATE (RFC1951) compresses the given SAML message. * * @param message SAML message * * @return DEFLATE compressed message * * @throws MessageEncodingException thrown if there is a problem compressing the message */ protected String deflateAndBase64Encode(SAMLObject message) throws MessageEncodingException { log.debug("Deflating and Base64 encoding SAML message"); try { String messageStr = XMLHelper.nodeToString(marshallMessage(message)); ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); Deflater deflater = new Deflater(Deflater.DEFLATED, true); DeflaterOutputStream deflaterStream = new DeflaterOutputStream(bytesOut, deflater); deflaterStream.write(messageStr.getBytes("UTF-8")); deflaterStream.finish(); return Base64.encodeBytes(bytesOut.toByteArray(), Base64.DONT_BREAK_LINES); } catch (IOException e) { throw new MessageEncodingException("Unable to DEFLATE and Base64 encode SAML message", e); } }
/** {@inheritDoc} */ protected void encodeToTransport(MessageContext messageContext) throws MessageEncodingException { Element envelopeElem = marshallMessage(messageContext.getOutboundMessage()); preprocessTransport(messageContext); try { OutTransport outTransport = messageContext.getOutboundMessageTransport(); Writer out = new OutputStreamWriter(outTransport.getOutgoingStream(), "UTF-8"); XMLHelper.writeNode(envelopeElem, out); out.flush(); } catch (UnsupportedEncodingException e) { log.error("JVM does not support required UTF-8 encoding"); throw new MessageEncodingException("JVM does not support required UTF-8 encoding"); } catch (IOException e) { log.error("Unable to write message content to outbound stream", e); throw new MessageEncodingException("Unable to write message content to outbound stream", e); } }
/** {@inheritDoc} */ protected void doEncode(MessageContext messageContext) throws MessageEncodingException { if (!(messageContext instanceof SAMLMessageContext)) { log.error("Invalid message context type, this encoder only support SAMLMessageContext"); throw new MessageEncodingException( "Invalid message context type, this encoder only support SAMLMessageContext"); } if (!(messageContext.getOutboundMessageTransport() instanceof HTTPOutTransport)) { log.error("Invalid outbound message transport type, this encoder only support HTTPOutTransport"); throw new MessageEncodingException( "Invalid outbound message transport type, this encoder only support HTTPOutTransport"); } SAMLMessageContext artifactContext = (SAMLMessageContext) messageContext; HTTPOutTransport outTransport = (HTTPOutTransport) artifactContext.getOutboundMessageTransport(); outTransport.setCharacterEncoding("UTF-8"); if (postEncoding) { postEncode(artifactContext, outTransport); } else { getEncode(artifactContext, outTransport); } }
/** * Gets the signature algorithm URI to use with the given signing credential. * * @param credential the credential that will be used to sign the message * @param config the SecurityConfiguration to use (may be null) * * @return signature algorithm to use with the given signing credential * * @throws MessageEncodingException thrown if the algorithm URI could not be derived from the supplied credential */ protected String getSignatureAlgorithmURI(Credential credential, SecurityConfiguration config) throws MessageEncodingException { SecurityConfiguration secConfig; if (config != null) { secConfig = config; } else { secConfig = Configuration.getGlobalSecurityConfiguration(); } String signAlgo = secConfig.getSignatureAlgorithmURI(credential); if (signAlgo == null) { throw new MessageEncodingException("The signing credential's algorithm URI could not be derived"); } return signAlgo; }
/** * Encode the message context to the transport. * * @param messageContext the message context to process * @throws MessageEncodingException thrown if there is a problem encoding the message context * to the transport */ protected void encodeToTransport(MessageContext messageContext) throws MessageEncodingException { Element envelopeElem = marshallMessage(messageContext.getOutboundMessage()); preprocessTransport(messageContext); try { OutTransport outTransport = messageContext.getOutboundMessageTransport(); Writer out = new OutputStreamWriter(outTransport.getOutgoingStream(), "UTF-8"); XMLHelper.writeNode(envelopeElem, out); out.flush(); } catch (UnsupportedEncodingException e) { log.error("JVM does not support required UTF-8 encoding"); throw new MessageEncodingException("JVM does not support required UTF-8 encoding"); } catch (IOException e) { log.error("Unable to write message content to outbound stream", e); throw new MessageEncodingException("Unable to write message content to outbound stream", e); } }
/** * Gets the signature algorithm URI to use with the given signing credential. * * @param credential the credential that will be used to sign the message * @param config the SecurityConfiguration to use (may be null) * * @return signature algorithm to use with the given signing credential * * @throws MessageEncodingException thrown if the algorithm URI could not be derived from the supplied credential */ protected String getSignatureAlgorithmURI(Credential credential, SecurityConfiguration config) throws MessageEncodingException { SecurityConfiguration secConfig; if (config != null) { secConfig = config; } else { secConfig = Configuration.getGlobalSecurityConfiguration(); } String signAlgo = secConfig.getSignatureAlgorithmURI(credential); if (signAlgo == null) { throw new MessageEncodingException("The signing credential's algorithm URI could not be derived"); } return signAlgo; }
/** * Generates the signature over the string of concatenated form control data as indicated by the SimpleSign spec. * * @param signingCredential credential that will be used to sign * @param algorithmURI algorithm URI of the signing credential * @param formData form control data to be signed * * @return base64 encoded signature of form control data * * @throws MessageEncodingException there is an error computing the signature */ protected String generateSignature(Credential signingCredential, String algorithmURI, String formData) throws MessageEncodingException { log.debug(String.format( "Generating signature with key type '%s', algorithm URI '%s' over form control string '%s'", SecurityHelper.extractSigningKey(signingCredential).getAlgorithm(), algorithmURI, formData)); String b64Signature = null; try { byte[] rawSignature = SigningUtil.signWithURI(signingCredential, algorithmURI, formData.getBytes("UTF-8")); b64Signature = Base64.encodeBytes(rawSignature, Base64.DONT_BREAK_LINES); log.debug("Generated digital signature value (base64-encoded) {}", b64Signature); } catch (SecurityException e) { log.error("Error during URL signing process", e); throw new MessageEncodingException("Unable to sign form control string", e); } catch (UnsupportedEncodingException e) { // UTF-8 encoding is required to be supported by all JVMs } return b64Signature; }
} catch (SecurityException e) { log.error("Error during URL signing process", e); throw new MessageEncodingException("Unable to sign URL query string", e); } catch (UnsupportedEncodingException e) {
/** * Process the outbound {@link HandlerChain} for the message context, if any. * * @param messageContext the message context to process * @throws MessageEncodingException thrown if a handler indicates a problem handling the message */ protected void processOutboundHandlerChain(MessageContext messageContext) throws MessageEncodingException { HandlerChainResolver outboundHandlerChainResolver = messageContext.getOutboundHandlerChainResolver(); if (outboundHandlerChainResolver != null) { log.debug("Invoking outbound handler chain on message context"); try { for (HandlerChain outboundHandlerChain : outboundHandlerChainResolver.resolve(messageContext)) { if (outboundHandlerChain != null) { invokeHandlerChain(outboundHandlerChain, messageContext); } } } catch (HandlerException e) { log.error("Encountered HandlerException when encoding message: {}", e.getMessage()); throw new MessageEncodingException("Handler exception while encoding message", e); } } }
/** * Process the outbound {@link HandlerChain} for the message context, if any. * * @param messageContext the message context to process * @throws MessageEncodingException thrown if a handler indicates a problem handling the message */ protected void processOutboundHandlerChain(MessageContext messageContext) throws MessageEncodingException { HandlerChainResolver outboundHandlerChainResolver = messageContext.getOutboundHandlerChainResolver(); if (outboundHandlerChainResolver != null) { log.debug("Invoking outbound handler chain on message context"); try { for (HandlerChain outboundHandlerChain : outboundHandlerChainResolver.resolve(messageContext)) { if (outboundHandlerChain != null) { invokeHandlerChain(outboundHandlerChain, messageContext); } } } catch (HandlerException e) { log.error("Encountered HandlerException when encoding message: {}", e.getMessage()); throw new MessageEncodingException("Handler exception while encoding message", e); } } }
/** * Helper method that marshals the given message. * * @param message message the marshall and serialize * @return marshaled message * @throws org.opensaml.ws.message.encoder.MessageEncodingException * thrown if the give message can not be marshaled into its DOM representation */ public static Element marshallMessage(XMLObject message) throws MessageEncodingException { try { if (message.getDOM() != null) { log.debug("XMLObject already had cached DOM, returning that element"); return message.getDOM(); } Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(message); if (marshaller == null) { throw new MessageEncodingException("Unable to marshall message, no marshaller registered for message object: " + message.getElementQName()); } Element messageElem = marshaller.marshall(message); if (log.isTraceEnabled()) { log.trace("Marshalled message into DOM:\n{}", XMLHelper.nodeToString(messageElem)); } return messageElem; } catch (MarshallingException e) { log.error("Encountered error marshalling message to its DOM representation", e); throw new MessageEncodingException("Encountered error marshalling message into its DOM representation", e); } }
/** {@inheritDoc} */ protected void doEncode(MessageContext messageContext) throws MessageEncodingException { if (!(messageContext instanceof SAMLMessageContext)) { log.error("Invalid message context type, this encoder only support SAMLMessageContext"); throw new MessageEncodingException( "Invalid message context type, this encoder only support SAMLMessageContext"); } if (!(messageContext.getOutboundMessageTransport() instanceof HTTPOutTransport)) { log.error("Invalid outbound message transport type, this encoder only support HTTPOutTransport"); throw new MessageEncodingException( "Invalid outbound message transport type, this encoder only support HTTPOutTransport"); } SAMLMessageContext samlMsgCtx = (SAMLMessageContext) messageContext; SAMLObject outboundMessage = samlMsgCtx.getOutboundSAMLMessage(); if (outboundMessage == null) { throw new MessageEncodingException("No outbound SAML message contained in message context"); } String endpointURL = getEndpointURL(samlMsgCtx).buildURL(); if (samlMsgCtx.getOutboundSAMLMessage() instanceof ResponseAbstractType) { ((ResponseAbstractType) samlMsgCtx.getOutboundSAMLMessage()).setRecipient(endpointURL); } signMessage(samlMsgCtx); samlMsgCtx.setOutboundMessage(outboundMessage); postEncode(samlMsgCtx, endpointURL); }
/** * Base64 and POST encodes the outbound message and writes it to the outbound transport. * * @param messageContext current message context * @param endpointURL endpoint URL to which to encode message * * @throws MessageEncodingException thrown if there is a problem encoding the message */ protected void postEncode(SAMLMessageContext messageContext, String endpointURL) throws MessageEncodingException { log.debug("Invoking Velocity template to create POST body"); try { VelocityContext context = new VelocityContext(); populateVelocityContext(context, messageContext, endpointURL); HTTPOutTransport outTransport = (HTTPOutTransport) messageContext.getOutboundMessageTransport(); HTTPTransportUtils.addNoCacheHeaders(outTransport); HTTPTransportUtils.setUTF8Encoding(outTransport); HTTPTransportUtils.setContentType(outTransport, "text/html"); Writer out = new OutputStreamWriter(outTransport.getOutgoingStream(), "UTF-8"); velocityEngine.mergeTemplate(velocityTemplateId, "UTF-8", context, out); out.flush(); } catch (Exception e) { log.error("Error invoking Velocity template", e); throw new MessageEncodingException("Error creating output document", e); } }
/** {@inheritDoc} */ protected void doEncode(MessageContext messageContext) throws MessageEncodingException { if (!(messageContext instanceof SAMLMessageContext)) { log.error("Invalid message context type, this encoder only support SAMLMessageContext"); throw new MessageEncodingException( "Invalid message context type, this encoder only support SAMLMessageContext"); } if (!(messageContext.getOutboundMessageTransport() instanceof HTTPOutTransport)) { log.error("Invalid outbound message transport type, this encoder only support HTTPOutTransport"); throw new MessageEncodingException( "Invalid outbound message transport type, this encoder only support HTTPOutTransport"); } SAMLMessageContext samlMsgCtx = (SAMLMessageContext) messageContext; SAMLObject outboundMessage = samlMsgCtx.getOutboundSAMLMessage(); if (outboundMessage == null) { throw new MessageEncodingException("No outbound SAML message contained in message context"); } String endpointURL = getEndpointURL(samlMsgCtx).buildURL(); if (samlMsgCtx.getOutboundSAMLMessage() instanceof StatusResponseType) { ((StatusResponseType) samlMsgCtx.getOutboundSAMLMessage()).setDestination(endpointURL); } signMessage(samlMsgCtx); samlMsgCtx.setOutboundMessage(outboundMessage); postEncode(samlMsgCtx, endpointURL); }
/** {@inheritDoc} */ protected void doEncode(MessageContext messageContext) throws MessageEncodingException { validateMessageContent(messageContext); SAMLMessageContext samlMsgCtx = (SAMLMessageContext) messageContext; SAMLObject samlMessage = samlMsgCtx.getOutboundSAMLMessage(); if (samlMessage == null) { throw new MessageEncodingException("No outbound SAML message contained in message context"); } signMessage(samlMsgCtx); Envelope envelope = buildSOAPMessage(samlMsgCtx, samlMessage); Element envelopeElem = marshallMessage(envelope); try { HTTPOutTransport outTransport = (HTTPOutTransport) messageContext.getOutboundMessageTransport(); HTTPTransportUtils.addNoCacheHeaders(outTransport); HTTPTransportUtils.setUTF8Encoding(outTransport); HTTPTransportUtils.setContentType(outTransport, "text/xml"); outTransport.setHeader("SOAPAction", "http://www.oasis-open.org/committees/security"); Writer out = new OutputStreamWriter(outTransport.getOutgoingStream(), "UTF-8"); XMLHelper.writeNode(envelopeElem, out); out.flush(); } catch (UnsupportedEncodingException e) { log.error("JVM does not support required UTF-8 encoding"); throw new MessageEncodingException("JVM does not support required UTF-8 encoding"); } catch (IOException e) { log.error("Unable to write message content to outbound stream", e); throw new MessageEncodingException("Unable to write message content to outbound stream", e); } }
/** * Performs HTTP GET based encoding. * * @param artifactContext current request context * @param outTransport outbound HTTP transport * * @throws MessageEncodingException thrown if there is a problem GET encoding the artifact */ protected void getEncode(SAMLMessageContext artifactContext, HTTPOutTransport outTransport) throws MessageEncodingException { log.debug("Performing HTTP GET SAML 2 artifact encoding"); URLBuilder urlBuilder = getEndpointURL(artifactContext); List<Pair<String, String>> params = urlBuilder.getQueryParams(); AbstractSAMLArtifact artifact = buildArtifact(artifactContext); if(artifact == null){ log.error("Unable to build artifact for message to relying party"); throw new MessageEncodingException("Unable to builder artifact for message to relying party"); } params.add(new Pair<String, String>("SAMLart", artifact.base64Encode())); if (checkRelayState(artifactContext.getRelayState())) { params.add(new Pair<String, String>("RelayState", artifactContext.getRelayState())); } outTransport.sendRedirect(urlBuilder.buildURL()); }
/** {@inheritDoc} */ protected void doEncode(MessageContext messageContext) throws MessageEncodingException { if (!(messageContext instanceof SAMLMessageContext)) { log.error("Invalid message context type, this encoder only support SAMLMessageContext"); throw new MessageEncodingException( "Invalid message context type, this encoder only support SAMLMessageContext"); } if (!(messageContext.getOutboundMessageTransport() instanceof HTTPOutTransport)) { log.error("Invalid outbound message transport type, this encoder only support HTTPOutTransport"); throw new MessageEncodingException( "Invalid outbound message transport type, this encoder only support HTTPOutTransport"); } SAMLMessageContext samlMsgCtx = (SAMLMessageContext) messageContext; String endpointURL = getEndpointURL(samlMsgCtx).buildURL(); setResponseDestination(samlMsgCtx.getOutboundSAMLMessage(), endpointURL); removeSignature(samlMsgCtx); String encodedMessage = deflateAndBase64Encode(samlMsgCtx.getOutboundSAMLMessage()); String redirectURL = buildRedirectURL(samlMsgCtx, endpointURL, encodedMessage); HTTPOutTransport out = (HTTPOutTransport) messageContext.getOutboundMessageTransport(); HTTPTransportUtils.addNoCacheHeaders(out); HTTPTransportUtils.setUTF8Encoding(out); out.sendRedirect(redirectURL); }