-
Notifications
You must be signed in to change notification settings - Fork 98
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into dependabot/maven/commons-io-commons-io-2.14.0
- Loading branch information
Showing
3 changed files
with
230 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
package com.coveo.saml; | ||
|
||
import java.io.StringWriter; | ||
import java.security.cert.X509Certificate; | ||
|
||
import javax.xml.XMLConstants; | ||
import javax.xml.namespace.QName; | ||
import javax.xml.parsers.DocumentBuilder; | ||
import javax.xml.parsers.DocumentBuilderFactory; | ||
import javax.xml.transform.OutputKeys; | ||
import javax.xml.transform.Transformer; | ||
import javax.xml.transform.TransformerFactory; | ||
import javax.xml.transform.dom.DOMSource; | ||
import javax.xml.transform.stream.StreamResult; | ||
|
||
import org.opensaml.core.config.InitializationService; | ||
import org.opensaml.core.xml.XMLObjectBuilderFactory; | ||
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; | ||
import org.opensaml.core.xml.io.Marshaller; | ||
import org.opensaml.saml.common.xml.SAMLConstants; | ||
import org.opensaml.saml.saml2.metadata.AssertionConsumerService; | ||
import org.opensaml.saml.saml2.metadata.EntityDescriptor; | ||
import org.opensaml.saml.saml2.metadata.KeyDescriptor; | ||
import org.opensaml.saml.saml2.metadata.NameIDFormat; | ||
import org.opensaml.saml.saml2.metadata.SPSSODescriptor; | ||
import org.opensaml.saml.saml2.metadata.SingleLogoutService; | ||
import org.opensaml.security.credential.Credential; | ||
import org.opensaml.security.credential.UsageType; | ||
import org.opensaml.security.x509.BasicX509Credential; | ||
import org.opensaml.xmlsec.keyinfo.KeyInfoGenerator; | ||
import org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.w3c.dom.Document; | ||
|
||
|
||
public class MetadataUtils { | ||
|
||
private static final Logger logger = LoggerFactory.getLogger(SamlClient.class); | ||
|
||
public static String generateSpMetadata(String entityId, String assertionConsumerServiceURL, String logoutServiceURL) { | ||
return generateSpMetadata(entityId, assertionConsumerServiceURL, logoutServiceURL, null); | ||
} | ||
|
||
public static String generateSpMetadata(String entityId, String assertionConsumerServiceURL, String singleLogoutServiceURL, X509Certificate certificate) { | ||
try { | ||
InitializationService.initialize(); | ||
|
||
EntityDescriptor spEntityDescriptor = createSAMLObject(EntityDescriptor.class); | ||
if (spEntityDescriptor == null) { | ||
return null; | ||
} | ||
spEntityDescriptor.setEntityID(entityId); | ||
SPSSODescriptor spSSODescriptor = createSAMLObject(SPSSODescriptor.class); | ||
if (spSSODescriptor == null) { | ||
return null; | ||
} | ||
|
||
spSSODescriptor.setWantAssertionsSigned(false); | ||
spSSODescriptor.setAuthnRequestsSigned(false); | ||
|
||
if (certificate != null) { | ||
|
||
spSSODescriptor.setWantAssertionsSigned(true); | ||
spSSODescriptor.setAuthnRequestsSigned(true); | ||
|
||
X509KeyInfoGeneratorFactory keyInfoGeneratorFactory = new X509KeyInfoGeneratorFactory(); | ||
keyInfoGeneratorFactory.setEmitEntityCertificate(true); | ||
KeyInfoGenerator keyInfoGenerator = keyInfoGeneratorFactory.newInstance(); | ||
|
||
KeyDescriptor encKeyDescriptor = createSAMLObject(KeyDescriptor.class); | ||
if (encKeyDescriptor == null) { | ||
return null; | ||
} | ||
|
||
encKeyDescriptor.setUse(UsageType.ENCRYPTION); | ||
|
||
Credential credential = new BasicX509Credential(certificate); | ||
|
||
try { | ||
encKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(credential)); | ||
} | ||
catch (Exception e) { | ||
logger.error("Error while creating credentials", e); | ||
} | ||
spSSODescriptor.getKeyDescriptors().add(encKeyDescriptor); | ||
|
||
KeyDescriptor signKeyDescriptor = createSAMLObject(KeyDescriptor.class); | ||
if (signKeyDescriptor == null) { | ||
return null; | ||
} | ||
|
||
signKeyDescriptor.setUse(UsageType.SIGNING); // Set usage | ||
|
||
try { | ||
signKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(credential)); | ||
} | ||
catch (SecurityException e) { | ||
logger.error("Error while creating credentials", e); | ||
} | ||
spSSODescriptor.getKeyDescriptors().add(signKeyDescriptor); | ||
} | ||
|
||
SingleLogoutService singleLogoutService = createSAMLObject(SingleLogoutService.class); | ||
if (singleLogoutService == null) { | ||
return null; | ||
} | ||
singleLogoutService.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI); | ||
singleLogoutService.setLocation(singleLogoutServiceURL); | ||
spSSODescriptor.getSingleLogoutServices().add(singleLogoutService); | ||
|
||
NameIDFormat nameIDFormat = createSAMLObject(NameIDFormat.class); | ||
if (nameIDFormat == null) { | ||
return null; | ||
} | ||
|
||
nameIDFormat.setFormat("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); | ||
spSSODescriptor.getNameIDFormats().add(nameIDFormat); | ||
|
||
AssertionConsumerService assertionConsumerService = createSAMLObject(AssertionConsumerService.class); | ||
if (assertionConsumerService == null) { | ||
return null; | ||
} | ||
assertionConsumerService.setIndex(1); | ||
assertionConsumerService.setBinding(SAMLConstants.SAML2_POST_BINDING_URI); | ||
|
||
assertionConsumerService.setLocation(assertionConsumerServiceURL); | ||
spSSODescriptor.getAssertionConsumerServices().add(assertionConsumerService); | ||
|
||
spSSODescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS); | ||
|
||
spEntityDescriptor.getRoleDescriptors().add(spSSODescriptor); | ||
|
||
DocumentBuilder builder; | ||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); | ||
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); | ||
|
||
builder = factory.newDocumentBuilder(); | ||
Document document = builder.newDocument(); | ||
Marshaller out = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(spEntityDescriptor); | ||
out.marshall(spEntityDescriptor, document); | ||
|
||
TransformerFactory transformerfactory = TransformerFactory.newInstance(); | ||
transformerfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); | ||
Transformer transformer = transformerfactory.newTransformer(); | ||
StringWriter stringWriter = new StringWriter(); | ||
StreamResult streamResult = new StreamResult(stringWriter); | ||
DOMSource source = new DOMSource(document); | ||
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); | ||
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); | ||
transformer.transform(source, streamResult); | ||
stringWriter.close(); | ||
|
||
return stringWriter.toString(); | ||
} | ||
catch (Exception e) { | ||
logger.error("Error while generation SP metadata", e); | ||
return null; | ||
} | ||
|
||
} | ||
|
||
public static <T> T createSAMLObject(final Class<T> clazz) { | ||
XMLObjectBuilderFactory builderFactory = XMLObjectProviderRegistrySupport.getBuilderFactory(); | ||
|
||
QName defaultElementName = null; | ||
try { | ||
defaultElementName = (QName) clazz.getDeclaredField("DEFAULT_ELEMENT_NAME").get(null); | ||
} | ||
catch (Exception e) { | ||
logger.error("Error while creating SAML object", e); | ||
return null; | ||
} | ||
T object = (T) builderFactory.getBuilder(defaultElementName).buildObject(defaultElementName); | ||
|
||
return object; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package com.coveo.saml; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.fail; | ||
|
||
import java.io.InputStream; | ||
import java.security.KeyStore; | ||
import java.security.cert.Certificate; | ||
import java.security.cert.X509Certificate; | ||
|
||
import org.junit.Test; | ||
|
||
|
||
public class MetadataUtilsTest { | ||
|
||
@Test | ||
public void generateSpMetadata_AllNull() { | ||
String metadata = MetadataUtils.generateSpMetadata(null, null, null); | ||
assertEquals( | ||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><md:EntityDescriptor xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\"> <md:SPSSODescriptor AuthnRequestsSigned=\"false\" WantAssertionsSigned=\"false\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\"> <md:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"/> <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat> <md:AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" index=\"1\"/> </md:SPSSODescriptor></md:EntityDescriptor>", | ||
metadata.replace("\r\n", "").replace("\n", "")); | ||
} | ||
|
||
@Test | ||
public void generateSpMetadata_AllFields() { | ||
String metadata = MetadataUtils.generateSpMetadata("testSp", "http://localhost:8080/consume", "http://localhost:8080/logout"); | ||
assertEquals( | ||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><md:EntityDescriptor xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"testSp\"> <md:SPSSODescriptor AuthnRequestsSigned=\"false\" WantAssertionsSigned=\"false\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\"> <md:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"http://localhost:8080/logout\"/> <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat> <md:AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"http://localhost:8080/consume\" index=\"1\"/> </md:SPSSODescriptor></md:EntityDescriptor>", | ||
metadata.replace("\r\n", "").replace("\n", "")); | ||
} | ||
|
||
@Test | ||
public void generateSpMetadata_AllFieldsAndCertificat() { | ||
Certificate cert = null; | ||
try { | ||
InputStream keyStoreInputStream = this.getClass().getResourceAsStream("/com/coveo/saml/test.p12"); | ||
|
||
KeyStore keystore = KeyStore.getInstance("PKCS12"); | ||
keystore.load(keyStoreInputStream, "test".toCharArray()); | ||
cert = keystore.getCertificate("tester"); | ||
} | ||
catch (Exception e) { | ||
fail(); | ||
} | ||
|
||
String metadata = MetadataUtils.generateSpMetadata("testSp", "http://localhost:8080/consume", "http://localhost:8080/logout", (X509Certificate) cert); | ||
assertEquals( | ||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><md:EntityDescriptor xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"testSp\"> <md:SPSSODescriptor AuthnRequestsSigned=\"true\" WantAssertionsSigned=\"true\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\"> <md:KeyDescriptor use=\"encryption\"> <ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"> <ds:X509Data> <ds:X509Certificate>MIIDCTCCAfGgAwIBAgIBATANBgkqhkiG9w0BAQsFADBIMQswCQYDVQQGEwJERTEMMAoGA1UECBMDTlJXMQ0wCwYDVQQKEwR0ZXN0MQ0wCwYDVQQLEwR0ZXN0MQ0wCwYDVQQDEwR0ZXN0MB4XDTIwMDQyNzEzNDcwMFoXDTIxMDQyNzEzNDcwMFowSDELMAkGA1UEBhMCREUxDDAKBgNVBAgTA05SVzENMAsGA1UEChMEdGVzdDENMAsGA1UECxMEdGVzdDENMAsGA1UEAxMEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALhZvjhIbJYV+ufRs/wF2W3710XzK8Cdg19pXZw9rOP5OE54ixAh6Hbbe0AHLTAt+T1Ljepqshwyo3an85q1JyuSLkPJBGks7WQKT6x0389V8c2nwbxOU2FkuIrOAG1y2rCmh+zjbndSBRLVMLPRwm7He+zeLH2yDl8tlPT3rVOzPX6/SEvhnG2yz2qsz1tNeski+9gK8+Anzzu+Ze2uf/q2y7tEFgrNOdkxEHtta4kqjglbacWougyNKFbRzILsDLJP7S0csssunXdIuYNmdsQ857Emjh1Yth4ZHaks8Np4TBRfjX+91PSQ5CTlw4zDijk/vNPgQ39cnY6SiucMEnkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAIGnBuviT6kDVK/b2mhCKKROp3bEqIaO3Ccl55H1ZKQNaY/xw4FUxaMGTdUuVo3Kbh5AT5iyEd+U+hd0skG4HbQ0nPkeEg15w07fh04mgTccC/IPAyrT++w9yiHOrXB0R6sXlwLOebXK6/6GQdt6pNDPc1GJaDhYhmI0IoXGO2iVFRlefqCSmGSRRbW4hU5SIdPrmCX/oOfnGBVN3Vo3wQtq9MAUTYnzpdVKBWaAbwzJdWXkF5GbHue5lxOnKmZB7ctd7VZk+L+dtmCozABk+NjdF0nGnjc3zIHD3EE+NCIas9jYPr0Ib8SReNsVL46zF3w1BvxQfkpMLIQThXyoZ/w==</ds:X509Certificate> </ds:X509Data> </ds:KeyInfo> </md:KeyDescriptor> <md:KeyDescriptor use=\"signing\"> <ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"> <ds:X509Data> <ds:X509Certificate>MIIDCTCCAfGgAwIBAgIBATANBgkqhkiG9w0BAQsFADBIMQswCQYDVQQGEwJERTEMMAoGA1UECBMDTlJXMQ0wCwYDVQQKEwR0ZXN0MQ0wCwYDVQQLEwR0ZXN0MQ0wCwYDVQQDEwR0ZXN0MB4XDTIwMDQyNzEzNDcwMFoXDTIxMDQyNzEzNDcwMFowSDELMAkGA1UEBhMCREUxDDAKBgNVBAgTA05SVzENMAsGA1UEChMEdGVzdDENMAsGA1UECxMEdGVzdDENMAsGA1UEAxMEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALhZvjhIbJYV+ufRs/wF2W3710XzK8Cdg19pXZw9rOP5OE54ixAh6Hbbe0AHLTAt+T1Ljepqshwyo3an85q1JyuSLkPJBGks7WQKT6x0389V8c2nwbxOU2FkuIrOAG1y2rCmh+zjbndSBRLVMLPRwm7He+zeLH2yDl8tlPT3rVOzPX6/SEvhnG2yz2qsz1tNeski+9gK8+Anzzu+Ze2uf/q2y7tEFgrNOdkxEHtta4kqjglbacWougyNKFbRzILsDLJP7S0csssunXdIuYNmdsQ857Emjh1Yth4ZHaks8Np4TBRfjX+91PSQ5CTlw4zDijk/vNPgQ39cnY6SiucMEnkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAIGnBuviT6kDVK/b2mhCKKROp3bEqIaO3Ccl55H1ZKQNaY/xw4FUxaMGTdUuVo3Kbh5AT5iyEd+U+hd0skG4HbQ0nPkeEg15w07fh04mgTccC/IPAyrT++w9yiHOrXB0R6sXlwLOebXK6/6GQdt6pNDPc1GJaDhYhmI0IoXGO2iVFRlefqCSmGSRRbW4hU5SIdPrmCX/oOfnGBVN3Vo3wQtq9MAUTYnzpdVKBWaAbwzJdWXkF5GbHue5lxOnKmZB7ctd7VZk+L+dtmCozABk+NjdF0nGnjc3zIHD3EE+NCIas9jYPr0Ib8SReNsVL46zF3w1BvxQfkpMLIQThXyoZ/w==</ds:X509Certificate> </ds:X509Data> </ds:KeyInfo> </md:KeyDescriptor> <md:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"http://localhost:8080/logout\"/> <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat> <md:AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"http://localhost:8080/consume\" index=\"1\"/> </md:SPSSODescriptor></md:EntityDescriptor>", | ||
metadata.replace("\r\n", "").replace("\n", "")); | ||
} | ||
|
||
} |
Binary file not shown.