class EidasMetadataService {

  byte[] generate(EidasSigner signer) throws CertificateEncodingException, IOException, MarshallingException,
    SignatureException, TransformerFactoryConfigurationError, TransformerException
  {
    EntityDescriptor entityDescriptor = new EntityDescriptorBuilder().buildObject();
    entityDescriptor.setID(id);
    entityDescriptor.setEntityID(entityId);
    entityDescriptor.setValidUntil(new DateTime(validUntil.getTime()));

    IDPSSODescriptor idpDescriptor = new IDPSSODescriptorBuilder().buildObject();
    idpDescriptor.setWantAuthnRequestsSigned(true);
    idpDescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS);

    KeyDescriptor kd = new KeyDescriptorBuilder().buildObject();
    kd.setUse(UsageType.SIGNING);
    KeyInfo ki = new KeyInfoBuilder().buildObject();
    X509Data x509 = new X509DataBuilder().buildObject();
    org.opensaml.xmlsec.signature.X509Certificate x509Cert = new X509CertificateBuilder().buildObject();
    x509Cert.setValue(new String(Base64.getEncoder().encode(sigCert.getEncoded()), StandardCharsets.UTF_8));
    x509.getX509Certificates().add(x509Cert);
    ki.getX509Datas().add(x509);
    kd.setKeyInfo(ki);
    idpDescriptor.getKeyDescriptors().add(kd);

    kd = new KeyDescriptorBuilder().buildObject();
    kd.setUse(UsageType.ENCRYPTION);
    ki = new KeyInfoBuilder().buildObject();
    x509 = new X509DataBuilder().buildObject();
    x509Cert = new X509CertificateBuilder().buildObject();
    x509Cert.setValue(new String(Base64.getEncoder().encode(encCert.getEncoded()), StandardCharsets.UTF_8));
    x509.getX509Certificates().add(x509Cert);
    ki.getX509Datas().add(x509);
    kd.setKeyInfo(ki);
    EncryptionMethod encMethod = new EncryptionMethodBuilder().buildObject();
    encMethod.setAlgorithm(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM);
    kd.getEncryptionMethods().add(encMethod);
    idpDescriptor.getKeyDescriptors().add(kd);

    SingleSignOnService sso = new SingleSignOnServiceBuilder().buildObject();
    sso.setBinding(SAMLConstants.SAML2_POST_BINDING_URI);
    sso.setLocation(postEndpoint);
    idpDescriptor.getSingleSignOnServices().add(sso);

    sso = new SingleSignOnServiceBuilder().buildObject();
    sso.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
    sso.setLocation(redirectEndpoint);
    idpDescriptor.getSingleSignOnServices().add(sso);

    for ( EidasNameIdType nameIDType : this.supportedNameIdTypes )
    {
      NameIDFormat nif = new NameIDFormatBuilder().buildObject();
      nif.setFormat(nameIDType.value);
      idpDescriptor.getNameIDFormats().add(nif);
    }

    for ( EidasPersonAttributes att : attributes )
    {
      Attribute a = new AttributeBuilder().buildObject();
      a.setFriendlyName(att.getFriendlyName());
      a.setName(att.getValue());
      idpDescriptor.getAttributes().add(a);
    }

    entityDescriptor.getRoleDescriptors().add(idpDescriptor);

    Organization organization = new OrganizationBuilder().buildObject();
    OrganizationDisplayName odn = new OrganizationDisplayNameBuilder().buildObject();
    odn.setValue(organisation.getDisplayName());
    odn.setXMLLang(organisation.getLangId());
    organization.getDisplayNames().add(odn);
    OrganizationName on = new OrganizationNameBuilder().buildObject();
    on.setValue(organisation.getName());
    on.setXMLLang(organisation.getLangId());
    organization.getOrganizationNames().add(on);
    OrganizationURL ourl = new OrganizationURLBuilder().buildObject();
    ourl.setValue(organisation.getUrl());
    ourl.setXMLLang(organisation.getLangId());
    organization.getURLs().add(ourl);
    entityDescriptor.setOrganization(organization);

    ContactPerson cp = new ContactPersonBuilder().buildObject();
    Company comp = new CompanyBuilder().buildObject();
    comp.setName(technicalContact.getCompany());
    cp.setCompany(comp);
    GivenName gn = new GivenNameBuilder().buildObject();
    gn.setName(technicalContact.getGivenName());
    cp.setGivenName(gn);
    SurName sn = new SurNameBuilder().buildObject();
    sn.setName(technicalContact.getSurName());
    cp.setSurName(sn);
    EmailAddress email = new EmailAddressBuilder().buildObject();
    email.setAddress(technicalContact.getEmail());
    cp.getEmailAddresses().add(email);
    TelephoneNumber tel = new TelephoneNumberBuilder().buildObject();
    tel.setNumber(technicalContact.getTel());
    cp.getTelephoneNumbers().add(tel);
    cp.setType(ContactPersonTypeEnumeration.TECHNICAL);
    entityDescriptor.getContactPersons().add(cp);

    cp = new ContactPersonBuilder().buildObject();
    comp = new CompanyBuilder().buildObject();
    comp.setName(supportContact.getCompany());
    cp.setCompany(comp);
    gn = new GivenNameBuilder().buildObject();
    gn.setName(supportContact.getGivenName());
    cp.setGivenName(gn);
    sn = new SurNameBuilder().buildObject();
    sn.setName(supportContact.getSurName());
    cp.setSurName(sn);
    email = new EmailAddressBuilder().buildObject();
    email.setAddress(supportContact.getEmail());
    cp.getEmailAddresses().add(email);
    tel = new TelephoneNumberBuilder().buildObject();
    tel.setNumber(supportContact.getTel());
    cp.getTelephoneNumbers().add(tel);
    cp.setType(ContactPersonTypeEnumeration.SUPPORT);
    entityDescriptor.getContactPersons().add(cp);

    EntityAttributes entAttr = new EntityAttributesBuilder().buildObject(EntityAttributes.DEFAULT_ELEMENT_NAME);
    Namespace assertion = new Namespace(SAMLConstants.SAML20_NS, SAMLConstants.SAML20_PREFIX);
    entAttr.getNamespaceManager().registerNamespaceDeclaration(assertion);

    Attribute attr = new AttributeBuilder().buildObject();
    attr.setName("urn:oasis:names:tc:SAML:attribute:assurance-certification");
    attr.setNameFormat(Attribute.URI_REFERENCE);
    XSAny any = new XSAnyBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
    any.setTextContent("http://eidas.europa.eu/LoA/high");
    attr.getAttributeValues().add(any);
    entAttr.getAttributes().add(attr);

    Attribute protocolAttribute = new AttributeBuilder().buildObject();
    protocolAttribute.setName(PROTOCOL_VERSION_URI);
    protocolAttribute.setNameFormat(Attribute.URI_REFERENCE);
    XSAny protocolVersion = new XSAnyBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME,
                                                           new QName("xs:string"));
    protocolVersion.setTextContent(SUPPORTED_PROTOCOL_VERSION);
    protocolAttribute.getAttributeValues().add(protocolVersion);
    entAttr.getAttributes().add(protocolAttribute);

    Attribute appliccationIdentifier = new AttributeBuilder().buildObject();
    appliccationIdentifier.setName(APPLICATION_IDENTIFIER_URI);
    appliccationIdentifier.setNameFormat(Attribute.URI_REFERENCE);
    XSAny applicationIdentifierVersion = new XSAnyBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME,
                                                                        new QName("xs:string"));
    applicationIdentifierVersion.setTextContent("German eIDAS Middleware version: " + middlewareVersion);
    appliccationIdentifier.getAttributeValues().add(applicationIdentifierVersion);
    entAttr.getAttributes().add(appliccationIdentifier);

    if (requesterIdFlag)
    {
      Attribute requesterIdAttribute = new AttributeBuilder().buildObject();
      requesterIdAttribute.setName("http://macedir.org/entity-category");
      requesterIdAttribute.setNameFormat(Attribute.URI_REFERENCE);
      XSAny requesterIdValue = new XSAnyBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);
      requesterIdValue.setTextContent("http://eidas.europa.eu/entity-attributes/termsofaccess/requesterid");
      requesterIdAttribute.getAttributeValues().add(requesterIdValue);
      entAttr.getAttributes().add(requesterIdAttribute);
    }

    Extensions ext = new ExtensionsBuilder().buildObject();
    ext.getUnknownXMLObjects().add(entAttr);

    DigestMethod dm = new DigestMethodBuilder().buildObject();
    dm.setAlgorithm(MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256);
    ext.getUnknownXMLObjects().add(dm);

    SigningMethod sm = new SigningMethodBuilder().buildObject();
    sm.setAlgorithm(XMLSignature.ALGO_ID_SIGNATURE_ECDSA_SHA256);
    sm.setMinKeySize(256);
    ext.getUnknownXMLObjects().add(sm);

    sm = new SigningMethodBuilder().buildObject();
    sm.setAlgorithm(EidasSigner.DIGEST_NONSPEC.equals(signer.getSigDigestAlg())
      ? XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256 : XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256_MGF1);
    sm.setMinKeySize(3072);
    sm.setMaxKeySize(4096);
    ext.getUnknownXMLObjects().add(sm);

    entityDescriptor.setExtensions(ext);

    List<Signature> sigs = new ArrayList<>();
    if (doSign)
    {
      XMLSignatureHandler.addSignature(entityDescriptor,
                                       signer.getSigKey(),
                                       signer.getSigCert(),
                                       signer.getSigType(),
                                       signer.getSigDigestAlg());
      sigs.add(entityDescriptor.getSignature());
    }

    EntityDescriptorMarshaller arm = new EntityDescriptorMarshaller();
    Element all = arm.marshall(entityDescriptor);
    if (!sigs.isEmpty())
    {
      Signer.signObjects(sigs);
    }

    Transformer trans = Utils.getTransformer();
    trans.setOutputProperty(OutputKeys.ENCODING, StandardCharsets.UTF_8.name());

    byte[] result = null;
    try (ByteArrayOutputStream bout = new ByteArrayOutputStream())
    {
      trans.transform(new DOMSource(all), new StreamResult(bout));
      result = bout.toByteArray();
    }
    return result;
  }

    EidasMetadataService(String id,
                       String entityId,
                       Date validUntil,
                       X509Certificate sigCert,
                       X509Certificate encCert,
                       EidasOrganisation organisation,
                       EidasContactPerson technicalContact,
                       EidasContactPerson supportContact,
                       String postEndpoint,
                       String redirectEndpoint,
                       List<EidasPersonAttributes> attributes,
                       List<EidasNameIdType> supportedNameIdTypes,
                       String middlewareVersion,
                       boolean doSign,
                       boolean requesterIdFlag);

}