Showing posts with label SSL and Certificates. Show all posts
Showing posts with label SSL and Certificates. Show all posts

Friday, August 1, 2014

Setting up a secure WCF web service from "scratch" with ASP.NET Identity

For this example, I'm using Visual Studio 2013 with Update 2 installed.  Update 2 will ensure that the templates use the latest and greatest versions of all technologies involved.

Add a project using the WCF Service Application template.  You could also use the Asp.Net Web Application template, but I think you'll just end up with a bunch of extra references you don't need.  Now take a look at the web.config file.  You'll notice that using this template automatically adds the protocolMapping element which allows the service to be accessed using https.  If you did not use this template, you would have had to add this for https support.  You'll also notice that there are no endpoints or bindings configured.  That's because WCF has default bindings and IIS can determine the endpoint by examining the .svc file.

I delete the sample service created for you.

Next, I find it convenient to host the web services I'm developing on my local IIS.  Click on My Project, Web tab, and type http://{name of your computer}.{domain}.{com}/{something meaningful}.  It is important to use the fully qualified name of your computer because the certificate you will soon use is named the same and WCF won't like it if they are not the same.  Click Create Virtual Directory.  Open up IIS Manager, and change the app pool of the site to one with an associated identity of your (hopefully powerful) account.  This becomes important when you connect to a database for authentication.

Add your web service by using the WCF Service template.  Build the solution, and right click the service.  Choose View in Browser to make sure all is well so far.

Now it's time to enable SSL (https).  Read here to do this.  At this point, you'll want to change the Project URL to include https on the Web tab when you go to My Project.  I think it's a good idea to go ahead and setup a binding configuration and an endpoint.  As I said earlier, WCF can figure all this out because of default values.  However, Visual Studio's Add Service Reference automatic proxy generator can get confused when you are setting up a client.  You can either use the Microsoft Service Configuration Editor (Right click web.config and choose Edit WCF Configuration) tool or edit the web.config by hand.  If you use the tool, just click the Service folder, and choose Create a New Service.  This will walk you through a wizard to create the service node.  After that there is a link to create the binding configuration.  You end up with this inside the <system.serviceModel> element:

    <bindings>
      <basicHttpsBinding>
        <binding name="BasicHttpsBindingConfig" />
      </basicHttpsBinding>
    </bindings>
    <services>
      <service name="AssemblyName.YourService">
        <endpoint address="https://ComputerName.domain.whatever/IISSite/YourService.svc"
          binding="basicHttpsBinding" bindingConfiguration="BasicHttpsBindingConfig"
          contract="AssemblyName.IYourService" />
      </service>
    </services>

You also need to set the multipleSiteBindingEnabled to False when you are using https:

<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="false" />

At this point, I would create a little test app in a completely different solution and make sure you can call your new web service.  A Windows Console app will work great.  Just Add, Service Reference and specify the endpoint of the service.  By adding the reference, Visual Studio will add the following to your app.config file:

        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpsBinding_IYourService">
                    <security mode="Transport" />
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="https://ComputerName.domain.whatever/IISSite/YourService.svc"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpsBinding_IYourService"
                contract="BenefitsServiceReference.IYourService" name="BasicHttpsBinding_IYourService" />
        </client>

There are a couple of things to note here.  Why does the client configuration set up for basicHttpBinding when the server configuration is set up for basicHttpsBinding?  Essentially basicHttpBinding and basicHttpsBinding are exactly the same.  The only difference is that basicHttpsBinding includes the <security mode="Transport" /> by default.

Now that the web service is up and running with SSL we want to configure it to require credentials in order to access it.  You can use the ASP.NET Membership, but this has been superceded by ASP.NET Identity and WCF doesn't directly interface with ASP.NET Identity yet.  So, we will create a custom user name and password validator which we will eventually use to authenticate with ASP.NET Identity.  First, we need to create the validator class itself.  Add references to the following:
  • System.IdentityModel
  • System.IdentityModel.Selectors
Add this class to your project:

Imports System.IdentityModel.Selectors
Imports System.ServiceModel

Public Class CustomUserNameValidator
    Inherits UserNamePasswordValidator

    Public Overrides Sub Validate(userName As String, password As String)

        If Not (userName = "Steve" AndAlso password = "topsecret") Then

            Throw New FaultException("Unknown Username or Incorrect Password")

        End If

    End Sub

End Class

It's pretty obvious what is happening here.  If your User Name and Password is not right, an exception gets thrown back to the client.  Now we have to configure the web service to use this.  First of all we need to edit our binding configuration.  Remember that by default basicHttpsBinding uses Transport as the Security mode.  We need to change this to TransportWithMessageCredential.  We are protecting (SSL) the service at the transport (IIS) level and authenticating at the message (web service) level.  Here is the binding configuration after we do that:

    <bindings>
      <basicHttpsBinding>
        <binding name="BasicHttpsBindingConfig">
          <security mode="TransportWithMessageCredential" />
        </binding>
      </basicHttpsBinding>
    </bindings>

You also need to add a serviceCredentials behavior to your existing behavior element:

    <behaviors>
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
          <serviceCredentials>
            <userNameAuthentication userNamePasswordValidationMode="Custom"
              customUserNamePasswordValidatorType="AssemblyName.CustomUserNameValidator, AssemblyName" />
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>

As with most configuration changes, here you're definitely better off using the Service Configuration Editor tool.  This is where we specify our custom class for validating the user name and password.  Note the specific way that the class is specified along with the assembly that contains the class.

The only configuration change in your client console app is changing the security mode to "TransportWithMessageCredential".  When you run your console, you will notice that you'll get an exception if you fail to provide credentials.  To specify the client's credentials, do the following:

        YourServiceProxy.ClientCredentials.UserName.UserName = "Steve"
        YourServiceProxy.ClientCredentials.UserName.Password = "topsecret"

If the client provides accurate credentials, then the service will return results; otherwise, an exception will be thrown.

With this infrastructure in place, you can change the code in the CustomUserNameValidator class to connect to any database you want to authenticate.  I mentioned earlier my desire to access ASP.NET Identity to authenticate users for the web service.  In a separate solution, I have set up an ASP.NET MVC Web Application and configured it with ASP.NET Identity authentication.  I am going to use this web application to administer user accounts for my web service.  I simply add this class to my MVC project:

Imports Microsoft.AspNet.Identity
Imports Microsoft.AspNet.Identity.EntityFramework

Public Module SecurityManager

    Public Function IsAuthenticated(userName As String, password As String) As Boolean

        Dim MyUserStore As UserStore(Of IdentityUser) = New UserStore(Of IdentityUser)
        Dim MyUserManager As UserManager(Of IdentityUser) = New UserManager(Of IdentityUser)(MyUserStore)

        Dim MyUser As IdentityUser = MyUserManager.Find(userName, password)

        If MyUser Is Nothing Then
            Return False
        Else
            Return True
        End If

    End Function

End Module

Now, if I can call this function from my CustomUserNameValidator in my WCF project, I will be authenticating using ASP.NET Identity.  First add a reference to the dll produced by building the MVC project.  Then, simply add this code to your CustomUserNameValidator:

        If Not SecurityManager.IsAuthenticated(userName, password) Then
            Throw New FaultException("Unknown Username or Incorrect Password")
        End If

Of course, it's not quite that easy.  You need to do two more things:
  1. Use NuGet to install the same version of Entity Framework that your MVC project uses.  This will setup your web.config with some special Entity Framework stuff that you will need.
  2. Copy the connectionStrings section out of the MVC web.config file and put it into the WCF web.config file.  This connection string contains the database where the identity data is.
To get the name of the user who is accessing your service use this line:

System.ServiceModel.ServiceSecurityContext.Current.PrimaryIdentity.Name

So there we go.  We now have a secure web service.  Only accessible via https and secured by credentials stored in an ASP.NET Identity database.

Wednesday, July 30, 2014

Protect a WCF service using SSL - Part 2

In a prior post, I described how to protect a WCF service using SSL.  In the service .config file, you must do the following in order to expose the https endpoint:

<configuration>
  <system.serviceModel>
    <protocolMapping>
      <add scheme="https" binding="basicHttpsBinding" />
    </protocolMapping>
  </system.serviceModel>
</configuration>

The client configuration also needs some specific settings:

    <system.serviceModel>
        <bindings>
          <basicHttpsBinding>
            <binding name="BasicHttpsBinding_IDayOfTheWeekService"></binding>
          </basicHttpsBinding>
        </bindings>
        <client>
            <endpoint address="https://computername.yourdomain.net/WcfSecureServer/DayOfTheWeekService.svc"
                binding="basicHttpsBinding" bindingConfiguration="BasicHttpsBinding_IDayOfTheWeekService"
                contract="DayOfTheWeekReference.IDayOfTheWeekService" name="BasicHttpsBinding_IDayOfTheWeekService" />
        </client>
    </system.serviceModel>

Note that I am using basicHttpsBinding.  This just came out with .NET 4.5.  It's exactly the same as basicHttpBinding except the <security mode="Transport" /> is default so you don't have to specify this.  Also, if you generate your own certificate, you will get an error message that says something like:
Could not establish trust relationship for the SSL/TLS secure channel with authority 'localhost'
This is because WCF doesn't trust your self generated cert.  However, if you generate a certificate with the name of the computer that it is issued to, and specify the fully qualified name of the computer in the URL like I did above, WCF will allow it.

Tuesday, July 15, 2014

Protect a WCF service using SSL - Part 1

We have a scenario where we are exposing a WCF service over the internet that is hosted in IIS.  We want the communication with the service to be protected using SSL.  The encryption/decryption is handled completely by IIS, so there is minimal need to edit the configuration of the service itself (See Part 2).  First you need a certificate.  To create your own for testing:

  1. Open IIS (as administrator).
  2. Choose the top level node in the Connections pane.
  3. Click Server Certificates in the IIS section of the middle pane.
  4. Click Create Self Signed Certificate.  The friendly name should match the name of the computer.
Now configure SSL:
  1. Right click Default Web Site and choose Edit Bindings.
  2. Click Add, choose HTTPS, and select the certificate you just created.
Finally, set up your service to require SSL:
  1. Click on the web application that hosts your service.
  2. In the middle pane, IIS section, double click on SSL Settings and check the "Require SSL" box.

Saturday, April 5, 2014

How to enable SSL on a web site in IIS


  1. Open IIS.
  2. Click the root node in the tree view at the left.
  3. Double click Server Certificates.
  4. In the list on the right, choose either Create Self Signed Certificate or Import.
  5. Click the site you want to secure.
  6. Click Bindings on the right and click Add.
  7. Choose https and select the certificate in the drop down list.
That's it.  Now you're site is listening on port 443 (default for https requests).  If you're using a self-signed certificate, your browser will throw up all kinds of red flags, but you are encrypted now.

Lots of good info here.

Tuesday, August 13, 2013

Symmetric and Asymmetric Encryption (and SSL)

Symmetric encryption requires one shared private key.  This same key is used by Person A to encrypt the message and by Person B to decrypt the message.  They can go back and forth all day long using the same key.  It's great and it's private.  However, the problem is getting the key to the other person.  Physically giving the key to the other person is great, but obviously not practical for an internet situation.

Asymmetric encryption is another technique.  Asymmetric means one-way.  It is another means of encrypting a message, but it can only occur in one direction.  Person A can encrypt a message that only Person B can decrypt.  How?  Mathematics.  You don't really need to understand the cryptography details to understand this.  Person B has a private key.  Mathematically, Person B can derive a public key from this private key.  Due to the miracle of modern math, this "public" key can be used to encrypt a message, but not decrypt the message.  Person B can give this public key out to anyone that he wants to receive a secure message from and only Person B will be able to decrypt the message.  So Person B sends Person A the public key.  Person A encrypts his message using the public key, and sends it to Person B who decrypts the message using the private key.  The big breakthrough here is that a private key never has to be given out.

How are these two methods used?  SSL.  A combination of these two methods is used to establish a secure browsing session.  The web server has a certificate installed that contains the private key.  When average Joe points his browser to the web site, the server will generate a certificate that contains the public key derived from this private key and send it to the browser.  The browser generates something called a "session key" (AKA shared private key ), and sends this to the web server using the public key in the certificate.  Now, the web server and the browser both know the session key (AKA shared private key) and can begin communicating both ways using symmetric encryption.

To summarize, the shared private key is securely given to the web server by using asymmetric encryption.  Once the shared private key is in both party's hands, symmetric encryption can commence.