• Receiving and Processing a SAML 2.0 Response With an HttpServlet Using OpenSAML


    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.

      1 import java.io.PrintWriter;
      2 import java.io.IOException;
      3 import java.io.File;
      4 import java.io.FileInputStream;
      5 import java.io.InputStream;
      6 
      7 import java.util.List;
      8 
      9 import java.security.KeyFactory;
     10 import java.security.KeyStore;
     11 import java.security.PublicKey;
     12 import java.security.cert.CertificateFactory;
     13 import java.security.cert.X509Certificate;
     14 import java.security.interfaces.RSAPrivateKey;
     15 import java.security.spec.X509EncodedKeySpec;
     16 
     17 import javax.servlet.ServletException;
     18 import javax.servlet.http.HttpServlet;
     19 import javax.servlet.http.HttpServletRequest;
     20 import javax.servlet.http.HttpServletResponse;
     21 
     22 import org.opensaml.common.binding.BasicSAMLMessageContext;
     23 import org.opensaml.saml2.binding.decoding.HTTPPostDecoder;
     24 import org.opensaml.ws.message.MessageContext;
     25 import org.opensaml.ws.transport.http.HttpServletRequestAdapter;
     26 import org.opensaml.saml2.core.Assertion;
     27 import org.opensaml.saml2.core.Attribute;
     28 import org.opensaml.saml2.core.AttributeStatement;
     29 import org.opensaml.saml2.core.Response;
     30 import org.opensaml.saml2.encryption.Decrypter;
     31 import org.opensaml.xml.XMLObject;
     32 import org.opensaml.xml.encryption.DecryptionException;
     33 import org.opensaml.xml.encryption.InlineEncryptedKeyResolver;
     34 import org.opensaml.xml.security.keyinfo.StaticKeyInfoCredentialResolver;
     35 import org.opensaml.xml.security.x509.BasicX509Credential;
     36 import org.opensaml.xml.signature.Signature;
     37 import org.opensaml.xml.signature.SignatureValidator;
     38 import org.opensaml.xml.validation.ValidationException;
     39 
     40 /**
     41  * @author kevnls
     42  * If you use this code as a base for your implementation please leave this comment intact.
     43  * You should add your own name in addition.
     44  */
     45 
     46 public class ProcessSAML extends HttpServlet {
     47    
     48     /** 
     49      * Processes requests for both HTTP GET and POST methods.
     50      * @param request servlet request
     51      * @param response servlet response
     52      * @throws ServletException if a servlet-specific error occurs
     53      * @throws IOException if an I/O error occurs
     54      */
     55 
     56     protected void processRequest(HttpServletRequest request, HttpServletResponse response)
     57     throws ServletException, IOException {
     58 
     59         response.setContentType("text/html;charset=UTF-8");
     60         PrintWriter out = response.getWriter();
     61 
     62         File signatureVerificationPublicKeyFile = new File("C:\\Documents and Settings\\kevnls\\My Documents\\NetBeansProjects\\SAMLReceiver\\files\\IdPSigningCert.cer");
     63         File decryptionPrivateKeyFile = new File("C:\\Documents and Settings\\kevnls\\My Documents\\NetBeansProjects\\SAMLReceiver\\files\\SPEncryptionCert.jks");
     64         String decryptionPrivateKeyName = "pvktmp:bd5ba0e0-9718-48ea-b6e6-32cd9c852d76";
     65         String decryptionPrivateKeyPassword = "!c3c0ld";
     66 
     67         try
     68         {
     69             //bootstrap the opensaml stuff
     70             org.opensaml.DefaultBootstrap.bootstrap();
     71 
     72             // get the message context
     73             MessageContext messageContext = new BasicSAMLMessageContext();
     74             messageContext.setInboundMessageTransport(new HttpServletRequestAdapter(request));
     75             HTTPPostDecoder samlMessageDecoder = new HTTPPostDecoder();
     76             samlMessageDecoder.decode(messageContext);
     77 
     78             // get the SAML Response
     79             Response samlResponse = (Response)messageContext.getInboundMessage();
     80 
     81             //get the certificate from the file
     82             InputStream inputStream2 = new FileInputStream(signatureVerificationPublicKeyFile);
     83             CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
     84             X509Certificate certificate = (X509Certificate)certificateFactory.generateCertificate(inputStream2);
     85             inputStream2.close();
     86 
     87             //pull out the public key part of the certificate into a KeySpec
     88             X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(certificate.getPublicKey().getEncoded());
     89 
     90             //get KeyFactory object that creates key objects, specifying RSA
     91             KeyFactory keyFactory = KeyFactory.getInstance("RSA");
     92 
     93             //generate public key to validate signatures
     94             PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
     95 
     96             //create credentials
     97             BasicX509Credential publicCredential = new BasicX509Credential();
     98 
     99             //add public key value
    100             publicCredential.setPublicKey(publicKey);
    101 
    102             //create SignatureValidator
    103             SignatureValidator signatureValidator = new SignatureValidator(publicCredential);
    104 
    105             //get the signature to validate from the response object
    106             Signature signature = samlResponse.getSignature();
    107 
    108             //try to validate
    109             try
    110             {
    111                 signatureValidator.validate(signature);
    112             }
    113             catch (ValidationException ve)
    114             {
    115                 out.println("Signature is not valid.");
    116                 out.println(ve.getMessage());
    117                 return;
    118             }
    119 
    120             //no validation exception was thrown
    121             out.println("Signature is valid.");
    122 
    123             //start decryption of assertion
    124 
    125             //load up a KeyStore
    126             KeyStore keyStore = KeyStore.getInstance("JKS");
    127             keyStore.load(new FileInputStream(decryptionPrivateKeyFile), decryptionPrivateKeyPassword.toCharArray());
    128 
    129             RSAPrivateKey privateKey = (RSAPrivateKey) keyStore.getKey(decryptionPrivateKeyName, decryptionPrivateKeyPassword.toCharArray());
    130 
    131             //create the credential
    132             BasicX509Credential decryptionCredential = new BasicX509Credential();
    133             decryptionCredential.setPrivateKey(privateKey);
    134 
    135             StaticKeyInfoCredentialResolver skicr = new StaticKeyInfoCredentialResolver(decryptionCredential);
    136 
    137             //create a decrypter
    138             Decrypter decrypter = new Decrypter(null, skicr, new InlineEncryptedKeyResolver());
    139 
    140             //decrypt the first (and only) assertion
    141             Assertion decryptedAssertion;
    142 
    143             try
    144             {
    145                 decryptedAssertion = decrypter.decrypt(samlResponse.getEncryptedAssertions().get(0));
    146             }
    147             catch (DecryptionException de)
    148             {
    149                 out.println("Assertion decryption failed.");
    150                 out.println(de.getMessage());
    151                 return;
    152             }
    153 
    154             out.println("Assertion decryption succeeded.");
    155 
    156             //loop through the nodes to get the Attributes
    157             //this is where you would do something with these elements
    158             //to tie this user with your environment
    159             List attributeStatements = decryptedAssertion.getAttributeStatements();
    160             for (int i = 0; i < attributeStatements.size(); i++)
    161             {
    162                 List attributes = attributeStatements.get(i).getAttributes();
    163                 for (int x = 0; x < attributes.size(); x++)
    164                 {
    165                     String strAttributeName = attributes.get(x).getDOM().getAttribute("Name");
    166 
    167                     List attributeValues = attributes.get(x).getAttributeValues();
    168                     for (int y = 0; y < attributeValues.size(); y++)
    169                     {
    170                         String strAttributeValue = attributeValues.get(y).getDOM().getTextContent();
    171                         out.println(strAttributeName + ": " + strAttributeValue + " ");
    172                     }
    173                 }
    174             }
    175         }
    176         catch (Exception ex)
    177         {
    178             ex.printStackTrace();
    179         }
    180 
    181     } 
    182 
    183     // 
    184     /** 
    185      * Handles the HTTP GET method.
    186      * @param request servlet request
    187      * @param response servlet response
    188      * @throws ServletException if a servlet-specific error occurs
    189      * @throws IOException if an I/O error occurs
    190      */
    191     @Override
    192     protected void doGet(HttpServletRequest request, HttpServletResponse response)
    193     throws ServletException, IOException {
    194         processRequest(request, response);
    195     } 
    196 
    197     /** 
    198      * Handles the HTTP POST method.
    199      * @param request servlet request
    200      * @param response servlet response
    201      * @throws ServletException if a servlet-specific error occurs
    202      * @throws IOException if an I/O error occurs
    203      */
    204     @Override
    205     protected void doPost(HttpServletRequest request, HttpServletResponse response)
    206     throws ServletException, IOException {
    207         processRequest(request, response);
    208     }
    209 
    210     /** 
    211      * Returns a short description of the servlet.
    212      * @return a String containing servlet description
    213      */
    214     @Override
    215     public String getServletInfo() {
    216         return "This servlet processes a SAML 2.0 Response.  It verifies the signature, " +
    217                 "decrypts an assertion, and parses out the data in the attribute statements.  " +
    218                 "If you use this code as a base for your implementation please leave the @author comment intact.  " +
    219                 "You should add your own name in addition.";
    220     }// 
    221 
    222 }

    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.

  • 相关阅读:
    软件工程概论之web基础
    java动手动脑——异常处理
    Java动手动脑——多态和继承
    java字串
    数据结构——用栈来判断回文字符串
    java一个能记录生成多少个对象的类
    openwrt设置默认登陆密码
    在ubuntu中安装luci解决iwinfo.h No such file or directory问题
    添加mysamba
    更改默认打开wifi功能
  • 原文地址:https://www.cnblogs.com/xzs603/p/2883744.html
Copyright © 2020-2023  润新知