Correct signature handling for SAML2 AuthNRequest
Implements the following bindings for AuthNRequest - REDIRECT - POST (future PR) Has been tested with - Keycloak - SSOCircle - Okta - SimpleSAMLPhp Fixes gh-7711
This commit is contained in:
+6
-52
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,12 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.samples;
|
||||
|
||||
import org.springframework.security.saml2.Saml2Exception;
|
||||
package org.springframework.security.saml2.provider.service.authentication;
|
||||
|
||||
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.xml.security.algorithms.JCEMapper;
|
||||
import org.apache.xml.security.encryption.XMLCipherParameters;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -57,23 +54,16 @@ import org.opensaml.security.credential.CredentialSupport;
|
||||
import org.opensaml.xmlsec.encryption.support.DataEncryptionParameters;
|
||||
import org.opensaml.xmlsec.encryption.support.EncryptionException;
|
||||
import org.opensaml.xmlsec.encryption.support.KeyEncryptionParameters;
|
||||
import org.springframework.security.saml2.Saml2Exception;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.InflaterOutputStream;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.crypto.SecretKey;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.zip.Deflater.DEFLATED;
|
||||
import static org.opensaml.security.crypto.KeySupport.generateKey;
|
||||
|
||||
/**
|
||||
@@ -83,8 +73,6 @@ import static org.opensaml.security.crypto.KeySupport.generateKey;
|
||||
*/
|
||||
public class OpenSamlActionTestingSupport {
|
||||
|
||||
static Base64 UNCHUNKED_ENCODER = new Base64(0, new byte[] { '\n' });
|
||||
|
||||
/** ID used for all generated {@link Response} objects. */
|
||||
final static String REQUEST_ID = "request";
|
||||
|
||||
@@ -94,40 +82,6 @@ public class OpenSamlActionTestingSupport {
|
||||
/** ID used for all generated {@link Assertion} objects. */
|
||||
final static String ASSERTION_ID = "assertion";
|
||||
|
||||
static String encode(byte[] b) {
|
||||
return UNCHUNKED_ENCODER.encodeToString(b);
|
||||
}
|
||||
|
||||
static byte[] decode(String s) {
|
||||
return UNCHUNKED_ENCODER.decode(s);
|
||||
}
|
||||
|
||||
static byte[] deflate(String s) {
|
||||
try {
|
||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
DeflaterOutputStream deflater = new DeflaterOutputStream(b, new Deflater(DEFLATED, true));
|
||||
deflater.write(s.getBytes(UTF_8));
|
||||
deflater.finish();
|
||||
return b.toByteArray();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new Saml2Exception("Unable to deflate string", e);
|
||||
}
|
||||
}
|
||||
|
||||
static String inflate(byte[] b) {
|
||||
try {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
InflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true));
|
||||
iout.write(b);
|
||||
iout.finish();
|
||||
return new String(out.toByteArray(), UTF_8);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new Saml2Exception("Unable to inflate string", e);
|
||||
}
|
||||
}
|
||||
|
||||
static EncryptedAssertion encryptAssertion(Assertion assertion, X509Certificate certificate) {
|
||||
Encrypter encrypter = getEncrypter(certificate);
|
||||
try {
|
||||
+19
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,7 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.samples;
|
||||
|
||||
package org.springframework.security.saml2.provider.service.authentication;
|
||||
|
||||
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
|
||||
import net.shibboleth.utilities.java.support.xml.BasicParserPool;
|
||||
@@ -52,7 +53,6 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.util.AssertionErrors;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
@@ -63,6 +63,7 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -73,19 +74,18 @@ import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.UUID;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.matchesRegex;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.springframework.security.samples.OpenSamlActionTestingSupport.buildConditions;
|
||||
import static org.springframework.security.samples.OpenSamlActionTestingSupport.buildIssuer;
|
||||
import static org.springframework.security.samples.OpenSamlActionTestingSupport.buildSubject;
|
||||
import static org.springframework.security.samples.OpenSamlActionTestingSupport.buildSubjectConfirmation;
|
||||
import static org.springframework.security.samples.OpenSamlActionTestingSupport.buildSubjectConfirmationData;
|
||||
import static org.springframework.security.samples.OpenSamlActionTestingSupport.encryptNameId;
|
||||
import static org.springframework.security.samples.OpenSamlActionTestingSupport.inflate;
|
||||
import static org.springframework.security.saml2.provider.service.authentication.OpenSamlActionTestingSupport.buildConditions;
|
||||
import static org.springframework.security.saml2.provider.service.authentication.OpenSamlActionTestingSupport.buildIssuer;
|
||||
import static org.springframework.security.saml2.provider.service.authentication.OpenSamlActionTestingSupport.buildSubject;
|
||||
import static org.springframework.security.saml2.provider.service.authentication.OpenSamlActionTestingSupport.buildSubjectConfirmation;
|
||||
import static org.springframework.security.saml2.provider.service.authentication.OpenSamlActionTestingSupport.buildSubjectConfirmationData;
|
||||
import static org.springframework.security.saml2.provider.service.authentication.OpenSamlActionTestingSupport.encryptNameId;
|
||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
|
||||
import static org.springframework.security.web.WebAttributes.AUTHENTICATION_EXCEPTION;
|
||||
@@ -133,10 +133,15 @@ public class Saml2LoginIntegrationTests {
|
||||
mockMvc.perform(
|
||||
get("http://localhost:8080/saml2/authenticate/simplesamlphp")
|
||||
.param("RelayState", "relay state value with spaces")
|
||||
.param("OtherParam", "OtherParamValue")
|
||||
.param("OtherParam2", "OtherParamValue2")
|
||||
)
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(header().string("Location", startsWith("https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php?SAMLRequest=")))
|
||||
.andExpect(header().string("Location", containsString("RelayState=relay%20state%20value%20with%20spaces")));
|
||||
.andExpect(header().string("Location", containsString("RelayState=relay%20state%20value%20with%20spaces")))
|
||||
//check order of parameters
|
||||
.andExpect(header().string("Location", matchesRegex(".*\\?SAMLRequest\\=.*\\&RelayState\\=.*\\&SigAlg\\=.*\\&Signature\\=.*")));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -151,7 +156,7 @@ public class Saml2LoginIntegrationTests {
|
||||
String request = parameters.getFirst("SAMLRequest");
|
||||
AssertionErrors.assertNotNull("SAMLRequest parameter is missing", request);
|
||||
request = URLDecoder.decode(request);
|
||||
request = inflate(OpenSamlActionTestingSupport.decode(request));
|
||||
request = Saml2Utils.samlInflate(Saml2Utils.samlDecode(request));
|
||||
AuthnRequest authnRequest = (AuthnRequest) fromXml(request);
|
||||
String destination = authnRequest.getDestination();
|
||||
assertEquals(
|
||||
@@ -298,7 +303,7 @@ public class Saml2LoginIntegrationTests {
|
||||
String xml = toXml(response);
|
||||
return mockMvc.perform(post("http://localhost:8080/login/saml2/sso/simplesamlphp")
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
.param("SAMLResponse", OpenSamlActionTestingSupport.encode(xml.getBytes(UTF_8))))
|
||||
.param("SAMLResponse", Saml2Utils.samlEncode(xml.getBytes(UTF_8))))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl(redirectUrl));
|
||||
}
|
||||
Reference in New Issue
Block a user