Uploaded image for project: 'OpenAM'
  1. OpenAM
  2. OPENAM-12126

SP signature validation fails with identical IdP EntityIDs but differenet key pairs

    Details

    • Type: Bug
    • Status: Open
    • Priority: Major
    • Resolution: Unresolved
    • Affects Version/s: 11.0.0, 11.0.1, 11.0.2, 11.0.3, 12.0.0, 12.0.1, 12.0.2, 12.0.3, 12.0.4, 13.0.0, 13.5.0, 13.5.1, 14.0.0, 14.1.0, 14.1.1, 14.5.0, 14.5.1
    • Fix Version/s: None
    • Component/s: SAML
    • Labels:
    • Environment:
      Mac OS X 10.11.6

      java version "1.7.0_76"
      Java(TM) SE Runtime Environment (build 1.7.0_76-b13)
      Java HotSpot(TM) 64-Bit Server VM (build 24.76-b04, mixed mode)

      Apache Tomcat 8

      OpenAM 13.0.0

      Description

      Bug description

      Signature validation of the SAML assertion fails because the certificate used to validate the signature is cached based on the entityID and role, but not based on the realm.

      How to reproduce the issue

      Details steps outlining how to recreate the issue (remove this text)

      1. Configure OpenAM instance 1 to be used as SP
      2. Created hosted SP in default realm
      3. Create sub-realm ‘test’
      4. Created hosted SP in sub-realm ‘test’
      5. Configure OpenAM instance 2 to be used as IdP
      6. Created two key pairs ‘idp1’ and ‘idp2’ in the default keystore of the 'IdP'
      7. Restart OpenAM deployment container
      8. Created hosted IdP in default realm using key pair ‘idp1’
      9. Created hosted IdP in default sub-realm 'test' using key pair ‘idp1’
      10. Configured the remote SP/IdP entities in the realms of IdP and SP
      11. Perform SP-initiated SSO with SP in sub-realm 'test'; use 'frontchannel bindings' and NameID Format 'transient'
      12. Restart browser, logout or remove the SSO tracking cookies for OpenAM
      13. Perform SP-initiated SSO with SP in default realm; use 'frontchannel bindings' and NameID Format 'transient'
      Expected behaviour
      SP-initiated SSO should lead 'single sign on succeeded'
      
      Current behaviour
      SAML based SSO fails on SP side
      
      excerpt from SP Federation debug log; code OpenAM 13.0.0
      libSAML2:11/21/2017 12:28:45:097 PM CET: Thread[http-nio-8080-exec-10,5,main]: TransactionId[f2dabd42-483e-4f03-a507-959ffeb48837-1147]
      ERROR: spAssertionConsumer.jsp: SSO failed.
      com.sun.identity.saml2.common.SAML2Exception: The signing certificate does not match what's defined in the entity metadata.
              at com.sun.identity.saml2.xmlsig.FMSigProvider.verify(FMSigProvider.java:326)
              at com.sun.identity.saml2.assertion.impl.AssertionImpl.isSignatureValid(AssertionImpl.java:667)
              at com.sun.identity.saml2.common.SAML2Utils.verifyResponse(SAML2Utils.java:549)
              at com.sun.identity.saml2.profile.SPACSUtils.processResponse(SPACSUtils.java:1010)
              at org.apache.jsp.saml2.jsp.spAssertionConsumer_jsp._jspService(spAssertionConsumer_jsp.java:314)
              at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
              at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
              at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:438)
      

      Work around

      Use unique EntityIDs in all realms

      Code analysis (OpenAM 13.0.0)

      com.sun.identity.saml2.common.SAML2Utils.java
      ...
          public static Map verifyResponse(
                  final HttpServletRequest httpRequest,
                  final HttpServletResponse httpResponse,
                  final Response response,
                  final String orgName,
                  final String hostEntityId,
                  final String profileBinding)
                  throws SAML2Exception {
      ....
              if (response.isSigned()) {
                  IDPSSODescriptorElement idpSSODescriptor = null;
                  try {
                      idpSSODescriptor = saml2MetaManager.getIDPSSODescriptor(orgName, idpEntityId);
                  } catch (SAML2MetaException sme) {
                      String[] data = { orgName, idpEntityId };
                      LogUtil.error(Level.INFO, LogUtil.IDP_METADATA_ERROR, data, null);
                      throw new SAML2Exception(sme);
                  }
                  if (idpSSODescriptor != null) {
                      Set<X509Certificate> verificationCerts = KeyUtil.getVerificationCerts(idpSSODescriptor, idpEntityId,
                              SAML2Constants.IDP_ROLE);
                      if (CollectionUtils.isEmpty(verificationCerts) || !response.isSignatureValid(verificationCerts)) {
                          debug.error(method + "Response is not signed or signature is not valid.");
                          String[] data = { orgName, hostEntityId, idpEntityId };
                          LogUtil.error(Level.INFO, LogUtil.POST_RESPONSE_INVALID_SIGNATURE, data, null);
                          throw new SAML2Exception(bundle.getString("invalidSignInResponse"));
                      }
      ....
      
      com.sun.identity.saml2.key.KeyUtil.java
      ...
          public static Set<X509Certificate> getVerificationCerts(RoleDescriptorType roleDescriptor, String entityID,
                  String role) {
              String classMethod = "KeyUtil.getVerificationCerts: ";
      
              // first try to get it from cache
              String index = entityID.trim() + "|" + role;
              Set<X509Certificate> certificates = sigHash.get(index);
              if (certificates != null) {
                  return certificates;
              }
      
              certificates = new LinkedHashSet<>(3);
              // else get it from meta
              if (roleDescriptor == null) {
                  SAML2SDKUtils.debug.error(
                      classMethod+
                      "Null RoleDescriptorType input for entityID=" +
                      entityID + " in "+role+" role."
                  );
                  return null;
              }
              List<KeyDescriptorType> keyDescriptors = getKeyDescriptors(roleDescriptor, SAML2Constants.SIGNING);
              if (keyDescriptors.isEmpty()) {
                  SAML2SDKUtils.debug.error(
                      classMethod+
                      "No signing KeyDescriptor for entityID=" +
                      entityID + " in "+role+" role."
                  );
                  return certificates;
              }
      
              for (KeyDescriptorType keyDescriptor : keyDescriptors) {
                  certificates.add(getCert(keyDescriptor));
              }
              if (certificates.isEmpty()) {
                  SAML2SDKUtils.debug.error(
                      classMethod +
                      "No signing cert for entityID=" +
                      entityID + " in "+role+" role."
                  );
                  return null;
              }
              sigHash.put(index, certificates);
              return certificates;
          }
      

      As the method KeyUtil.getVerificationCerts(....) is used in different use-cases as well other bugs could be most likely.

        Attachments

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              bthalmayr Bernhard Thalmayr
            • Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated: