// Setup advanced info about metadata
 @Bean
 public ExtendedMetadata extendedMetadata() {
   ExtendedMetadata extendedMetadata = new ExtendedMetadata();
   extendedMetadata.setIdpDiscoveryEnabled(true);
   extendedMetadata.setSignMetadata(false);
   return extendedMetadata;
 }
  /**
   * Initializes SSO by creating AuthnRequest assertion and sending it to the IDP using the default
   * binding. Default IDP is used to send the request.
   *
   * @param options values specified by caller to customize format of sent request
   * @throws SAMLException error initializing SSO
   * @throws SAMLRuntimeException in case context doesn't contain required entities or contains
   *     invalid data
   * @throws MetadataProviderException error retrieving needed metadata
   * @throws MessageEncodingException error forming SAML message
   */
  public void sendAuthenticationRequest(SAMLMessageContext context, WebSSOProfileOptions options)
      throws SAMLException, MetadataProviderException, MessageEncodingException {

    // Verify we deal with a local SP
    if (!SPSSODescriptor.DEFAULT_ELEMENT_NAME.equals(context.getLocalEntityRole())) {
      throw new SAMLException(
          "WebSSO can only be initialized for local SP, but localEntityRole is: "
              + context.getLocalEntityRole());
    }

    // Load the entities from the context
    SPSSODescriptor spDescriptor = (SPSSODescriptor) context.getLocalEntityRoleMetadata();
    IDPSSODescriptor idpssoDescriptor = (IDPSSODescriptor) context.getPeerEntityRoleMetadata();
    ExtendedMetadata idpExtendedMetadata = context.getPeerExtendedMetadata();

    if (spDescriptor == null || idpssoDescriptor == null || idpExtendedMetadata == null) {
      throw new SAMLException(
          "SPSSODescriptor, IDPSSODescriptor or IDPExtendedMetadata are not present in the SAMLContext");
    }
    log.debug(
        "idpExtendedMetadata.getSigningAlgorithm=" + idpExtendedMetadata.getSigningAlgorithm());
    idpExtendedMetadata.setSigningAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512");

    SingleSignOnService ssoService =
        getSingleSignOnService(options, idpssoDescriptor, spDescriptor);
    AssertionConsumerService consumerService =
        getAssertionConsumerService(options, idpssoDescriptor, spDescriptor);

    AuthnRequest authRequest = getAuthnRequest(context, options, consumerService, ssoService);
    if (authRequest == null) {
      throw new SAMLException("Erreur dans getAuthnRequest null");
    }
    // authRequest.setForceAuthn(Boolean.TRUE);
    log.debug("getAuthnRequest.providerName=" + authRequest.getProviderName());
    // TODO optionally implement support for conditions, subject

    context.setCommunicationProfileId(getProfileIdentifier());
    context.setOutboundMessage(authRequest);
    context.setOutboundSAMLMessage(authRequest);
    context.setPeerEntityEndpoint(ssoService);
    context.setPeerEntityRoleMetadata(idpssoDescriptor);
    context.setPeerExtendedMetadata(idpExtendedMetadata);

    if (options.getRelayState() != null) {
      context.setRelayState(options.getRelayState());
    }

    boolean sign =
        spDescriptor.isAuthnRequestsSigned() || idpssoDescriptor.getWantAuthnRequestsSigned();
    log.debug("signature?" + sign + " avec http://www.w3.org/2001/04/xmldsig-more#rsa-sha512");
    context
        .getLocalExtendedMetadata()
        .setSigningAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512");
    sendMessage(context, sign);

    SAMLMessageStorage messageStorage = context.getMessageStorage();
    if (messageStorage != null) {
      messageStorage.storeMessage(authRequest.getID(), authRequest);
    }
  }
  /**
   * Method populates fields localEntityId, localEntityRole, localEntityMetadata,
   * localEntityRoleMetadata and peerEntityRole. In case fields localAlias, localEntityId,
   * localEntiyRole or peerEntityRole are set they are used, defaults of default SP and IDP as a
   * peer are used instead.
   *
   * @param samlContext context to populate
   * @throws org.opensaml.saml2.metadata.provider.MetadataProviderException in case metadata do not
   *     contain expected entities or localAlias is specified but not found
   */
  private void populateLocalEntity(SAMLMessageContext samlContext)
      throws MetadataProviderException {

    String localEntityId = samlContext.getLocalEntityId();
    QName localEntityRole = samlContext.getLocalEntityRole();

    if (localEntityId == null) {
      throw new MetadataProviderException(
          "No hosted service provider is configured and no alias was selected");
    }

    EntityDescriptor entityDescriptor = metadata.getEntityDescriptor(localEntityId);
    RoleDescriptor roleDescriptor =
        metadata.getRole(localEntityId, localEntityRole, SAMLConstants.SAML20P_NS);
    ExtendedMetadata extendedMetadata = metadata.getExtendedMetadata(localEntityId);

    if (entityDescriptor == null || roleDescriptor == null) {
      throw new MetadataProviderException(
          "Metadata for entity "
              + localEntityId
              + " and role "
              + localEntityRole
              + " wasn't found");
    }

    samlContext.setLocalEntityMetadata(entityDescriptor);
    samlContext.setLocalEntityRoleMetadata(roleDescriptor);
    samlContext.setLocalExtendedMetadata(extendedMetadata);

    if (extendedMetadata.getSigningKey() != null) {
      samlContext.setLocalSigningCredential(
          keyManager.getCredential(extendedMetadata.getSigningKey()));
    } else {
      samlContext.setLocalSigningCredential(keyManager.getDefaultCredential());
    }
  }
  /**
   * @param args
   * @throws IOException
   * @throws MarshallingException
   */
  public static void main(final String[] args) throws Exception {
    final Arguments arguments = new Arguments();
    final JCommander commander = new JCommander(arguments);
    try {
      commander.parse(args);
    } catch (final ParameterException e) {
      System.err.println(e.getMessage());
      final StringBuilder builder = new StringBuilder();
      commander.usage(builder);
      System.err.println(builder);
      return;
    }

    final KeyStore keystore = KeyStore.getInstance("jks");
    InputStream keystoreStream = null;
    try {
      keystoreStream = new FileInputStream(arguments.getKeystore());
      keystore.load(keystoreStream, arguments.getKeystorePassword().toCharArray());
    } finally {
      if (keystoreStream != null) keystoreStream.close();
    }

    final Map<String, String> keyPasswords = new HashMap<>();

    if (arguments.getEncryptionKey() != null)
      keyPasswords.put(arguments.getEncryptionKey(), arguments.getEncryptionKeyPassword());

    if (arguments.getSigningKey() != null)
      keyPasswords.put(arguments.getSigningKey(), arguments.getSigningKeyPassword());

    if (arguments.getTlsKey() != null)
      keyPasswords.put(arguments.getTlsKey(), arguments.getTlsKeyPassword());

    bootstrap();

    final JKSKeyManager keyManager = new JKSKeyManager(keystore, keyPasswords, null);
    final MetadataGenerator generator = new MetadataGenerator();
    generator.setKeyManager(keyManager);

    generator.setEntityId(arguments.getEntityId());
    generator.setEntityAlias(arguments.getAlias());
    generator.setEntityBaseURL(arguments.getBaseURL());
    generator.setSignMetadata(arguments.isSignMetadata());
    generator.setRequestSigned(arguments.isRequestSigned());
    generator.setWantAssertionSigned(arguments.isWantAssertionSigned());
    generator.setSigningKey(arguments.getSigningKey());
    generator.setEncryptionKey(arguments.getEncryptionKey());

    if (arguments.getTlsKey() != null && arguments.getTlsKey().length() > 0) {
      generator.setTlsKey(arguments.getTlsKey());
    }

    final Collection<String> bindingsSSO = new LinkedList<String>();
    final Collection<String> bindingsHoKSSO = new LinkedList<String>();
    final AllowedSSOBindings defaultBinding = arguments.getSsoDefaultBinding();

    int assertionConsumerIndex = 0;

    for (final AllowedSSOBindings binding : arguments.getSsoBindings()) {

      // Set default binding
      if (binding.equals(defaultBinding)) {
        assertionConsumerIndex = bindingsSSO.size() + bindingsHoKSSO.size();
      }

      // Set included bindings
      if (AllowedSSOBindings.SSO_POST.equals(binding)) {
        bindingsSSO.add(SAMLConstants.SAML2_POST_BINDING_URI);
      } else if (AllowedSSOBindings.SSO_ARTIFACT.equals(binding)) {
        bindingsSSO.add(SAMLConstants.SAML2_ARTIFACT_BINDING_URI);
      } else if (AllowedSSOBindings.SSO_PAOS.equals(binding)) {
        bindingsSSO.add(SAMLConstants.SAML2_PAOS_BINDING_URI);
      } else if (AllowedSSOBindings.HOKSSO_POST.equals(binding)) {
        bindingsHoKSSO.add(SAMLConstants.SAML2_POST_BINDING_URI);
      } else if (AllowedSSOBindings.HOKSSO_ARTIFACT.equals(binding)) {
        bindingsHoKSSO.add(SAMLConstants.SAML2_ARTIFACT_BINDING_URI);
      }
    }

    // Set bindings
    generator.setBindingsSSO(bindingsSSO);
    generator.setBindingsHoKSSO(bindingsHoKSSO);
    generator.setAssertionConsumerIndex(assertionConsumerIndex);

    // Discovery
    if (arguments.isIncludeDiscovery()) {
      generator.setIncludeDiscovery(true);
      if (arguments.getCustomDiscoveryURL() != null
          && arguments.getCustomDiscoveryURL().length() > 0) {
        generator.setCustomDiscoveryURL(arguments.getCustomDiscoveryURL());
      }
    } else {
      generator.setIncludeDiscovery(false);
    }

    generator.setNameID(arguments.getNameID());

    final EntityDescriptor descriptor = generator.generateMetadata();
    final ExtendedMetadata extendedMetadata = generator.generateExtendedMetadata();
    extendedMetadata.setSecurityProfile(arguments.getSecurityProfile());
    extendedMetadata.setSslSecurityProfile(arguments.getSslSecurityProfile());
    extendedMetadata.setRequireLogoutRequestSigned(arguments.isRequireLogoutRequestSigned());
    extendedMetadata.setRequireLogoutResponseSigned(arguments.isRequireLogoutResponseSigned());
    extendedMetadata.setRequireArtifactResolveSigned(arguments.isRequireArtifactResolveSigned());

    Writer metadataWriter = null;
    try {
      metadataWriter = new FileWriter(arguments.getMetadataOutput());
      metadataWriter.write(getMetadataAsString(descriptor));
    } finally {
      if (metadataWriter != null) {
        metadataWriter.flush();
        metadataWriter.close();
      }
    }

    if (arguments.getExtendedMetadataOutput() != null) {
      Writer extendedMetadataWriter = null;
      try {
        extendedMetadataWriter = new FileWriter(arguments.getExtendedMetadataOutput());
        extendedMetadataWriter.write(
            getConfiguration(arguments.getExtendedMetadataOutput().getName(), extendedMetadata));
      } finally {
        if (extendedMetadataWriter != null) {
          extendedMetadataWriter.flush();
          extendedMetadataWriter.close();
        }
      }
    }
  }
  private static String getConfiguration(
      final String fileName, final ExtendedMetadata extendedMetadata) {
    final StringBuilder sb = new StringBuilder();
    sb.append(
        "<bean class=\"org.springframework.security.saml.metadata.ExtendedMetadataDelegate\">\n");
    sb.append("    <constructor-arg>\n");
    sb.append(
        "        <bean class=\"org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider\">\n");
    sb.append("            <constructor-arg>\n");
    sb.append("                <value type=\"java.io.File\">classpath:security/")
        .append(fileName)
        .append("</value>\n");
    sb.append("            </constructor-arg>\n");
    sb.append("            <property name=\"parserPool\" ref=\"parserPool\"/>\n");
    sb.append("        </bean>\n");
    sb.append("    </constructor-arg>\n");
    sb.append("    <constructor-arg>\n");
    sb.append(
        "        <bean class=\"org.springframework.security.saml.metadata.ExtendedMetadata\">\n");
    sb.append("           <property name=\"local\" value=\"true\"/>\n");
    sb.append("           <property name=\"alias\" value=\"")
        .append(extendedMetadata.getAlias())
        .append("\"/>\n");
    sb.append("           <property name=\"securityProfile\" value=\"")
        .append(extendedMetadata.getSecurityProfile())
        .append("\"/>\n");
    sb.append("           <property name=\"sslSecurityProfile\" value=\"")
        .append(extendedMetadata.getSslSecurityProfile())
        .append("\"/>\n");
    sb.append("           <property name=\"signingKey\" value=\"")
        .append(extendedMetadata.getSigningKey())
        .append("\"/>\n");
    sb.append("           <property name=\"encryptionKey\" value=\"")
        .append(extendedMetadata.getEncryptionKey())
        .append("\"/>\n");

    if (extendedMetadata.getTlsKey() != null) {
      sb.append("           <property name=\"tlsKey\" value=\"")
          .append(extendedMetadata.getTlsKey())
          .append("\"/>\n");
    }

    sb.append("           <property name=\"requireArtifactResolveSigned\" value=\"")
        .append(extendedMetadata.isRequireArtifactResolveSigned())
        .append("\"/>\n");
    sb.append("           <property name=\"requireLogoutRequestSigned\" value=\"")
        .append(extendedMetadata.isRequireLogoutRequestSigned())
        .append("\"/>\n");
    sb.append("           <property name=\"requireLogoutResponseSigned\" value=\"")
        .append(extendedMetadata.isRequireLogoutResponseSigned())
        .append("\"/>\n");

    if (extendedMetadata.isIdpDiscoveryEnabled()) {
      sb.append("           <property name=\"idpDiscoveryURL\" value=\"")
          .append(extendedMetadata.getIdpDiscoveryURL())
          .append("\"/>\n");
      sb.append("           <property name=\"idpDiscoveryResponseURL\" value=\"")
          .append(extendedMetadata.getIdpDiscoveryResponseURL())
          .append("\"/>\n");
    }

    sb.append("        </bean>\n");
    sb.append("    </constructor-arg>\n");
    sb.append("</bean>");

    return sb.toString();
  }