Joven hacker sonriendo

Hackeamos su software

cero falsos positivos

Inteligencia experta + tecnología especializada

Autenticar Aplicación Cliente Usando OpenSAML

Nuestros ethical hackers explican cómo evitar vulnerabilidades de seguridad mediante la programación segura en Java al autenticar la aplicación del cliente usando OpenSAML. Esto es posible gracias a la implementación del servicio Single-Sign-on en el entorno web.

Necesidad

Implementar el servicio de Single-Sign-On en entornos Web.

Contexto

A continuación se describen las circunstancias bajo las cuales la siguiente solución tiene sentido:

  1. Se está desarrollando una aplicación en Java.

  2. Se está haciendo uso de OpenSAML 2.x.

Solución

  1. El lenguaje SAML (en inglés, Security Assertion Markup Language) es un estándar basado en XML para el intercambio de datos de autenticación y autorización entre dominios de seguridad, es decir, un proveedor de identidad y un proveedor de servicio.

  2. El problema esencial que SAML busca resolver es el de aportar el servicio de autenticación única SSO. Lo cual es un reto un tanto difícil.

  3. En SAML el principal (usualmente un usuario o una aplicación de cliente) se ha autenticado con un proveedor de identidad. El proveedor de identidad provee servicios de autenticación al principal, SAML no especifica la implementación de estos servicios, esto es relevante para el principal y el proveedor de identidad.

  4. El proveedor de servicios depende de que el proveedor de identidad autentique e identifique al principal. Bajo demanda del principal, el proveedor de identidad pasa una aserción SAML al proveedor de servicios. Dependiendo de la aserción, el proveedor de servicios toma una decisión de control de acceso.

  5. SAML es una especificación definida por OASIS. Y existen diferentes implementaciones de este protocolo. También existen múltiples implementaciones, algunas son:

    • Enterprise Sign On Engine (ESOE)

    • simpleSAMLphp, SAML V2.0 SP, SAML V2.0 IdP.

    • Lasso - Liberty Alliance Single Sign-On.

    • OpenSSO

    • OpenSAML

    • Shibboleth

    • SourceID

    • ZXID

  6. Según el autor. La biblioteca OpenSAML es un conjunto de bibliotecas de C\++ y Java para soportar a los desarrolladores que trabajan con SAML.

  7. El primer paso que ha de realizarse antes de usar la biblioteca es invocar el método estático de inicialización bootstrap definido en la clase DefaultBootstrap.

    opensaml.java
    1
    2
    3
    4
    5
    6
    try {
      DefaultBootstrap.bootstrap();
      }
      catch (ConfigurationException e) {
        e.printStackTrace();
      }
    
  8. Continuando con la inicialización, es necesario instanciar la clase BasicParserPool de manera que se tenga un objeto capaz de interpretar los mensajes XML que se han de intercambiar.

    1
    2
    BasicParserPool parserPool = new BasicParserPool();
    parserPool.setNamespaceAware(true);
    
  9. La clase Request define como se comportan las solicitudes a SAML. En este objeto se almacena una lista de AssertionArtifact que son referencias a mensajes de SAML. La siguiente clase muestra como puede manipularse el request para adicionar artifacts.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    private static Request buildSAML1ArtifactResolve(String base64Artifact)
    {
      XMLObjectBuilderFactory bf = Configuration.getBuilderFactory();
      Request request = (Request) bf.getBuilder(Request.DEFAULT_ELEMENT_NAME)
        .buildObject(Request.DEFAULT_ELEMENT_NAME);
      AssertionArtifact assertionArtifact = (AssertionArtifact)bf.getBuilder
        (AssertionArtifact.DEFAULT_ELEMENT_NAME).buildObject(AssertionArtifact.DEFAULT_ELEMENT_NAME);
      assertionArtifact.setAssertionArtifact(base64Artifact);
      request.getAssertionArtifacts().add(assertionArtifact);
      // add other data to Request as appropriate
      return request;
    }
    
  10. Para poder enviar la solicitud en formato XML, se debe usar un envolvente:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    Envelope envelope = buildSOAP11Envelope(request);
    
    private static Envelope buildSOAP11Envelope(XMLObject payload) {
      XMLObjectBuilderFactory bf = Configuration.getBuilderFactory();
      Envelope envelope = (Envelope)bf.getBuilder(Envelope.DEFAULT_ELEMENT_NAME)
        .buildObject(Envelope.DEFAULT_ELEMENT_NAME);
      Body body = (Body)bf.getBuilder(Body.DEFAULT_ELEMENT_NAME)
        .buildObject(Body.DEFAULT_ELEMENT_NAME);
      body.getUnknownXMLObjects().add(payload);
      envelope.setBody(body);
      return envelope;
    }
    
  11. El paso siguiente es crear el contexto de la comunicación usando el protocolo de acceso a objetos simple SOAP, inicializar el protocolo seguro SSL/TLS y obtener un objeto que represente el cliente de las solicitudes.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    // SOAP context used by the SOAP client
    BasicSOAPMessageContext soapContext = new BasicSOAPMessageContext();
    soapContext.setOutboundMessage(envelope);
    // This part is for client TLS support
    X509Credential clientTLSCred = getClientTLSCred(clientTLSPrivateKeyResourceName,
       clientTLSCertificateResourceName);
    StaticClientKeyManager keyManager = new StaticClientKeyManager(clientTLSCred.getPrivateKey(),
       clientTLSCred.getEntityCertificate());
    // Build the SOAP client
    HttpClientBuilder clientBuilder = new HttpClientBuilder();
    clientBuilder.setHttpsProtocolSocketFactory(new TLSProtocolSocketFactory(keyManager,
       new DelegateToApplicationX509TrustManager()));
    HttpSOAPClient soapClient = new HttpSOAPClient(clientBuilder.buildClient(), parserPool);
    
  12. Con estos datos, es posible intercambiar mensajes de SAML.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    //Ejemplo de envío de mensajes SAML.
    
    String serverEndpoint = "https://idp.ejemplo.org:8443/idp/profile/SAML1/SOAP/ArtifactResolve";
    // Send the message
    try {
      soapClient.send(serverEndpoint, soapContext);
    }
    catch (SOAPException e) {
      e.printStackTrace();
    }
    catch (SecurityException e) {
      e.printStackTrace();
    }
    
    1
    2
    3
    4
    5
    // Ejemplo de recepción de mensajes SAML.
    // Access the SOAP response envelope
    Envelope soapResponse = (Envelope) soapContext.getInboundMessage();
    System.out.println("SOAP Response was:");
    System.out.println(XMLHelper.prettyPrintXML(soapResponse.getDOM()));
    
  13. Código completo:

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    package brent.test;
    
    import java.io.IOException;
    import java.net.Socket;
    import java.security.KeyException;
    import java.security.Principal;
    import java.security.PrivateKey;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    import javax.net.ssl.X509KeyManager;
    import org.opensaml.DefaultBootstrap;
    import org.opensaml.saml1.core.AssertionArtifact;
    import org.opensaml.saml1.core.Request;
    import org.opensaml.ws.soap.client.BasicSOAPMessageContext;
    import org.opensaml.ws.soap.client.http.HttpClientBuilder;
    import org.opensaml.ws.soap.client.http.HttpSOAPClient;
    import org.opensaml.ws.soap.client.http.TLSProtocolSocketFactory;
    import org.opensaml.ws.soap.common.SOAPException;
    import org.opensaml.ws.soap.soap11.Body;
    import org.opensaml.ws.soap.soap11.Envelope;
    import org.opensaml.xml.Configuration;
    import org.opensaml.xml.ConfigurationException;
    import org.opensaml.xml.XMLObject;
    import org.opensaml.xml.XMLObjectBuilderFactory;
    import org.opensaml.xml.parse.BasicParserPool;
    import org.opensaml.xml.security.SecurityException;
    import org.opensaml.xml.security.SecurityHelper;
    import org.opensaml.xml.security.x509.X509Credential;
    import org.opensaml.xml.security.x509.X509Util;
    import org.opensaml.xml.util.DatatypeHelper;
    import org.opensaml.xml.util.XMLHelper;
    import edu.internet2.middleware.shibboleth.DelegateToApplicationX509TrustManager;
    
    public class SAML1ArtifactResolveExample {
      public static void main(String[] args) {
        String base64Artifact = "...base64encodedSAML1ArtifactData...";
        String serverEndpoint = "https://idp.ejemplo.org:8443/idp/profile/SAML1/SOAP/ArtifactResolve";
        String clientTLSPrivateKeyResourceName = "client.key";
        String clientTLSCertificateResourceName = "client.crt";
        try {
          DefaultBootstrap.bootstrap();
        }
        catch (ConfigurationException e) {
          e.printStackTrace();
        }
        BasicParserPool parserPool = new BasicParserPool();
        parserPool.setNamespaceAware(true);
        // Build the outgoing message structures
        Request request = buildSAML1ArtifactResolve(base64Artifact);
        Envelope envelope = buildSOAP11Envelope(request);
        // SOAP context used by the SOAP client
        BasicSOAPMessageContext soapContext = new BasicSOAPMessageContext();
        soapContext.setOutboundMessage(envelope);
        // This part is for client TLS support
        X509Credential clientTLSCred =
        getClientTLSCred(clientTLSPrivateKeyResourceName,
        clientTLSCertificateResourceName);
        StaticClientKeyManager keyManager = new StaticClientKeyManager(clientTLSCred.getPrivateKey(),
          clientTLSCred.getEntityCertificate());
        // Build the SOAP client
        HttpClientBuilder clientBuilder = new HttpClientBuilder();
        clientBuilder.setHttpsProtocolSocketFactory(new TLSProtocolSocketFactory(
          keyManager,
        new DelegateToApplicationX509TrustManager()));
        HttpSOAPClient soapClient = new HttpSOAPClient(clientBuilder.buildClient(),parserPool);
        // Send the message
        try {
          soapClient.send(serverEndpoint, soapContext);
        }
      catch (SOAPException e) {
          e.printStackTrace();
        }
      catch (SecurityException e) {
          e.printStackTrace();
        }
        // Access the SOAP response envelope
        Envelope soapResponse = (Envelope) soapContext.getInboundMessage();
        System.out.println("SOAP Response was:");
        System.out.println(XMLHelper.prettyPrintXML(soapResponse.getDOM()));
      }
    
      private static Envelope buildSOAP11Envelope(XMLObject payload) {
        XMLObjectBuilderFactory bf = Configuration.getBuilderFactory();
        Envelope envelope = (Envelope)bf.getBuilder(Envelope.DEFAULT_ELEMENT_NAME)
          .buildObject(Envelope.DEFAULT_ELEMENT_NAME);
        Body body = (Body)bf.getBuilder(Body.DEFAULT_ELEMENT_NAME)
          .buildObject(Body.DEFAULT_ELEMENT_NAME);
        body.getUnknownXMLObjects().add(payload);
        envelope.setBody(body);
        return envelope;
      }
    
      private static Request buildSAML1ArtifactResolve(String base64Artifact)
      {
        XMLObjectBuilderFactory bf = Configuration.getBuilderFactory();
        Request request = (Request) bf.getBuilder(Request.DEFAULT_ELEMENT_NAME)
         .buildObject(Request.DEFAULT_ELEMENT_NAME);
        AssertionArtifact assertionArtifact =(AssertionArtifact)bf.getBuilder
         (AssertionArtifact.DEFAULT_ELEMENT_NAME).buildObject(AssertionArtifact.DEFAULT_ELEMENT_NAME);
        assertionArtifact.setAssertionArtifact(base64Artifact);
        request.getAssertionArtifacts().add(assertionArtifact);
        // add other data to Request as appropriate
        return request;
      }
    
      private static X509Credential getClientTLSCred(String clientTLSPrivateKeyResourceName,
        String clientTLSCertificateResourceName) {
          PrivateKey privateKey = null;
          X509Certificate cert = null;
          try {
            privateKey = SecurityHelper.decodePrivateKey(DatatypeHelper.inputstreamToString(
              SAML1ArtifactResolveExample.class.getResourceAsStream
            (clientTLSPrivateKeyResourceName),null).getBytes(), null);
            cert = X509Util.decodeCertificate(DatatypeHelper.inputstreamToString
           (SAML1ArtifactResolveExample.class.getResourceAsStream
             (clientTLSCertificateResourceName),null).getBytes()).iterator().next();
          }
        catch (KeyException e) {
            e.printStackTrace();
          }
        catch (IOException e) {
            e.printStackTrace();
          }
        catch (CertificateException e) {
            e.printStackTrace();
          }
          return SecurityHelper.getSimpleCredential(cert, privateKey);
       }
    }
    
    class StaticClientKeyManager implements X509KeyManager {
      private static final String clientAlias = "myStaticAlias";
      private PrivateKey privateKey;
      private X509Certificate cert;
    
      public StaticClientKeyManager(PrivateKey newPrivateKey, X509Certificate newCert) {
        privateKey = newPrivateKey;
        cert = newCert;
      }
      /** {@inheritDoc} */
    
      public String chooseClientAlias(String[] as, Principal[] aprincipal, Socket socket) {
        System.out.println("chooseClientAlias");
        return clientAlias;
      }
      /** {@inheritDoc} */
    
      public String chooseServerAlias(String s, Principal[] aprincipal, Socketsocket) {
        System.out.println("chooseServerAlias");
        return null;
      }
      /** {@inheritDoc} */
    
      public X509Certificate[] getCertificateChain(String s) {
        System.out.println("getCertificateChain");
        return new X509Certificate[] {cert};
      }
      /** {@inheritDoc} */
    
      public String[] getClientAliases(String s, Principal[] aprincipal) {
        System.out.println("getClientAliases");
        return new String[] {clientAlias};
      }
      /** {@inheritDoc} */
    
      public PrivateKey getPrivateKey(String s) {
        System.out.println("getPrivateKey");
        return privateKey;
      }
    
      /** {@inheritDoc} */
    
      public String[] getServerAliases(String s, Principal[] aprincipal) {
        System.out.println("getServerAliases");
        return null;
      }
    }
    

Descargas

Puedes descargar el código fuente pulsando en el siguiente enlace:

SAML1ArtifactResolveExample.java Código que muestra como usar opensaml.




Haz un comentario

Estado de los servicios - Términos de Uso