Wednesday, August 10, 2016

Signing and sending AuthnRequests in OpenSAML V3

Signing and sending a AuthnRequest in OpenSAML V3 is forutunatly not that different from how it was done in OpenSAML V2.

As described in this post on AuthnRequests OpenSAML V2, the AuthnRequest is the SAML request that starts a typical SSO authentication process. This is the SP requesting the IDP to authenticate a user.

As in V2 the HTTPRedirectDeflateEncoder is used, the main difference lays in the message context.

One of the major changes between V2 and V3 is the message contexts. I V2 the message contexts where basically one object containing general properties about the message and its destination. In V3 the message context concept has been expanded to be more flexible contain more information. In the new context structure there is a context object per purpose.

In my book A Guide to OpenSAML V3 I cover the use of the new message contexts in detail.

To sign and send a AuthnRequest, three contexts are needed.

The main context is created and the AuthnRequest is set.
MessageContext context = new MessageContext();
context.setMessage(authnRequest);

The SAMLPeerEntityContext and SAMLEndpointContext are created and configured to point to the endpoint of the message.
SAMLPeerEntityContext peerEntityContext = context.getSubcontext(SAMLPeerEntityContext.class, true);
SAMLEndpointContext endpointContext = peerEntityContext.getSubcontext(SAMLEndpointContext.class, true);
endpointContext.setEndpoint(idpEndpoint);

Next, the security parameters context is created and populated with signing information
SignatureSigningParameters signatureSigningParameters = new SignatureSigningParameters();
signatureSigningParameters.setSigningCredential(SPCredentials.getCredential());
signatureSigningParameters.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
context.getSubcontext(SecurityParametersContext.class, true).setSignatureSigningParameters(signatureSigningParameters);

Next, the HTTPRedirectDeflateEncoder is created and populated with context and the http request object.
HTTPRedirectDeflateEncoder encoder = new HTTPRedirectDeflateEncoder();
encoder.setMessageContext(context);
encoder.setHttpServletResponse(httpServletResponse);

Lastly the encoder is initialized and the message is encoded
encoder.initialize();
encoder.encode();

For more detailed information on AuthnRequest, message context and all the new stuff in OpenSAML V3, please consider buying my book A Guide to OpenSAML V3.
A Guide to OpenSAML V3

12 comments:

  1. I exactly use the code you described in this post an in your book. I now have the problem that my IDP (openAM) can't verify the signature of my request.
    The problem is that the encoder does not include the signing section in the SAML request so openAM cant verify.
    When i debug the code i can see the the security context is available with all needed data from the keystore but after encoding there is no signature section in the request.
    openAM receives:

    SAML2Utils.decodeFromRedirect: input string is ===>nZNNj9owEIbv/RWR75AP0iW1SFYpaFWkbYuA3UMvyNgTsOrYqcdh2X+/JpCWwy6HSpEseV6/M/PMZHJ/rFVwAIvS6JzEw4gEoLkRUu9y8rR+GGTkvvg0QVarpKFl6/Z6CX9aQBeUiGCdfzc1Gtsa7ArsQXJ4Wj7mZO9cQ8NQGc7U3qCjWZRFIaIhwcw/lpq5LuNFZxrQrEbHdjCsmfaHGHCjNXAnD9K9DgV4r53U4Wr1cwlCWh8Ja3CsVJJhKEVDgvksJ5voCxuxLIZxksXbtMqqbDxKq22UjkX8+U6kXobYwlz7ZNrlJIni8SAa+W8dR3R0R2MPIUp/kWBhjTPcqK9Sn3G0VlPDUCL1tQJSx+mq/P5Ik2FEt2cR0m/r9WJQei4V444Ezz3a5ITWw9ZIzzBv+zWX5KQ4s6dd1fba4bYB66dDit+s2XjyG2wm4bVZ0Y/1h389ny2Mkvw1KJUyL1MLzEFOKqYQSPBgbM3cxwnjYdzdSDGoOiltNTbAZSVBkPBvosvqgOgWye+Ng6MLpqZumJV4wgTHE7a+6WvZVPmWllD9F4KbMk75ydtfL/zxYqw4jd4vGIi1Zb4RY11P7r2Kikvwg/7+ha9/n+IN<===

    After decoding its:
    SAML2Utils.decodeFromRedirect: Return value:

    kap_sso_spurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport

    Do you have any idea what could prevent the Encoder to add the signature section to the SAML request?

    ReplyDelete
    Replies
    1. Are you sure that the signature is not sent? When doing the redirect the signature is sent as its own URL parameter. Please try my chrome extension for debugging SAML, https://chrome.google.com/webstore/detail/saml-devtools-extension/jndllhgbinhiiddokbeoeepbppdnhhio

      When you get redirected to OpenAM there should be a request marked green with your SAML request. Please paste the information under the SAML tab.

      Delete
    2. This comment has been removed by the author.

      Delete
    3. This comment has been removed by the author.

      Delete
  2. I cant post the SAML Request here, but there is no signature in the SAML Request.
    I only can see it as a Query Parameter:
    SAMLRequest: nZNNb9swDIbv%2BxWG7v5csyRC7MJLUCxAtwWJu8MugSzRqTBb8kQ5Tf99ZSfeclhz2ImASL4kH1KL%2B1NTe0cwKLVKSRxExAPFtZDqkJKn4sGfkfvswwJZUyctzTv7rLbwuwO0Xo4Ixrq8pVbYNWB2YI6Sw9P2MSXP1rY0DNGyAwQNU84In2ulgFt5lPY1EBAiauKtnJZUzA4NXNJ0C4o1t5NrfZAq3O2%2Bb0FI4zxhA5bltWQYStESb71Kyf5u%2FomV83I6qSbJbBJBnNzFUTSPWByLpGSlC0PsYK1cMWVTkkTx1I8%2B%2BtGkiKc0ntFkHkxnyU%2FibYy2muv6s1RnOp1RVDOUSF2vgNRyusu%2FPtIkiGh5DkL6pSg2fu4wVYxb4v0YSSc9acdeIT2zva3XXoqT7LwKOnRtrhVuC7BxWST7xdq9I79HZxxh1%2BYivBbNxm1%2Fcyrr1UbXkr96eV3rl6UBZiElFasRiPegTcPs%2B4XjIB5epPCrIZR2ClvgspIgSPin0OWiQAz35c7Jwsl6S920zEjsccGpxzcOfx22rN1oW6j%2BC8XNME55r%2B2eN868aCP6E3CHBqIwzA2ijR3J%2Fauj7OJ8Z76%2F7utflb0B
    SigAlg: http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256
    Signature: 35ig5MWFAOkuGmGTXNjseXGnGf%2BSy3V%2FwhlYxFHmJm9SojcdCcvBhzQLmR%2FzPv44D0XIQsP8RDcIx2TI56X2iHoiRvNkMvl%2Bq%2BhqA8QEfJoYufW1cvw8xU5%2Bqjs2yjeHAzA1bhsnI55rBCiUOM2h2eMgFe1vk3rXR3nlaha8cR8%3D

    I think openAM expects the signature directly in the SAML request or do i need to change something in the openAM configuration?

    ReplyDelete
    Replies
    1. The standard states that, when using the Redirect binding, the signature should not be sent in the SAML XML but in a separate URL parameter.

      Delete
    2. OK..i managed to fix the openAM config and now i can see the following in my openAM Log:

      QuerySignatureUtil.verify: Query string to be verifed:
      SAMLRequest=nZNNb9swDIbv%2BxWG7v6sUztC7MJLUCxAtwWJu8MugSrTiTBZ8kQ5Tf99la8thzWHnQRIL1%2BSD6nJw76T3g4MCq0KEgcR8UBx3Qi1Kchz%2Fejn5KH8NEHWyaSn1WC3agm%2FB0DrVYhgrIubaoVDB2YFZic4PC%2BfCrK1tqdhKDVncqvR0jzKoxBRE2%2FmgoVi9pjxrNM9KNahZRsIOqbc0fhcKwXcip2wb0EDzmsjVLhafV9CI4x7CTuwrJKCYSiannjzWUHW4xHjUZ4laZtl4zSN4rt0%2FNIm8X2W85RlYydDHGCuXDJlC5JEceZHd340ruOIjnIap8H9KPlJvIXRVnMtPwt1wjEYRTVDgdTVCkgtp6vq6xNNgoi%2BnERIv9T1wq8cl5ZxS7wfF7TJAa2DrZCeYN7268%2FJSXliT49Vm2uH2wbsMh1S%2FmL92pFfYz8Jr83Ky1i%2Fuej5bKGl4G9eJaV%2BnRpgFgrSMolAvEdtOmY%2FThgH8fFGNH57lNJBYQ9ctAIaEv5JdF4daI6L5PbGwt56U931zAg8YIL9Adul6WvZVLqWltD%2BF4KbMk75wdtdL9zxqk1zGL1bMGhqw1wj2tgLuX9VVJ4fP%2Bjv7%2FP19ynfAQ%3D%3D&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-md5&Signature=C8Bo2%2BNcvMQnMm%2BySJ1MeayiWRbRB8U%2BF6BD%2BTYPjwEOJ7GC8AnQr8BruTZNwQK%2FiE6vBzGEYm0dl%2BLGcWfJyGlrU3LONeT%2BTMXJIHOXZCXGaL%2By%2Bag8k0CrC4AEanwtuP%2Fz4whYDAuP55Sa%2FmGRqRkVL0826FZQQsfr%2B6sYnWM%3D
      libSAML2:03/09/2017 11:58:14:685 AM MEZ: Thread[http-nio-8080-exec-5,5,main]: TransactionId[60a6a771-5f39-41a6-aad3-1795875ec28b-534]
      QuerySignatureUtil.verify: Query string to be verifed (re-arranged):
      SAMLRequest=nZNNb9swDIbv%2BxWG7v6sUztC7MJLUCxAtwWJu8MugSrTiTBZ8kQ5Tf99la8thzWHnQRIL1%2BSD6nJw76T3g4MCq0KEgcR8UBx3Qi1Kchz%2Fejn5KH8NEHWyaSn1WC3agm%2FB0DrVYhgrIubaoVDB2YFZic4PC%2BfCrK1tqdhKDVncqvR0jzKoxBRE2%2FmgoVi9pjxrNM9KNahZRsIOqbc0fhcKwXcip2wb0EDzmsjVLhafV9CI4x7CTuwrJKCYSiannjzWUHW4xHjUZ4laZtl4zSN4rt0%2FNIm8X2W85RlYydDHGCuXDJlC5JEceZHd340ruOIjnIap8H9KPlJvIXRVnMtPwt1wjEYRTVDgdTVCkgtp6vq6xNNgoi%2BnERIv9T1wq8cl5ZxS7wfF7TJAa2DrZCeYN7268%2FJSXliT49Vm2uH2wbsMh1S%2FmL92pFfYz8Jr83Ky1i%2Fuej5bKGl4G9eJaV%2BnRpgFgrSMolAvEdtOmY%2FThgH8fFGNH57lNJBYQ9ctAIaEv5JdF4daI6L5PbGwt56U931zAg8YIL9Adul6WvZVLqWltD%2BF4KbMk75wdtdL9zxqk1zGL1bMGhqw1wj2tgLuX9VVJ4fP%2Bjv7%2FP19ynfAQ%3D%3D&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-md5
      libSAML2:03/09/2017 11:58:14:685 AM MEZ: Thread[http-nio-8080-exec-5,5,main]: TransactionId[60a6a771-5f39-41a6-aad3-1795875ec28b-534]
      QuerySignatureUtil.verify: SigAlg query parameter value: http://www.w3.org/2001/04/xmldsig-more#rsa-md5
      libSAML2:03/09/2017 11:58:14:686 AM MEZ: Thread[http-nio-8080-exec-5,5,main]: TransactionId[60a6a771-5f39-41a6-aad3-1795875ec28b-534]
      QuerySignatureUtil.verify: Signature query parameter value:
      C8Bo2+NcvMQnMm+ySJ1MeayiWRbRB8U+F6BD+TYPjwEOJ7GC8AnQr8BruTZNwQK/iE6vBzGEYm0dl+LGcWfJyGlrU3LONeT+TMXJIHOXZCXGaL+y+ag8k0CrC4AEanwtuP/z4whYDAuP55Sa/mGRqRkVL0826FZQQsfr+6sYnWM=
      libSAML2:03/09/2017 11:58:14:691 AM MEZ: Thread[http-nio-8080-exec-5,5,main]: TransactionId[60a6a771-5f39-41a6-aad3-1795875ec28b-534]
      SPSSOFederate: SingleSignOnService URL :http://openamstage.managed-connectivity.de:80/login/SSORedirect/metaAlias/idp
      libSAML2:03/09/2017 11:58:14:691 AM MEZ: Thread[http-nio-8080-exec-5,5,main]: TransactionId[60a6a771-5f39-41a6-aad3-1795875ec28b-534]
      ERROR: UtilProxySAMLAuthenticator.authenticate: authn request destination verification failed.

      I am not sure why this error happens.
      I think i used the correct keys and have the correct Public Key in my Service Provider.
      I also checked the Algo and think its correct.

      Do you have an idea why openAM throws this error?

      Delete
    3. I think that the destination field in your AuthnRequest and the one for OpenAM does not match. Your destination is "http://openamstage.managed-connectivity.de/login/SSORedirect/metaAlias/idp". Make sure this is the same as the in the OpenAM metadata.

      Delete
  3. Thx for this tip!
    This was exactly my problem...
    In openAM the destination was defined with the http Port in The URL (http://openamstage.managed-connectivity.de:80/login/SSORedirect/metaAlias/idp). I changed it in des Request and now it works perfectly!

    ReplyDelete
  4. I have an issue with HTTPPostEncoder and signing messages. HTTPRedirectDeflateEncoder works ok, URL has parameter signature and it is recognized by IDP. But when using HTTPPostEncoder, SAMLRequest (as POSTed param) contains only:


    https://yourlearningsaas.mybluemix.net:8443/oid/saml


    urn:oasis:names:tc:SAML:2.0:ac:classes:Password



    no signature is included. Code example:

    private void redirectUserWithRequest(HttpServletResponse httpServletResponse, AuthnRequest authnRequest) {
    MessageContext context = new MessageContext();

    context.setMessage(authnRequest);

    SAMLPeerEntityContext peerEntityContext = context.getSubcontext(SAMLPeerEntityContext.class, true);

    SAMLEndpointContext endpointContext = peerEntityContext.getSubcontext(SAMLEndpointContext.class, true);
    endpointContext.setEndpoint(getIPDEndpoint());

    SignatureSigningParameters signatureSigningParameters = new SignatureSigningParameters();
    signatureSigningParameters.setSigningCredential(spCredential);
    signatureSigningParameters.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);

    context.getSubcontext(SecurityParametersContext.class, true)
    .setSignatureSigningParameters(signatureSigningParameters);

    VelocityEngine velocityEngine = new VelocityEngine();
    velocityEngine.setProperty(RuntimeConstants.ENCODING_DEFAULT, "UTF-8");
    velocityEngine.setProperty(RuntimeConstants.OUTPUT_ENCODING, "UTF-8");
    velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
    velocityEngine.setProperty("classpath.resource.loader.class",
    "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
    velocityEngine.init();
    HTTPPostEncoder encoder = new HTTPPostEncoder();
    encoder.setVelocityEngine(velocityEngine);
    encoder.setMessageContext(context);
    encoder.setHttpServletResponse(httpServletResponse);

    /*
    * HTTPRedirectDeflateEncoder encoder2 = new
    * HTTPRedirectDeflateEncoder();
    *
    * encoder2.setMessageContext(context);
    * encoder2.setHttpServletResponse(httpServletResponse);
    */
    try {
    encoder.initialize();
    } catch (ComponentInitializationException e) {
    throw new RuntimeException(e);
    }

    System.out.println("AuthnRequest: " + authnRequest);
    OpenSAMLUtils.logSAMLObject(authnRequest);

    System.out.println("Redirecting to IDP");
    try {
    encoder.encode();
    } catch (MessageEncodingException e) {
    throw new RuntimeException(e);
    }
    }

    private AuthnRequest buildAuthnRequest() {
    AuthnRequest authnRequest = OpenSAMLUtils.buildSAMLObject(AuthnRequest.class);
    authnRequest.setIssueInstant(new DateTime());
    authnRequest.setDestination(getIPDSSODestination());
    authnRequest.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
    authnRequest.setAssertionConsumerServiceURL(getAssertionConsumerEndpoint());
    authnRequest.setID(OpenSAMLUtils.generateSecureRandomId());
    authnRequest.setIssuer(buildIssuer());
    authnRequest.setNameIDPolicy(buildNameIdPolicy());
    authnRequest.setRequestedAuthnContext(buildRequestedAuthnContext());

    return authnRequest;
    }

    Am I missing anything?

    ReplyDelete
    Replies
    1. Here is what worked:

      private AuthnRequest buildAuthnRequest() {
      AuthnRequest authnRequest = OpenSAMLUtils.buildSAMLObject(AuthnRequest.class);
      authnRequest.setIssueInstant(new DateTime());
      authnRequest.setDestination(getIPDSSODestination());
      authnRequest.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
      authnRequest.setAssertionConsumerServiceURL(getAssertionConsumerEndpoint());
      authnRequest.setID(OpenSAMLUtils.generateSecureRandomId());
      authnRequest.setIssuer(buildIssuer());
      authnRequest.setNameIDPolicy(buildNameIdPolicy());
      authnRequest.setRequestedAuthnContext(buildRequestedAuthnContext());

      Signature signature = OpenSAMLUtils.buildSAMLObject(Signature.class);

      signature.setSigningCredential(spCredential);
      signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
      signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);

      authnRequest.setSignature(signature);

      try {
      XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(authnRequest).marshall(authnRequest);
      } catch (MarshallingException e1) {
      e1.printStackTrace();
      }

      try {
      Signer.signObject(signature);
      } catch (SignatureException e) {
      e.printStackTrace();
      }

      return authnRequest;
      }

      I have to manually sign the object.

      The HTTPRedirectDeflateEncoder is doing signature in other way....

      Delete