Posts
27
Comments
59
Trackbacks
0
April 2008 Entries
Neuron-ESB Links

As I posted yesterday, Neudesic released version 2.0 of their .Net/WCF-based ESB.  I have a few links surrounding this release.

Neuron 2.0 - The WCF and SOA Enabler

Neuron ESB 2.0 Released!

posted @ Wednesday, April 30, 2008 10:15 AM | Feedback (0)
Neudesic Releases Neuron-ESB 2.0

Neudesic, the company I work for, released version 2.0 of our .Net-based Enterprise Service Bus today.  Press release follows:

IRVINE, CALIF. – April 29, 2008 - Neudesic, a leading provider of business solutions that leverage the capabilities of the Microsoft product line, announced today the release of version 2.0 of Neuron-ESB. Neuron-ESB is an Enterprise Service Bus that extends the Microsoft Platform by providing real-time messaging, integration and web service management. Neuron-ESB accelerates SOA adoption by helping companies successfully implement real-time integration across their enterprise, allowing timely response to changing events within their business.

Neuron-ESB is built on the Microsoft Windows Communication Framework (WCF) technology to provide real-time reliable messaging options for companies adopting SOA. Neuron-ESB manages all communication over the bus by sending messages over “Topics” using a publish-subscribe pattern and supports federated, geographic deployments. Neuron-ESB helps companies administer and automate complex tasks and is proven to significantly reduce the infrastructure, development, training and long term support costs for businesses developing SOA solutions.

“Neuron-ESB provides the messaging backbone for all of our critical applications,” said Jeffrey Sullivan, Chief Information Officer of ThinkCash. “Neuron-ESB allowed us to leverage our developers much more effectively while providing us the ability to go to market quickly with new solutions. We were able to shift our service development from the architect role to the more ubiquitous developer role while, decreasing our deployment time of new services by 50%. We started with just 1 developer who received 4 days of Neuron-ESB training. Within 6 months and no additional training, we had a 15X increase in the number of our internal developers who were able to use Neuron-ESB.”

Neuron-ESB 2.0 delivers a unique set of capabilities that extend and combine key strategic Microsoft technologies such as Microsoft BizTalk Server 2006 R2 & RFID, Microsoft Office SharePoint Server 3.0, Microsoft SQL Server, Microsoft Dynamics, Microsoft Office, .NET 3.0/3.5, Windows Communication Foundation (WCF), Windows Workflow Foundation (WF), WCF Line of Business Adapters and MSMQ. The synergy between Neuron-ESB and these products empower companies to develop more robust and business-aware applications with far less effort and complexity.

“Neuron-ESB 2.0 represents a significant evolution for the Microsoft Platform while addressing the Enterprise Service Bus needs of every customer running Microsoft Windows.  Our technology allows businesses to effectively leverage their Microsoft investments to deliver real-time solutions,” stated Marty Wasznicky, Vice President of Product Development. “Our product provides a new level of flexibility and ease of use that will help companies increase their productivity while reducing their development and operational costs. Moreover we’ve formed a strategic partnership with SOA Software and achieved certification as a Governed Service Platform through the Open Governance Initiative. Our customers can be confident that Neuron-ESB will enhance the fidelity of their Governance initiatives.”

“The Open Governance Initiative is rapidly gaining momentum amongst platform vendors, Governance solution providers, and end-user customers,” said Frank Martinez, Executive Vice President of SOA Software.  “The addition of Neuron-ESB, as a Microsoft .NET and WCF based ESB to the list of Governed Service Platforms highlights the importance of this certification for platform vendors.”

posted @ Tuesday, April 29, 2008 5:48 PM | Feedback (2)
Java Web Service Interop with a .NET Client
I recently had a requirement to interop with a web service (written in Java, not that it matters all that much what it was written in) with a quasi-unique set of security requirements.  They were as follows:

  • SOAP 1.1
  • Transport security was an option.  Production endpoint was using SSL; test endpoint was not.  Need the flexibility to turn this on or off.
  • Message Security consisted of two tokens (both WS-Security 1.0)
    • Unsigned username token with plaintext password (forget the argument about plain text using a non-encrypted channel)
    • X.509 token signing the SOAP message body
      • This is a client cert to authenticate the client to the service
      • X.509 token was not embedded within the message itself; it was an external reference to a cert using a thumbprint lookup
I started down the path of using WCF.  None of the out-of-the-box bindings fit the bill for this, so I realized I needed to write some custom stuff.  However, I soon realized I was mired in a bog of custom code to get this to work.  I had started to create a custom binding (with a "two-token" binding element), but I was faced with a myriad of complexities and too little experience to navigate it successfully.  The WCF stack is a highly complex framework, and diving into it to develop completely custom code shouldn't be taken lightly.  I hope to revisit this problem with WCF to implement it the "right" way, considering this is the .NET connected systems technology going forward.  When I do, I'll post the solution here.

Anyway, at this point, I shifted gears and  looked to WSE3.0 to solve this problem.  As with WCF, none of the turnkey assertion policies worked for me, so I had to roll up my sleeves and start working on custom code.  The solution was fairly straightforward, although I had to go through several iterations to turn the various knobs "just so" to get the interop down exactly.

I first started by creating a custom assertion policy, like so:

public class TwoTokenSecurityAssertion : SecurityPolicyAssertion
{
    // Fields
    private TokenProvider<UsernameToken> usernameTokenProvider;
    private TokenProvider<X509SecurityToken> x509TokenProvider;

    // Methods
    public override SoapFilter CreateClientInputFilter(FilterCreationContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        return new ClientInputFilter(this);
    }

    public override SoapFilter CreateClientOutputFilter(FilterCreationContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        return new ClientOutputFilter(this);
    }

    public override SoapFilter CreateServiceInputFilter(FilterCreationContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        return new ServiceInputFilter(this);
    }

    public override SoapFilter CreateServiceOutputFilter(FilterCreationContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        return new ServiceOutputFilter(this);
    }

    public TokenProvider<UsernameToken> UsernameTokenProvider
    {
        get
        {
            return this.usernameTokenProvider;
        }
        set
        {
            this.usernameTokenProvider = value;
        }
    }

    public TokenProvider<X509SecurityToken> X509TokenProvider
    {
        get
        {
            return this.x509TokenProvider;
        }
        set
        {
            this.x509TokenProvider = value;
        }
    }

    // Nested Types

    // Called when a message is inbound to the client
    protected class ClientInputFilter : ReceiveSecurityFilter
    {
        // Methods
        public ClientInputFilter(TwoTokenSecurityAssertion assertion)
            : base(assertion.ServiceActor, true, assertion.ClientActor)
        {
        }

        public override void ValidateMessageSecurity(SoapEnvelope envelope, Security security)
        {
            // no-op

            // We don't expect any security back on the response, so this is a no-op
        }

    }

    // Called when a message is outbound from the client
    protected class ClientOutputFilter : SecureConversationClientSendSecurityFilter
    {
        // Fields
        private UsernameToken upToken;
        private X509SecurityToken x509Token;

        // Methods
        public ClientOutputFilter(TwoTokenSecurityAssertion assertion)
            : base(assertion)
        {
            if (assertion.X509TokenProvider != null)
            {
                this.x509Token = assertion.X509TokenProvider.GetToken();
            }
            if (assertion.UsernameTokenProvider != null)
            {
                this.upToken = assertion.UsernameTokenProvider.GetToken();
            }
        }

        public override void SecureMessage(SoapEnvelope envelope, Security security, MessageProtectionRequirements
                                           message)
        {
            if (envelope == null)
            {
                throw new ArgumentNullException("envelope");
            }
            if (security == null)
            {
                throw new ArgumentNullException("security");
            }

            // Grab the username token and add it as-is to the header
            UsernameToken usernameToken = ClientOutputFilter.ProvideClientToken<UsernameToken>(this.upToken, this.
                                          GetServiceActor(envelope.CurrentSoap));
            security.Tokens.Add(usernameToken);

            // Create the x509 token, but we won't add it to the security headers directly
            X509SecurityToken x509Token = ClientOutputFilter.ProvideServiceToken<X509SecurityToken>(this.x509Token, this.
                                          GetServiceActor(envelope.CurrentSoap));

            // Create a message signature.  We will sign with the x509 token
            MessageSignature signature = new MessageSignature(x509Token);

            // Create a new keyinfo for the x.509 token.  This is so that the signature will have a reference to the
            binary token.
            if (signature.KeyInfo == null)
            {
                signature.KeyInfo = new KeyInfo();
            }

            // create reference so signature can use it.
            SecurityTokenReference tokenRef = new SecurityTokenReference(x509Token, SecurityTokenReference.
                                              SerializationOptions.KeyIdentifier);

            // Add the keyinfo ref to the signature
            signature.KeyInfo.AddClause(tokenRef);

            // we only want to sign the body
            signature.SignatureOptions = message.SignatureOptions;

            // and add the signature to the header
            security.Elements.Add(signature);

        }

        // Written to replace internal static method on CredentialSet
        private static TSecurityToken ProvideClientToken<TSecurityToken>(TSecurityToken token, string actor) where
                                                                         TSecurityToken : SecurityToken
        {

            if (token != null)
            {
                return token;
            }
            TSecurityToken clientToken = default(TSecurityToken);
            if ((actor != null) && (SoapContext.Current != null))
            {
                clientToken = SoapContext.Current.Credentials[actor].GetClientToken<TSecurityToken>();
            }
            if (clientToken == default(TSecurityToken))
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to determine
                                                    client token to use. Client token type requested was '{0}'. The
                                                    token must be provided either through policy by specifying the token
                                                    in the policy assertion or through code by calling
                                                    WebServicesClientProtocol.SetCredentials or using properties on the
                                                    SoapContext.Credentials.", new object[] { typeof(TSecurityToken).
                                                    ToString() }));
            }
            return clientToken;

        }

        // Written to replace internal static method on CredentialSet
        private static TSecurityToken ProvideServiceToken<TSecurityToken>(TSecurityToken token, string actor) where
                                                                          TSecurityToken : SecurityToken
        {
            if (token != null)
            {
                return token;
            }
            TSecurityToken serviceToken = default(TSecurityToken);
            if ((actor != null) && (SoapContext.Current != null))
            {
                serviceToken = SoapContext.Current.Credentials[actor].GetServiceToken<TSecurityToken>();
            }
            if (serviceToken == default(TSecurityToken))
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to determine
                                                    service token to use. Service token type requested was '{0}'. The
                                                    token must be provided either through policy by specifying the token
                                                    in the policy assertion or through code by calling
                                                    WebServicesClientProtocol.SetCredentials or using properties on the
                                                    SoapContext.Credentials.", new object[] { typeof(TSecurityToken).
                                                    ToString() }));
            }
            return serviceToken;
        }

    }

    // Not used since we're only dealing with client-side.
    protected class ServiceInputFilter : ReceiveSecurityFilter
    {
        // Fields
        private X509SecurityToken x509Token;

        // Methods
        public ServiceInputFilter(TwoTokenSecurityAssertion assertion)
            : base(assertion.ServiceActor, false)
        {
            if (assertion.X509TokenProvider != null)
            {
                this.x509Token = assertion.X509TokenProvider.GetToken();
            }
        }

        public override void ValidateMessageSecurity(SoapEnvelope envelope, Microsoft.Web.Services3.Security.Security
                                                     security)
        {
            throw new NotImplementedException();
        }
    }

    // Not used since we're only dealing with client-side.
    protected class ServiceOutputFilter : SendSecurityFilter
    {
        // Methods
        public ServiceOutputFilter(TwoTokenSecurityAssertion assertion)
            : base(assertion.ServiceActor, false)
        {
        }

        public override void SecureMessage(SoapEnvelope envelope, Microsoft.Web.Services3.Security.Security security)
        {
            throw new NotImplementedException();
        }
    }
}

Next, I created a web reference to the endpoint and set the WSE3.0 policy in code, as in:

using (MyWebReference.MyInterfaceClient proxy = new MyWebReference.MyInterfaceClient())
{

    // In some ways, "client" vs. "server" is a misnomer here.  What we're actually providing as a client credential is
    a
    // username token, whereas the "server" credential is really a client-side cert to prove to the server that we
    // are who we say we are.
    proxy.SetClientCredential<UsernameToken>(new UsernameToken("sanitized", "sanitized", PasswordOption.SendPlainText));
    proxy.SetServiceCredential<X509SecurityToken>(X509TokenProvider.CreateToken(StoreLocation.LocalMachine, StoreName.
                                                  TrustedPeople, "<sanitized>", X509FindType.FindByThumbprint));

    // Create a custom policy
    Policy myPolicy = new Policy();

    // Create a new policy assertion
    TwoTokenSecurityAssertion myAssertion = new TwoTokenSecurityAssertion();

    // define that we only want to sign the body with the
    myAssertion.Protection.Request.SignatureOptions = SignatureOptions.IncludeSoapBody;

    // Add the assertion to the policy
    myPolicy.Assertions.Add(myAssertion);

    // and finally, set the policy for this client to the custom one we just created
    proxy.SetPolicy(myPolicy);

    proxy.CallMethod();

}

This created a SOAP message as follows, which is exactly what I was looking for:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-
                          instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.
                          org/ws/2004/08/addressing" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-
                          wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-
                          wssecurity-utility-1.0.xsd">
<soap:Header>
  <wsa:Action>
  </wsa:Action>
  <wsa:MessageID>urn:uuid:18667fd7-8a87-4729-a168-ab12379478df</wsa:MessageID>
  <wsa:ReplyTo>
    <wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
  </wsa:ReplyTo>
  <wsa:To>https://webserver/destination</wsa:To>
  <wsse:Security soap:mustUnderstand="1">
    <wsu:Timestamp wsu:Id="Timestamp-462c82e9-9d0a-4584-b07f-e7f6e49aefcb">
      <wsu:Created>2008-04-20T20:24:04Z</wsu:Created>
      <wsu:Expires>2008-04-20T20:29:04Z</wsu:Expires>
    </wsu:Timestamp>
    <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                                  wsu:Id="SecurityToken-ea80bb90-bdc2-4a35-9605-9b6e770b1b4f">
      <wsse:Username>sanitized</wsse:Username>
      <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.
                          0#PasswordText">sanitized</wsse:Password>
      <wsse:Nonce>sanizited</wsse:Nonce>
      <wsu:Created>2008-04-20T20:24:04Z</wsu:Created>
    </wsse:UsernameToken>
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
      <SignedInfo>
        <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:ds="http://www.w3.
                                             org/2000/09/xmldsig#" />
        <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
        <Reference URI="#Id-af786227-f58a-441a-b62e-9bb220c6bcbc">
          <Transforms>
            <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
          </Transforms>
          <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
          <DigestValue>sanitized</DigestValue>
        </Reference>
      </SignedInfo>
      <SignatureValue>sanitized</SignatureValue>
      <KeyInfo>
        <wsse:SecurityTokenReference>
          <wsse:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.
                                        1#ThumbprintSHA1" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-
                                        200401-wss-soap-message-security-1.0#Base64Binary">sanitized</wsse:KeyIdentifier>
        </wsse:SecurityTokenReference>
      </KeyInfo>
    </Signature>
  </wsse:Security>
</soap:Header>
<soap:Body wsu:Id="Id-af786227-f58a-441a-b62e-9bb220c6bcbc">
    <TestMethod />
</soap:Body>
</soap:Envelope>

As I mentioned earlier, the "right" way to do this is with WCF, so if I get some time to research doing this with WCF, I'll post my results here.
posted @ Monday, April 21, 2008 10:17 AM | Feedback (2)
News
Comment and opinions are my own and do not reflect on my company in any way.