August 7, 2009


A couple of weeks ago I was asked to write a web service that needed to provide a single operation (method) that allowed the user to upload a file using a Secure Sockets Layer (SSL / HTTPS) .

Although they changed their minds and went for a solution using a VPN connection, I still decided to create the web service in case they went back on their decision again.

This article outlines the steps you need to follow in order to create a service to upload a file over HTTPS using WCF. And it’s been quite a while since I wrote my last article for this blog. I figured this would make an interesting topic…

Table Of Contents

WCF Service Contract

Let’s start by laying the groundwork for the WCF Service. As stated the service must enable users to upload a file to the web server which hosts it. Thus the resulting service contract only contains one method, aptly named Upload(…).

Start by opening up Visual Studio 2008 and create a new blank solution called WcfOverHttps. Next add a new WCF Service Library titled CGeers.Wcf.Services. This class library contains some automatically generated files (IService1.cs, Service.cs, App.config). Rename the IService.cs file to IFileUploadService.cs.

Figure 1 – Solution Explorer

Solution Explorer

The code for the service contract is displayed in Listing 1:

Listing 1 – Service Contract

[ServiceContract(Namespace = "https://cgeers.wordpress.com/wcf/services")]
public interface IFileUploadService
    FileReceivedInfo Upload(FileInfo fileInfo);

As you can see displayed in Figure 1 and Listing 1 above they mention two other classes, namely:

  1. FileInfo
  2. FileReceivedInfo

These classes are both decorated with the MessageContract attribute. To upload the file I opted to use streaming. WCF stipulates that the parameter that holds the data to be streamed must be the only parameter in the method.

You could opt to change the type of the fileInfo parameter to Stream, but then you cannot send any additional information about the file along with it. You can circumvent this issue by creating a new class which contains all of the information you want to transmit. By decorating this class with the MessageContract attribute you can use this class (or message) as a parameter or return type in your service operations.

The FileInfo class specifies the structure of a SOAP envelope for a particular message.

Listing 2 – FileInfo class

public class FileInfo
    [MessageHeader(MustUnderstand = true)]
    public string FileName { get; set; }

    [MessageHeader(MustUnderstand = true)]
    public long Length { get; set; }

    [MessageBodyMember(Order = 1)]
    public Stream Stream { get; set; }

As you can see in Listing 2 the FileInfo class contains three properties. Apart from the actual file (Stream property) the filename and length of the file are also provided.

By applying the MessageHeader attribute to the FileName and Length propery you place this information in the header of the SOAP message. When streaming a file the body of the SOAP message must only contain the actual file itself. By applying the MessageBodyMember attribute to the Stream property you place it in the body of the SOAP message.

If your service operation contains a parameter of a type which has been decorated with the MessageContract attribute then all of the parameters and the return type used in this operation must be of a type to which this attribute has been applied.

You cannot mix parameters of a primitive type with message contracts. For instance if you want the Upload(…) method to return a boolean indicating if the upload succeeded or failed then you have to wrap this in another message contract.

Listing 3 displays how to setup the return value as a message contract. Apart from the boolean value (body) the header of this SOAP message contains the filename of the uploaded file and an optional message property which contains some information about why the upload failed.

Listing 3 – FileReceivedInfo class

public class FileReceivedInfo
    [MessageHeader(MustUnderstand = true)]
    public string FileName { get; set; }

    [MessageHeader(MustUnderstand = true)]
    public string Message { get; set; }

    [MessageBodyMember(Order = 1)]
    public bool UploadSucceeded { get; set; }

If you try to mix message contracts and parameters of a primitive type you’ll receive the following error message:

System.InvalidOperationException: The operation ‘Method’ could not be loaded because it has a parameter or return type of type System.ServiceModel.Channels.Message or a type that has MessageContractAttribute and other parameters of different types. When using System.ServiceModel.Channels.Message or types with MessageContractAttribute, the method must not use any other types of parameters.

Top of page

Service Implementation

Now that the contract for the service has been established it’s time to provide an actual implementation for it.

Start by renaming the Service1.cs file to FileUploadService.cs and add a reference to the System.Configuration assembly.

Listing 4 displays the code for implementing the service. The code is pretty straightforward. Check out the comments in the code for more explanation. Basically it reads the incoming stream and saves it to a file using familiar .NET code.

The Upload(…) method’s return type is of the FileReceivedInfo type. If the upload succeeds the UploadSucceeded property is set to true, if it fails this property is set to false and the message property is populated with the message property of the exception that occured.

Listing 4 – Service Implementation

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall,
    ConcurrencyMode = ConcurrencyMode.Single)]
public class FileUploadService : IFileUploadService
    #region IFileUploadService Members

    public FileReceivedInfo Upload(FileInfo fileInfo)
            // Determine to which directory the file needs to be uploaded
            string uploadDirectory =
            if (String.IsNullOrEmpty(uploadDirectory))
                throw new InvalidOperationException("...");

            // Try to create the upload directory if it does not yet exist
            if (!Directory.Exists(uploadDirectory))

            // Check if a file with the same filename is already 
            // present in the upload directory. If this is the case 
            // then delete this file
            string path = Path.Combine(uploadDirectory, fileInfo.FileName);
            if (File.Exists(path))
                // Check if the file is read-only
                if ((File.GetAttributes(path) & FileAttributes.ReadOnly) ==
                    File.SetAttributes(path, FileAttributes.Normal);

            // Read the incoming stream and save it to file
            const int bufferSize = 2048;
            byte[] buffer = new byte[bufferSize];
            using (FileStream outputStream = new FileStream(path,
                FileMode.Create, FileAccess.Write))
                int bytesRead = fileInfo.Stream.Read(buffer, 0, bufferSize);
                while (bytesRead > 0)
                    outputStream.Write(buffer, 0, bytesRead);
                    bytesRead = fileInfo.Stream.Read(buffer, 0, bufferSize);
            return new FileReceivedInfo
                           FileName = fileInfo.FileName,
                           Message = String.Empty,
                           UploadSucceeded = true
        catch (Exception ex)
            return new FileReceivedInfo
                           FileName = fileInfo.FileName,
                           Message = ex.Message,
                           UploadSucceeded = false


Top of page

Hosting The Service

Now that the service has been setup it’s time to create a host for it. To keep things simple a console application will do. Add a new Console Application project to your solution named ConsoleHost. Next add references to the System.ServiceModel assembly and the CGeers.Wcf.Services project.

The code for the Main(…) method of the console application is slim and simple. Take a look at Listing 5.

Listing 5 – Hosting the FileUpload service

static void Main(string[] args)
    ServiceHost host = new ServiceHost(typeof(FileUploadService));
    Console.WriteLine("FileUpload Service Host");
    Console.WriteLine("Service Started!");
    foreach (Uri address in host.BaseAddresses)
        Console.WriteLine("Listening on " + address);
    Console.WriteLine("Press any key to close the host...");

A new ServiceHost instance is created for the FileUploadService type and the host is then started. But before you launch the console application you still need to configure the service. Instead of doing this through code I opted to do this via an application configuration file. The next section addresses how you can easily configure your service.

Top of page

Configuring The Host

Start by adding an application configuration (App.config) file to the ConsoleHost project. Next specify the directory in which the service should save the incoming files.

Listing 6 – Upload directory

  <add key="uploadDirectory" value="C:\temp\upload" />

Now add a node to the configuration file. Within this node three child nodes will be added in order to configure the service. The first such node specifies the service’s behavior.

Listing 7 – Service Behavior

    <behavior name="FileUploadServiceBehavior">
      <serviceMetadata httpGetEnabled="False" httpsGetEnabled="True" />
      <serviceDebug includeExceptionDetailInFaults="False" />

The behavior specifies that the service should not propagate exception details and that it’s metadata should be shared over HTTPS (httpsGetEnabled) instead of HTTP (httpGetEnabled).

Next up is the binding for the service. Since the goal is to use HTTPS the built-in BasicHttpBinding fits the bill. A couple of its properties need to be tweaked to fit the needs of the service, namely:

  1. transferMode: Set it to Streamed to enable streaming.
  2. messageEncoding: The message encoding is set to MTOM encoding which is a mechanism for transmitting binary attachements with SOAP messages.
  3. maxReceivedMessageSize: Set to 64 megabytes to allow large files to be uploaded.
  4. maxBufferSize: Set to 64 kilobytes.
  5. receiveTimeout: Set to 10 minutes. If the file fails to upload within this time frame an exception will be thrown.

Remark: Configure these settings according to the needs of your application. For my application a maximum of 64 Mb for the file size and 64 KB for the buffer are sufficient.

Listing 8 – The Binding

    <!-- buffer: 64KB; max size: 64MB -->
    <binding name="FileUploadServiceBinding"
             maxReceivedMessageSize="67108864" maxBufferSize="65536"
             closeTimeout="00:01:00" openTimeout="00:01:00"
             receiveTimeout="00:10:00" sendTimeout="00:01:00">
      <security mode="Transport">
        <transport clientCredentialType="None" />

Last but not least is the configuration for the service itself.

Listing 9: FileUploadService configuration

  <service behaviorConfiguration="FileUploadServiceBehavior"
    <endpoint address="" binding="basicHttpBinding" contract="CGeers.Wcf.Services.IFileUploadService"
        <dns value="localhost" />
    <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
        <add baseAddress="" />

The service’s ABC’s are specified and it’s hooked up to the behavior and binding we configured earlier. The address is specified as being the location of the service. As you can see in the service’s binding the security is set to Transport which requires that you provide an HTTPS based address.

Remark: Note that for the metadata exchange (mex) the mexHttpsBinding is specified and not the default mexHttpBinding.

If you run the ConsoleHost project now you receive the following result:

Figure 2 – ConsoleHost
Console Host

Top of page

SSL Certificate

If you run the ConsoleHost project now and enter the address in a browser you’ll receive an error. Before you can proceed , you need to bind an SSL Certificate to port 8741 for the IP address

First you need to create an SSL certificate. You can do this using the makecert.exe command-line utility. Just start a new Visual Studio command prompt and enter the following command in order to create a X509 certificate which will be used for SSL communication on the localhost (

makecert.exe -sr LocalMachine -ss My -n CN= -sky exchange -sk -pe

After this command is executed, the new certificate with the private key is created and stored in the LocalMachine Personal store. To see it, use the MMC-certificate snap-in.

After executing this command your new certificate is created and stored in the LocalMachine Personal store. Open the MMC-certificate snap-in to see it.

  1. Click Start, Run and enter the command “mmc”
  2. Click OK to start the MMC console
  3. Click File | Add/Remove snap-in…
  4. In the resulting dialog that pops up click the Add option and choose the Certificates snap-in
  5. Confirm your changes by closing the dialog windows

Afterwards you can view you new certificate in the MMC console as displayed in Figure 3.

Figure 3 – MMC Console Certificates snap-in
MMC Console Certificates Snap-in

Next you need to bind this new certificate to the port 8741. Return to the Visual Studio command prompt and enter the following command to do so:

httpcfg.exe set ssl -i -h cfdf5d1140b94614d047dd77c7bd4d4d4422e1c9 -Personal

Replace the “cfdf5d1140b94614d047dd77c7bd4d4d4422e1c9” value with the thumbprint of your own certificate. You can retrieve the thumbprint using the Certificates MMC snap-in as shown in Figure 3. Be sure to remove all the spaces.

If you rerun the ConsoleHost application after creating the certificate and binding it to you should receive the standard WCF service test page after entering the address in a browser.

Figure 4 – FileUploadService

Top of page

Client Application

Voila, now that the file upload service is ready we can begin to consume it. Let’s test this…

Add a new console application project named ConsoleClient to your solution. Next add a reference to the System.ServiceModel assembly. When done start the ConsoleHost application in non-debug mode. We need to add a reference to this service to the client application. For this the service needs to be up and running.

Right click on the ConsoleClient project within the Solution Explorer and choose “Add Service Reference…”. The Add Service Reference dialog is displayed. Enter the address of the service ( in the address field and click Go. The SSL certificate will be presented to the calling application (= Visual Studio). You’ll be prompted to accept the certificate. Click Yes to proceed.

Figure 5 – SSL Certificate Security Alert In Visual Studio 2008
SSL Certificate Security Alert

Rename the namespace ServiceReference1 to ServiceReferences and click Advanced. The Service Reference Settings dialog is shown. Make sure that the option “Always generate message contracts” is checked. Click OK to close this dialog. Click OK once more to let Visual Studio add the service reference and generate the necessary proxy classes.

Remark: If you don’t check the option “Always generate message contracts” then the message contract’s properties will appear as individual parameters in the proxy. And for the result type you’ll only get the property that is marked as the body of the SOAP message.

Unchecked the Upload(…) method on the proxy will be generated as such:

Listing 10: Always Generate Message Contracts

bool Upload(string fileName, long length, Stream stream);

instead of

FileReceivedInfo Upload(FileInfo fileInfo);

Once the necessary proxy classes have been generated consuming the service is an easy task. Consider the following code in the Main(…) method of the ConsoleClient application.

Listing 11 – ConsoleClient Application

using (FileUploadServiceClient proxy = new FileUploadServiceClient())
    FileStream inputStream = File.Open(@"c:\temp\uploadtest.txt", FileMode.Open);

    ServiceReferences.FileInfo fileInfo = 
        new ServiceReferences.FileInfo("uploaded.txt", inputStream.Length, inputStream);
    FileReceivedInfo info = proxy.Upload(fileInfo);
    if (info.UploadSucceeded)
        Console.WriteLine(String.Format("File {0} uploaded", info.FileName));
        Console.WriteLine(String.Format("File {0} upload failed", info.FileName));

Top of page

Authenticate The Server

Make sure the ConsoleHost application is still running and start the ConsoleClient application for the first time. Upon hitting the line of code that calls the Upload(…) method on the proxy you’ll receive the following error message:

Could not establish trust relationship for the SSL/TLS secure channel with authority ‘’.

This means that the server (ConsoleHost) presented the SSL certificate to the client application during the handshake and the client did not validate it. Thus a trusted relationship for the SSL/TLS secure channel could not be established.

To solve this problem add the following code to the beginning of the Main(…) method of the ConsoleClient application.

Listing 12 – Validating The Server

ServicePointManager.ServerCertificateValidationCallback += customXertificateValidation;

The code for the customXertificateValidation(…) method is as follows:

private static bool customXertificateValidation(object sender, X509Certificate cert, 
    X509Chain chain, SslPolicyErrors error)
    X509Certificate2 certificate = (X509Certificate2)cert;
    if (certificate.Subject == "CN=" && !String.IsNullOrEmpty(certificate.Thumbprint) &&
        certificate.Thumbprint.ToLower() == "cfdf5d1140b94614d047dd77c7bd4d4d4422e1c9")
        return true;
    return false;

When the customXertificateValidation(…) method is called (callback) by WCF to validate the server certificate you have the opportunity to inspect the certificate (= cert parameter). In this example the value of the Subject and Thumbprint are checked in order to validate the identity of the server. If they match then the server is authenticated, if not then the SSL connection cannot be established.

Remark: Feel free to adjust the validation of the SSL certificate to suit your own needs.

Upon running the client again after applying these changes you’ll receive the message “File uploaded.txt uploaded”.

Top of page

Authenticate The Client

At this point you could consider your file upload service complete, but one crucial item remains. Namely authenticating the client by the server.

I don’t want anyone who happens to stumble upon my service to be able to upload files to the web server. To block unauthorized users we have to validate the client. Luckily WCF allows you do so without much hassle.

Let’s return to the CGeers.Wcf.Service project for a while. Add a reference to the System.IdentityModel assembly and add a new class called CustomUserNameValidator.

Listing 13 – CustomUserNameValidator

public class CustomUserNameValidator : UserNamePasswordValidator
    public override void Validate(string userName, string password)
        if (userName != "Christophe" || password != "WCFRocks!")
            // This throws an informative fault to the client.
            throw new FaultException("Incorrect username or password");

This class derives from the UserNamePasswordValidator which can be found in the System.IdentityModel.Selectors namespace. Only one method needs to be overriden, namely the Validate(…) method. Here you need to specify your own custom code in order to validate the provided username and password. Just throw a FaultException if you cannot validate the user.

Next you need to configure the ConsoleHost application so that it uses the CustomUserNameValidator class in order to validate the clients. Open the App.config file of the ConsoleHost application and adjust the following settings:

For the basicHttpBinding set the required clientCredentialType of the transport security to Basic. This requires that the client present the necessary credentials (username & password).

Listing 14 – Adjusting The Binding

<security mode="Transport">
  <transport clientCredentialType="Basic" />

Then add the following bit of configuration to the service behavior to tell the service how to validate the client credentials.

Listing 15 – Adjusting The Service Behavior

    customUserNamePasswordValidatorType="CGeers.Wcf.Services.CustomUserNameValidator, CGeers.Wcf.Services" />

The service is now correctly setup and requires the client to provide the necessary credentials. If you run the ConsoleClient application now you’ll receive the following error message:

The HTTP request is unauthorized with client authentication scheme ‘Anonymous’. The authentication header received from the server was ‘Basic realm=””‘.

The configuration for the client application needs to reflect the changes we made to the service. Right click on the service reference and choose “Update Service Reference”.

Running the client application again will now result in the following error message:

The HTTP request was forbidden with client authentication scheme ‘Basic’.

You still need to adjust the client application so that it passes the correct credentials to the service. Add the following lines of code to the ConsoleClient’s Main(…) method.

Listing 16 – Client Credentials

proxy.ClientCredentials.UserName.UserName = "Christophe";
proxy.ClientCredentials.UserName.Password = "WCFRocks!";

That’s it! The client will now present valid credentials to the service and an SSL connection will be established.

Remark: The username and password validation in this article is kept short for illustrative purposes. Replace this with you’re own validation logic in real-world applications. A better approach would be to validate the username and password against a database containing the user’s credentials.

Top of page


I hope you enjoyed this tutorial on how to transfer files over HTTPS using WCF. To recap what we’ve covered from a developer’s point of view:

  • Streaming a file over WCF
  • Configuring a WCF service for communication over HTTPS(directories)
  • Generating and binding a test SSL certificate to a port
  • Consuming a service over HTTPS
  • Authenticating the server
  • Authenticating the client

Any comments, questions, tips…etc. are always appreciated.

Top of page


You can find the source code for this article on the Download page of this blog.

Top of page


21 Responses to “WCF over HTTPS”

  1. […] WCF over HTTPS – this blog give some demo about how-to host https-service and client. However, the difference is that we acces web resouce requiring cookies generation. […]

  2. Cha Says:

    Not sure if you had encounter this issue?

    FileStream inputStream = File.Open(@”c:\temp\uploadtest.txt”, FileMode.Open);

    ServiceReferences.FileInfo fileInfo =
    new ServiceReferences.FileInfo(“uploaded.txt”, inputStream.Length, inputStream);

    FileReceivedInfo info = proxy.Upload(fileInfo);

    Using the same code, my Upload prompts for 3 parameters (filename, message, uploaded) instead of fileInfo:

    FileReceivedInfo info = proxy.Upload(fileInfo);

    My interface:

    FileReceivedInfo Upload(FileInfo fileInfo);

    Not sure why it’s ignoring my paramter on WCF side. It seems it swapping the FileReceivedInfo’s properties for paramters? I recompiled and add new references (several times). Can you point me in the right direction in why? Maybe my config?

  3. Christophe Says:


    As stated in the article you have to check the option “Always generate message contracts” when generating your client proxies if you want to avoid that the properties of the message contracts appear as individual parameters in the proxy classes.

  4. Matjaz Says:

    What modifications are required to host your service in IIS, and then consume it?

    I receive a MessageSecurityException: The HTTP request is unauthorized with client authentication scheme ‘Basic’.The authentication header received from the server was ‘Negotiate,NTLM,Basic realm=””

    As I am informed, IIS is using it’s own authentication and you can not plug in a custom authentication.

  5. dan gershpny Says:

    Cool post…
    Very helpfull

  6. Raj Says:

    Simply Superb!!!

  7. Nikhil Says:

    Hi, we have hosted our WCF service according to your method. We have a static IP over which, we have hosted the service on port 8741. We can get the service manifest only if we try from the same machine where it is hosted. However, we want to host it over the internet for our customers. We have tried from another machine which is connected to the internet, but we get a message saying the service cannot be found. Interestingly, the static IP address responds correctly if we ping from the internet.

    Should we do anything else other than what you have given above?

    Thanks a lot in advance.



  8. Andres95 Says:

    A great post, thanx a lot…

  9. p mac Says:

    This is a fantastic post. I use it regularly as an example for evangelizing WCF.

  10. jpaquete Says:

    Many thanks man, you’re the man!

  11. t science Says:

    This is a very informative post. I wrote a WCF web service over ssl several months back, but had not yet run across your post. If I had, my task would have been much easier, as you present all the steps required in a clear and concise manner. Thank you for taking the time to write and post this.

  12. shcjwmvgmt@mailinator.com Says:

    Thanks for writing up the article. I was having a lot of difficulty setting up WCF over ssl until I found your simple instructions.

  13. themikest Says:

    This is the best and simplest tutorial, it should be on the MSDN! Thank you!

    My only problem is that my browser does not require the authentication. Is it normal?

    And a little comment: in windows 7 the httpcfg.exe is not present, netsh is used instead. E.g.:
    instead: httpcfg.exe set ssl -i -h cfdf5d1140b94614d047dd77c7bd4d4d4422e1c9 -Personal
    use this: netsh http add sslcert ipport= certhash=cfdf5d1140b94614d047dd77c7bd4d4d4422e1c9 appid={00112233-4455-6677-8899-AABBCCDDEEFF}

    where the appid is: right click on the ConsoleHost project, choose Properties then click on the Asembly Information button and copy the GUID.

  14. Mau Says:

    Hi Christophe, are you interested in some consulting?
    I need to setup my env with https and after several hours trying to figure it out, still haven’t been able to success on it.
    Please reply to my email accordingly.

  15. Jo Zun Says:

    Thanks for ur guide 😀 help me through my struggling over first https wcf project 😀

  16. asdasdasd Says:

    getting error while doing netshs
    SSL certificate add failed…

  17. asdasdasd Says:

    does work now.. with adding
    in MMC you need to do this:
    make sure you’ve installed the certificate in the service account (MMC -> add snap-in -> certificates -> computer account) and NOT in your personal

  18. Michael Says:

    Hey! This is fantastic post. This is what I need to implement in my new project.
    I have problem while implementing service, I am getting error while running host. at following line.

    ServiceHost host = new ServiceHost(service);
    Error: The type initializer for ‘System.ServiceModel.Diagnostics.TraceUtility’ threw an exception.

    How can I solve this issue?
    Is there a need to have same configuration in Service implementation project and Hosting application project?

    My Config File on is as follows.

    And my Config file on Console Host application is as follows:

    Can any one help me to correct this config files?

  19. Michael Says:


    My Problem is solved. It was mistake in my app.Config file on ConsoleHost. I put all the config information outside the tag.

    After that I had problem with SSL things, but i found solution from the other Replies over here only and from MSDN site.

    thanks, This is really helpful post for me.

    I just want to know that, How can I use this solution to host my service and use it from Different machines over Internet. As I need to host this service on Internet so that our user can use this service from outside of our domain.

    Please send me some information for hoting it on web and consuming it using different windows client.


  20. william Says:

    I got this whole thing to kinda work… Only thing is i get a 403 error on everything I request. if I use my browser to see the web service everything is blank..

  21. For the reason that the admin of this site is working, no hesitation very shortly it will be famous, due to its
    quality contents.

Comments are closed.

%d bloggers like this: