Polish Saml Tests

Fixes gh-8403
Fixes gh-8404
This commit is contained in:
Josh Cummings 2020-03-31 17:17:40 -06:00
parent 7056c2d9de
commit 8904361a37
No known key found for this signature in database
GPG Key ID: 49EF60DD7FF83443
9 changed files with 384 additions and 541 deletions

View File

@ -117,5 +117,4 @@ public class Saml2WebSsoAuthenticationFilter extends AbstractAuthenticationProce
return new String(b, UTF_8);
}
}
}

View File

@ -19,17 +19,16 @@ package org.springframework.security.saml2.provider.service.authentication;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.EncryptedAssertion;
import org.opensaml.saml.saml2.core.EncryptedID;
@ -37,15 +36,17 @@ import org.opensaml.saml.saml2.core.NameID;
import org.opensaml.saml.saml2.core.Response;
import org.springframework.security.core.Authentication;
import org.springframework.security.saml2.credentials.Saml2X509Credential;
import static java.util.Collections.emptyList;
import static org.springframework.security.saml2.provider.service.authentication.Saml2CryptoTestSupport.encryptAssertion;
import static org.springframework.security.saml2.provider.service.authentication.Saml2CryptoTestSupport.encryptNameId;
import static org.springframework.security.saml2.provider.service.authentication.Saml2CryptoTestSupport.signXmlObject;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2AuthenticationObjects.assertion;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2AuthenticationObjects.response;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2X509Credentials.assertingPartyCredentials;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2X509Credentials.relyingPartyCredentials;
import static org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects.assertion;
import static org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects.encrypted;
import static org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects.response;
import static org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects.signed;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2X509Credentials.assertingPartyEncryptingCredential;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2X509Credentials.assertingPartyPrivateCredential;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2X509Credentials.assertingPartySigningCredential;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2X509Credentials.relyingPartyDecryptingCredential;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2X509Credentials.relyingPartyVerifyingCredential;
import static org.springframework.test.util.AssertionErrors.assertTrue;
import static org.springframework.util.StringUtils.hasText;
@ -57,37 +58,23 @@ import static org.springframework.util.StringUtils.hasText;
*/
public class OpenSamlAuthenticationProviderTests {
private static String username = "test@saml.user";
private static String recipientUri = "https://localhost/login/saml2/sso/idp-alias";
private static String recipientEntityId = "https://localhost/saml2/service-provider-metadata/idp-alias";
private static String idpEntityId = "https://some.idp.test/saml2/idp";
private static String DESTINATION = "https://localhost/login/saml2/sso/idp-alias";
private static String RELYING_PARTY_ENTITY_ID = "https://localhost/saml2/service-provider-metadata/idp-alias";
private static String ASSERTING_PARTY_ENTITY_ID = "https://some.idp.test/saml2/idp";
private OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();
private OpenSamlImplementation saml = OpenSamlImplementation.getInstance();
private OpenSamlAuthenticationProvider provider;
private OpenSamlImplementation saml;
private Saml2AuthenticationToken token;
@Rule
public ExpectedException exception = ExpectedException.none();
@Before
public void setup() {
this.saml = OpenSamlImplementation.getInstance();
this.provider = new OpenSamlAuthenticationProvider();
this.token = new Saml2AuthenticationToken(
"responseXml",
recipientUri,
idpEntityId,
recipientEntityId,
relyingPartyCredentials()
);
}
@Test
public void supportsWhenSaml2AuthenticationTokenThenReturnTrue() {
assertTrue(
OpenSamlAuthenticationProvider.class + "should support " + this.token.getClass(),
this.provider.supports(this.token.getClass())
OpenSamlAuthenticationProvider.class + "should support " + Saml2AuthenticationToken.class,
this.provider.supports(Saml2AuthenticationToken.class)
);
}
@ -101,247 +88,191 @@ public class OpenSamlAuthenticationProviderTests {
@Test
public void authenticateWhenUnknownDataClassThenThrowAuthenticationException() {
Assertion assertion = defaultAssertion();
this.token = responseXml(assertion);
this.exception.expect(authenticationMatcher(Saml2ErrorCodes.UNKNOWN_RESPONSE_CLASS));
this.provider.authenticate(this.token);
Assertion assertion = this.saml.buildSamlObject(Assertion.DEFAULT_ELEMENT_NAME);
this.provider.authenticate(token(this.saml.serialize(assertion)));
}
@Test
public void authenticateWhenXmlErrorThenThrowAuthenticationException() {
this.token = new Saml2AuthenticationToken(
"invalid xml string",
recipientUri,
idpEntityId,
recipientEntityId,
relyingPartyCredentials()
);
this.exception.expect(authenticationMatcher(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA));
this.provider.authenticate(this.token);
Saml2AuthenticationToken token = token("invalid xml");
this.provider.authenticate(token);
}
@Test
public void authenticateWhenInvalidDestinationThenThrowAuthenticationException() {
Response response = response(recipientUri + "invalid", idpEntityId);
this.token = responseXml(response);
this.exception.expect(authenticationMatcher(Saml2ErrorCodes.INVALID_DESTINATION));
this.provider.authenticate(this.token);
Response response = response(DESTINATION + "invalid", ASSERTING_PARTY_ENTITY_ID);
response.getAssertions().add(assertion());
signed(response, assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential());
this.provider.authenticate(token);
}
@Test
public void authenticateWhenNoAssertionsPresentThenThrowAuthenticationException() {
Response response = response(recipientUri, idpEntityId);
this.token = responseXml(response);
this.exception.expect(
authenticationMatcher(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA, "No assertions found in response.")
);
this.provider.authenticate(this.token);
Saml2AuthenticationToken token = token(response(), assertingPartySigningCredential());
this.provider.authenticate(token);
}
@Test
public void authenticateWhenInvalidSignatureOnAssertionThenThrowAuthenticationException() {
Response response = response(recipientUri, idpEntityId);
Assertion assertion = defaultAssertion();
response.getAssertions().add(assertion);
this.token = responseXml(response);
this.exception.expect(authenticationMatcher(Saml2ErrorCodes.INVALID_SIGNATURE));
this.provider.authenticate(this.token);
Response response = response();
response.getAssertions().add(assertion());
Saml2AuthenticationToken token = token(response);
this.provider.authenticate(token);
}
@Test
public void authenticateWhenOpenSAMLValidationErrorThenThrowAuthenticationException() throws Exception {
Response response = response(recipientUri, idpEntityId);
Assertion assertion = defaultAssertion();
this.exception.expect(authenticationMatcher(Saml2ErrorCodes.INVALID_ASSERTION));
Response response = response();
Assertion assertion = assertion();
assertion
.getSubject()
.getSubjectConfirmations()
.get(0)
.getSubjectConfirmationData()
.setNotOnOrAfter(DateTime.now().minus(Duration.standardDays(3)));
signXmlObject(
assertion,
assertingPartyCredentials(),
recipientEntityId
);
signed(assertion, assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
response.getAssertions().add(assertion);
this.token = responseXml(response);
this.exception.expect(authenticationMatcher(Saml2ErrorCodes.INVALID_ASSERTION));
this.provider.authenticate(this.token);
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential());
this.provider.authenticate(token);
}
@Test
public void authenticateWhenMissingSubjectThenThrowAuthenticationException() {
Response response = response(recipientUri, idpEntityId);
Assertion assertion = defaultAssertion();
assertion.setSubject(null);
signXmlObject(
assertion,
assertingPartyCredentials(),
recipientEntityId
);
response.getAssertions().add(assertion);
this.token = responseXml(response);
this.exception.expect(authenticationMatcher(Saml2ErrorCodes.SUBJECT_NOT_FOUND));
Response response = response();
Assertion assertion = assertion();
assertion.setSubject(null);
signed(assertion, assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
response.getAssertions().add(assertion);
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential());
this.provider.authenticate(token);
}
@Test
public void authenticateWhenUsernameMissingThenThrowAuthenticationException() throws Exception {
Response response = response(recipientUri, idpEntityId);
Assertion assertion = defaultAssertion();
this.exception.expect(authenticationMatcher(Saml2ErrorCodes.USERNAME_NOT_FOUND));
Response response = response();
Assertion assertion = assertion();
assertion
.getSubject()
.getNameID()
.setValue(null);
signXmlObject(
assertion,
assertingPartyCredentials(),
recipientEntityId
);
signed(assertion, assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
response.getAssertions().add(assertion);
this.token = responseXml(response);
this.exception.expect(authenticationMatcher(Saml2ErrorCodes.USERNAME_NOT_FOUND));
this.provider.authenticate(this.token);
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential());
this.provider.authenticate(token);
}
@Test
public void authenticateWhenAssertionContainsValidationAddressThenItSucceeds() throws Exception {
Response response = response(recipientUri, idpEntityId);
Assertion assertion = defaultAssertion();
Response response = response();
Assertion assertion = assertion();
assertion.getSubject().getSubjectConfirmations().forEach(
sc -> sc.getSubjectConfirmationData().setAddress("10.10.10.10")
);
signXmlObject(
assertion,
assertingPartyCredentials(),
recipientEntityId
);
signed(assertion, assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
response.getAssertions().add(assertion);
this.token = responseXml(response);
this.provider.authenticate(this.token);
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential());
this.provider.authenticate(token);
}
@Test
public void authenticateWhenEncryptedAssertionWithoutSignatureThenItFails() throws Exception {
Response response = response(recipientUri, idpEntityId);
Assertion assertion = defaultAssertion();
EncryptedAssertion encryptedAssertion = encryptAssertion(assertion, assertingPartyCredentials());
response.getEncryptedAssertions().add(encryptedAssertion);
this.token = responseXml(response);
this.exception.expect(authenticationMatcher(Saml2ErrorCodes.INVALID_SIGNATURE));
this.provider.authenticate(this.token);
Response response = response();
EncryptedAssertion encryptedAssertion = encrypted(assertion(), assertingPartyEncryptingCredential());
response.getEncryptedAssertions().add(encryptedAssertion);
Saml2AuthenticationToken token = token(response, relyingPartyDecryptingCredential());
this.provider.authenticate(token);
}
@Test
public void authenticateWhenEncryptedAssertionWithSignatureThenItSucceeds() throws Exception {
Response response = response(recipientUri, idpEntityId);
Assertion assertion = defaultAssertion();
signXmlObject(
assertion,
assertingPartyCredentials(),
recipientEntityId
);
EncryptedAssertion encryptedAssertion = encryptAssertion(assertion, assertingPartyCredentials());
Response response = response();
Assertion assertion = signed(assertion(), assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
EncryptedAssertion encryptedAssertion = encrypted(assertion, assertingPartyEncryptingCredential());
response.getEncryptedAssertions().add(encryptedAssertion);
this.token = responseXml(response);
this.provider.authenticate(this.token);
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential(), relyingPartyDecryptingCredential());
this.provider.authenticate(token);
}
@Test
public void authenticateWhenEncryptedAssertionWithResponseSignatureThenItSucceeds() throws Exception {
Response response = response(recipientUri, idpEntityId);
Assertion assertion = defaultAssertion();
EncryptedAssertion encryptedAssertion = encryptAssertion(assertion, assertingPartyCredentials());
Response response = response();
EncryptedAssertion encryptedAssertion = encrypted(assertion(), assertingPartyEncryptingCredential());
response.getEncryptedAssertions().add(encryptedAssertion);
signXmlObject(
response,
assertingPartyCredentials(),
recipientEntityId
);
this.token = responseXml(response);
provider.authenticate(this.token);
signed(response, assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential(), relyingPartyDecryptingCredential());
this.provider.authenticate(token);
}
@Test
public void authenticateWhenEncryptedNameIdWithSignatureThenItSucceeds() throws Exception {
Response response = response(recipientUri, idpEntityId);
Assertion assertion = defaultAssertion();
Response response = response();
Assertion assertion = assertion();
NameID nameId = assertion.getSubject().getNameID();
EncryptedID encryptedID = encryptNameId(nameId, assertingPartyCredentials());
EncryptedID encryptedID = encrypted(nameId, assertingPartyEncryptingCredential());
assertion.getSubject().setNameID(null);
assertion.getSubject().setEncryptedID(encryptedID);
signXmlObject(
assertion,
assertingPartyCredentials(),
recipientEntityId
);
response.getAssertions().add(assertion);
this.token = responseXml(response);
this.provider.authenticate(this.token);
signed(assertion, assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential(), relyingPartyDecryptingCredential());
this.provider.authenticate(token);
}
@Test
public void authenticateWhenDecryptionKeysAreMissingThenThrowAuthenticationException() throws Exception {
Response response = response(recipientUri, idpEntityId);
Assertion assertion = defaultAssertion();
EncryptedAssertion encryptedAssertion = encryptAssertion(assertion, assertingPartyCredentials());
response.getEncryptedAssertions().add(encryptedAssertion);
this.token = responseXml(response);
this.token = new Saml2AuthenticationToken(
this.token.getSaml2Response(),
recipientUri,
idpEntityId,
recipientEntityId,
emptyList()
);
this.exception.expect(
authenticationMatcher(Saml2ErrorCodes.DECRYPTION_ERROR, "No valid decryption credentials found.")
);
this.provider.authenticate(this.token);
Response response = response();
EncryptedAssertion encryptedAssertion = encrypted(assertion(), assertingPartyEncryptingCredential());
response.getEncryptedAssertions().add(encryptedAssertion);
Saml2AuthenticationToken token = token(this.saml.serialize(response));
this.provider.authenticate(token);
}
@Test
public void authenticateWhenDecryptionKeysAreWrongThenThrowAuthenticationException() throws Exception {
Response response = response(recipientUri, idpEntityId);
Assertion assertion = defaultAssertion();
EncryptedAssertion encryptedAssertion = encryptAssertion(assertion, assertingPartyCredentials());
response.getEncryptedAssertions().add(encryptedAssertion);
this.token = responseXml(response);
this.token = new Saml2AuthenticationToken(
this.token.getSaml2Response(),
recipientUri,
idpEntityId,
recipientEntityId,
assertingPartyCredentials()
);
this.exception.expect(
authenticationMatcher(Saml2ErrorCodes.DECRYPTION_ERROR, "Failed to decrypt EncryptedData")
);
this.provider.authenticate(this.token);
Response response = response();
EncryptedAssertion encryptedAssertion = encrypted(assertion(), assertingPartyEncryptingCredential());
response.getEncryptedAssertions().add(encryptedAssertion);
Saml2AuthenticationToken token = token(this.saml.serialize(response), assertingPartyPrivateCredential());
this.provider.authenticate(token);
}
@Test
public void writeObjectWhenTypeIsSaml2AuthenticationThenNoException() throws IOException {
Response response = response(recipientUri, idpEntityId);
Assertion assertion = defaultAssertion();
signXmlObject(
assertion,
assertingPartyCredentials(),
recipientEntityId
);
EncryptedAssertion encryptedAssertion = encryptAssertion(assertion, assertingPartyCredentials());
Response response = response();
Assertion assertion = signed(assertion(), assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
EncryptedAssertion encryptedAssertion = encrypted(assertion, assertingPartyEncryptingCredential());
response.getEncryptedAssertions().add(encryptedAssertion);
this.token = responseXml(response);
Saml2Authentication authentication = (Saml2Authentication) this.provider.authenticate(this.token);
Saml2AuthenticationToken token = token(response, relyingPartyVerifyingCredential(), relyingPartyDecryptingCredential());
Saml2Authentication authentication = (Saml2Authentication) this.provider.authenticate(token);
// the following code will throw an exception if authentication isn't serializable
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(1024);
@ -350,27 +281,6 @@ public class OpenSamlAuthenticationProviderTests {
objectOutputStream.flush();
}
private Assertion defaultAssertion() {
return assertion(
username,
idpEntityId,
recipientEntityId,
recipientUri
);
}
private Saml2AuthenticationToken responseXml(XMLObject assertion) {
String xml = this.saml.serialize(assertion);
return new Saml2AuthenticationToken(
xml,
recipientUri,
idpEntityId,
recipientEntityId,
relyingPartyCredentials()
);
}
private Matcher<Saml2AuthenticationException> authenticationMatcher(String code) {
return authenticationMatcher(code, null);
}
@ -402,4 +312,14 @@ public class OpenSamlAuthenticationProviderTests {
}
};
}
private Saml2AuthenticationToken token(Response response, Saml2X509Credential... credentials) {
String payload = this.saml.serialize(response);
return token(payload, credentials);
}
private Saml2AuthenticationToken token(String payload, Saml2X509Credential... credentials) {
return new Saml2AuthenticationToken(payload,
DESTINATION, ASSERTING_PARTY_ENTITY_ID, RELYING_PARTY_ENTITY_ID, Arrays.asList(credentials));
}
}

View File

@ -23,6 +23,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.opensaml.saml.common.xml.SAMLConstants;
import org.opensaml.saml.saml2.core.AuthnRequest;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
@ -30,7 +31,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.containsString;
import static org.springframework.security.saml2.provider.service.authentication.Saml2Utils.samlDecode;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2X509Credentials.relyingPartyCredentials;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2X509Credentials.relyingPartySigningCredential;
import static org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.withRelyingPartyRegistration;
import static org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding.POST;
import static org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding.REDIRECT;
@ -55,7 +56,7 @@ public class OpenSamlAuthenticationRequestFactoryTests {
.providerDetails(c -> c.webSsoUrl("https://destination/sso"))
.providerDetails(c -> c.entityId("remote-entity-id"))
.localEntityIdTemplate("local-entity-id")
.credentials(c -> c.addAll(relyingPartyCredentials()))
.credentials(c -> c.add(relyingPartySigningCredential()))
.build();
contextBuilder = Saml2AuthenticationRequestContext.builder()
.issuer("https://issuer")

View File

@ -16,24 +16,22 @@
package org.springframework.security.saml2.provider.service.authentication;
import java.util.Arrays;
import java.util.Map;
import org.junit.Test;
import org.opensaml.security.credential.BasicCredential;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.credential.CredentialSupport;
import org.opensaml.security.credential.UsageType;
import org.opensaml.xmlsec.crypto.XMLSigningUtil;
import org.springframework.security.saml2.credentials.Saml2X509Credential;
import org.springframework.web.util.UriUtils;
import java.util.List;
import java.util.Map;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.opensaml.xmlsec.signature.support.SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2X509Credentials.assertingPartyCredentials;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2X509Credentials.relyingPartyCredentials;
import static org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects.getSigningCredential;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2X509Credentials.assertingPartySigningCredential;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2X509Credentials.relyingPartyVerifyingCredential;
public class OpenSamlImplementationTests {
@ -45,12 +43,12 @@ public class OpenSamlImplementationTests {
@Test
public void signQueryParametersWhenDataSuppliedReturnsValidSignature() throws Exception {
OpenSamlImplementation impl = OpenSamlImplementation.getInstance();
List<Saml2X509Credential> signCredentials = relyingPartyCredentials();
List<Saml2X509Credential> verifyCredentials = assertingPartyCredentials();
Saml2X509Credential signingCredential = assertingPartySigningCredential();
Saml2X509Credential verifyingCredential = relyingPartyVerifyingCredential();
String samlRequest = "saml-request-example";
String encoded = Saml2Utils.samlEncode(samlRequest.getBytes(UTF_8));
String relayState = "test relay state";
Map<String, String> parameters = impl.signQueryParameters(signCredentials, encoded, relayState);
Map<String, String> parameters = impl.signQueryParameters(Arrays.asList(signingCredential), encoded, relayState);
String queryString = "SAMLRequest=" +
UriUtils.encode(encoded, ISO_8859_1) +
@ -62,21 +60,11 @@ public class OpenSamlImplementationTests {
byte[] signature = Saml2Utils.samlDecode(parameters.get("Signature"));
boolean result = XMLSigningUtil.verifyWithURI(
getOpenSamlCredential(verifyCredentials.get(1), "local-sp-entity-id", UsageType.SIGNING),
getSigningCredential(verifyingCredential, "local-sp-entity-id"),
ALGO_ID_SIGNATURE_RSA_SHA256,
signature,
queryString.getBytes(UTF_8)
);
assertThat(result).isTrue();
}
private Credential getOpenSamlCredential(Saml2X509Credential credential, String localSpEntityId, UsageType usageType) {
BasicCredential cred = CredentialSupport.getSimpleCredential(
credential.getCertificate(),
credential.getPrivateKey()
);
cred.setEntityId(localSpEntityId);
cred.setUsageType(usageType);
return cred;
}
}

View File

@ -16,15 +16,16 @@
package org.springframework.security.saml2.provider.service.authentication;
import org.junit.Test;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import java.util.UUID;
import org.junit.Test;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.saml2.provider.service.authentication.Saml2Utils.samlDecode;
import static org.springframework.security.saml2.provider.service.authentication.Saml2Utils.samlInflate;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2X509Credentials.relyingPartyCredentials;
import static org.springframework.security.saml2.provider.service.authentication.TestSaml2X509Credentials.relyingPartySigningCredential;
/**
* Tests for {@link Saml2AuthenticationRequestFactory} default interface methods
@ -36,7 +37,7 @@ public class Saml2AuthenticationRequestFactoryTests {
.providerDetails(c -> c.webSsoUrl("https://example.com/destination"))
.providerDetails(c -> c.entityId("remote-entity-id"))
.localEntityIdTemplate("local-entity-id")
.credentials(c -> c.addAll(relyingPartyCredentials()))
.credentials(c -> c.add(relyingPartySigningCredential()))
.build();
@Test

View File

@ -1,169 +0,0 @@
/*
* Copyright 2002-2019 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.saml2.provider.service.authentication;
import org.springframework.security.saml2.Saml2Exception;
import org.springframework.security.saml2.credentials.Saml2X509Credential;
import org.apache.xml.security.algorithms.JCEMapper;
import org.apache.xml.security.encryption.XMLCipherParameters;
import org.opensaml.core.xml.io.MarshallingException;
import org.opensaml.saml.common.SignableSAMLObject;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.EncryptedAssertion;
import org.opensaml.saml.saml2.core.EncryptedID;
import org.opensaml.saml.saml2.core.NameID;
import org.opensaml.saml.saml2.encryption.Encrypter;
import org.opensaml.security.SecurityException;
import org.opensaml.security.credential.BasicCredential;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.credential.CredentialSupport;
import org.opensaml.security.credential.UsageType;
import org.opensaml.security.x509.BasicX509Credential;
import org.opensaml.xmlsec.SignatureSigningParameters;
import org.opensaml.xmlsec.encryption.support.DataEncryptionParameters;
import org.opensaml.xmlsec.encryption.support.EncryptionException;
import org.opensaml.xmlsec.encryption.support.KeyEncryptionParameters;
import org.opensaml.xmlsec.signature.support.SignatureConstants;
import org.opensaml.xmlsec.signature.support.SignatureException;
import org.opensaml.xmlsec.signature.support.SignatureSupport;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.X509Certificate;
import java.util.List;
import javax.crypto.SecretKey;
import static java.util.Arrays.asList;
import static org.opensaml.security.crypto.KeySupport.generateKey;
final class Saml2CryptoTestSupport {
static void signXmlObject(SignableSAMLObject object, List<Saml2X509Credential> signingCredentials, String entityId) {
SignatureSigningParameters parameters = new SignatureSigningParameters();
Credential credential = getSigningCredential(signingCredentials, entityId);
parameters.setSigningCredential(credential);
parameters.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
parameters.setSignatureReferenceDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA256);
parameters.setSignatureCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
try {
SignatureSupport.signObject(object, parameters);
} catch (MarshallingException | SignatureException | SecurityException e) {
throw new Saml2Exception(e);
}
}
static EncryptedAssertion encryptAssertion(Assertion assertion, List<Saml2X509Credential> encryptionCredentials) {
X509Certificate certificate = getEncryptionCertificate(encryptionCredentials);
Encrypter encrypter = getEncrypter(certificate);
try {
Encrypter.KeyPlacement keyPlacement = Encrypter.KeyPlacement.valueOf("PEER");
encrypter.setKeyPlacement(keyPlacement);
return encrypter.encrypt(assertion);
}
catch (EncryptionException e) {
throw new Saml2Exception("Unable to encrypt assertion.", e);
}
}
static EncryptedID encryptNameId(NameID nameID, List<Saml2X509Credential> encryptionCredentials) {
X509Certificate certificate = getEncryptionCertificate(encryptionCredentials);
Encrypter encrypter = getEncrypter(certificate);
try {
Encrypter.KeyPlacement keyPlacement = Encrypter.KeyPlacement.valueOf("PEER");
encrypter.setKeyPlacement(keyPlacement);
return encrypter.encrypt(nameID);
}
catch (EncryptionException e) {
throw new Saml2Exception("Unable to encrypt nameID.", e);
}
}
private static Encrypter getEncrypter(X509Certificate certificate) {
Credential credential = CredentialSupport.getSimpleCredential(certificate, null);
final String dataAlgorithm = XMLCipherParameters.AES_256;
final String keyAlgorithm = XMLCipherParameters.RSA_1_5;
SecretKey secretKey = generateKeyFromURI(dataAlgorithm);
BasicCredential dataCredential = new BasicCredential(secretKey);
DataEncryptionParameters dataEncryptionParameters = new DataEncryptionParameters();
dataEncryptionParameters.setEncryptionCredential(dataCredential);
dataEncryptionParameters.setAlgorithm(dataAlgorithm);
KeyEncryptionParameters keyEncryptionParameters = new KeyEncryptionParameters();
keyEncryptionParameters.setEncryptionCredential(credential);
keyEncryptionParameters.setAlgorithm(keyAlgorithm);
Encrypter encrypter = new Encrypter(dataEncryptionParameters, asList(keyEncryptionParameters));
return encrypter;
}
private static SecretKey generateKeyFromURI(String algoURI) {
try {
String jceAlgorithmName = JCEMapper.getJCEKeyAlgorithmFromURI(algoURI);
int keyLength = JCEMapper.getKeyLengthFromURI(algoURI);
return generateKey(jceAlgorithmName, keyLength, null);
}
catch (NoSuchAlgorithmException | NoSuchProviderException e) {
throw new Saml2Exception(e);
}
}
private static X509Certificate getEncryptionCertificate(List<Saml2X509Credential> encryptionCredentials) {
X509Certificate certificate = null;
for (Saml2X509Credential credential : encryptionCredentials) {
if (credential.isEncryptionCredential()) {
certificate = credential.getCertificate();
break;
}
}
if (certificate == null) {
throw new Saml2Exception("No valid encryption certificate found");
}
return certificate;
}
private static Saml2X509Credential hasSigningCredential(List<Saml2X509Credential> credentials) {
for (Saml2X509Credential c : credentials) {
if (c.isSigningCredential()) {
return c;
}
}
return null;
}
private static Credential getSigningCredential(List<Saml2X509Credential> signingCredential,
String localSpEntityId
) {
Saml2X509Credential credential = hasSigningCredential(signingCredential);
if (credential == null) {
throw new Saml2Exception("no signing credential configured");
}
BasicCredential cred = getBasicCredential(credential);
cred.setEntityId(localSpEntityId);
cred.setUsageType(UsageType.SIGNING);
return cred;
}
private static BasicX509Credential getBasicCredential(Saml2X509Credential credential) {
return CredentialSupport.getSimpleCredential(
credential.getCertificate(),
credential.getPrivateKey()
);
}
}

View File

@ -0,0 +1,225 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.saml2.provider.service.authentication;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.UUID;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.xml.security.encryption.XMLCipherParameters;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.opensaml.core.xml.io.MarshallingException;
import org.opensaml.saml.common.SAMLVersion;
import org.opensaml.saml.common.SignableSAMLObject;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.Conditions;
import org.opensaml.saml.saml2.core.EncryptedAssertion;
import org.opensaml.saml.saml2.core.EncryptedID;
import org.opensaml.saml.saml2.core.Issuer;
import org.opensaml.saml.saml2.core.NameID;
import org.opensaml.saml.saml2.core.Response;
import org.opensaml.saml.saml2.core.Subject;
import org.opensaml.saml.saml2.core.SubjectConfirmation;
import org.opensaml.saml.saml2.core.SubjectConfirmationData;
import org.opensaml.saml.saml2.encryption.Encrypter;
import org.opensaml.security.SecurityException;
import org.opensaml.security.credential.BasicCredential;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.credential.CredentialSupport;
import org.opensaml.security.credential.UsageType;
import org.opensaml.xmlsec.SignatureSigningParameters;
import org.opensaml.xmlsec.encryption.support.DataEncryptionParameters;
import org.opensaml.xmlsec.encryption.support.EncryptionException;
import org.opensaml.xmlsec.encryption.support.KeyEncryptionParameters;
import org.opensaml.xmlsec.signature.support.SignatureConstants;
import org.opensaml.xmlsec.signature.support.SignatureException;
import org.opensaml.xmlsec.signature.support.SignatureSupport;
import org.springframework.security.saml2.Saml2Exception;
import org.springframework.security.saml2.credentials.Saml2X509Credential;
final class TestOpenSamlObjects {
private static OpenSamlImplementation saml = OpenSamlImplementation.getInstance();
private static String USERNAME = "test@saml.user";
private static String DESTINATION = "https://localhost/login/saml2/sso/idp-alias";
private static String RELYING_PARTY_ENTITY_ID = "https://localhost/saml2/service-provider-metadata/idp-alias";
private static String ASSERTING_PARTY_ENTITY_ID = "https://some.idp.test/saml2/idp";
private static SecretKey SECRET_KEY =
new SecretKeySpec(Base64.getDecoder().decode("shOnwNMoCv88HKMEa91+FlYoD5RNvzMTAL5LGxZKIFk="), "AES");
static Response response() {
return response(DESTINATION, ASSERTING_PARTY_ENTITY_ID);
}
static Response response(String destination, String issuerEntityId) {
Response response = saml.buildSamlObject(Response.DEFAULT_ELEMENT_NAME);
response.setID("R"+UUID.randomUUID().toString());
response.setIssueInstant(DateTime.now());
response.setVersion(SAMLVersion.VERSION_20);
response.setID("_" + UUID.randomUUID().toString());
response.setDestination(destination);
response.setIssuer(issuer(issuerEntityId));
return response;
}
static Assertion assertion() {
return assertion(USERNAME, ASSERTING_PARTY_ENTITY_ID, RELYING_PARTY_ENTITY_ID, DESTINATION);
}
static Assertion assertion(
String username,
String issuerEntityId,
String recipientEntityId,
String recipientUri
) {
Assertion assertion = saml.buildSamlObject(Assertion.DEFAULT_ELEMENT_NAME);
assertion.setID("A"+ UUID.randomUUID().toString());
assertion.setIssueInstant(DateTime.now());
assertion.setVersion(SAMLVersion.VERSION_20);
assertion.setIssueInstant(DateTime.now());
assertion.setIssuer(issuer(issuerEntityId));
assertion.setSubject(subject(username));
assertion.setConditions(conditions());
SubjectConfirmation subjectConfirmation = subjectConfirmation();
subjectConfirmation.setMethod(SubjectConfirmation.METHOD_BEARER);
SubjectConfirmationData confirmationData = subjectConfirmationData(recipientEntityId);
confirmationData.setRecipient(recipientUri);
subjectConfirmation.setSubjectConfirmationData(confirmationData);
assertion.getSubject().getSubjectConfirmations().add(subjectConfirmation);
return assertion;
}
static Issuer issuer(String entityId) {
Issuer issuer = saml.buildSamlObject(Issuer.DEFAULT_ELEMENT_NAME);
issuer.setValue(entityId);
return issuer;
}
static Subject subject(String principalName) {
Subject subject = saml.buildSamlObject(Subject.DEFAULT_ELEMENT_NAME);
if (principalName != null) {
subject.setNameID(nameId(principalName));
}
return subject;
}
static NameID nameId(String principalName) {
NameID nameId = saml.buildSamlObject(NameID.DEFAULT_ELEMENT_NAME);
nameId.setValue(principalName);
return nameId;
}
static SubjectConfirmation subjectConfirmation() {
return saml.buildSamlObject(SubjectConfirmation.DEFAULT_ELEMENT_NAME);
}
static SubjectConfirmationData subjectConfirmationData(String recipient) {
SubjectConfirmationData subject = saml.buildSamlObject(SubjectConfirmationData.DEFAULT_ELEMENT_NAME);
subject.setRecipient(recipient);
subject.setNotBefore(DateTime.now().minus(Duration.millis(5 * 60 * 1000)));
subject.setNotOnOrAfter(DateTime.now().plus(Duration.millis(5 * 60 * 1000)));
return subject;
}
static Conditions conditions() {
Conditions conditions = saml.buildSamlObject(Conditions.DEFAULT_ELEMENT_NAME);
conditions.setNotBefore(DateTime.now().minus(Duration.millis(5 * 60 * 1000)));
conditions.setNotOnOrAfter(DateTime.now().plus(Duration.millis(5 * 60 * 1000)));
return conditions;
}
static Credential getSigningCredential(Saml2X509Credential credential, String entityId) {
BasicCredential cred = getBasicCredential(credential);
cred.setEntityId(entityId);
cred.setUsageType(UsageType.SIGNING);
return cred;
}
static BasicCredential getBasicCredential(Saml2X509Credential credential) {
return CredentialSupport.getSimpleCredential(
credential.getCertificate(),
credential.getPrivateKey()
);
}
static <T extends SignableSAMLObject> T signed(T signable, Saml2X509Credential credential, String entityId) {
SignatureSigningParameters parameters = new SignatureSigningParameters();
Credential signingCredential = getSigningCredential(credential, entityId);
parameters.setSigningCredential(signingCredential);
parameters.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
parameters.setSignatureReferenceDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA256);
parameters.setSignatureCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
try {
SignatureSupport.signObject(signable, parameters);
} catch (MarshallingException | SignatureException | SecurityException e) {
throw new Saml2Exception(e);
}
return signable;
}
static EncryptedAssertion encrypted(Assertion assertion, Saml2X509Credential credential) {
X509Certificate certificate = credential.getCertificate();
Encrypter encrypter = getEncrypter(certificate);
try {
return encrypter.encrypt(assertion);
}
catch (EncryptionException e) {
throw new Saml2Exception("Unable to encrypt assertion.", e);
}
}
static EncryptedID encrypted(NameID nameId, Saml2X509Credential credential) {
X509Certificate certificate = credential.getCertificate();
Encrypter encrypter = getEncrypter(certificate);
try {
return encrypter.encrypt(nameId);
}
catch (EncryptionException e) {
throw new Saml2Exception("Unable to encrypt nameID.", e);
}
}
private static Encrypter getEncrypter(X509Certificate certificate) {
String dataAlgorithm = XMLCipherParameters.AES_256;
String keyAlgorithm = XMLCipherParameters.RSA_1_5;
BasicCredential dataCredential = new BasicCredential(SECRET_KEY);
DataEncryptionParameters dataEncryptionParameters = new DataEncryptionParameters();
dataEncryptionParameters.setEncryptionCredential(dataCredential);
dataEncryptionParameters.setAlgorithm(dataAlgorithm);
Credential credential = CredentialSupport.getSimpleCredential(certificate, null);
KeyEncryptionParameters keyEncryptionParameters = new KeyEncryptionParameters();
keyEncryptionParameters.setEncryptionCredential(credential);
keyEncryptionParameters.setAlgorithm(keyAlgorithm);
Encrypter encrypter = new Encrypter(dataEncryptionParameters, keyEncryptionParameters);
Encrypter.KeyPlacement keyPlacement = Encrypter.KeyPlacement.valueOf("PEER");
encrypter.setKeyPlacement(keyPlacement);
return encrypter;
}
}

View File

@ -1,112 +0,0 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.saml2.provider.service.authentication;
import java.util.UUID;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.opensaml.saml.common.SAMLVersion;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.Conditions;
import org.opensaml.saml.saml2.core.Issuer;
import org.opensaml.saml.saml2.core.NameID;
import org.opensaml.saml.saml2.core.Response;
import org.opensaml.saml.saml2.core.Subject;
import org.opensaml.saml.saml2.core.SubjectConfirmation;
import org.opensaml.saml.saml2.core.SubjectConfirmationData;
final class TestSaml2AuthenticationObjects {
private static OpenSamlImplementation saml = OpenSamlImplementation.getInstance();
static Response response(String destination, String issuerEntityId) {
Response response = saml.buildSamlObject(Response.DEFAULT_ELEMENT_NAME);
response.setID("R"+UUID.randomUUID().toString());
response.setIssueInstant(DateTime.now());
response.setVersion(SAMLVersion.VERSION_20);
response.setID("_" + UUID.randomUUID().toString());
response.setDestination(destination);
response.setIssuer(issuer(issuerEntityId));
return response;
}
static Assertion assertion(
String username,
String issuerEntityId,
String recipientEntityId,
String recipientUri
) {
Assertion assertion = saml.buildSamlObject(Assertion.DEFAULT_ELEMENT_NAME);
assertion.setID("A"+ UUID.randomUUID().toString());
assertion.setIssueInstant(DateTime.now());
assertion.setVersion(SAMLVersion.VERSION_20);
assertion.setIssueInstant(DateTime.now());
assertion.setIssuer(issuer(issuerEntityId));
assertion.setSubject(subject(username));
assertion.setConditions(conditions());
SubjectConfirmation subjectConfirmation = subjectConfirmation();
subjectConfirmation.setMethod(SubjectConfirmation.METHOD_BEARER);
SubjectConfirmationData confirmationData = subjectConfirmationData(recipientEntityId);
confirmationData.setRecipient(recipientUri);
subjectConfirmation.setSubjectConfirmationData(confirmationData);
assertion.getSubject().getSubjectConfirmations().add(subjectConfirmation);
return assertion;
}
static Issuer issuer(String entityId) {
Issuer issuer = saml.buildSamlObject(Issuer.DEFAULT_ELEMENT_NAME);
issuer.setValue(entityId);
return issuer;
}
static Subject subject(String principalName) {
Subject subject = saml.buildSamlObject(Subject.DEFAULT_ELEMENT_NAME);
if (principalName != null) {
subject.setNameID(nameId(principalName));
}
return subject;
}
static NameID nameId(String principalName) {
NameID nameId = saml.buildSamlObject(NameID.DEFAULT_ELEMENT_NAME);
nameId.setValue(principalName);
return nameId;
}
static SubjectConfirmation subjectConfirmation() {
return saml.buildSamlObject(SubjectConfirmation.DEFAULT_ELEMENT_NAME);
}
static SubjectConfirmationData subjectConfirmationData(String recipient) {
SubjectConfirmationData subject = saml.buildSamlObject(SubjectConfirmationData.DEFAULT_ELEMENT_NAME);
subject.setRecipient(recipient);
subject.setNotBefore(DateTime.now().minus(Duration.millis(5 * 60 * 1000)));
subject.setNotOnOrAfter(DateTime.now().plus(Duration.millis(5 * 60 * 1000)));
return subject;
}
static Conditions conditions() {
Conditions conditions = saml.buildSamlObject(Conditions.DEFAULT_ELEMENT_NAME);
conditions.setNotBefore(DateTime.now().minus(Duration.millis(5 * 60 * 1000)));
conditions.setNotOnOrAfter(DateTime.now().plus(Duration.millis(5 * 60 * 1000)));
return conditions;
}
}

View File

@ -16,19 +16,17 @@
package org.springframework.security.saml2.provider.service.authentication;
import org.springframework.security.saml2.Saml2Exception;
import org.springframework.security.saml2.credentials.Saml2X509Credential;
import org.opensaml.security.crypto.KeySupport;
import java.io.ByteArrayInputStream;
import java.security.KeyException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import org.opensaml.security.crypto.KeySupport;
import org.springframework.security.saml2.Saml2Exception;
import org.springframework.security.saml2.credentials.Saml2X509Credential;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType.DECRYPTION;
@ -37,36 +35,28 @@ import static org.springframework.security.saml2.credentials.Saml2X509Credential
import static org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType.VERIFICATION;
final class TestSaml2X509Credentials {
static List<Saml2X509Credential> assertingPartyCredentials() {
return Arrays.asList(
new Saml2X509Credential(
idpPrivateKey(),
idpCertificate(),
SIGNING,
DECRYPTION
),
new Saml2X509Credential(
spCertificate(),
ENCRYPTION,
VERIFICATION
)
);
static Saml2X509Credential assertingPartySigningCredential() {
return new Saml2X509Credential(idpPrivateKey(), idpCertificate(), SIGNING);
}
static List<Saml2X509Credential> relyingPartyCredentials() {
return Arrays.asList(
new Saml2X509Credential(
spPrivateKey(),
spCertificate(),
SIGNING,
DECRYPTION
),
new Saml2X509Credential(
idpCertificate(),
ENCRYPTION,
VERIFICATION
)
);
static Saml2X509Credential assertingPartyEncryptingCredential() {
return new Saml2X509Credential(spCertificate(), ENCRYPTION);
}
static Saml2X509Credential assertingPartyPrivateCredential() {
return new Saml2X509Credential(idpPrivateKey(), idpCertificate(), SIGNING, DECRYPTION);
}
static Saml2X509Credential relyingPartyVerifyingCredential() {
return new Saml2X509Credential(idpCertificate(), VERIFICATION);
}
static Saml2X509Credential relyingPartySigningCredential() {
return new Saml2X509Credential(spPrivateKey(), spCertificate(), SIGNING);
}
static Saml2X509Credential relyingPartyDecryptingCredential() {
return new Saml2X509Credential(spPrivateKey(), spCertificate(), DECRYPTION);
}
private static X509Certificate certificate(String cert) {