Implement a C# Client that uses WebServices over S

2020-05-18 03:44发布

问题:

So I've got a ServiceReference added to a C# Console Application which calls a Web Service that is exposed from Oracle.

I've got everything setup and it works like peaches when it's not using SSL (http). I'm trying to set it up using SSL now, and I'm running into issues with adding it to the Service References (or even Web References). For example, the URL (https) that the service is being exposed on, isn't returning the appropriate web methods when I try to add it into Visual Studio.

The underlying connection was closed: An unexpected error occurred on a send. Received an unexpected EOF or 0 bytes from the transport stream. Metadata contains a reference that cannot be resolved: 'https://srs204.mywebsite.ca:7776/SomeDirectory/MyWebService?WSDL'

Another quandary I've got is in regards to certificate management and deployment. I've got about 1000 external client sites that will need to use this little utility and they'll need the certificate installed in the appropriate cert store in order to connect to the Web Service. Not sure on the best approach to handling this. Do they need to be in the root store?

I've spent quite a few hours on the web looking over various options but can't get a good clean answer anywhere.

To summarize, I've got a couple of questions here:

1) Anybody have some good links on setting up Web Services in Visual Studio that use SSL?

2) How should I register the certificate? Which store should it exist in? Can I just use something like CertMgr to register it?

There's gotta be a good book/tutorial/whatever that will show me common good practices on setting something like this up. I just can't seem to find it!

回答1:

Well, I've figured this out. It took me far longer than I care to talk about, but I wanted to share my solution since it's a HUGE pet peeve of mine to see the standard. "Oh I fixed it! Thanks!" posts that leave everyone hanging on what actually happened.

So.

The root problem was that by default Visual Studio 2008 uses TLS for the SSL handshake and the Oracle/Java based Webservice that I was trying to connect to was using SSL3.

When you use the "Add Service Reference..." in Visual Studio 2008, you have no way to specify that the security protocol for the service point manager should be SSL3.

Unless.

You take a static WSDL document and use wsdl.exe to generate a proxy class.

wsdl /l:CS /protocol:SOAP /namespace:MyNamespace MyWebService.wsdl

Then you can use the C Sharp Compiler to turn that proxy class into a library (.dll) and add it to your .Net projects "References".

csc /t:library /r:System.Web.Services.dll /r:System.Xml.dll MyWebService.cs

At this point you also need to make sure that you've included System.Web.Services in your "References" as well.

Now you should be able to call your web service without an issue in the code. To make it work you're going to need one magic line of code added before you instantiate the service.

// We're using SSL here and not TLS. Without this line, nothing workie.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;

Okay, so I was feeling pretty impressed with myself as testing was great on my dev box. Then I deployed to another client box and it wouldn't connect again due to a permissions/authority issue. This smelled like certificates to me (whatever they smell like). To resolve this, I used certmgr.exe to register the certificate for the site to the Trusted Root on the Local Machine.

certmgr -add -c "c:\someDir\yourCert.cer" -s -r localMachine root

This allows me to distribute the certificate to our client sites and install it automatically for the users. I'm still not sure on how "security friendly" the different versions of windows will be in regards to automated certificate registrations like this one, but it's worked great so far.

Hope this answer helps some folks. Thanks to blowdart too for all of your help on this one and providing some insight.



回答2:

It sounds like the web service is using a self signed certificate. Frankly this isn't the best approach.

Assuming you're a large organisation and it's internal you can setup your own trusted certificate authority, this is especially easy with Active Directory. From that CA the server hosting the Oracle service could request a certificate and you can use AD policy to trust your internal CA's root certificate by placing it in the trusted root of the machine store. This would remove the need to manually trust or accept the certificate on the web service.

If the client machines are external then you're going to have to get the folks exposing the service to either purchase a "real" certificate from one of the well known CAs like Verisign, Thawte, GeoTrust etc. or as part of your install bundle the public certificate and install it into Trusted Root certificate authorities at the machine level on every machine. This has problems, for example no way to revoke the certificate, but will remove the prompt.



回答3:

Thanks for this great tip, took a quick look around at your stuff and you have a lot of good ideas going on. Here's my little bit to add -- I'm figuring out webMethods and (surprise!) it has the same problems as the Oracle app server you connected to (SSL3 instead of TLS). Your approach worked great, here's my addendum.

Given static class "Factory," provide these two handy-dandy items:

/// <summary>
/// Used when dispatching code from the Factory (for example, SSL3 calls)
/// </summary>
/// <param name="flag">Make this guy have values for debugging support</param>
public delegate void CodeDispatcher(ref string flag);

/// <summary>
/// Run code in SSL3 -- this is not thread safe. All connections executed while this
/// context is active are set with this flag. Need to research how to avoid this...
/// </summary>
/// <param name="flag">Debugging context on exception</param>
/// <param name="dispatcher">Dispatching code</param>
public static void DispatchInSsl3(ref string flag, CodeDispatcher dispatcher)
{
  var resetServicePoint = false;
  var origSecurityProtocol = System.Net.ServicePointManager.SecurityProtocol;
  try
  {
    System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Ssl3;
    resetServicePoint = true;
    dispatcher(ref flag);
  }
  finally
  {
    if (resetServicePoint)
    {
      try { System.Net.ServicePointManager.SecurityProtocol = origSecurityProtocol; }
      catch { }
    }
  }
}

And then to consume this stuff (as you have no doubt already guessed, but put a drum roll in here anyway):

    var readings = new ArchG2.Portal.wmArchG201_Svc_fireWmdReading.wmdReading[] {
      new ArchG2.Portal.wmArchG201_Svc_fireWmdReading.wmdReading() {
        attrID = 1, created = DateTime.Now.AddDays(-1), reading = 17.34, userID = 2
      },
      new ArchG2.Portal.wmArchG201_Svc_fireWmdReading.wmdReading() {
        attrID = 2, created = DateTime.Now.AddDays(-2), reading = 99.76, userID = 3
      },
      new ArchG2.Portal.wmArchG201_Svc_fireWmdReading.wmdReading() {
        attrID = 3, created = DateTime.Now.AddDays(-5), reading = 82.17, userID = 4
      }
    };
    ArchG2.Portal.Utils.wmArchG201.Factory.DispatchInSsl3(ref flag, (ref string flag_inner) =>
    {
      // creates the binding, endpoint, etc. programatically to avoid mucking with
      // SharePoint web.config.
      var wsFireWmdReading = ArchG2.Portal.Utils.wmArchG201.Factory.Get_fireWmdReading(ref flag_inner, LH, Context);
      wsFireWmdReading.fireWmdReading(readings);
    });

That does the trick -- when I get some more time I'll solve the threading issue (or not).



回答4:

Since I have no reputation to comment, I'd like to mention that Mat Nadrofsky's answer and code sample for forcing SSL3 is also the solution for an error similar to

An error occurred while making the HTTP request to https://xxxx/whatever. This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server.

Just use

// We're using SSL here and not TLS. Without this line, nothing workie.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;

as mentioned by Mat. Tested with an SAP NetWeaver PI server in HTTPS. Thanks!



回答5:

Mat,

I had such issues too and I have a way to avoid using certmgr.exe to add certificates to trusted root on a remote machine.

X509Store store;
store = new X509Store("ROOT", StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(certificate);

The 'certificate object' can be created like this:

X509Certificate2 certificate = new X509Certificate2("Give certificate location path here");