/** * Determines whether given SingleSignOn service can be used together with this profile. Bindings * POST, Artifact and Redirect are supported for WebSSO. * * @param endpoint endpoint * @return true if endpoint is supported * @throws MetadataProviderException in case system can't verify whether endpoint is supported or * not */ protected boolean isEndpointSupported(SingleSignOnService endpoint) throws MetadataProviderException { return org.opensaml.common.xml.SAMLConstants.SAML2_POST_BINDING_URI.equals( endpoint.getBinding()) || org.opensaml.common.xml.SAMLConstants.SAML2_ARTIFACT_BINDING_URI.equals( endpoint.getBinding()) || org.opensaml.common.xml.SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals( endpoint.getBinding()); }
/** * Encodes the SAML 2.0 based request XML object into its corresponding Base64 notation, based on * the type of SAML 2.0 binding. * * @param requestMessage the {@link RequestAbstractType} XML object to be encoded * @param binding the SAML 2.0 binding type * @return encoded {@link String} corresponding to the request XML object * @throws SSOException if an error occurs while encoding SAML2 request */ protected static String encodeRequestMessage(RequestAbstractType requestMessage, String binding) throws SSOException { Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(requestMessage); Element authDOM; try { // Marshall this element, and its children, and root them in a newly created Document authDOM = marshaller.marshall(requestMessage); } catch (MarshallingException e) { throw new SSOException( "Error occurred while encoding SAML2 request, failed to marshall the SAML 2.0. " + "Request element XMLObject to its corresponding W3C DOM element", e); } StringWriter writer = new StringWriter(); // Writes the node out to the writer using the DOM XMLHelper.writeNode(authDOM, writer); if (SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals(binding)) { // Compress the message, Base 64 encode and URL encode Deflater deflater = new Deflater(Deflater.DEFLATED, true); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); try (DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(byteArrayOutputStream, deflater)) { deflaterOutputStream.write(writer.toString().getBytes(Charset.forName("UTF-8"))); } catch (IOException e) { throw new SSOException("Error occurred while deflate encoding SAML2 request", e); } String encodedRequestMessage = Base64.encodeBytes(byteArrayOutputStream.toByteArray(), Base64.DONT_BREAK_LINES); try { return URLEncoder.encode(encodedRequestMessage, "UTF-8").trim(); } catch (UnsupportedEncodingException e) { throw new SSOException("Error occurred while encoding SAML2 request", e); } } else if (SAMLConstants.SAML2_POST_BINDING_URI.equals(binding)) { return Base64.encodeBytes( writer.toString().getBytes(Charset.forName("UTF-8")), Base64.DONT_BREAK_LINES); } else { logger.log( Level.FINE, "Unsupported SAML2 HTTP Binding. Defaulting to " + SAMLConstants.SAML2_POST_BINDING_URI); return Base64.encodeBytes( writer.toString().getBytes(Charset.forName("UTF-8")), Base64.DONT_BREAK_LINES); } }
protected String encodeRequestMessage(RequestAbstractType requestMessage, String binding) throws SSOAgentException { Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(requestMessage); Element authDOM = null; try { authDOM = marshaller.marshall(requestMessage); StringWriter rspWrt = new StringWriter(); XMLHelper.writeNode(authDOM, rspWrt); if (SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals(binding)) { // Compress the message, Base 64 encode and URL encode Deflater deflater = new Deflater(Deflater.DEFLATED, true); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(byteArrayOutputStream, deflater); deflaterOutputStream.write(rspWrt.toString().getBytes(Charset.forName("UTF-8"))); deflaterOutputStream.close(); String encodedRequestMessage = Base64.encodeBytes(byteArrayOutputStream.toByteArray(), Base64.DONT_BREAK_LINES); return URLEncoder.encode(encodedRequestMessage, "UTF-8").trim(); } else if (SAMLConstants.SAML2_POST_BINDING_URI.equals(binding)) { return Base64.encodeBytes(rspWrt.toString().getBytes(), Base64.DONT_BREAK_LINES); } else { LOGGER.log( Level.FINE, "Unsupported SAML2 HTTP Binding. Defaulting to " + SAMLConstants.SAML2_POST_BINDING_URI); return Base64.encodeBytes(rspWrt.toString().getBytes(), Base64.DONT_BREAK_LINES); } } catch (MarshallingException e) { throw new SSOAgentException("Error occurred while encoding SAML2 request", e); } catch (UnsupportedEncodingException e) { throw new SSOAgentException("Error occurred while encoding SAML2 request", e); } catch (IOException e) { throw new SSOAgentException("Error occurred while encoding SAML2 request", e); } }
@Override protected void internalInit() { CommonHelper.assertTrue( CommonHelper.isNotBlank(this.idpMetadata) || CommonHelper.isNotBlank(this.idpMetadataPath), "Either idpMetadata or idpMetadataPath must be provided"); CommonHelper.assertNotBlank("callbackUrl", this.callbackUrl); if (!this.callbackUrl.startsWith("http")) { throw new TechnicalException("SAML callbackUrl must be absolute"); } if (CommonHelper.isNotBlank(this.keystorePath) || CommonHelper.isNotBlank(this.keystorePassword) || CommonHelper.isNotBlank(this.privateKeyPassword)) { CommonHelper.assertNotBlank("keystorePath", this.keystorePath); CommonHelper.assertNotBlank("keystorePassword", this.keystorePassword); CommonHelper.assertNotBlank("privateKeyPassword", this.privateKeyPassword); // load private key from the keystore and provide it as OpenSAML credentials this.credentialProvider = new CredentialProvider(this.keystorePath, this.keystorePassword, this.privateKeyPassword); this.decrypter = new EncryptionProvider(this.credentialProvider).buildDecrypter(); } // Bootsrap OpenSAML try { DefaultBootstrap.bootstrap(); NamedKeyInfoGeneratorManager manager = Configuration.getGlobalSecurityConfiguration().getKeyInfoGeneratorManager(); X509KeyInfoGeneratorFactory generator = new X509KeyInfoGeneratorFactory(); generator.setEmitEntityCertificate(true); generator.setEmitEntityCertificateChain(true); manager.registerFactory(Saml2Client.SAML_METADATA_KEY_INFO_GENERATOR, generator); } catch (ConfigurationException e) { throw new SamlException("Error bootstrapping OpenSAML", e); } // required parserPool for XML processing final StaticBasicParserPool parserPool = newStaticBasicParserPool(); final AbstractMetadataProvider idpMetadataProvider = idpMetadataProvider(parserPool); final XMLObject md; try { md = idpMetadataProvider.getMetadata(); } catch (MetadataProviderException e) { throw new SamlException("Error initializing idpMetadataProvider", e); } // If no idpEntityId declared, select first EntityDescriptor entityId as our IDP entityId if (this.idpEntityId == null) { this.idpEntityId = getIdpEntityId(md); } // Generate our Service Provider metadata Saml2MetadataGenerator metadataGenerator = new Saml2MetadataGenerator(); if (this.credentialProvider != null) { metadataGenerator.setCredentialProvider(this.credentialProvider); metadataGenerator.setAuthnRequestSigned(true); } // If the spEntityId is blank, use the callback url if (CommonHelper.isBlank(this.spEntityId)) { this.spEntityId = getCallbackUrl(); } metadataGenerator.setEntityId(this.spEntityId); // Assertion consumer service url is the callback url metadataGenerator.setAssertionConsumerServiceUrl(getCallbackUrl()); // for now same for logout url metadataGenerator.setSingleLogoutServiceUrl(getCallbackUrl()); AbstractMetadataProvider spMetadataProvider = metadataGenerator.buildMetadataProvider(); // Initialize metadata provider for our SP and get the XML as a String try { spMetadataProvider.initialize(); this.spMetadata = metadataGenerator.printMetadata(); } catch (MetadataProviderException e) { throw new TechnicalException("Error initializing spMetadataProvider", e); } catch (MarshallingException e) { logger.warn("Unable to print SP metadata", e); } // Put IDP and SP metadata together ChainingMetadataProvider metadataManager = new ChainingMetadataProvider(); try { metadataManager.addMetadataProvider(idpMetadataProvider); metadataManager.addMetadataProvider(spMetadataProvider); } catch (MetadataProviderException e) { throw new TechnicalException("Error adding idp or sp metadatas to manager", e); } // Build the contextProvider this.contextProvider = new Saml2ContextProvider(metadataManager, this.idpEntityId, this.spEntityId); // Get an AuthnRequest builder this.authnRequestBuilder = new Saml2AuthnRequestBuilder(forceAuth, comparisonType, destinationBindingType); // Build the WebSSO handler for sending and receiving SAML2 messages MessageEncoder encoder = null; if (SAMLConstants.SAML2_POST_BINDING_URI.equals(destinationBindingType)) { // Get a velocity engine for the HTTP-POST binding (building of an HTML document) VelocityEngine velocityEngine = VelocityEngineFactory.getEngine(); encoder = new HTTPPostEncoder(velocityEngine, "/templates/saml2-post-binding.vm"); } else if (SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals(destinationBindingType)) { encoder = new HTTPRedirectDeflateEncoder(); } else { throw new UnsupportedOperationException( "Binding type - " + destinationBindingType + " is not supported"); } // Do we need binding specific decoder? MessageDecoder decoder = new Pac4jHTTPPostDecoder(parserPool); this.handler = new Saml2WebSSOProfileHandler( this.credentialProvider, encoder, decoder, parserPool, destinationBindingType); // Build provider for digital signature validation and encryption this.signatureTrustEngineProvider = new SignatureTrustEngineProvider(metadataManager); // Build the SAML response validator this.responseValidator = new Saml2ResponseValidator(); if (this.maximumAuthenticationLifetime != null) { this.responseValidator.setMaximumAuthenticationLifetime(this.maximumAuthenticationLifetime); } }