user2094311
user2094311

Reputation:

Create SAML response based on SAML request

I have developed a Java web application, and I want to implement SAML. These are the steps I believe is right to implement SAML.

  1. The Service Provider(SP, my application in this case) sends a SAML authentication request to IdP.
  2. The IdP then validates it and create a SAML response assertion and signs it with the certificate, and send back to SP.
  3. SP then validates it with public key of certificate in keystore, and proceeds further based on that.

I have got a sample code and I am able to create SAML request and its like this

<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
    ID="_c7b796f4-bc16-4fcc-8c1d-36befffc39c2" Version="2.0"
    IssueInstant="2014-10-30T11:21:08Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
    AssertionConsumerServiceURL="http://localhost:8080/mywebapp/consume.jsp">
    <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">http://localhost:8080/mywebapp
    </saml:Issuer>
    <samlp:NameIDPolicy
        Format="urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified"
        AllowCreate="true"></samlp:NameIDPolicy>
    <samlp:RequestedAuthnContext Comparison="exact">
        <saml:AuthnContextClassRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
        </saml:AuthnContextClassRef>
    </samlp:RequestedAuthnContext>
</samlp:AuthnRequest>

I can encode it and send to IdP.

I want to create sample Java code to get this SAML request and then create a SAML response. How can I decode the request and validate it and create response? And Do I need to sign the saml response with certificate? and then send back to the SP?

Thanks.

Upvotes: 1

Views: 11888

Answers (2)

lexicore
lexicore

Reputation: 43709

The steps you've listed are more or less correct. The only thing I'd point to is that you have to be careful with the meaning if the word sends (ex. in "SP ... sends a SAML authentication request to IdP"). SAML allows authentications scenarios with zero direct communication between SP and IdP.

Another small addition is that SP may also sign his request, so you may have signature validation on both sides. Validation on the SP side is obligatory.

If you want to implement SAML, you may want to check one of the existing solutions, for example Shibboleth. If you're on platforms like Spring and JBoss you may want to check Spring Security SAML or JBoss PicketLink. If you want to go lower-level, check OpenSAML.

In my corp we have JBoss as standard and are very happy with PicketLink.

Upvotes: 1

Harika Mamidi
Harika Mamidi

Reputation: 472

Although this is an old post, I am adding sample code and references which I found useful.

SAMLResponse = hreq.getParameter("SAMLResponse");
InputSource inputSource = new InputSource(new StringReader(SAMLResponse));
SAMLReader samlReader = new SAMLReader();                   
response2 = org.opensaml.saml2.core.Response)samlReader.readFromFile(inputSource);

Now Validate the digital signature :

org.opensaml.saml2.core.Response response2 = (org.opensaml.saml2.core.Response)samlReader.readFromFile(inputSource);  
//To fetch the digital signature from the response.
Signature signature  = response2.getSignature(); 
X509Certificate certificate = (X509Certificate) keyStore.getCertificate(domainName);
//pull out the public key part of the certificate into a KeySpec
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(certificate.getPublicKey().getEncoded());
//get KeyFactory object that creates key objects, specifying RSA - java.security.KeyFactory
KeyFactory keyFactory = KeyFactory.getInstance("RSA");                  
//generate public key to validate signatures
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
//we have the public key                    
BasicX509Credential publicCredential = new BasicX509Credential();
//add public key value
publicCredential.setPublicKey(publicKey);
//create SignatureValidator
SignatureValidator signatureValidator = new SignatureValidator(publicCredential);
//try to validate
try{
signatureValidator.validate(signature); 
catch(Exception e){
//
} 

Now fetch the assertion map :

samlDetailsMap = setSAMLDetails(response2);

In the above logic use the below private method to pull all the assertion attributes. Finally you will have map of all the fields that are sent to you.

 private Map<String, String> setSAMLDetails(org.opensaml.saml2.core.Response  response2){
        Map<String, String> samlDetailsMap = new HashMap<String, String>();
        try {
            List<Assertion> assertions = response2.getAssertions();
            LOGGER.error("No of assertions : "+assertions.size());
            for(Assertion assertion:assertions){
                List<AttributeStatement> attributeStatements = assertion.getAttributeStatements();
                for(AttributeStatement attributeStatement: attributeStatements){
                    List<Attribute> attributes = attributeStatement.getAttributes();
                    for(Attribute attribute: attributes){
                        String name = attribute.getName();                          
                        List<XMLObject> attributes1 = attribute.getAttributeValues();
                        for(XMLObject xmlObject : attributes1){
                            if(xmlObject instanceof XSString){
                                samlDetailsMap.put(name, ((XSString) xmlObject).getValue());
                                LOGGER.error("Name is : "+name+" value is : "+((XSString) xmlObject).getValue());
                            }else if(xmlObject instanceof XSAnyImpl){
                                String value = ((XSAnyImpl) xmlObject).getTextContent();

                                samlDetailsMap.put(name, value);

                            }         
                    }
                }
            }       
       }
      } catch (Exception e) {             
          LOGGER.error("Exception occurred while setting the saml details");        
        }       
        LOGGER.error("Exiting from  setSAMLDetails method"); 
        return samlDetailsMap;
    }

Add new class SAMLReader as below :

import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.opensaml.DefaultBootstrap;
import org.opensaml.xml.Configuration;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.io.UnmarshallingException;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


public class SAMLReader {

 private static DocumentBuilder builder;

 static{
        try{
            DefaultBootstrap.bootstrap ();
            DocumentBuilderFactory factory = 
                    DocumentBuilderFactory.newInstance ();
                factory.setNamespaceAware (true);        
            builder = factory.newDocumentBuilder ();
        }catch (Exception ex){
            ex.printStackTrace ();
        }
    }



/**
 * 
 * @param filename
 * @return
 * @throws IOException
 * @throws UnmarshallingException
 * @throws SAXException
 */
public XMLObject readFromFile (String filename)
            throws IOException, UnmarshallingException, SAXException{
            return fromElement (builder.parse (filename).getDocumentElement ());    
}
/**
 *      
 * @param is
 * @return
 * @throws IOException
 * @throws UnmarshallingException
 * @throws SAXException
 */
public XMLObject readFromFile (InputStream is)
                throws IOException, UnmarshallingException, SAXException{
                return fromElement (builder.parse (is).getDocumentElement ());    
}
/**
 *      
 * @param is
 * @return
 * @throws IOException
 * @throws UnmarshallingException
 * @throws SAXException
 */
public XMLObject readFromFile (InputSource  is)
                throws IOException, UnmarshallingException, SAXException{                   
                return fromElement (builder.parse (is).getDocumentElement ());    
}

/**
 * 
 * @param element
 * @return
 * @throws IOException
 * @throws UnmarshallingException
 * @throws SAXException
 */
public static XMLObject fromElement (Element element)
            throws IOException, UnmarshallingException, SAXException{   
    return Configuration.getUnmarshallerFactory ()
                .getUnmarshaller (element).unmarshall (element);    
 }

}

Upvotes: 4

Related Questions