package uk.gov.ida.verifyserviceprovider.factories; import org.opensaml.saml.saml2.metadata.SPSSODescriptor; import org.opensaml.saml.security.impl.MetadataCredentialResolver; import org.opensaml.security.crypto.KeySupport; import org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine; import uk.gov.ida.saml.metadata.EidasMetadataConfiguration; import uk.gov.ida.saml.metadata.EidasMetadataResolverRepository; import uk.gov.ida.saml.metadata.EidasTrustAnchorResolver; import uk.gov.ida.saml.metadata.MetadataResolverConfigBuilder; import uk.gov.ida.saml.metadata.bundle.MetadataResolverBundle; import uk.gov.ida.saml.metadata.factories.DropwizardMetadataResolverFactory; import uk.gov.ida.saml.metadata.factories.MetadataSignatureTrustEngineFactory; import uk.gov.ida.saml.security.EidasValidatorFactory; import uk.gov.ida.saml.security.IdaKeyStore; import uk.gov.ida.saml.security.IdaKeyStoreCredentialRetriever; import uk.gov.ida.saml.security.MetadataBackedEncryptionCredentialResolver; import uk.gov.ida.saml.security.SamlAssertionsSignatureValidator; import uk.gov.ida.saml.security.SecretKeyDecryptorFactory; import uk.gov.ida.shared.utils.manifest.ManifestReader; import uk.gov.ida.verifyserviceprovider.configuration.EuropeanIdentityConfiguration; import uk.gov.ida.verifyserviceprovider.configuration.VerifyServiceProviderConfiguration; import uk.gov.ida.verifyserviceprovider.factories.saml.AuthnRequestFactory; import uk.gov.ida.verifyserviceprovider.factories.saml.ResponseFactory; import uk.gov.ida.verifyserviceprovider.factories.saml.SignatureValidatorFactory; import uk.gov.ida.verifyserviceprovider.resources.GenerateAuthnRequestResource; import uk.gov.ida.verifyserviceprovider.resources.TranslateSamlResponseResource; import uk.gov.ida.verifyserviceprovider.resources.VersionNumberResource; import uk.gov.ida.verifyserviceprovider.services.AssertionTranslator; import uk.gov.ida.verifyserviceprovider.services.ClassifyingAssertionTranslator; import uk.gov.ida.verifyserviceprovider.services.EidasAssertionTranslator; import uk.gov.ida.verifyserviceprovider.services.EidasUnsignedAssertionTranslator; import uk.gov.ida.verifyserviceprovider.services.EntityIdService; import uk.gov.ida.verifyserviceprovider.services.ResponseService; import uk.gov.ida.verifyserviceprovider.services.UnsignedAssertionsResponseHandler; import uk.gov.ida.verifyserviceprovider.services.VerifyAssertionTranslator; import uk.gov.ida.verifyserviceprovider.utils.DateTimeComparator; import uk.gov.ida.verifyserviceprovider.validators.InstantValidator; import javax.ws.rs.client.Client; import java.security.KeyException; import java.security.KeyPair; import java.security.PrivateKey; import java.util.List; import java.util.Optional; import java.util.Timer; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static uk.gov.ida.verifyserviceprovider.factories.saml.ResponseFactory.createStringToResponseTransformer; import static uk.gov.ida.verifyserviceprovider.validators.EidasEncryptionAlgorithmValidatorHelper.anEidasEncryptionAlgorithmValidator; public class VerifyServiceProviderFactory { private final VerifyServiceProviderConfiguration configuration; private final ResponseFactory responseFactory; private final DateTimeComparator dateTimeComparator; private final EntityIdService entityIdService; private final MetadataResolverBundle<VerifyServiceProviderConfiguration> verifyMetadataBundler; private final MetadataResolverBundle<VerifyServiceProviderConfiguration> msaMetadataBundle; private final ManifestReader manifestReader; private final Client client; private final IdaKeyStore keyStore; private final SamlAssertionsSignatureValidator hubSignatureValidator; public VerifyServiceProviderFactory( VerifyServiceProviderConfiguration configuration, MetadataResolverBundle<VerifyServiceProviderConfiguration> verifyMetadataBundler, MetadataResolverBundle<VerifyServiceProviderConfiguration> msaMetadataBundle, Client client) throws KeyException { this.configuration = configuration; List<KeyPair> decryptionKeyPairs = getDecryptionKeyPairs( configuration.getSamlPrimaryEncryptionKey(), configuration.getSamlSecondaryEncryptionKey() ); this.keyStore = new IdaKeyStore(null, decryptionKeyPairs); this.responseFactory = new ResponseFactory(decryptionKeyPairs); this.dateTimeComparator = new DateTimeComparator(configuration.getClockSkew()); this.entityIdService = new EntityIdService(configuration.getServiceEntityIds()); this.verifyMetadataBundler = verifyMetadataBundler; this.msaMetadataBundle = msaMetadataBundle; this.manifestReader = new ManifestReader(); this.client = client; this.hubSignatureValidator = new SignatureValidatorFactory().getSignatureValidator(getHubSignatureTrustEngine()); } private List<KeyPair> getDecryptionKeyPairs(PrivateKey primary, PrivateKey secondary) throws KeyException { if (secondary == null) { return singletonList(createKeyPair(primary)); } else { return asList(createKeyPair(primary), createKeyPair(secondary)); } } private KeyPair createKeyPair(PrivateKey key) throws KeyException { return new KeyPair(KeySupport.derivePublicKey(key), key); } public GenerateAuthnRequestResource getGenerateAuthnRequestResource() throws Exception { MetadataCredentialResolver metadataCredentialResolver = getHubMetadataCredentialResolver(); MetadataBackedEncryptionCredentialResolver encryptionCredentialResolver = new MetadataBackedEncryptionCredentialResolver(metadataCredentialResolver, SPSSODescriptor.DEFAULT_ELEMENT_NAME); EncrypterFactory encrypterFactory = new EncrypterFactory(encryptionCredentialResolver, configuration.getVerifyHubMetadata().getExpectedEntityId()); PrivateKey signingKey = configuration.getSamlSigningKey(); AuthnRequestFactory authnRequestFactory = new AuthnRequestFactory( configuration.getHubSsoLocation(), createKeyPair(signingKey), manifestReader, encrypterFactory ); return new GenerateAuthnRequestResource( authnRequestFactory, configuration.getHubSsoLocation(), entityIdService ); } public TranslateSamlResponseResource getTranslateSamlResponseResource() { if (configuration.getMsaMetadata().isPresent()) { return getTranslateMatchingSamlResponseResource(); } else if (isEidasEnabled()){ return getEidasEnabledTranslateNonMatchingSamlResponseResource(); } else { return getTranslateNonMatchingSamlResponseResource(); } } private TranslateSamlResponseResource getTranslateMatchingSamlResponseResource() { ResponseService matchingResponseService = responseFactory.createMatchingResponseService( getHubSignatureTrustEngine(), responseFactory.createMsaAssertionTranslator(getMsaSignatureTrustEngine(), new SignatureValidatorFactory(), dateTimeComparator), dateTimeComparator ); return new TranslateSamlResponseResource(matchingResponseService, entityIdService); } private TranslateSamlResponseResource getEidasEnabledTranslateNonMatchingSamlResponseResource() { VerifyAssertionTranslator verifyAssertionTranslator = responseFactory.createVerifyIdpAssertionTranslator( hubSignatureValidator, dateTimeComparator, configuration.getHashingEntityId() ); EidasMetadataResolverRepository eidasMetadataResolverRepository = getEidasMetadataResolverRepository(); IdaKeyStoreCredentialRetriever idaKeyStoreCredentialRetriever = new IdaKeyStoreCredentialRetriever(keyStore); UnsignedAssertionsResponseHandler unsignedAssertionsResponseHandler = new UnsignedAssertionsResponseHandler( new EidasValidatorFactory(eidasMetadataResolverRepository), createStringToResponseTransformer(), new InstantValidator(dateTimeComparator), new SecretKeyDecryptorFactory(idaKeyStoreCredentialRetriever), anEidasEncryptionAlgorithmValidator(), hubSignatureValidator ); EidasAssertionTranslator eidasAssertionTranslator = responseFactory.createEidasAssertionTranslator( dateTimeComparator, eidasMetadataResolverRepository, configuration.getEuropeanIdentity().get(), configuration.getHashingEntityId() ); EidasUnsignedAssertionTranslator eidasUnsignedAssertionTranslator = responseFactory.createEidasUnsignedAssertionTranslator( dateTimeComparator, eidasMetadataResolverRepository, configuration.getEuropeanIdentity().get(), configuration.getHashingEntityId() ); AssertionTranslator assertionTranslator = new ClassifyingAssertionTranslator( verifyAssertionTranslator, eidasAssertionTranslator, eidasUnsignedAssertionTranslator); return new TranslateSamlResponseResource( responseFactory.createNonMatchingResponseService( getHubSignatureTrustEngine(), assertionTranslator, dateTimeComparator, unsignedAssertionsResponseHandler ), entityIdService); } private TranslateSamlResponseResource getTranslateNonMatchingSamlResponseResource() { VerifyAssertionTranslator assertionTranslator = responseFactory.createVerifyIdpAssertionTranslator( hubSignatureValidator, dateTimeComparator, configuration.getHashingEntityId() ); return new TranslateSamlResponseResource( responseFactory.createNonMatchingResponseService( getHubSignatureTrustEngine(), assertionTranslator, dateTimeComparator, null ), entityIdService); } public VersionNumberResource getVersionNumberResource() { return new VersionNumberResource(manifestReader); } private ExplicitKeySignatureTrustEngine getHubSignatureTrustEngine() { return verifyMetadataBundler.getSignatureTrustEngine(); } private MetadataCredentialResolver getHubMetadataCredentialResolver() { return verifyMetadataBundler.getMetadataCredentialResolver(); } private ExplicitKeySignatureTrustEngine getMsaSignatureTrustEngine() { return msaMetadataBundle.getSignatureTrustEngine(); } private EidasMetadataResolverRepository getEidasMetadataResolverRepository() { return new EidasMetadataResolverRepository( getEidasTrustAnchorResolver(), configuration.getEuropeanIdentity().get(), new DropwizardMetadataResolverFactory(), new Timer(), new MetadataSignatureTrustEngineFactory(), new MetadataResolverConfigBuilder(), client ); } private EidasTrustAnchorResolver getEidasTrustAnchorResolver() { EidasMetadataConfiguration metadataConfiguration = configuration.getEuropeanIdentity().get(); return new EidasTrustAnchorResolver(metadataConfiguration.getTrustAnchorUri(), client, metadataConfiguration.getTrustStore()); } private boolean isEidasEnabled() { Optional<EuropeanIdentityConfiguration> eidasConfig = configuration.getEuropeanIdentity(); return eidasConfig.map(EuropeanIdentityConfiguration::isEnabled).orElse(false); } }