Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting WSDL descriptions securely #5572

Open
TehWardy opened this issue Jun 12, 2024 · 3 comments
Open

Getting WSDL descriptions securely #5572

TehWardy opened this issue Jun 12, 2024 · 3 comments
Labels

Comments

@TehWardy
Copy link

TehWardy commented Jun 12, 2024

As I said in a comment on #3318 ...

That solves the problem once the client is generated ...
How did you solve the issue of getting the WSDL description in the first place with a client cert?

I can't find an answer to this anywhere.

Since this ticket is long closed I figured I ask here instead:

I've been given an SSL cer file and told "here's the URL to our service, add ?wsdl to the end to get the metadata endpoint", but I can't figure out how in the VS UI to get the WSDL description, so I wrote some code to do it and i'm getting a TLS handshake error from the service.

I've raised the question about the error from the server with the service provider but I was hoping there was a built in way to get this.

How do I get the WSDL for a secure service that can only be communicated with using client cert?

@TehWardy
Copy link
Author

I tried to do this manually using some raw C# but I got a TLS failure from the server ...

using System.Reflection;
using System.Security.Cryptography.X509Certificates;

public class WSDL
{
    public async ValueTask<string> GetDescription(string wsdlUrl, string certResource)
    {
        var handler = new HttpClientHandler();
        handler.ClientCertificates.Add(GetCert(certResource));

        handler.ServerCertificateCustomValidationCallback =
            (message, cert, chain, sslPolicyErrors) => true;

        using var client = new HttpClient(handler);
        return await client.GetStringAsync(wsdlUrl);
    }
    
    X509Certificate2 GetCert(string certResource)
    {
        Stream certStream = Assembly
            .GetExecutingAssembly()
            .GetManifestResourceStream(certResource);

        certStream.Seek(0, SeekOrigin.Begin);

        var memStream = new MemoryStream();
        certStream.CopyTo(memStream);
        return new(memStream.ToArray());
    }
}

Ultimately my question remains the same ...
What am I missing here, and is there a way to achieve this in VS UI somewhere?

@mconnew
Copy link
Member

mconnew commented Jun 12, 2024

Did the certificate resource contain the private key as well as the public key? At least on Windows, when you export a certificate including the private key, you are asked to provide a password so I would expect the X509Certificate2 constructor overload to be the one which accepts a password. This suggests to me that you might only have the public certificate in your resource file. Use the extension method GetRSAPrivateKey and check if the private key value is null. If it's null, then you don't have the private key, or it could be a different type of key than RSA. There are equivalent extension methods for different key types. These are DSACertificateExtensions.GetDSAPrivateKey and ECDsaCertificateExtensions.GetECDsaPrivateKey. If you know what type it is, use the relevant method, otherwise you might need to check all three. Basically, this is how you can validate you have the private key in your certificate blob. If you don't have the private key, HttpClientHandler can't use it.

Another useful piece of information to know, if you create an X509Certificate2 instance from a byte array which includes a private key, and you are running on Windows, it creates an ephemeral certificate which includes storing the private key on disk (properly protected so random users can't read it). If you don't Dispose the X509Certificate2 instance before the program exits, you leave this file on disk, and because of the nature of how Windows stores them, there's no way to know that it's not needed any more and you end up using that disk space forever.

If you are hitting a WCF SOAP service, use ?SingleWsdl and not ?Wsdl in the url to get a single file with everything you need in it. If you use ?Wsdl, it contains imports to other files hosted by WCF and you would need to parse the XML, read these url's and go and fetch those too. It's easier to just use ?SingleWsdl.

Other than making sure your client certificate has a private key, everything looks correct to me.

As for your question about the VS WCF Connected Services tool, there's currently no capability to do this. You best option is what you're doing, download it yourself and then point the tooling at the file you downloaded. If you are going to automate some aspect of this, once you have the download part working, you can use the dotnet-svcutil nuget command line tool to generate the client.

@TehWardy
Copy link
Author

It looks like the cer file i'm loading doesn't contain the private key.
The API call to GetRSAPrivateKey returns null.

I looked at a bunch of the method calls on it ... private key found anywhere.
My understanding is that that the cert is to be used as client cert to verify any calls I make are legit calls.

I'm a little confused by the provider process though as they had me generate a key and then csr which they returned a a cer file for.

Do I need to now add the private key on my end or something in order to use this (I wasn't provided this info in any docs)?

This is exactly what I think I need to do as there doesn't seem to be any UI for this, is this documented somewhere (specifically doing this with the client cert) ...

If you are going to automate some aspect of this, once you have the download part working, you can use the dotnet-svcutil nuget command line tool to generate the client.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants