I've got two systems and I want to use a distributed transaction, so I can assure that they are always synchronized. The transaction must be over internet, as both servers will not be in the same domain.
Here are the configurations I've already made about certificates and SSL:
- Created a self-signed certificate A for machine A,
installed on both Trusted Root Authoritiesand Personal stores
- Created a self-signed certificate B for machine B,
installed on both Trusted Root Authoritiesand Personal stores
- Installed certificate B with the public key on the machine
A on both Trusted Root Authoritiesand Personal stores
- Installed certificate A with the public key on the machine
B on both Trusted Root Authoritiesand Personal stores
Theoretically, SSL should suffice with those configurations.
On MSDTC configurations:
- Enabled network DTC access
- Allowed inbound and outbound on both machine
A and B
- Using 'mutual authentication required' (also tested with 'no authentication required')
- Allowed remote clients
And next are the configurations I've made on WS-AT:
- Enabled WS-Atomic, of course
- On machine A, set the certificate A as the Endpoint
Certificate
- On machine A, set the certificate B as the Authorized
Certificates
- On machine B, set the certificate B as the Endpoint
Certificate
- On machine B, set the certificate A as the Authorized
Certificates
- Set the port to 442 (the one I'm using on tets only)
For instance, the address that I'm using for communication between the servers are "https://IP:442/ws" and always machine A (client) calls machine B (server), never the opposite. I'musing WSHttpBinding.
Both certificates' subject name (common name) matches with machine name, as told as required in msdn (can't insert link yet).
Theoretically, MSDTC and WS-AT configurations are all set with this.
In code, using WCF, everything needed are well done, like the attributes TransactionFlow, TransactionScopeRequired, etc.
An importante note is that it's working when I run both applications on the same intranet or locally, but when I try over the internet, I got this
error:
"The flowed transaction could not be unmarshaled. The following exception occurred: The MSDTC transaction manager's WS-AtomicTransaction protocol service 'Version10' is disabled and cannot unmarshal
incoming transactions."
Does anyone know what I'm missing?
Searching on the internet, this error seems to be caused by a failure on the SSL negotiation, but I just don't know whats missing on the configuration.
Main link I'm using as guide: "MSDN: Configurint WS-Atomic Transaction Support" (can't insert link yet, but that's the topics name on msdn)
--- EDIT/UPDATE 1 ---
After registering the certificate as sslcert from netsh on port 442 on the server (machine B), the error changes to:
"The flowed transaction could not be unmarshaled. The following exception occurred: The WS-AtomicTransaction protocol service could not unmarshal the flowed transaction. The following exception
occured: A fault reply with code CoordinatorRegistrationFailed was received. The fault reason follows: The WS-AT protocol service failed to register with its coordinator. A connection could not be established."
While searching I've fount it's because the server (machine B) is not trusting the client (machine A), but still don't know what's missing.
-- CODE PART --
I'm using the following configuration of bindings:
WSHttpBinding binding = BindingFactory.CreateWSHttpBinding();
binding.TransactionFlow = true;
binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Message.NegotiateServiceCredential = NegotiateServiceCredential;
binding.Security.Message.EstablishSecurityContext = true;
binding.ReliableSession.Enabled = false;
binding.ReliableSession.Ordered = false;
A litte bit of code implementation, just showing some examples:
// Interface between apps
public class IProvider
{
[TransactionFlow(TransactionFlowOption.Allowed)]
void Method(int parameter1);
}
---
// Server side implementation of interface
[ServiceBehavior(ReleaseServiceInstanceOnTransactionComplete = false)]
public class Provider
{
[OperationBehavior(TransactionScopeRequired = true)]
public void Method(int parameter1)
{
return parameter1; // only for example
}
}
---
// Code that is calling the other service
TransactionOptions transactionOptions = new TransactionOptions()
{
Timeout = new TimeSpan(0, 15, 0),
IsolationLevel = IsolationLevel.ReadCommitted,
};
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
{
// Code calling the other service on machine B...
using (IProvider provider = new Provider())
provider.Method(1);
scope.Complete;
}
It's important to say again that it's working both locally and client and server inside intranet.