Receiving and Processing a SAML 2.0 Response With an HttpServlet Using OpenSAML
2013-01-30 22:21
579 查看
Due to the popularity of my prior posts on processing SAML using OpenSAML I put together this code that shows a more real-world-example of how you would setup an HttpServlet to sit and listen for SAML Responses that are posted to it. Then it just shows what you might do with the Response to get what you need out of it. Again, I'm just verifying the Signature on the Response, Decrypting the Assertion and then looping through the Attribute nodes to look at the name/value pairs. There are other things that you might want to do such as receiving the signing-key from the Response itself (if it's being sent), or verifying a Signature on the Assertion if that's what's being signed. The trouble I've found with SAML implementations is that the spec is consistent, but how people use it isn't, so you'll undoubtedly need to tweak something depending on what your requirements are, but this code should get you started.
I've also got a full NetBeans project with this code posted here. I've found that NetBeans is the easiest way to get an HttpServlet running locally if you install the version with the web and app server bundled in.
Anyway, hope this is useful to someone.
import java.io.PrintWriter; import java.io.IOException; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.List; import java.security.KeyFactory; import java.security.KeyStore; import java.security.PublicKey; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.security.spec.X509EncodedKeySpec; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.opensaml.common.binding.BasicSAMLMessageContext; import org.opensaml.saml2.binding.decoding.HTTPPostDecoder; import org.opensaml.ws.message.MessageContext; import org.opensaml.ws.transport.http.HttpServletRequestAdapter; import org.opensaml.saml2.core.Assertion; import org.opensaml.saml2.core.Attribute; import org.opensaml.saml2.core.AttributeStatement; import org.opensaml.saml2.core.Response; import org.opensaml.saml2.encryption.Decrypter; import org.opensaml.xml.XMLObject; import org.opensaml.xml.encryption.DecryptionException; import org.opensaml.xml.encryption.InlineEncryptedKeyResolver; import org.opensaml.xml.security.keyinfo.StaticKeyInfoCredentialResolver; import org.opensaml.xml.security.x509.BasicX509Credential; import org.opensaml.xml.signature.Signature; import org.opensaml.xml.signature.SignatureValidator; import org.opensaml.xml.validation.ValidationException; /** * @author kevnls * If you use this code as a base for your implementation please leave this comment intact. * You should add your own name in addition. */ public class ProcessSAML extends HttpServlet { /** * Processes requests for both HTTP GET and POST methods. * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); File signatureVerificationPublicKeyFile = new File("C:\\Documents and Settings\\kevnls\\My Documents\\NetBeansProjects\\SAMLReceiver\\files\\IdPSigningCert.cer"); File decryptionPrivateKeyFile = new File("C:\\Documents and Settings\\kevnls\\My Documents\\NetBeansProjects\\SAMLReceiver\\files\\SPEncryptionCert.jks"); String decryptionPrivateKeyName = "pvktmp:bd5ba0e0-9718-48ea-b6e6-32cd9c852d76"; String decryptionPrivateKeyPassword = "!c3c0ld"; try { //bootstrap the opensaml stuff org.opensaml.DefaultBootstrap.bootstrap(); // get the message context MessageContext messageContext = new BasicSAMLMessageContext(); messageContext.setInboundMessageTransport(new HttpServletRequestAdapter(request)); HTTPPostDecoder samlMessageDecoder = new HTTPPostDecoder(); samlMessageDecoder.decode(messageContext); // get the SAML Response Response samlResponse = (Response)messageContext.getInboundMessage(); //get the certificate from the file InputStream inputStream2 = new FileInputStream(signatureVerificationPublicKeyFile); CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); X509Certificate certificate = (X509Certificate)certificateFactory.generateCertificate(inputStream2); inputStream2.close(); //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 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); //generate public key to validate signatures PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); //create credentials BasicX509Credential publicCredential = new BasicX509Credential(); //add public key value publicCredential.setPublicKey(publicKey); //create SignatureValidator SignatureValidator signatureValidator = new SignatureValidator(publicCredential); //get the signature to validate from the response object Signature signature = samlResponse.getSignature(); //try to validate try { signatureValidator.validate(signature); } catch (ValidationException ve) { out.println("Signature is not valid."); out.println(ve.getMessage()); return; } //no validation exception was thrown out.println("Signature is valid."); //start decryption of assertion //load up a KeyStore KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream(decryptionPrivateKeyFile), decryptionPrivateKeyPassword.toCharArray()); RSAPrivateKey privateKey = (RSAPrivateKey) keyStore.getKey(decryptionPrivateKeyName, decryptionPrivateKeyPassword.toCharArray()); //create the credential BasicX509Credential decryptionCredential = new BasicX509Credential(); decryptionCredential.setPrivateKey(privateKey); StaticKeyInfoCredentialResolver skicr = new StaticKeyInfoCredentialResolver(decryptionCredential); //create a decrypter Decrypter decrypter = new Decrypter(null, skicr, new InlineEncryptedKeyResolver()); //decrypt the first (and only) assertion Assertion decryptedAssertion; try { decryptedAssertion = decrypter.decrypt(samlResponse.getEncryptedAssertions().get(0)); } catch (DecryptionException de) { out.println("Assertion decryption failed."); out.println(de.getMessage()); return; } out.println("Assertion decryption succeeded."); //loop through the nodes to get the Attributes //this is where you would do something with these elements //to tie this user with your environment List attributeStatements = decryptedAssertion.getAttributeStatements(); for (int i = 0; i < attributeStatements.size(); i++) { List attributes = attributeStatements.get(i).getAttributes(); for (int x = 0; x < attributes.size(); x++) { String strAttributeName = attributes.get(x).getDOM().getAttribute("Name"); List attributeValues = attributes.get(x).getAttributeValues(); for (int y = 0; y < attributeValues.size(); y++) { String strAttributeValue = attributeValues.get(y).getDOM().getTextContent(); out.println(strAttributeName + ": " + strAttributeValue + " "); } } } } catch (Exception ex) { ex.printStackTrace(); } } // /** * Handles the HTTP GET method. * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } /** * Handles the HTTP POST method. * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } /** * Returns a short description of the servlet. * @return a String containing servlet description */ @Override public String getServletInfo() { return "This servlet processes a SAML 2.0 Response. It verifies the signature, " + "decrypts an assertion, and parses out the data in the attribute statements. " + "If you use this code as a base for your implementation please leave the @author comment intact. " + "You should add your own name in addition."; }// }
I've also got a full NetBeans project with this code posted here. I've found that NetBeans is the easiest way to get an HttpServlet running locally if you install the version with the web and app server bundled in.
Anyway, hope this is useful to someone.
相关文章推荐
- iPhone HTTP Streaming with FFMpeg and an Open Source Segmenter
- HTTP Streaming with FFMpeg and an Open Source Segmenter
- iPhone HTTP Streaming with FFMpeg and an Open Source Segmenter
- HTTP Streaming with FFMpeg and an Open Source Segmenter
- How to Make an HTTP Connection Using TCP/IP with RSocket
- Selenium – A exception with a null response was thrown sending an HTTP request
- sso with ad credential and saml2.0 integration(2)
- sso with ad credential and saml2.0 integration(3)-AD认证篇
- BEA-141150 - An error occurred while preparing application component uri of application application with HTTP response responseCode: message
- Using Open vSwitch with libvirt and QEMU/KVM
- C# WebAPI with HttpWebRequest, HttpWebResponse and HttpContext Example
- [转]Run a Http Response Filter together with an Ajax Update Panel
- An Introduction to Language Processing with Perl and Prolog: An Outline of Theories, Implementation,
- get an image file with XMLHttpRequest and encode with base64
- Using OpenLDAP with Microsoft SQL Server and Oracle Backend Databases
- FLV Flash video streaming with ASP.NET 2.0, IIS and HTTP handler
- p3:An open source pcap packet and NetFlow file analysis tool using Hadoop MapReduce and Hive.
- How to Pop Open an image window that resizes to fit the image in ASP.net 1.x and 2.0 Beta 1
- BEA-141150 - An error occurred while preparing application component uri of application application with HTTP response responseC
- Issue 71 - pymssql - Undefined symbols on Mac, CentOS, Redhat with pre-compiled build - A fast MS SQL Server client library for Python directly using C API instead of ODBC. It is Python DB-API 2.0 compliant. Works on Linux, *BSD, Solaris, Mac OS X and Win