Monday, January 2, 2012

CXF and MS CRM 2011

Background
Early last year, Microsoft released a new version of their CRM product, Dynamics CRM 2011. My company has a Java product that integrates with MS CRM and we were successfully able to call the webservice that is included with CRM 2011 using SAML tokens. Everything was working great until we reconfigured CRM for on-premise authentication. In the previous version of MS CRM, the on-premise authentication used transport layer NTLM/Kerberos authentication which Java supports. With CRM 2011, however, Microsoft is using WCF 4.0 which uses message level NTLM/Kerberos based on SPNEGO WS-Trust. (Specification) We searched extensively, but could not find an implementation of this protocol for any open source Java Web Service framework. So I along with a coworker set out to implement this for CXF. After several weeks, we were successful and were able to invoke the CRM web service from Java. We submitted this functionality back to the CXF project. (JIRA) Recently, Colm O hEigeartaigh was kind enough to review the patch and integrate it into the trunk of the CXF codebase. (With some major refactoring to make it fit better) This works out of the box with the latest CXF code, but the configuration of this is far from trivial, so this blog post explains how to configure this new functionality.

CXF and MS CRM 2011
The first step in getting this all to work is to setup Java to authenticate using Kerberos with the Active Directory primary domain controller. This requires you to create a login.conf as follows:
spnego-client {
 com.sun.security.auth.module.Krb5LoginModule required;
};

and a krb5.conf as follows:
[libdefaults]
 default_realm = <NTdomain>
 default_tkt_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc
 default_tgs_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc
 permitted_enctypes   = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc

[realms]
 <NTdomain>  = {
  kdc = <PrimaryDomainController>
  default_domain = <NTdomain>
}

[domain_realm]
 .<NTdomain> = <NTdomain>

Then, in my Java code, I manually configure the following the system properies so that JAAS will use these files:
System.setProperty("java.security.auth.login.config", "C:\\projects\\login.conf");
   System.setProperty("java.security.krb5.conf", "C:\\projects\\krb5.conf");

CRM 2011 includes a custom policy element in the WSDL that needs to be asserted in CXF. If this is not done, CXF will error out because it doesn't have a policy provider to assert the custom policy. This custom policy can be asserted using the following classes, the first is the PolicyProvider itself:
public class XRMAuthPolicyProvider extends AbstractPolicyInterceptorProvider
{
 public XRMAuthPolicyProvider()
 {
   super(Arrays
     .asList(new QName[]{new QName("http://schemas.microsoft.com/xrm/2011/Contracts/Services",
       "AuthenticationPolicy", "ms-xrm")}));
   getInInterceptors().add(new XRMAuthPolicyInterceptor());
 }
}

and the second is the associated CXF interceptor:
public class XRMAuthPolicyInterceptor extends AbstractSoapInterceptor
{
 public XRMAuthPolicyInterceptor()
 {
   super(Phase.PRE_PROTOCOL);
   addAfter(PolicyBasedWSS4JInInterceptor.class.getName());
   addAfter(PolicyBasedWSS4JOutInterceptor.class.getName());
 }

 public void handleMessage(SoapMessage message) throws Fault
 {
   AssertionInfoMap aim = message.get(AssertionInfoMap.class);
   if (null == aim)
   {
     return;
   }
   QName qname = new QName("http://schemas.microsoft.com/xrm/2011/Contracts/Services",
     "AuthenticationPolicy", "ms-xrm");
   Collection ais = aim.get(qname);
   if (null == ais || ais.size() == 0)
   {
     return;
   }
   for (AssertionInfo ai : ais)
   {
     ai.setAsserted(true);
   }
 }
}

The policy provider can be added to web service client as follows:
   Client client = ClientProxy.getClient(port);
   Bus bus = ((EndpointImpl) client.getEndpoint()).getBus();
   PolicyInterceptorProviderRegistry pipr = bus
     .getExtension(PolicyInterceptorProviderRegistry.class);
   pipr.register(new XRMAuthPolicyProvider());

Once the policy provider is added, CXF needs to be configured to use Kerberos authentication. For my setup, I was using an Active Directory username and password, so I had to create an implementation of SpnegoClientAction that uses GSSName.NT_USER_NAME rather than the default GSSName.NT_HOSTBASED_SERVICE:
public class XRMSpnegoClientAction implements SpnegoClientAction
{
  private org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
    .getLog(DefaultSpnegoClientAction.class);

  protected String serviceName;
  protected GSSContext secContext;
  protected boolean mutualAuth;

  /**
   * Whether to enable mutual authentication or not.
   */
  public void setMutualAuth(boolean mutualAuthentication)
  {
    mutualAuth = mutualAuthentication;
  }

  /**
   * The Service Name
   */
  public void setServiceName(String serviceName)
  {
    this.serviceName = serviceName;
  }

  /**
   * Obtain a service ticket
   */
  public byte[] run()
  {
    try
    {
      GSSManager gssManager = GSSManager.getInstance();
      Oid oid = new Oid("1.3.6.1.5.5.2");

      GSSName gssService = gssManager.createName(serviceName, GSSName.NT_USER_NAME);
      secContext = gssManager.createContext(gssService, oid, null, GSSContext.DEFAULT_LIFETIME);

      secContext.requestMutualAuth(mutualAuth);
      secContext.requestCredDeleg(Boolean.FALSE);

      byte[] token = new byte[0];
      return secContext.initSecContext(token, 0, token.length);
    }
    catch (GSSException e)
    {
      if (log.isDebugEnabled())
      {
        log.debug("Error in obtaining a Kerberos token", e);
      }
    }

    return null;
  }

  /**
   * Get the GSSContext that was created after a service ticket was obtained
   */
  public GSSContext getContext()
  {
    return secContext;
  }
}

Then I added the following code to hook Kerberos into my web service client:
   // Active Directory domain username and password
   final String username = "<username>";
   final String password = "<password>";
   // Kerberos service provider name, e.g. RestrictedKrbHost/<computername>
   String spn = "RestrictedKrbHost/<crm_server>";
   // Kerberos JAAS client as configured in login.conf
   String jaasClient = "spnego-client";
   // Active Directory username and password
   CallbackHandler callbackHandler = new NamePasswordCallbackHandler(username, password);

   client.getRequestContext().put("ws-security.kerberos.jaas.context", jaasClient);
   client.getRequestContext().put("ws-security.kerberos.spn", spn);
   client.getRequestContext().put("ws-security.callback-handler", callbackHandler);
   client.getRequestContext().put("ws-security.spnego.client.action", new XRMSpnegoClientAction());

Once the custom policy provider is added and the Kerberos authentication is setup, the client configuration is complete.

For reference, the complete client code example is below:
   URL wsdlURL = OrganizationService.WSDL_LOCATION;

   System.setProperty("java.security.auth.login.config", "C:\\projects\\login.conf");
   System.setProperty("java.security.krb5.conf", "C:\\projects\\krb5.conf");

   // Active Directory domain username and password
   final String username = "<username>";
   final String password = "<password>";
   // Kerberos service provider name, e.g. RestrictedKrbHost/<computername>
   String spn = "RestrictedKrbHost/<crm_server>";
   // Kerberos JAAS client as configured in login.conf
   String jaasClient = "spnego-client";
   // Active Directory username and password
   CallbackHandler callbackHandler = new NamePasswordCallbackHandler(username, password);

   OrganizationService ss = new OrganizationService(wsdlURL, SERVICE_NAME);
   IOrganizationService port = ss.getCustomBindingIOrganizationService();

   Client client = ClientProxy.getClient(port);
   Bus bus = ((EndpointImpl) client.getEndpoint()).getBus();
   PolicyInterceptorProviderRegistry pipr = bus
     .getExtension(PolicyInterceptorProviderRegistry.class);
   pipr.register(new XRMAuthPolicyProvider());

   client.getRequestContext().put("ws-security.kerberos.jaas.context", jaasClient);
   client.getRequestContext().put("ws-security.kerberos.spn", spn);
   client.getRequestContext().put("ws-security.callback-handler", callbackHandler);
   client.getRequestContext().put("ws-security.spnego.client.action", new XRMSpnegoClientAction());

   // call webservice
   ColumnSet colSet = new ColumnSet();
   colSet.setAllColumns(true);
   colSet.setColumns(new ArrayOfstring());
   QueryByAttribute qba = new QueryByAttribute();
   qba.setEntityName("account");
   qba.setColumnSet(colSet);
   ArrayOfstring aos2 = new ArrayOfstring();
   aos2.getString().add("accountid");
   qba.setAttributes(aos2);
   ArrayOfanyType aoat = new ArrayOfanyType();
   aoat.getAnyType().add("2D08FEC1-9734-E111-9454-000C295D5DEA");
   qba.setValues(aoat);
   EntityCollection entityCol = port.retrieveMultiple(qba);
   List props = entityCol.getEntities().getEntity().get(0)
     .getAttributes().getKeyValuePairOfstringanyType();
   for (KeyValuePairOfstringanyType prop : props)
   {
     if ("name".equals(prop.getKey()))
     {
       System.out.println(prop.getKey() + " = " + prop.getValue());
     }
   }

Since our team spent several weeks working on this example, I hope this walkthrough is helpful for other developers out there. This will not only work for CRM 2011, but any WCF webservice that uses message level NTLM/Kerberos authentication.

**Update (2013-08-26)** With newer versions of CXF (tested against CXF 2.6.9) it is necessary to include a bindings file when generating the client code using wsdl2java:
<jaxb:bindings version="2.1"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jaxb:globalBindings generateElementProperty="false" mapSimpleTypeDef="true"/>
</jaxb:bindings>

99 comments:

  1. Tom, could you provide more information about your IIS configuration. I acquired TGT using AS, and -commit succeeded.
    Then i have such exception:
    org.apache.cxf.interceptor.Fault: General security error (An error occurred in trying to obtain a service ticket)

    Thanks!

    ReplyDelete
    Replies
    1. Hi Dimitry, have you solved this problem? We're facing the same.

      Delete
  2. Sadly, I was not involved in setting up the MS CRM instance. That was handled by our netops team, so I don't have access to those details.

    ReplyDelete
    Replies
    1. Hurray!!! Successfully connected to MS CRM on premise. This is a great article...very helpful. Thanks Tom!

      Delete
  3. Hi tom,
    in your class XRMSpnegoClientAction you implements SpnegoClientAction but in wss4j SpnegoClientaction is a claas

    Can you help me to resolve the problem ?

    Thanks

    ReplyDelete
  4. You need the very latest trunk code for wss4j. If you look at trunk, it is an interface in the latest version:

    http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/main/java/org/apache/ws/security/spnego/SpnegoClientAction.java?revision=1227128&view=markup

    This is all very bleeding edge, so you'll need the trunk versions of both CXF and wss4j.

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Thank you, Tom!
    Nice work, helpful message.
    But unfortunately I've faced a problem:
    my (ColumnSet) colSet variable has not a method setEntityName(String), but a setEntityName(JAXBElement&<String>) instead.
    It concerns also several analogous setters.
    Wat's wrong?
    I've generated from wsdl using cxf library.
    Thank you.

    ReplyDelete
  7. Ok, I've fixed previous type mismatch problem by creating corresponding JAXBElement<T> type variables instead of T type
    using values of @XmlElementRef notations in corresponding classes of package com.microsoft.schemas.xrm._2011.contracts.
    But when running the sample, I've faced with error at the earliest stages of it.
    Namely, at the line where IOrganizationService port is being obtained (it's just line 47 in my ServiceTest.java):
    IOrganizationService port = ss.getCustomBindingIOrganizationService()
    the exception appears:
    -----------
    java.lang.NoSuchFieldError: QUALIFIED
    at org.apache.cxf.service.model.SchemaInfo.setSchema(SchemaInfo.java:146)
    at org.apache.cxf.wsdl11.SchemaUtil.extractSchema(SchemaUtil.java:136)
    at org.apache.cxf.wsdl11.SchemaUtil.getSchemas(SchemaUtil.java:81)
    at org.apache.cxf.wsdl11.SchemaUtil.getSchemas(SchemaUtil.java:65)
    ............
    at org.apache.cxf.jaxws.ServiceImpl.createPort(ServiceImpl.java:465)
    at org.apache.cxf.jaxws.ServiceImpl.getPort(ServiceImpl.java:332)
    at org.apache.cxf.jaxws.ServiceImpl.getPort(ServiceImpl.java:319)
    at javax.xml.ws.Service.getPort(Service.java:92)
    at com.microsoft.schemas.xrm._2011.contracts.OrganizationService.getCustomBindingIOrganizationService(OrganizationService.java:65)
    at com.mypackage.msdynamics.onpremise.ServiceTest.run(ServiceTest.java:47)
    -----------
    Can anyone help me and explain what goes wrong?
    Thank you.

    ReplyDelete
  8. Double check your JAXB version. The JAXB version used with the latest version of CXF is newer than what comes with the JDK 6. It takes some effort to get it all working because of this. (The JAXB jars have to be 'endorsed' to override what comes with the JDK) If that doesn't resolve the issue, it may help to look at the cxf source at the line in error--it might give you some ideas on what's wrong. This is all very experimental, so you definitely have to get your hands dirty to get it all working.

    ReplyDelete
  9. Tom, thank you.
    At least now I know where to start.

    ReplyDelete
  10. Hi, Tom.
    Thanks to your previous advice, I've solved my problem with type inconsistency and simultaneously have overcome some another problems with inappropriate libraries and cast type errors.
    But I'm stacking now because of null value of the pipr variable: my bus is occurred not to have an extension of type PolicyInterceptorProviderRegistry:

    PolicyInterceptorProviderRegistry pipr = bus
    .getExtension(PolicyInterceptorProviderRegistry.class); // -----> null

    Can you suppose, please, what's wrong?
    Any idea may be helpful.
    Thank you.

    ReplyDelete
  11. Hmm, not sure. I was always able to get the PolicyInterceptorProviderRegistry without issue. It might be a new change with the latest code or something might not be getting initialized properly. You'll have to debug through the code to see where that gets initialized in CXF--I'm not familiar with that part of the code.

    ReplyDelete
  12. Looks like CXF didn't like the response from CRM. What version of CRM are you using? Also, you might want to compare the response to what CXF is expecting. (It wouldn't surprise me is there are valid responses that CXF isn't handling properly)

    ReplyDelete
  13. hi Tom!
    We at our company are looking to integrate the CRM 2011 webservice from a java application. We do not have the on-premise authentication setup because this is not needed.

    Can you give me some more information on how you managed to communicate with the webservice using SAML tokens?
    thank you very much!!

    ReplyDelete
  14. Hi Tom,

    I have the same problem at the same point as jja77 =/

    This is where it goes wrong according to the logs :

    FEIN: Invoking handleMessage on interceptor org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor@6ec44aaf
    11.10.2012 20:40:56 org.apache.cxf.phase.PhaseInterceptorChain doIntercept
    FEIN: Invoking handleMessage on interceptor org.apache.cxf.interceptor.URIMappingInterceptor@2c91e143
    11.10.2012 20:40:56 org.apache.cxf.interceptor.URIMappingInterceptor handleMessage
    FEIN: Invoking HTTP method null
    11.10.2012 20:40:56 org.apache.cxf.interceptor.URIMappingInterceptor handleMessage
    FEIN: URIMappingInterceptor can only handle HTTP GET, not HTTP null
    11.10.2012 20:40:56 org.apache.cxf.phase.PhaseInterceptorChain doIntercept
    FEIN: Invoking handleMessage on interceptor org.apache.cxf.interceptor.DocLiteralInInterceptor@2d83e895
    11.10.2012 20:40:56 org.apache.cxf.phase.PhaseInterceptorChain unwind
    FEIN: Invoking handleFault on interceptor org.apache.cxf.interceptor.DocLiteralInInterceptor@2d83e895
    11.10.2012 20:40:56 org.apache.cxf.phase.PhaseInterceptorChain unwind

    any thoughts on this?

    ReplyDelete
  15. I really have no idea. I don't think we ran into anything like that. I don't have access to the CRM environments anymore, did you look at the request/response in Fiddler? That might give you some idea about what's going on. (Or turning up the logging for CXF to see if there's any clues there)

    ReplyDelete
  16. Hi Tom,

    just FYI the problem seems to be this:

    org.apache.cxf.interceptor.Fault: Unexpected element {http://schemas.xmlsoap.org/ws/2005/02/trust}RequestSecurityTokenResponseCollection found. Expected {http://schemas.xmlsoap.org/ws/2005/02/trust}RequestSecurityTokenResponse.

    now I looked at the schema at http://schemas.xmlsoap.org/ws/2005/02/trust
    for ws-trust.

    There he seems to find this Collection of responses:


    instead of just a singular response.

    Any thoughts on this =/?

    ReplyDelete
  17. Somehow i cannot post the xml here..... =/

    so I´ll put it in words.

    At the http://schemas.xmlsoap.org/ws/2005/02/trust xml he finds a RequestSecurityTokenResponseCollection element instead of a singular RequestSecurityTokenResponse element.

    ReplyDelete
  18. I would take this issue up with the CXF user list. Seems like either CXF isn't doing something correctly or there might be more configuration needed. I haven't looked at the code recently enough to be of any help.

    ReplyDelete
  19. Hi Tom,
    maybe I can get your input on our login.conf file.
    This is what we use:

    spnego-client {
    com.sun.security.auth.module.Krb5LoginModule
    required
    useSubjectCredsOnly=false
    useTicketCache=true
    doNotPrompt=true;
    };

    What´s your take on this?

    ReplyDelete
  20. Here's what I used--you'll have to substitute your values for your windows domain.

    login.conf:
    spnego-client {
    com.sun.security.auth.module.Krb5LoginModule required;
    };

    krb5.conf (if EXAMPLE.COM is the windows domain suffix):
    [libdefaults]
    default_realm = EXAMPLE.COM
    default_tkt_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc
    default_tgs_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc
    permitted_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc

    [realms]
    EXAMPLE.COM = {
    kdc =
    default_domain = EXAMPLE.COM
    }

    [domain_realm]
    .EXAMPLE.COM = EXAMPLE.COM

    We forced these files to be used by explicitly setting the properties in our client code:
    System.setProperty("java.security.auth.login.config", "C:\\projects\\login.conf");
    System.setProperty("java.security.krb5.conf", "C:\\projects\\krb5.conf");

    ReplyDelete
  21. Sorry the realms section above was mangled by the filters:
    [realms]
    EXAMPLE.COM = {
    kdc = <WINDOWS_DOMAIN_CONTROLLER>
    default_domain = EXAMPLE.COM
    }

    ReplyDelete
  22. Hmm ... we have the same... =/

    Crm 2011 is working with WS-Trust 1.3 right?

    ReplyDelete
  23. I just tried this with the latest CXF (2.7.0) and I get the same exception as you:

    Caused by: org.apache.cxf.interceptor.Fault: Unexpected element {http://schemas.xmlsoap.org/ws/2005/02/trust}RequestSecurityTokenResponseCollection found. Expected {http://schemas.xmlsoap.org/ws/2005/02/trust}RequestSecurityTokenResponse.

    Looks like some of the changes to the STSClient didn't make it in or aren't working correctly. It would be best to submit this as a bug to CXF itself. Here's a link to the original SPNEGO patch:
    https://issues.apache.org/jira/browse/CXF-3635

    ReplyDelete
  24. Hi Tom,

    thanks for the patch info.
    We have implemented it and seem to have gotten a little further, at least as far as my understanding is concerned^^.

    But I cannot find the apache cxf version that you seemed to have been working with.

    I have tried many and always received a different build error.
    I could build it with version 2.3.8 and some own changes to the code.

    What version did you use?

    ReplyDelete
  25. The code seems to work out of the box with cxf 3.5.0

    But I have received following error :

    None of the policy alternatives can be satisfied.
    at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:146)
    at $Proxy45.execute(Unknown Source)
    at com.example.spnego.Main.main(Main.java:121)

    I assume the SpnegoSTSClient who builds the XML to request the SecurityToken is not able to match crm´s policy?

    ReplyDelete
  26. Where did you get CXF 3.5.0? (typo maybe?) The latest version I see on the download page is 2.7.0: http://cxf.apache.org/download.html

    ReplyDelete
  27. Oh sorry,

    indeed a typo^^.
    I meant cxf 2.3.5

    Does this scenario also work for AD-authentication without sts?


    ReplyDelete
  28. Jair, this is AD-authentication without sts. The STSClient is used to negotiate SPNEGO directly with the endpoint. We're just reusing a lot of the code in the STSClient because the requests/responses are the same as talking to an STS.

    ReplyDelete
  29. OK,

    do you have any idea why I receive the policy exception =/?

    ReplyDelete
  30. Are you sure you have the policty provider registered?

    pipr.register(new XRMAuthPolicyProvider());

    If the policy provider isn't there, I get the same exception.

    ReplyDelete
  31. I was finally able to get this working using CXF 2.7.0. I had to add the following to disable the additional validation that's happening with the new version of CXF. (There might be an easier way to set this property, but this is the only way I found to get it to work)

    STSClient sts = new STSClient(bus);
    sts.setFeatures(Arrays.asList(new Feature()
    {
    @Override
    public void initialize(Server server, Bus bus)
    {
    }
    @Override
    public void initialize(Client client, Bus bus)
    {
    ServiceInfo si = client.getEndpoint().getEndpointInfo().getService();
    si.setProperty("soap.force.doclit.bare", true);
    }
    @Override
    public void initialize(InterceptorProvider interceptorProvider, Bus bus)
    {
    }

    @Override
    public void initialize(Bus bus)
    {
    }
    }));
    client.getRequestContext().put("ws-security.sts.client", sts);

    ReplyDelete
    Replies
    1. You could also do the following instead:

      public void initialize(Client client, Bus bus) {
      bus.getProperties().put("soap.no.validate.parts", true);
      }

      I prefer it because the property name is more explicit in what you are try to achieve.

      Delete
  32. Hi Tom, this indeed solved the issue RSTR Collection in cxf 2.7.0 =)
    does this only return the second leg in spnego ? Or why is the response suddenly not a collection anymore.

    My problems don´t seem to come to an end though. now that I applied your fix for the RSTR-C issue i get this error :

    org.apache.cxf.ws.policy.PolicyException: Cannot encrypt data
    at org.apache.cxf.ws.security.wss4j.policyhandlers.AbstractBindingBuilder.policyNotAsserted(AbstractBindingBuilder.java:290)

    Is this a password encryption problem?

    P.S: Sorry to keep bugging you with my problems.
    I am kind of new to the whole Java to CRM thing and you are the only one who could give me any good answers so far.

    ReplyDelete
  33. Jair, the response is still a RSTR Collection, it just skips the extra validation that is throwing the exception. I truly think this is a bug and it should expect both a RSTR or a RSTR Collection, but it would take more investigation to prove that.

    I don't think it's a password encryption problem. I think you need to install the Java Cryptography Extensions Unlimited Strength Jurisdiction Policy for the JRE/JDK you're using. CRM requires stronger encryption than what the JRE/JDK can do by default.

    ReplyDelete
  34. Hi Tom,

    thanks for the information.

    Regarding the Cryptography library i found the following:

    if i don´t add the Cryptography Extensions i get this error in the log:

    java.lang.ClassNotFoundException: org.bouncycastle.jce.provider.BouncyCastleProvider

    If i copy the Cryptography extensions jar files into my jdk\lib folder the above exception vanishes, but my problem still persists in the same way.

    i use jre6 and the respective Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 6

    ReplyDelete
  35. Try removing the bouncy castle jars from your classpath. The newer JDK's come with a JCE provider, so bouncy castle shouldn't be necessary. (And it seems like bouncy castle impl is not unlimited strength which might be why it's still failing for you)

    ReplyDelete
  36. Hi Tom,
    I had aleady removed the bouncy castle jars when I added the Cryptography extensions to my runtime lib folder.

    this is what my class path looks like:
    (I ad to remove the ">" and "<" because of the filters =/)

    xml version="1.0" encoding="UTF-8"
    classpath
    classpathentry kind="src" path=""/
    classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/
    classpathentry kind="con" path="org.eclipse.jst.ws.cxf.core.CXF_CLASSPATH_CONTAINER/Apache CXF/2.7.0"
    attributes
    attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/
    /attributes
    /classpathentry
    classpathentry kind="lib" path="C:/Apps/Dev/apache-cxf-2.7.0/bin"/
    classpathentry kind="output" path=""/
    /classpath"

    is there any way to force cxf to use the cryptography extensions?

    ReplyDelete
  37. Btw,

    does this encryption problem have anything to do with using Using X.509 Certificates?

    Because in our scenario there is no certificate involved. I was reading the
    http://cxf.apache.org/docs/ws-security.html documentation and could only find information on encryption regarding certificates.

    Do I have to set some property in cxf on false so it doesn´t try to use a certificate, or does cxf have a fallback mechanism for this case?

    ReplyDelete
  38. To me the error resembles this one a lot:

    https://jira.talendforge.org/browse/TESB-3768?page=com.atlassian.jira.plugin.system.issuetabpanels%3Achangehistory-tabpanel

    ReplyDelete
  39. Jair, it's hard to say what might be causing it since I can't reproduce this. It's working for me and we've checked all the obvious stuff. Your best bet is to grab the source code for CXF and debug through this issue to see where it's getting hung up. There might be a root cause to the exception or you might find more information about what's going wrong.

    Another thing you might want to try is to create a stand-alone Java program rather than running this within a war project. That's what I typically do to get it working first before I integrate it into a project. It's easier to debug and quicker to try different things.

    ReplyDelete
    Replies
    1. Hi Tom,
      thanks for your answer on a saturday =). I will look into the cxf code and try debug it to find the root cause for my exception.

      I am thinking I might be doing something wrong outside of the cxf code. Can you maybe post an examlple of the OrganizationRequest you sent to your crm server to test the connection?

      Because i just send a WhoAmI request like this:
      URL wsdlURL = OrganizationService.WSDL_LOCATION;
      OrganizationService ss = new OrganizationService(wsdlURL);
      IOrganizationService port = ss.getCustomBindingIOrganizationService();
      OrganizationRequest tmp = new OrganizationRequest();
      tmp.setRequestName("WhoAmI");
      port.execute(tmp);

      Delete
  40. Hi Tom,

    I have tried to get the cxf source code running,
    but many classes are missing in the svn trunk.
    Like org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor;
    and
    org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;

    Are those classes located elsewhere?

    ReplyDelete
  41. Those are part of wss4j, a separate project:
    http://ws.apache.org/wss4j/

    ReplyDelete
  42. Hi Tom,

    i assume you have already once imported the cxf lib into eclipse. On the cxf website it says, that when you checkout the trunk from svn at
    http://svn.apache.org/repos/asf/cxf/trunk ,
    you can import all the subprojects into eclipse.
    My eclipse doesn´t find any projects inside the whole trunk =/

    Am I missing something here? Because i tried to import the cxf.apache* packages manually, but the load of errors make it practically impossible to get the code running this way.

    ReplyDelete
  43. Hi Tom,

    just ignore my last question. I have solved the encryption issue.

    I now get an unmarshalling error because of

    WARNING: Interceptor for {http://schemas.microsoft.com/xrm/2011/Contracts}OrganizationService#{http://schemas.microsoft.com/xrm/2011/Contracts/Services}Execute has thrown exception, unwinding now
    org.apache.cxf.interceptor.Fault: Unmarshalling Error: unknown typename: {http://schemas.microsoft.com/2003/10/Serialization/}guid

    It seems like I am an unlucky guy :D

    ReplyDelete
    Replies
    1. Hi Jair,

      I too am facing the encryption issue. Are you able to share how you went about solving the problem?

      Thanks (And also thanks to Tom for this very useful article!)

      Delete
    2. Hi Matthew,

      Sorry for my late answer here.
      If you are facing the encryption issue, you will have to install the "Java Cryptographic Extensions unlimited strength" for the JRE you are using.
      In this case installing means replacing the "local_policy.jar" and "US_export_policy.jar" in the security folder of your JRE with the ones delivered with the JCE Unlimited strength package.

      After that you should be aware, that if the problem still persists - although you replaced the JCE with JCE Unl. strength - the organization request that you use is probably faulty.

      Please let me know if this solved your problem

      Delete
  44. When I look at the
    http://schemas.microsoft.com/2003/1/Serialization/
    schema, there is indeed no element called "guid".

    how did you work around this issue?
    Did you bind guid to String somehow?

    ReplyDelete
  45. I have this in my Object factory in the respective schema:

    private final static QName _Guid_QNAME = new QName("http://schemas.microsoft.com/2003/10/Serialization/", "guid");

    ReplyDelete
  46. Here's an example request that's working for me:
    ColumnSet columnSet = new ColumnSet();
    columnSet.setAllColumns(true);
    columnSet.setColumns(new ArrayOfstring());
    Guid guid = new Guid();
    guid.setValue("{GUID_AS_STRING}"); // replace with your guid
    Entity entity = port.retrieve("account", guid, columnSet);

    ReplyDelete
  47. Hi Tom,

    when I generate my classes from the organization wsdl, it doesn´t generate the Class Guid you seem to have. I assume you used a binding file. when u generated the wsdl classes, to bind the jaxb components. Can you maybe post your bindings.txt?

    ReplyDelete
  48. Hi Tom,

    I have found the problem.
    Somehow when i create my java classes from the organization wsdl, Guid and other classes aren´t built. But the unmarshaller can only unmarshall through the object factory of com.microsoft.schemas._2003._10_serialization, which should refer to classes like "Guid" "Duration" etc. But it binds these classes to String =/. Is this a JAXB Binding issue? how does your binding.txt look?

    ReplyDelete
  49. I don't remember doing anything special when generating the java classes from the wsdl. (no binding.txt needed) Are you sure you're using the correct wsdl?

    ReplyDelete
  50. I'm actually having the same problem as Jair, no Guid class was generated. Have you already solved this issue? I generated the classes using this wsdl urls:

    wsdl2java -client -b javabindings.xml http://localhost:5555/Demo/XRMServices/2011/Organization.svc?wsdl

    The "Demo" part in the URL is the name of my CRM-organization.
    I had to use a binding file in order to avoid JAXBElement<String> bindings. The binding file I used is the following:


    <jaxb:bindings version="2.1"
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <jaxb:globalBindings generateElementProperty="false"/>
    </jaxb:bindings>

    ReplyDelete
  51. I couldn't get the wsdl2java to generate those classes
    either. At the end I just wrote the classes I needed myself, what isn't difficult. Just define your class with the right name and specify a Return method method with the data type you need. You can take a look at the generated Array type classes for a draft

    ReplyDelete
  52. Hello Tom,

    I'm currently building an integration to Microsoft CRM using java, i'm having lots of problems. Can you point me to any documentation about the webservices classes that are generated from the wsdl2java?
    At this point in time, i can not rewrite in java this lines of C#

    Owner owner = new Owner();
    owner.type = EntityName.systemuser.ToString();
    owner.Value = user.UserId;

    Can you help me?

    thx

    ReplyDelete
  53. I have an example CRM call in this very blog, check the 2nd half of the last code block. We didn't do much with owner, our work was more around the Contact, Quote and Opportunity entities.

    ReplyDelete
  54. Hi Miguel,

    to change owner you have to fire an Organization Request with RequestName="Assign" and two Parameters: Assignee and Target, both EntityReferences

    ReplyDelete
  55. Thx Tom,

    I've solved my problem using an entity reference to the "systemuser" entity.

    Do you have any kind of documentation about the java classes generated by the wsdl2java?

    regards

    ReplyDelete
  56. Not really. Microsoft has documentation on their webservices, but it will be generic and not specific to what the jaxb generated classes. That's the closest you'll find, but you have to translate it a bit to get it to work with the wsdl2java generated classes.

    ReplyDelete
  57. Hi Tom,

    thank you for this blog! I am now able to access the webservie without problems. A hint for anybody trying to access the CRM 2011 Webservice with SSL enabled is to use the new CXF Version 2.7.3 as documented here https://issues.apache.org/jira/browse/CXF-4758

    ReplyDelete
  58. Hi Tom and the rest,

    I have been struggling with this for quite some time and have managed to get it working somehow. When I provide a valid request, e.g. a Retrieve with an Id that exists, I do get a valid response. However if I change that retrieve using an id that does not exist I get an error. The service is returning a SOAP Fault as far as I understand, at least it is an HTTP 500 message, but the client fails to handle the SoapFault.

    Here are some of the error messages:
    org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor handleMessage
    WARNING:
    org.apache.ws.security.WSSecurityException: The signature or decryption was invalid
    at org.apache.ws.security.processor.SignatureProcessor.verifyXMLSignature(SignatureProcessor.java:450)

    ...

    Caused by: org.apache.ws.security.WSSecurityException: The signature or decryption was invalid
    at org.apache.ws.security.processor.SignatureProcessor.verifyXMLSignature(SignatureProcessor.java:450)
    at org.apache.ws.security.processor.SignatureProcessor.handleToken(SignatureProcessor.java:231)
    at org.apache.ws.security.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:396)
    at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:274)
    ... 19 more
    javax.xml.ws.soap.SOAPFaultException: The signature or decryption was invalid
    at org.apache.cxf.jaxws.DispatchImpl.mapException(DispatchImpl.java:287)
    at org.apache.cxf.jaxws.DispatchImpl.invoke(DispatchImpl.java:392)
    at org.apache.cxf.jaxws.DispatchImpl.invoke(DispatchImpl.java:243)

    ...

    Caused by: org.apache.cxf.binding.soap.SoapFault: The signature or decryption was invalid
    at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.createSoapFault(WSS4JInInterceptor.java:760)
    at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:331)
    at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:93)


    I have Googled this and found a some references to this specifying that it might be a bug in wss4j or xmlsec. I am using wss4j-1.6.9.jar and xmlsec-1.5.4.jar.

    I see that Jair mentions a similar error message above and suggests that providing a correct request will fix it.
    My request is valid, the only difference between a successful response and an unsuccessful is that I provide an Id that does not exist in CRM.
    The client should be able to handle a SoapFault.

    Any ideas?

    Regards
    Ørjan

    ReplyDelete
    Replies
    1. Hi Ørjan,

      i have experienced your problem too. I think the problem is, that CRM does not encrypt its response if it is a fault. My solution was to install a FaultInterceptor which gets the original (unencrypted) SOAP-Message, extracts the fault cause, and creates a new SoapFault containing the real reason of fault.

      This is only a workaround, but at least it returns senseful information.

      public class CRMFaultInterceptor extends AbstractSoapInterceptor {

      public CRMFaultInterceptor() {
      super(Phase.PRE_PROTOCOL);

      addBefore(PolicyBasedWSS4JInInterceptor.class.getName());

      @Override
      public void handleMessage(SoapMessage arg0) throws Fault {

      }

      @Override
      public void handleFault(SoapMessage m) {
      XMLStreamReader reader = m.getContent(XMLStreamReader.class);
      W3CDOMStreamReader w3r = (W3CDOMStreamReader) reader;
      String errorMessage = null;
      if (w3r != null) {
      try {
      while (w3r.hasNext()) {
      w3r.next();
      if (w3r.getCurrentNode().getLocalName().equals("Body")) {
      NodeList l = w3r.getCurrentNode().getChildNodes();
      for (int i = 0; i < l.getLength(); i++) {
      Node n = l.item(i);
      if (n.getLocalName().equals("Fault")) {
      NodeList l1 = n.getChildNodes();
      for (int j = 0; j < l1.getLength(); j++) {
      Node n1 = l1.item(j);
      if (n1.getLocalName().equals("Reason")) {
      NodeList l2 = n1.getChildNodes();
      for (int k = 0; k < l2.getLength(); k++) {
      Node n2 = l2.item(k);
      if (n2.getLocalName()
      .equals("Text")) {
      errorMessage = n2
      .getTextContent();

      }
      }
      }

      }
      }
      }
      }

      }

      if (errorMessage != null) {

      Exception e = m.getContent(Exception.class);

      if (e instanceof SoapFault) {
      SoapFault fault = (SoapFault) e;

      SoapFault newFault = new SoapFault(errorMessage, fault,
      fault.getFaultCode());
      m.setContent(Exception.class, newFault);
      }

      }

      } catch (XMLStreamException e) {
      e.printStackTrace();
      }
      }

      }

      Delete
    2. Sorry, while copying i lost two brackets, one after the constructor and one at the very end.

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

      Delete
    4. Hi Thomas, Where do we place this interceptor at?

      Delete
    5. Nevermind, at this point I'm stuck at where Ørjan and you were at: Caused by: org.apache.ws.security.WSSecurityException: The signature or decryption was invalid. Were you able to get through this?

      Delete
    6. Did you able to solve "The signature or decryption was invalid" issue?

      Delete
  59. The response I get is encrypted. That's why I'm assuming it is a SOAP fault since it is a http 500 and I'm not able to un-encrypt it.

    ReplyDelete
  60. Hi again,

    I've debugged this a bit further by enabling logging in CXF.
    It fails while validating the signature of the body element in the response, below is an excerpt from the log. It doesn't matter what the actual error from CRM is, it fails as long as it is a Fault.

    11.apr.2013 11:02:47 org.apache.jcp.xml.dsig.internal.DigesterOutputStream write
    FINE: <s:Body xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" u:Id="_2"><env:Fault><Code xmlns="http://www.w3.org/2003/05/soap-envelope"><Value>Sender</Value><Subcode><Value>a:DeserializationFailed</Value></Subcode></Code><Reason xmlns="http://www.w3.org/2003/05/soap-envelope"><Text xml:lang="nb-NO">The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://schemas.microsoft.com/xrm/2011/Contracts/Services:id. The InnerException message was 'There was an error deserializing the object of type System.Guid. The value 'F9D6E5AE-2032-E211-88C7-005056004C9X' cannot be parsed as the type 'Guid'.'. Please see InnerException for more details.</Text></Reason></env:Fault></s:Body>
    11.apr.2013 11:02:47 org.apache.jcp.xml.dsig.internal.dom.DOMReference validate
    FINE: Expected digest: UXn4DGytwBUfvXX2naORgG76jvk=
    11.apr.2013 11:02:47 org.apache.jcp.xml.dsig.internal.dom.DOMReference validate
    FINE: Actual digest: NzKaDWYL/OZ7mkGXYLtwVh4KMXk=
    11.apr.2013 11:02:47 org.apache.jcp.xml.dsig.internal.dom.DOMXMLSignature validate
    FINE: Reference[#_2] is valid: false

    I haven't validated that the message/signature from CRM is correct, but I would assume that it is correct and that it is CFX, wss4j or xmlsec which somehow messes this up?
    I have updated my client using the latest CXF (2.7.4), wss4j (1.6.10) and xmlsec (1.5.4).

    Regards
    Ørjan

    ReplyDelete
  61. Hi Tom,

    This is actually brilliant, one question though:

    All these really look liked you are actually using Kerberos auth in the end.

    So my question: is this ok or is there an actual way to enforce NTLM authentication?

    What I don't understand is that at the beginning of your post you talk about NTLM/KERBEROS but then the code seems to use only kerberos auth.

    Thanks in advance

    ReplyDelete
  62. We tried, but were never able to get NTLM working. Only Kerberos is works with this setup.

    ReplyDelete
  63. Hi Tom,

    Thank you for the blog and I appreciate all the comments. The information has helped me immensely. I'm now at the same point as Thomas and Jair were in that I'm receiving an unmarshalling error because wsdl2java did not create the Guid class.

    I'm using the same binding file as Thomas was above to avoid JAXBElement bindings, however it does not generate all of the classes.

    Do you remember what syntax you used with wsdl2java? Also did Jair ever share the sample Guid class? I'm using cxf2.7.6 and CRM 2011 rollup 14 if that makes any difference.

    Thanks,

    Brennan

    ReplyDelete
  64. Brennan,
    I'm having the same issue with CXF 2.6.9 and CRM 2011 rollup 11. The generated JAXB classes are definitely different in the newer version of CXF. (or the MS schemas have changes)
    Tom

    ReplyDelete
  65. Tom,

    Thanks for trying. I've created a new MSCRM 2011 instance without the roll-up's and I will try and earlier version of CXF to see if that makes a difference.

    I did try with xmlbeans binding and that generated all of the classes, but xmlbeans is definitely not as easy to work with as jaxb.

    Thanks,

    Brennan

    ReplyDelete
  66. Tom,

    I tried xmlbeans and I decided to spend more time trouble shooting the jaxb binding issue. I looked at the MSCRM wsdl and noticed that the classes were not being generated for simple types. I found an article that mentions that unless simple types are enumerations, they are not generated into java classes. The article also mentions how to create a binding file with a flag to generate the classes. I tried the flag and all of the classes are now being generated for me and this is against CRM 2011 rollup 14 and cxf 2.7.6.

    Link to generating Java classes for simple types:
    http://fusesource.com/docs/esb/4.2/jaxws/JAXWSCustomTypeMappingSimple.html

    Sample bindings file:




    Hopefully this helps you and others that are reading your blog.

    Thanks,

    Brennan

    ReplyDelete
  67. Its looks like my bindings xml was removed. Basically you need to create a bindings file which has flag mapSimpleTypeDef and that flag needs to be set to "true". Below is the line that I have in my bindings which generates all classes and also addresses the JAXBElement issue.

    jaxb:globalBindings generateElementProperty="false" mapSimpleTypeDef="true"

    Thanks,

    Brennan

    ReplyDelete
  68. Nice work!

    This made it work for me. I've updated the blog post to include the bindings.xml that I used.

    ReplyDelete
  69. Tom,

    Thanks for the update. It looks like there is a unmarshalling issue for picklists (OptionSetValue) possible due to namespaces? Have you been able to retrieve columns on entities that are configured as picklists?

    Thanks,

    Brennan

    ReplyDelete
    Replies
    1. Brennan,

      I am getting the same exception. Did you resolve it? I tried disabling the validation altogether but no luck.

      ((BindingProvider)port).getRequestContext().put("schema-validation-enabled", "false");

      Delete
  70. To be honest, we haven't been doing a lot of active development on CRM lately. It's been several years since I've had to do any significant work with the CRM webservices.

    ReplyDelete
  71. This comment has been removed by the author.

    ReplyDelete
  72. This comment has been removed by the author.

    ReplyDelete
  73. We have to struggle for almost three weeks to make
    the Java Client for CRM Dynamics 2011 work.
    Special Thanks to Groovy Tom for writing such a good Blog.
    Also to all the comments given by Alessandro Nisticò, Jair and Thomas
    Without which the task will never be completed.

    Sardar Saikh.

    ReplyDelete
  74. Hi Tom,

    I am generating java stub using apache CXF apache-cxf-3.0.0-milestone2 and did the process as you said.
    But when i am trying run the client then i get the Exception:

    WARNING: Interceptor for {http://schemas.microsoft.com/xrm/2011/Contracts}OrganizationService#{http://schemas.microsoft.com/xrm/2011/Contracts/Services}RetrieveMultiple has thrown exception, unwinding now
    org.apache.cxf.interceptor.Fault: No message with ID "kerberosLoginError" found in resource bundle "org/apache/xml/security/resource/xmlsecurity"
    at org.apache.cxf.ws.security.policy.interceptors.SpnegoContextTokenOutInterceptor.issueToken(SpnegoContextTokenOutInterceptor.java:117)
    at org.apache.cxf.ws.security.policy.interceptors.SpnegoContextTokenOutInterceptor.handleMessage(SpnegoContextTokenOutInterceptor.java:74)
    at org.apache.cxf.ws.security.policy.interceptors.SpnegoContextTokenOutInterceptor.handleMessage(SpnegoContextTokenOutInterceptor.java:46)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307)
    at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:502)
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:411)
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:314)
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:267)
    at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)
    at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:137)
    at $Proxy53.retrieveMultiple(Unknown Source)
    at test.main(test.java:84)
    Caused by: org.apache.wss4j.common.ext.WSSecurityException: No message with ID "kerberosLoginError" found in resource bundle "org/apache/xml/security/resource/xmlsecurity"
    Original Exception was javax.security.auth.login.LoginException: java.lang.NullPointerException
    at java.io.ByteArrayInputStream.(ByteArrayInputStream.java:89)
    at sun.security.util.DerValue.(DerValue.java:277)
    at sun.security.krb5.KrbAsRep.(KrbAsRep.java:46)
    at sun.security.krb5.KrbAsReq.getReply(KrbAsReq.java:448)
    at sun.security.krb5.Credentials.sendASRequest(Credentials.java:401)
    at sun.security.krb5.Credentials.acquireTGT(Credentials.java:350)
    at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Krb5LoginModule.java:662)
    at com.sun.security.auth.module.Krb5LoginModule.login(Krb5LoginModule.java:542)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    Can someone please help me on this.

    Thanks in Advance.

    Kiran

    ReplyDelete
    Replies
    1. I haven't tried that version of cxf, you might want to try a version that is confirmed working like 2.7.0 or 2.6.9.

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

    ReplyDelete
  76. Anyone have any experience creating SalesOrderDetails through this setup? I pretty much have everything working, and can query and create accounts, contacts, orders and detail lines, as well as code to automatically populate picklists and entityreferences, but I can't for the life of me get it to send a Money value to CRM.

    If i just stick a BigDecimal in the KeyValuePair, it doesn't complain and runs through fine, but the value is zero on CRM. If i intercept the marshaller and create a Money object and set the BigDecimal as the value, I get an unexpected failure on the server message (which digging into appears to be a class cast error). Setting the value as a string yields a generic failure message. I've tried setting the BigDecimal scale to 4 (matching what CRM returns) with a roundingmode of up, but no change.

    ReplyDelete
    Replies
    1. Here's the error message from the server. Looks like BigDecimals create a DecimalAttributeMetadata instead of MoneyAttributeMetadata, but i'm not sure how to override that for the Money fields. Sending the Money class doesn't help, since CXF just maps it as a container with a BigDecimal inside.

      >Crm Exception: Message: An unexpected error occurred., ErrorCode: -2147220970, InnerException: System.InvalidCastException: Unable to cast object of type 'Microsoft.Crm.Metadata.DecimalAttributeMetadata' to type 'Microsoft.Crm.Metadata.MoneyAttributeMetadata'.
      at Microsoft.Crm.ObjectModel.DoubleAttributeValidator.UpdatePrecision(Nullable`1 decimalValue, Nullable`1 floatValue, Money moneyValue, DoubleAttributeMetadata doubleMetadata, Entity entity, String propertyName, ExecutionContext platformContext)
      at Microsoft.Crm.ObjectModel.DoubleAttributeValidator.Validate(Entity entity, KeyValuePair`2 property, AttributeMetadata attributeMetadata, ExecutionContext platformContext)
      at Microsoft.Crm.ObjectModel.AttributeValidationPlugin.ValidateAttributeValue(Entity entity, KeyValuePair`2 property, AttributeMetadata attributeMetadata, ExecutionContext context, Entity parentEntity)
      at Microsoft.Crm.ObjectModel.AttributeValidationPlugin.PerformValidation(Entity entity, EntityMetadata entityMetadata, ExecutionContext platformContext, Entity parentEntity)
      at Microsoft.Crm.ObjectModel.AttributeValidationPlugin.Execute(IServiceProvider serviceProvider)
      at Microsoft.Crm.Extensibility.V5PluginProxyStep.ExecuteInternal(PipelineExecutionContext context)
      at Microsoft.Crm.Extensibility.VersionedPluginProxyStepBase.Execute(PipelineExecutionContext context)

      Delete
  77. • This is a very helpful blog. According the information here I was first able to connect to CRM 2011 On-Premise and invoke CRM web service calls through CXF. After looking into the CXF codebase, I figured out the correct implementation for SOAP message signing and encryption. I have been able to authenticate and send SOAP requests to the 2011/2013 On-Premise server through my standalone Java implementation. Many thanks!

    ReplyDelete
    Replies
    1. Hello,

      would you be able to provide me with your code as I'm in the same situation?

      thanks
      Lawrence

      Delete
  78. Hi Tom,
    Extremely helpful post ... it's gotten me very close on a similar project I'm working on that involves consuming a WCF 4.0-secured web service with the same message level NTLM/Kerberos based on SPNEGO WS-Trust. It's not CRM, but your instructions have gotten me very close to the finish line.

    Unfortunately, I've hit a hurdle at the very end that I wonder if you might have any insight into: With Kerberos debugging on, I can see that part of the application working successfully, but it gets caught in a loop afterward, continually reaching out to the domain controller for a new Kerberos key. The looping starts in SpnegoContextTokenOutInterceptor's handleMessage(SoapMessage) call: It tries to get the "ws-security.token.id" from the message, but it's not there, so with a null token, it requests a security token from the STSClient, and that request gets caught up in the same interceptor where the ws-security.token.id is null, and it just keeps rolling from there.

    I started out using CXF 3.0.3, but went back to 2.7.14 before typing this, to see if the version made a difference, but it didn't.

    It seems like there must be something small I can do to get around this, but I'm just not seeing it. Any other thoughts you might have would be greatly appreciated.

    All best,
    Mark

    ReplyDelete
    Replies
    1. It sounds like it's having trouble exchanging the kerberos ticket for the WS-Trust token. Either the STSClient isn't getting back the WS-Trust token or the token doesn't have what the STSClient is expecting. It might be helpful if you could post the exchange on gist or another code sharing site. (with sensitive info removed)

      Delete
    2. I'm not sure that it's even getting a token back in the STS Client. In this method:

      public SecurityToken requestSecurityToken(String appliesTo, String action, String requestType, String binaryExchange) throws Exception {

      AbstractSTSClient.STSResponse response = issue(appliesTo, action, requestType, binaryExchange);

      SecurityToken token = createSecurityToken(getDocumentElement(response.getResponse()), response.getEntropy());

      ...
      }

      It's getting caught in that loop at the very first line with its call to issue(...). I can see it building up the request, but when it goes to call client.invoke(...) with the request document that it's built, that call eventually gets caught back in the STSClient at that same issue(...) point. STSClient never progresses beyond that line.

      Here's the test class I've been using, which is essentially, I think, what you've done up there: https://gist.github.com/anonymous/172987e91dffcecb4792.

      Let me know if there are other files I can provide that will help. The web service is secured with WsHttpTransport, if that helps, too.

      Thanks!

      Mark

      Delete
    3. It's hard to tell what's going on without seeing the message exchange. It would be helpful if you could capture the exchange using fiddler or some other http capture tool. The fact that it is getting stuck in a look tells me it's not getting back what it is expecting, so there might be an error message in the response that STSClient doesn't understand. Keep in mind this code hasn't been tested much outside of CRM, so I wouldn't be surprised if there are scenarios it's not handling correctly.

      Delete
    4. Yeah, that's a good point ... it looks like this differs enough from CRM that I'm falling out of the context of this post. I've been working through it on the CXF mailing list, and will just keep pursuing it there. I do appreciate your feedback, though!

      Just one more thought before I go, though: I did a deeper debugging dive in response to a CXF list question at http://mail-archives.apache.org/mod_mbox/cxf-users/201501.mbox/%3CF4B5D9FC-F4B7-4A7D-8B1E-6B81556936DF@nexidia.com%3E. If you see anything helpful there, let me know; else thanks very much for taking the time here!

      Delete
  79. I was facing the Guid related issues mentioned in a number of posts above. The WSDL has it as a SimpleType and classes for it are not generated unless they are enumerations.

    To work around this - you have to use a binding file and set mapSimpleTypeDef attribute for globalBinding element to true.

    Please refer - https://access.redhat.com/documentation/en-US/Red_Hat_JBoss_Fuse/6.0/html/Developing_Applications_Using_JAX-WS/files/JAXWSCustomTypeMappingSimple.html

    ReplyDelete