Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package dk.gov.oio.saml.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.slf4j.Logger;
Expand All @@ -15,18 +18,39 @@
import org.opensaml.security.x509.BasicX509Credential;
import org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver;
import org.opensaml.xmlsec.encryption.support.DecryptionException;
import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
import org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;
import org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver;
import org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyResolver;
import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
import org.opensaml.xmlsec.keyinfo.impl.StaticKeyInfoCredentialResolver;
import org.opensaml.xmlsec.signature.support.SignatureConstants;

import dk.gov.oio.saml.util.ExternalException;
import dk.gov.oio.saml.util.InternalException;

public class AssertionService {
private static final Logger log = LoggerFactory.getLogger(AssertionService.class);

// Key transport and block encryption algorithms allowed by [OIO-ALG-01], identical in the OIOSAML Web
// SSO profiles 3.0.3 and 4.0.0. Anything else, RSA-1.5 in particular, is rejected before decryption.
// The digest and mask generation entries are not encryption algorithms: they are the parameters
// RSA-OAEP is used with, and the Decrypter checks them against the same list.
private static final Collection<String> ALLOWED_ENCRYPTION_ALGORITHMS = Collections.unmodifiableList(Arrays.asList(
EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP,
EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP11,
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128,
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256,
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM,
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES192_GCM,
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM,
SignatureConstants.ALGO_ID_DIGEST_SHA1,
EncryptionConstants.ALGO_ID_DIGEST_SHA256,
EncryptionConstants.ALGO_ID_DIGEST_SHA512,
EncryptionConstants.ALGO_ID_MGF1_SHA1,
EncryptionConstants.ALGO_ID_MGF1_SHA256,
EncryptionConstants.ALGO_ID_MGF1_SHA512));

public Assertion getAssertion(Response response) throws InternalException, ExternalException {
if (response.getEncryptedAssertions().size() > 0) {
EncryptedAssertion encryptedAssertion = response.getEncryptedAssertions().get(0);
Expand Down Expand Up @@ -64,7 +88,7 @@ private Assertion decryptAssertion(EncryptedAssertion encryptedAssertion) throws

ChainingEncryptedKeyResolver kekResolver = new ChainingEncryptedKeyResolver(encryptedKeyResolvers);

Decrypter decrypter = new Decrypter(null, keyResolver, kekResolver);
Decrypter decrypter = new Decrypter(null, keyResolver, kekResolver, ALLOWED_ENCRYPTION_ALGORITHMS, null);
decrypter.setRootInNewDocument(true);

return decrypter.decrypt(encryptedAssertion);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.xmlsec.encryption.support.EncryptionConstants;

import dk.gov.oio.saml.util.ExternalException;
import dk.gov.oio.saml.util.IdpUtil;
Expand Down Expand Up @@ -37,6 +38,43 @@ public void testGetPlaintextAssertion() throws Exception {
Assertions.assertNotNull(assertion);
}

@DisplayName("Test retrieving Assertion encrypted with AES-GCM")
@Test
public void testGetAssertionEncryptedWithGcm() throws Exception {
String nameID = "https://data.gov.dk/model/core/edi/person/uuid/37a5a1aa-67ce-4f70-b7c0-b8e678d585f7";

AssertionService assertionService = new AssertionService();
Assertion assertion = assertionService.getAssertion(IdpUtil.createResponse(true, true, true, nameID, TestConstants.SP_ENTITY_ID, TestConstants.SP_ASSERTION_CONSUMER_URL, UUID.randomUUID().toString(),
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM, EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP, TestConstants.SPEC_VERSION_OIOSAML_30));

Assertions.assertNotNull(assertion);
Assertions.assertEquals(nameID, assertion.getSubject().getNameID().getValue());
}

@DisplayName("Test that an Assertion with a key transport algorithm outside the profile is rejected")
@Test
public void testRejectAssertionWithDisallowedKeyTransportAlgorithm() throws Exception {
AssertionService assertionService = new AssertionService();

// RSA 1.5 key transport is not one of the algorithms allowed by [OIO-ALG-01]
Assertions.assertThrows(ExternalException.class, () -> {
assertionService.getAssertion(IdpUtil.createResponse(true, true, true, "NAMEID", TestConstants.SP_ENTITY_ID, TestConstants.SP_ASSERTION_CONSUMER_URL, UUID.randomUUID().toString(),
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256, EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSA15, TestConstants.SPEC_VERSION_OIOSAML_30));
});
}

@DisplayName("Test that an Assertion with a block encryption algorithm outside the profile is rejected")
@Test
public void testRejectAssertionWithDisallowedBlockEncryptionAlgorithm() throws Exception {
AssertionService assertionService = new AssertionService();

// Triple DES block encryption is not one of the algorithms allowed by [OIO-ALG-01]
Assertions.assertThrows(ExternalException.class, () -> {
assertionService.getAssertion(IdpUtil.createResponse(true, true, true, "NAMEID", TestConstants.SP_ENTITY_ID, TestConstants.SP_ASSERTION_CONSUMER_URL, UUID.randomUUID().toString(),
EncryptionConstants.ALGO_ID_BLOCKCIPHER_TRIPLEDES, EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP, TestConstants.SPEC_VERSION_OIOSAML_30));
});
}

@DisplayName("Test retrieving badly formatted plaintext Assertion")
@Test
public void testGetBadlyEncryptedAssertion() throws Exception {
Expand Down
19 changes: 13 additions & 6 deletions oiosaml/src/test/java/dk/gov/oio/saml/util/IdpUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public static MessageContext<SAMLObject> createMessageWithAssertion(
String inResponseToId,
String specVersion) throws Exception {
// Create proxy Response
Response response = createResponse(encrypted, validCert, validSignature, subjectNameID, recipientEntityId, assertionConsumerUrl, inResponseToId, specVersion);
Response response = createResponse(encrypted, validCert, validSignature, subjectNameID, recipientEntityId, assertionConsumerUrl, inResponseToId, EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256, EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP, specVersion);

// Build Proxy MessageContext and add response
MessageContext<SAMLObject> messageContext = new MessageContext<>();
Expand Down Expand Up @@ -109,9 +109,14 @@ public static Response createResponse(
String recipientEntityId,
String assertionConsumerUrl,
String inResponseToId) throws Exception {
return createResponse(encrypted, validCert, validSignature, subjectNameID, recipientEntityId, assertionConsumerUrl, inResponseToId, TestConstants.SPEC_VERSION_OIOSAML_30);
return createResponse(encrypted, validCert, validSignature, subjectNameID, recipientEntityId, assertionConsumerUrl, inResponseToId,
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256, EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP, TestConstants.SPEC_VERSION_OIOSAML_30);
}

/**
* @param dataAlgorithm algorithm the assertion itself is encrypted with
* @param keyTransportAlgorithm algorithm the encryption key is wrapped with
*/
public static Response createResponse(
boolean encrypted,
boolean validCert,
Expand All @@ -120,6 +125,8 @@ public static Response createResponse(
String recipientEntityId,
String assertionConsumerUrl,
String inResponseToId,
String dataAlgorithm,
String keyTransportAlgorithm,
String specVersion) throws Exception {

DateTime issueInstant = new DateTime();
Expand All @@ -145,7 +152,7 @@ public static Response createResponse(
Assertion assertion = createAssertion(issueInstant, subjectNameID, recipientEntityId, assertionConsumerUrl, specVersion);
SignAssertion(assertion, validSignature);
if (encrypted) {
EncryptedAssertion encryptedAssertion = encryptAssertion(assertion, validCert);
EncryptedAssertion encryptedAssertion = encryptAssertion(assertion, validCert, dataAlgorithm, keyTransportAlgorithm);
response.getEncryptedAssertions().add(encryptedAssertion);
}
else {
Expand Down Expand Up @@ -322,17 +329,17 @@ public static LogoutRequest createLogoutRequest(String nameID, String nameIDForm
return outgoingLR;
}

private static EncryptedAssertion encryptAssertion(Assertion assertion, boolean validCert) throws Exception {
private static EncryptedAssertion encryptAssertion(Assertion assertion, boolean validCert, String dataAlgorithm, String keyTransportAlgorithm) throws Exception {
X509Certificate certificate = getSPCertificate(validCert);

Credential keyEncryptionCredential = new BasicX509Credential(certificate);
DataEncryptionParameters encParams = new DataEncryptionParameters();

encParams.setAlgorithm(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256);
encParams.setAlgorithm(dataAlgorithm);

KeyEncryptionParameters kekParams = new KeyEncryptionParameters();
kekParams.setEncryptionCredential(keyEncryptionCredential);
kekParams.setAlgorithm(EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP);
kekParams.setAlgorithm(keyTransportAlgorithm);

Encrypter samlEncrypter = new Encrypter(encParams, kekParams);
samlEncrypter.setKeyPlacement(Encrypter.KeyPlacement.PEER);
Expand Down
Loading