[java-idp-testbed] 03/03: IDP-465 - Add ability to send SAML 1 attribute query requests
Tom Zeller
tzeller at dragonacea.biz
Thu Sep 27 10:45:48 EDT 2018
This is an automated email from the git hooks/post-receive script.
tzeller pushed a commit to branch master
in repository java-idp-testbed.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-testbed.git;a=commit;h=25b048a03fabcf3ac76aadb1efbfc4fac4f1da71
commit 25b048a03fabcf3ac76aadb1efbfc4fac4f1da71
Author: Tom Zeller <tzeller at dragonacea.biz>
AuthorDate: Thu Sep 27 09:45:17 2018 -0500
IDP-465 - Add ability to send SAML 1 attribute query requests
https://issues.shibboleth.net/jira/browse/IDP-465
---
src/main/java/sp/SAML1Controller.java | 231 ++++++++++++++++++++++++++++++++++
src/main/webapp/index.html | 43 +++++++
2 files changed, 274 insertions(+)
diff --git a/src/main/java/sp/SAML1Controller.java b/src/main/java/sp/SAML1Controller.java
index d7ea031..926af2b 100644
--- a/src/main/java/sp/SAML1Controller.java
+++ b/src/main/java/sp/SAML1Controller.java
@@ -17,22 +17,64 @@
package sp;
+import java.io.IOException;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.utilities.java.support.security.SecureRandomIdentifierGenerationStrategy;
import net.shibboleth.utilities.java.support.xml.SerializeSupport;
+import net.shibboleth.utilities.java.support.xml.XMLParserException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.conn.ssl.SSLContextBuilder;
+import org.apache.http.conn.ssl.SSLContexts;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.cryptacular.util.CertUtil;
+import org.cryptacular.util.KeyPairUtil;
+import org.joda.time.DateTime;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.io.MarshallingException;
+import org.opensaml.messaging.context.InOutOperationContext;
import org.opensaml.messaging.context.MessageContext;
import org.opensaml.saml.common.SAMLObject;
+import org.opensaml.saml.saml1.core.Request;
import org.opensaml.saml.saml1.core.Response;
+import org.opensaml.saml.saml1.core.Subject;
+import org.opensaml.saml.saml1.profile.SAML1ActionTestingSupport;
+import org.opensaml.security.SecurityException;
+import org.opensaml.security.x509.BasicX509Credential;
+import org.opensaml.soap.client.http.HttpSOAPClient;
+import org.opensaml.soap.messaging.context.SOAP11Context;
+import org.opensaml.soap.soap11.Body;
+import org.opensaml.soap.soap11.Envelope;
+import org.opensaml.xmlsec.SignatureSigningParameters;
+import org.opensaml.xmlsec.signature.SignableXMLObject;
+import org.opensaml.xmlsec.signature.support.SignatureConstants;
+import org.opensaml.xmlsec.signature.support.SignatureException;
+import org.opensaml.xmlsec.signature.support.SignatureSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.servlet.HandlerMapping;
import org.w3c.dom.Element;
@Controller
@@ -65,4 +107,193 @@ public class SAML1Controller extends BaseSAMLController {
return new ResponseEntity<>(formattedMessage, headers, HttpStatus.OK);
}
+ private String getSpEntityId(HttpServletRequest servletRequest) {
+ // TODO get from config somewhere
+ final String spId = getSpId(servletRequest);
+ return (spId == null) ? "https://sp.example.org" : "https://" + spId + ".example.org";
+ }
+
+ /**
+ * Get the SP id as a path variable, or <code>null</code> if not present.
+ *
+ * @param servletRequest the servlet request
+ * @return the SP id or <code>null</code>
+ */
+ @Nullable private String getSpId(HttpServletRequest servletRequest) {
+ final Object attr = servletRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
+ if (attr != null && attr instanceof Map) {
+ final Map pathVariables = (Map) attr;
+ final Object spId = pathVariables.get("spId");
+ log.trace("Found spID '{}'", spId);
+ if (spId != null) {
+ return spId.toString();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Send a SAML 1 attribute query.
+ *
+ * @param servletRequest the servlet request
+ * @param servletResponse the servlet response
+ * @param endpoint the endpoint to send the attribute query to
+ * @param principalName the name of the principal to query for
+ * @param trustedCertificate the trusted IdP public certificate
+ * @param trustedCertificatePassword the IdP certificate password
+ * @param clientCertificate the SP public certificate
+ * @param clientKey the SP private key
+ * @param clientPassword the SP password
+ * @return the SAML attribute query response is displayed
+ * @throws Exception if an error occurs
+ */
+ @RequestMapping(value = "/AttributeQuery", method = RequestMethod.POST) public ResponseEntity<String>
+ initSAML1AttributeQuery(HttpServletRequest servletRequest, HttpServletResponse servletResponse,
+ @RequestParam(value = "endpoint", required = true) String endpoint,
+ @RequestParam(value = "principalName", required = true) String principalName,
+ @RequestParam(value = "trustedTLSCertificate", required = true) String trustedTLSCertificate,
+ @RequestParam(value = "trustedTLSCertificatePassword", required = true) String trustedTLSCertificatePassword,
+ @RequestParam(value = "clientTLSCertificate", required = false) String clientTLSCertificate,
+ @RequestParam(value = "clientTLSPrivateKey", required = true) String clientTLSPrivateKey,
+ @RequestParam(value = "clientTLSPassword", required = true) String clientTLSPassword,
+ @RequestParam(value = "clientSigningCertificate", required = false) String clientSigningCertificate,
+ @RequestParam(value = "clientSigningPrivateKey", required = false) String clientSigningPrivateKey)
+ throws Exception {
+
+ final Resource trustedTLSCertificateResource = applicationContext.getResource(trustedTLSCertificate);
+ log.debug("Trusted TLS certificate resource '{}'", trustedTLSCertificateResource);
+
+ Resource clientTLSCertificateResource = null;
+ if (StringSupport.trimOrNull(clientTLSCertificate) != null) {
+ clientTLSCertificateResource = applicationContext.getResource(clientTLSCertificate);
+ }
+ log.debug("Client TLS certificate resource '{}'", clientTLSCertificateResource);
+
+ final Resource clientTLSPrivateKeyResource = applicationContext.getResource(clientTLSPrivateKey);
+ log.debug("Client TLS private key resource '{}'", clientTLSPrivateKeyResource);
+
+ final HttpClient httpClient = buildHttpClient(trustedTLSCertificateResource, trustedTLSCertificatePassword,
+ clientTLSCertificateResource, clientTLSPrivateKeyResource, clientTLSPassword);
+
+ final HttpSOAPClient httpSoapClient = new HttpSOAPClient();
+ httpSoapClient.setParserPool(parserPool);
+ httpSoapClient.setHttpClient(httpClient);
+
+ final Request attributeQuery = buildSAML1AttributeQueryRequest(servletRequest, principalName);
+
+ // Sign if client signing certificate is present
+ if (StringSupport.trimOrNull(clientSigningCertificate) != null) {
+ sign(attributeQuery, clientSigningCertificate, clientSigningPrivateKey);
+ }
+
+ final Envelope envelope = buildSOAP11Envelope(attributeQuery);
+
+ if (log.isDebugEnabled()) {
+ log.debug("Sending AttributeQuery to endpoint '{}':\n", endpoint, SerializeSupport.prettyPrintXML(
+ marshallerFactory.getMarshaller(envelope).marshall(envelope, parserPool.newDocument())));
+ }
+
+ final InOutOperationContext context = SAML2Controller.buildInOutOperationContext(envelope);
+
+ httpSoapClient.send(endpoint, context);
+
+ final Envelope soapResponse =
+ context.getInboundMessageContext().getSubcontext(SOAP11Context.class).getEnvelope();
+
+ final String formattedMessage = SerializeSupport.prettyPrintXML(soapResponse.getDOM());
+
+ final HttpHeaders headers = new HttpHeaders();
+ headers.add("Content-Type", "text/plain");
+
+ return new ResponseEntity<>(formattedMessage, headers, HttpStatus.OK);
+ }
+
+ /**
+ * Builds a basic SAML 1 attribute query.
+ *
+ * @param principalName the principal name
+ * @return the attribute query
+ */
+ @Nonnull public Request buildSAML1AttributeQueryRequest(@Nonnull final HttpServletRequest servletRequest,
+ @Nonnull final String principalName) {
+
+ final Subject subject = SAML1ActionTestingSupport.buildSubject(principalName);
+
+ final Request attributeQuery = SAML1ActionTestingSupport.buildAttributeQueryRequest(subject);
+ attributeQuery.setIssueInstant(new DateTime());
+ attributeQuery.setID(new SecureRandomIdentifierGenerationStrategy().generateIdentifier());
+ attributeQuery.getAttributeQuery().setResource(getSpEntityId(servletRequest));
+
+ // TODO AttributeDesignator
+
+ return attributeQuery;
+ }
+
+ /**
+ * Build a SOAP11 {@link Envelope} with the given payload.
+ *
+ * @param payload the payload
+ * @return the SOAP11 envelop
+ */
+ @Nonnull public Envelope buildSOAP11Envelope(@Nonnull final XMLObject payload) {
+ final Envelope envelope = XMLObjectProviderRegistrySupport.getBuilderFactory()
+ .<Envelope> getBuilderOrThrow(Envelope.DEFAULT_ELEMENT_NAME).buildObject(Envelope.DEFAULT_ELEMENT_NAME);
+ final Body body = XMLObjectProviderRegistrySupport.getBuilderFactory()
+ .<Body> getBuilderOrThrow(Body.DEFAULT_ELEMENT_NAME).buildObject(Body.DEFAULT_ELEMENT_NAME);
+ body.getUnknownXMLObjects().add(payload);
+ envelope.setBody(body);
+ return envelope;
+ }
+
+ @Nonnull public HttpClient buildHttpClient(@Nonnull final Resource trustedTLSCertificate,
+ @Nonnull final String trustedTLSCertificatePassword, @Nullable final Resource clientTLSCertificate,
+ @Nonnull final Resource clientTLSPrivateKey, @Nonnull final String clientTLSPassword) throws Exception {
+
+ final KeyStore trustStore = KeyStore.getInstance("PKCS12");
+ trustStore.load(trustedTLSCertificate.getInputStream(), trustedTLSCertificatePassword.toCharArray());
+
+ final PrivateKey clientPrivateKey = KeyPairUtil.readPrivateKey(clientTLSPrivateKey.getInputStream());
+
+ X509Certificate clientCert = null;
+ if (clientTLSCertificate != null) {
+ clientCert = CertUtil.readCertificate(clientTLSCertificate.getInputStream());
+ }
+
+ final KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null, null);
+ keyStore.setKeyEntry("sp", clientPrivateKey, clientTLSPassword.toCharArray(), new Certificate[] {clientCert});
+
+ final SSLContextBuilder sslContextBuilder = SSLContexts.custom();
+ sslContextBuilder.loadTrustMaterial(trustStore);
+ sslContextBuilder.loadKeyMaterial(keyStore, clientTLSPassword.toCharArray());
+
+ final SSLContext sslcontext = sslContextBuilder.build();
+
+ final CloseableHttpClient httpClient = HttpClients.custom().setSslcontext(sslcontext).build();
+
+ return httpClient;
+ }
+
+ public void sign(@Nonnull final SignableXMLObject signable, @Nonnull final String certificate,
+ @Nonnull final String privateKey)
+ throws SecurityException, MarshallingException, SignatureException, XMLParserException, IOException {
+
+ final Resource certificateResource = applicationContext.getResource(certificate);
+ log.debug("Signing certificate resource '{}'", certificateResource);
+
+ final Resource privateKeyResource = applicationContext.getResource(privateKey);
+ log.debug("Signing private key resource '{}'", privateKeyResource);
+
+ final X509Certificate cert = CertUtil.readCertificate(certificateResource.getInputStream());
+ final PrivateKey key = KeyPairUtil.readPrivateKey(privateKeyResource.getInputStream());
+ final BasicX509Credential cred = new BasicX509Credential(cert, key);
+
+ final SignatureSigningParameters signingParameters = new SignatureSigningParameters();
+ signingParameters.setSigningCredential(cred);
+ signingParameters.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
+ signingParameters.setSignatureCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
+
+ SignatureSupport.signObject(signable, signingParameters);
+ }
+
}
diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html
index 202aaed..73c6118 100644
--- a/src/main/webapp/index.html
+++ b/src/main/webapp/index.html
@@ -235,6 +235,49 @@
<input type="submit" value="SAML2AttributeQuery">
</form>
</li>
+<li>SAML 1 AttributeQuery
+<form id="saml1-attribute-query" action="sp/SAML1/AttributeQuery" method="POST">
+ <table>
+ <tr>
+ <td>endpoint</td>
+ <td><input id="saml1-attribute-query-endpoint" name="endpoint" value="https://localhost:9443/idp/profile/SAML1/SOAP/AttributeQuery" size="60" /></td>
+ </tr>
+ <tr>
+ <td>principalName</td>
+ <td><input id="saml1-attribute-query-principalName" name="principalName" value="jdoe" /></td>
+ </tr>
+ <tr>
+ <td>trustedTLSCertificate</td>
+ <td><input id="saml1-attribute-query-trustedTLSCertificate" name="trustedTLSCertificate" value="classpath:/credentials/idp-backchannel.p12" size="90" /></td>
+ </tr>
+ <tr>
+ <td>trustedTLSCertificatePassword</td>
+ <td><input id="saml1-attribute-query-trustedTLSCertificatePassword" name="trustedTLSCertificatePassword" value="changeit" size="20" /></td>
+ </tr>
+ <tr>
+ <td>clientTLSCertificate</td>
+ <td><input id="saml1-attribute-query-clientTLSCertificate" name="clientTLSCertificate" value="classpath:/credentials/sp.crt" size="90" /></td>
+ </tr>
+ <tr>
+ <td>clientTLSPrivateKey</td>
+ <td><input id="saml1-attribute-query-clientTLSPrivateKey" name="clientTLSPrivateKey" value="classpath:/credentials/sp.key" size="90" /></td>
+ </tr>
+ <tr>
+ <td>clientTLSPassword</td>
+ <td><input id="saml1-attribute-query-clientTLSPassword" name="clientTLSPassword" value="secret" size="20" /></td>
+ </tr>
+ <tr>
+ <td>clientSigningCertificate</td>
+ <td><input id="saml1-attribute-query-clientSigningCertificate" name="clientSigningCertificate" value="classpath:/credentials/sp.crt" size="90" /></td>
+ </tr>
+ <tr>
+ <td>clientSigningPrivateKey</td>
+ <td><input id="saml1-attribute-query-clientSigningPrivateKey" name="clientSigningPrivateKey" value="classpath:/credentials/sp.key" size="90" /></td>
+ </tr>
+ </table>
+ <input type="submit" value="SAML1AttributeQuery">
+</form>
+</li>
</ul>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list