Thursday, 5 November 2020

Managing Authentication Token Expiry In WebAssembly-based Blazor

 The Blazor WebAssembly project template doesn't feature an option to include authentication. If you want to add authentication to a WebAssembly-based Blazor application, you need to do it yourself. This article shows how to add application-wide authentication management and then to use built-in Razor components to protect the FetchData page in the standard template from unauthorised users.

There are some good starting points when looking at Authentication in Blazor applications. The official docs explain how to apply authentication to a Blazor Server application. On the client side, Chris Sainty has looked at managing authentication with an Identity database in one of his excellent series of Blazor articles. And Steve Sanderson (main Blazor bloke at Microsoft) provides a demo app that he showed at NDC Oslo in June this year.

Like the other examples, this article will show how to use a Web API endpoint to issue a JSON Web Token (JWT) to a validated user. Where this article builds on the other examples is in demonstrating how to manage the expiry of the token in the browser.

warning Warning

Just like input validation, client side authentication and authorisation management in Blazor can be circumvented. It is therefore very important that you properly protect server-side resources as well.

This walkthrough starts with the standard ASP.NET Core Hosted WebAssembly Blazor project:

Blazor WASM

I've called mine BlazorWasmAuthentication if you want to copy and paste code from here. The resulting solution includes 3 projects: Server, Client and Shared. Each will require amending.

Amend the Shared Project

The first changes are made to the Shared project. This is the .NET class library that holds code (mainly model classes) that are shared between the Client and Server projects. Add two classes, Credentials and LoginResult:

using System.ComponentModel.DataAnnotations;

namespace BlazorWasmAuthentication.Shared
{
    public class Credentials
    {
        [Required]
        public string Email { get; set; }

        [Required]
        public string Password { get; set; }
    }
}
using System;

namespace BlazorWasmAuthentication.Shared
{
    public class LoginResult
    {
        public string Token { get; set; }
        public DateTime Expiry { get; set; }
    }
}

Amend the Server Project

The Server project requires a few amendments. It needs to be configured to make use of ASP.NET Core authentication management with JWT bearer tokens. It also needs to provide an API that allows users to authenticate, and it needs to store the authorised user's credentials securely.

To simplify things, I won't configure an Identity database for the user credentials. Chris Sainty provides clear instructions on how to do this in his article, should you need help. The credentials for this example will be stored in an appSettings file, with the password hashed using the Identity PasswordHasher introduced in this article.

  1. Add an appSettings.json file to the server project with the following content:
    {
      "Jwt": {
        "Key": ITNN8mPfS2ivOqr1eRWK0Rac3sRAchQdG8BUy0pK4vQ3",
        "Issuer": "MyApp",
        "Audience": "MyAppAudience"
      },
      "Credentials": {
        "Email": "user@test.com",
        "Password": "AQAAAAEAACcQAAAAENsLEigZGIs6kEdhJ7X1d7ChFZ4TKQHHYZCDoLSiPYy/GpYw4lmMOalsn8g/7debnA=="
      }
    }
    
    The password has been hashed. Its original value was "test-password".
  2. The next step is to amend project to include an additional package: Microsoft.AspNetCore.Authentication.JwtBearer. You can add this in any way that you prefer. The simplest way is to add a package reference to the project file:
    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.0.0" />
  3. Next, you need to configure the application to use JWT bearer tokens. This is done in Startup, and first requires the addition of some using directives:
    using System.Text;
    using Microsoft.IdentityModel.Tokens;
    using Microsoft.AspNetCore.Authentication.JwtBearer;
    using Microsoft.Extensions.Configuration;
  4. Then you need to access the Configuration API. Inject the IConfiguration service into a constructor, and assign it to a public property:
    public Startup(IConfiguration configuration) => Configuration = configuration;
    
    public IConfiguration Configuration { get; }
  5. Configure authentication in ConfigureServices:
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:Issuer"],
            ValidAudience = Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
        };
    }); 
  6. Then add authentication and authorisation middleware to the request pipeline in the Configure method. Ensure that they are added after Routing and before EndPoint configuration:
    app.UseAuthentication();
    app.UseAuthorization();
  7. Add an [Authorize] attribute to the existing WeatherForecast controller:
    namespace BlazorWasmAuthentication.Server.Controllers
    {
        [Authorize]
        [ApiController]
        [Route("[controller]")]
        public class WeatherForecastController : ControllerBase
        {
    
    Remembering the warning at the top of this article, this is an important step. If you don't want unauthorised users to be able to access the information provided by the weather forecast service, it is not enough to use client side code to prevent access. Anyone with fairly basic knowledge of browser developer tools might be able to circumvent client-side restrictions.
  8. Finally, create a Web API controller named LoginContoller:
    using BlazorWasmAuthentication.Shared;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Configuration;
    using Microsoft.IdentityModel.Tokens;
    using System;
    using System.IdentityModel.Tokens.Jwt;
    using System.Security.Claims;
    using System.Text;
    
    namespace BlazorWasmAuthentication.Server.Controllers
    {
    
        [ApiController]
        public class LoginController : ControllerBase
        {
            private readonly IConfiguration_configuration;
    
            public LoginController(IConfiguration configuration) => _configuration = configuration;
    
            [HttpPost("api/login")]
    
            public LoginResult Login(Credentials credentials)
            {
                var expiry = DateTime.Now.AddMinutes(2);
                return ValidateCredentials(credentials) ? new LoginResult { Token = GenerateJWT(credentials.Email, expiry), Expiry = expiry } : new LoginResult();
            }
    
            bool ValidateCredentials(Credentials credentials)
            {
              var user = _configuration.GetSection("Credentials").Get<Credentials>();
              var passwordHasher = new PasswordHasher<string>();
              return passwordHasher.VerifyHashedPassword(null, user.Password, credentials.Password) == PasswordVerificationResult.Success;
            }
    
            private string GenerateJWT(string email, DateTime expiry)
            {
              var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));
              var token = new JwtSecurityToken(
                  _configuration["Jwt:Issuer"],
                  _configuration["Jwt:Audience"],
                  new[] { new Claim(ClaimTypes.Name, email) },
                  expires: expiry,
                  signingCredentials: new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256)
              );
              var tokenHandler = new JwtSecurityTokenHandler();
              return tokenHandler.WriteToken(token);
            }
        }
    }
    For the purposes of demonstration, the token expiry is set to 2 minutes. This is so that you can test expiry without growing old. The Web API entry point validates the credentials. In this example, the code simply reads the credentials stored in the configuration file and compares them to the posted values. If they are valid, a LoginResult is returned complete with a token and an expiry. Otherwise an empty LoginResult is returned. The code for generating the token is pretty much boilerplate, and lifted directly out of Steve Sanderson's demo.

The Client Application

Authentication management in the client application relies on two principal actors: a class that derives from AuthenticationStateProvider, implementing its GetAuthenticationStateAsync method; and a CascadingAuthenticationState component. The CascadingAuthenticationState component obtains the current authentication state of the user by subscribing to the AuthenticationStateProvider's AuthenticationStateChanged event. Then the CascadingAuthenticationState component makes that information available to children via a cascading value of type Task<AuthenticationState>. The AuthenticationStateProvider is responsible for setting the authentication status of the user.

  1. Start by adding a package reference to Microsoft.AspNetCore.Components.Authorization in the Client project's csproj file:
    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.0-preview1.19508.20" />
  2. Add a using directive to the _Imports.razor file to bring the contents of the package into scope along with the ASP.NET Core authentication package:
    @using Microsoft.AspNetCore.Authorization
    @using Microsoft.AspNetCore.Components.Authorization
  3. Add a folder named AuthenticationStateProviders, and inside it, add a C# class file named TokenAuthenticationStateProvider.cs with the following code:
    using Microsoft.AspNetCore.Components.Authorization;
    using Microsoft.JSInterop;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Claims;
    using System.Text.Json;
    using System.Threading.Tasks;
    
    namespace BlazorWasmAuthentication.Client.AuthenticationStateProviders
    {
        public class TokenAuthenticationStateProvider : AuthenticationStateProvider
        {
            private readonly IJSRuntime_jsRuntime;
    
            public TokenAuthenticationStateProvider(IJSRuntime jsRuntime)
            {
                _jsRuntime = jsRuntime;
            }
            
            public async Task SetTokenAsync(string token, DateTime expiry = default)
            {
                if (token == null)
                {
                    await _jsRuntime.InvokeAsync<object>("localStorage.removeItem", "authToken");
                    await _jsRuntime.InvokeAsync<object>("localStorage.removeItem", "authTokenExpiry");
                }
                else
                {
                    await _jsRuntime.InvokeAsync<object>("localStorage.setItem", "authToken", token);
                    await _jsRuntime.InvokeAsync<object>("localStorage.setItem", "authTokenExpiry", expiry);
                }
    
                NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
            }
    
            public async Task<string> GetTokenAsync()
            {
                var expiry = await _jsRuntime.InvokeAsync<object>("localStorage.getItem", "authTokenExpiry");
                if(expiry != null)
                {
                    if(DateTime.Parse(expiry.ToString()) > DateTime.Now)
                    {
                        return await _jsRuntime.InvokeAsync<string>("localStorage.getItem", "authToken");
                    }
                    else
                    {
                        await SetTokenAsync(null);
                    }
                }    
                return null;
            }
    
    
            public override async Task<AuthenticationState> GetAuthenticationStateAsync()
            {
                var token = await GetTokenAsync();
                var identity = string.IsNullOrEmpty(token)
                    ? new ClaimsIdentity()
                    : new ClaimsIdentity(ParseClaimsFromJwt(token), "jwt");
                return new AuthenticationState(new ClaimsPrincipal(identity));
            }
    
            private static IEnumerable<Claim> ParseClaimsFromJwt(string jwt)
            {
                var payload = jwt.Split('.')[1];
                var jsonBytes = ParseBase64WithoutPadding(payload);
                var keyValuePairs = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonBytes);
                return keyValuePairs.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString()));
            }
    
            private static byte[] ParseBase64WithoutPadding(string base64)
            {
                switch (base64.Length % 4)
                {
                    case 2: base64 += "=="; break;
                    case 3: base64 += "="; break;
                }
                return Convert.FromBase64String(base64);
            }
        }
    }
    This code is largely based on the Mission Control demo. The AuthenticationStateProvider includes a SetTokenAsync method and a GetTokenAsync method. The SetTokenAsync method uses Blazor's JavaScript interop service to use the browser's local storage feaure to store the token, if one is provided. It also stores the token's expiry time. If no token is provided, the method removes both the storage keys related to the token and its expiry time, effectively logging the user out. Finally, the method calls NotifyAuthenticationStateChanged, which raises the AuthenticationStateChanged event that the CascadingAuthenticationState component subscribes to, updating the CascadingAuthenticationState component about the current authentication status of the user.

    The GetTokenAsync method checks the expiry time of the token. If the expiry time has expired, the SetToken method is called without a token being provided, logging the user out. Otherwise a valid token is returned, if one exists.

    The final public method, which must be overridden in classes that derive from AuthenticationStateProvider, is the GetAuthenticationStateAsync method. This method parses the JSON Web Token and creates a ClaimsPrincipal (representing the current user) with either the identity information (ClaimsIdentity) obtained from the token, or an empty ClaimsIdentity if no token exists.
    info The method for parsing the JWT is taken from the Mission Control demo. JWTs contain three parts: a header, a payload (the source of the ClaimsIdentity information) and a signature. Each part is Base64 Url encoded and then the parts are joined using dots. The final output e.g. header.payload.signature forms the token. When using Base64 Url encoding, output padding is optional, and in fact is not included in the generation of JWTs. The System.Convert.FromBase64String method expects the input string to have output padding where necessary, and will raise a FormatException if it is missing. Therefore the additional private method at the end of the class is used to put padding characters (=) on to the end of the payload if they are needed before the string is decoded.
  4. The AuthenticationStateProvider needs to be registered with the dependency injection system. This is done in the ConfigureServices method in Startup. Add authentication services to the application too:
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthorizationCore();
        services.AddScoped<TokenAuthenticationStateProvider>();
        services.AddScoped<AuthenticationStateProvider>(provider => provider.GetRequiredService<TokenAuthenticationStateProvider>());
    }
    The TokenAuthenticationStateProvider is registered so that it can be injected directly into components etc, and then the injected service is registered as the implementation of AuthenticationStateProvider. This isn't necessarily a recommended pattern. But it makes things simpler for demo purposes. If you want to adopt a more robust approach, move the methods that get and set tokens from the TokenAuthenticationStateProvider into a separate service and use that where this demo explicitly injects the TokenAuthenticationStateProvider. Check Chris Sainty's AuthService for some inspiration.
  5. The next step involves creating the Login form. Add a new Razor Component to the Pages folder named Login.razor wih the following code:
    @inject HttpClient Http
    @inject TokenAuthenticationStateProvider AuthStateProvider
    
    <div class="container col-6">
        @if (loginFailure)
        {
            <div class="alert alert-danger">Your credentials did not work. Please try again.</div>
        }
        <div class="card">
            <div class="card-body">
                <h5 class="card-title">Login</h5>
                  <EditForm @ref="loginform" Model="credentials" OnValidSubmit="SubmitCredentials">
                    <DataAnnotationsValidator />
    
                    <div class="form-group">>
                        <label>Email address</label>
                        <InputText class="form-control" @bind-Value="credentials.Email" />
                        <ValidationMessage For="@(()=> credentials.Email)" />
                    </div>
                    <div class="form-group">
                        <label>Password</label>
                        <InputText type="password" class="form-control" @bind-Value="credentials.Password" />
                        <ValidationMessage For="@(()=> credentials.Password)" />
                    <div/>
                    <button type="submit" class="btn btn-outline-primary btn-sm">Submit</button>
                </EditForm>
            </div>
        </div>
    </div>
    @code {
        Credentials credentials = new Credentials();
        bool loginFailure;
    
        EditForm loginform { get; set; }
    
        async Task SubmitCredentials()
        {
            var result = await Http.PostJsonAsync<LoginResult>("api/login", credentials);
            loginFailure = result.Token == null;
            if (!loginFailure)
            {
                await AuthStateProvider.SetTokenAsync(result.Token, result.Expiry);
            }
        }
    }
    
    There is not much to explan here. If the form validation succeeds, the SubmitCredentials method is called. If the login is successful (indicated by the presence of a token in the response from the LoginController), the injected TokenAuthenticationStateProvider sets the token, which as you remember, results in the authentication status being updated with any component that subscribes to the NotifyAuthenticationStateChanged event.
  6. Now it's time to introduce the component that does subscribe to the NotifyAuthenticationStateChanged event, the CascadingAuthenticationState component. Open the App.razor file and replace the existing content with the following:
    <CascadingAuthenticationState>
        <Router AppAssembly="@typeof(Program).Assembly">
            <Found Context="routeData">
                <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                    <NotAuthorized>
                        <Login/>
                    </NotAuthorized>
                </AuthorizeRouteView>
            </Found>
            <NotFound>
                <LayoutView Layout="@typeof(MainLayout)">
                    <p>Sorry, there's nothing at this address.</p>
                </LayoutView>
            </NotFound>
        </Router>
    </CascadingAuthenticationState>
    
    First, you added the wrapped the entire application in the CascadingAuthenticationState component, ensuring that any other application component is able to receive its Task<AuthenticationState> cascading value as a parameter. You changed the RouteView component for an AuthorizeRouteView, which does the same except that it only displays the content of the page if the user is authenticated. If the user is not authenticated, the child content of the NotAuthorized component is displayed, i.e. the login component that you just created.
  7. Change the top of the FetchData.razor file to look like this:
    @page "/fetchdata"
    @using BlazorWasmAuthentication.Shared
    @using System.Net.Http.Headers;
    @inject HttpClient Http
    @inject TokenAuthenticationStateProvider TokenProvider
    @attribute[Authorize]
    <h1>Weather forecast</h1>
    You changes involve the addition of a using directive to bring System.Net.Http.Headers into scope; you injected the TokenAuthenticationStateProvider; and you added an [Authorize] attribute to the page. If you try to run the page at this stage, you should see the login form that you created:
    login form
  8. Now amend the @code block in Fetchdata as follows:
    @code {
        private WeatherForecast[] forecasts;
    
        protected override async Task OnInitializedAsync()
        {
            var token = await TokenProvider.GetTokenAsync();
            if (token != null)
            {
                Http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
                forecasts = await Http.GetJsonAsync<WeatherForecast[]>("WeatherForecast");
            }
        }
    }
    
    The existing code has been altered to obtain the JWT token and then add it to the api request for weather forecast data as a request header. Without this, there is no way for the API to authenticate the user. Remember, the GetTokenAsync method will log the user out if the token has expired. If that happens, the user will be presented with the login form again.
  9. Open the MainLayout.razor file and replace the About link with the following code:
    <AuthorizeView>Logged in as @context.User.Identity.Name 
        <button class="btn btn-sm btn-outline-dark" @onclick="@(() => TokenProvider.SetTokenAsync(null))">Logout</button>
    </AuthorizeView>
    

This last step completes the demo. Ensuring that the Server project is set as the Startup project, run the application in the browser. Navigate to the FetchData page and log in. You should see the data, and the message at the top of the page telling you that you are logged in together with a log out button. Click it, and you should be presented with the login form again. This time, after you have logged in, wait for a couple of minutes. Then refresh the page. You should get logged out and presented with the login form again.

Wednesday, 4 November 2020

Create SSL / TLS Self-Signed Certificates on Ubuntu 16.04 | 18.04 | 18.10

 Self-signed certificates are mostly used internally within labs or business environments… and not used externally for commercial use… as they’re not from trusted third-party certificate authorities… Only trusted certificate authorities (CA) can issue SSL/TLS certificates for commercial in the public domains…

If you install a self-signed certificate on a public website or entity, your web browser will prompt you that the resource can’t be trusted.. be the certificate installed isn’t from a trusted third-party… That’s why it’s mostly use internally for testing purposes..

This brief tutorial is going to show students and new users how to create self-signed certificates on Ubuntu systems to use internally or within a lab environment for test purposes…

To create a self-signed certificate on Ubuntu systems, follow the steps below

Step 1: Create a RSA Private Key

When creating a self-signed certificates, you must first create a server private key… This key should stay private and stored on the server and not shared externally… The private key is used to then create a public certificate that you can share with others…

To create a private key, run the commands below

openssl genrsa -aes128 -out server.key 2048

When creating a server private key, you will be prompted to create and confirm and password or passphrase. However, it’s best to create a key without a passphrase. To remove the passphrase from the key you just created, run the commands below.

Generating RSA private key, 2048 bit long modulus
....+++
...................+++
e is 65537 (0x010001)
Enter pass phrase for server.key:
Verifying - Enter pass phrase for server.key:

Run the commands below to remove the passphrass you entered above… It’s best not to keep the passphrase on the server key…

openssl rsa -in server.key -out server.key

When you’re done, you will have created a private key for the server called server.key.

Step 2: Create a Certificate Signing Request (CRS)

After creating the private key, run the commands below to create a certificate signing request using the server private key. Certificate signing request or CSR is used to provide some details of the entity and the resource you want to incorporate into the request…

To create a CSR request for the domain example.com, run the commands below

openssl req -new -days 365 -key server.key -out example.com.csr

When you run the above commands, you should be prompted with the questions below to incorporate into the certificate. Answer the highlighted lines as shown below.

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:New York
Locality Name (eg, city) []:Brooklyn
Organization Name (eg, company) [Internet Widgits Pty Ltd]:My Business
Organizational Unit Name (eg, section) []:Website
Common Name (e.g. server FQDN or YOUR name) []:example.com
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []: LEAVE BLANK
An optional company name []:

After that, a new certificate request file called example.com.csr should be created with the incorporated deatils above… This file can be used to genenerate SSL/TLS certificate for the domain example.com

Step 3: Create a Self-Signed Certificate

Now that the Private key and CSR are create, run the commands below to create a self-signed SSL certificate called example.com.crt that will be valid for 365 days…

openssl x509 -in example.com.csr -out example.com.crt -req -signkey server.key -days 365

You should then see the texts below:

Signature ok
subject=C = US, ST = New York, L = Brooklyn, O = My Business, OU = IT, CN = example.com, emailAddress = admin@example.com
Getting Private key

That’s it! you have just created a self-signed certificate called example.com.crt…

In fact, you can send the CSR file called example.com.csr to a trusted certificate authority to generate a trusted certificate for your externally used resources.

SelfCert: Create a Self-Signed Certificate Interactively (GUI) or Programmatically in .NET

 While this isn't new, I needed a new home for it since my old Pluralsight blog is gone now. Hopefully you'll find it helpful!

It's a bit of a pain to create self-signed certs using MAKECERT. So here's a GUI-based tool that uses a combination of the .NET Framework and the CryptoAPI to create self-signed X.509 certificates. And it's factored so that you can use the underlying library standalone - you can easily create certs programmatically now.

Here's the GUI:

The GUI has some nifty features: you can create a PFX file directly, or you can save directly to a cert store of your choice. When you save to a cert store, an extra dialog pops up showing you where the private key file resides, so that you can adjust the ACL accordingly. I've got a "view private key" feature that launches explorer with the /select argument, taking you to the private key file so that you can set the ACL on it. Anyway, this extra dialog gives you some quick info you typically want, like the thumbprint. And there are buttons for browsing the cert store and viewing the certificate as well from here.

The GUI gens the RSA key pair on a background thread, so a) the app doesn't lock up on you, and b) if you get tired of waiting for the key to gen, you can cancel easily enough :)

Here's some code that does this programmatically by calling the Pluralsight.Crypto library that is underneath all of this. Those of you who are familiar with the CryptoAPI will recognize the key abstraction here, CryptContext.

static void GenSelfSignedCert()

{

using (CryptContext ctx = new CryptContext())

{

ctx.Open();

X509Certificate2 cert = ctx.CreateSelfSignedCertificate(

new SelfSignedCertProperties

{

IsPrivateKeyExportable = true,

KeyBitLength = 4096,

Name = new X500DistinguishedName("cn=localhost"),

ValidFrom = DateTime.Today.AddDays(-1),

ValidTo = DateTime.Today.AddYears(1),

});

X509Certificate2UI.DisplayCertificate(cert);

}

}

Make sure you've got the Microsoft .NET Framework 3.5 installed. Self-Cert relies on it.

Download the project here, which includes binaries and sources. Feel free to use Pluralsight.Crypto in your own projects if you find it useful. Enjoy!

How to Create a Self Signed SSL Certificate with Windows Server

What to do

These steps will cover how to create and bind an SSL certificate using Windows Server.
 

Create the SSL Certificate

  1. Click on the Windows icon in the taskbar, Search for IIS, and open Internet Information Services (IIS) Manager. 

    tidy_fix_alt
     
  2. Click on the name of the server in the Connections column on the left. Double click the Server Certificates icon.

    tidy_fix_alt
     
  3. In the Actions column on the right hand side, click on Create Self Signed Certificate.

    tidy_fix_alt
     
  4. Enter the friendly name you wish to use to identify the certificate, and then click OK.

     tidy_fix_alt
     
  5. You now have an IIS Self Signed Certificate, valid for one year, which will be listed under Server Certificates. The common name, is the server name.  

    tidy_fix_alt

How to Bind the Self Signed Certificate

  1. Browse to the connections column on the left hand side, expand the sites folder and click on the website you wish to bind the SSL certificate to. Once you've done that, on the right hand side, click on Bindings in the Actions column.

    tidy_fix_alt
     
  2. Click the Add.. button. 

    tidy_fix_alt
     
  3. Click the Type drop down menu. Select https .  Click on the SSL Certificate drop down, choose the newly created SSL certificate. Click OK.

    tidy_fix_alt
     
  4. You should now see the bindings for port 443. You can now on click Close.

    tidy_fix_alt
     
  5. To test the new Self Signed SSL Certificate, open up a browser, and go to the website. If the certificate has been installed and created correctly, depending on the browser you are using, you will see a lock icon next to the URL, or it will say Secure. This is an example of how it would look in Google Chrome.
     
    • Chrome 

      tidy_fix_alt
       
    • Opera 

      tidy_fix_alt
       
    • Internet Explorer 

      tidy_fix_alt