Author Archives: Berend de Jong

Synology Docker MariaDb / .NET Core 6 / Homewizzard Electrical meter reading

HomeWizard P1 meter

Recently I came across a small device that can be connected to the Smart Energy Meter as it is used in the Netherlands, among other places:

HomeWizard.nl

This small device is connected to the P1 port of the smart meter. The HomeWizzard is then connected to the WiFi network.

The HomeWizzard device publishes a REST api at this endpoint https://[your-homewizzard-device-ip]/api/v1/data.

Continue reading
Share

Certificates / IIS / Visual Studio 2017

Introduction

This post will guide you through the process of creating a selfsigned certificate. I will also show you how to configure IIS and create a Visual Studio solution that uses Local IIS with the self signed certificate.

Several tips and tricks are include to help you better understand the use of certificates and solving problems you run into.

In this post I also refer to a somewhat old technique but still used in a lot of M2M communication: WCF.

Install HTTP Activation

HTTP Activation is not needed for regular ASP.NET sites but for WCF (and bindings as tcp) it should be installed at this point.

Go to “Control Panel”-> “Add Remove Programs” -> “Turn Windows Features on or off”.
Under “.NET Framework 3.5 (includes .NET 2.0 and 3.0)” check “Windows Communication Foundation “HTTP Activation”.
Under “.NET Framework 4.8 Advanced Services” -> “WCF Services” check “HTTP Activation”.

Install World Wide Web service and tools

Under “Internet Information Services” check “World Wide Web Services” and also check “Web Management Tools”.
Under “Internet Information Services” -> “World Wide Web Services” -> “Security” check “Basic”, “Digest” and “Windows” authentication.

Configure Website

In inetmgr goto your website and under the IIS category select “Authentication”, then enable the authorisation schemes you will need.

Create selfsigned certificate

Create certificate in administrator Power Shell prompt (dnsname option is important to set correctly, use output of hostname command):

$cert = New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname [YOUR PC HOSTNAME]

Use OpenSSL

You could also use OpenSSL to create your certificate, key and pfx file. You need to use the configuration file shown below to create a valid certificate.

The configuration specifies a subjectAltName which is needed for Chrome (and others) to accept the certificate as a valid certificate.

You will get an error message (This server could not prove that it is desktop-b1170c1; its security certificate does not specify Subject Alternative Names) if this configuration file is not used when creating the certificate.

[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
C = NL
ST = Groningen
L = Boerakker
O = bdejong.NL
OU = ICT
CN = desktop-b1170c1
[v3_req]
keyUsage = critical, digitalSignature, keyAgreement
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = desktop-b1170c1

To create the certificate and the private key execute this command:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout cert.key -out cert.pem -config .\openssl.cnf -sha256

Next create a pfx file, which can be used to import into your certificate store, with the command below

openssl pkcs12 -export -out key.pfx -inkey cert.key -in cert.pem

When using the OpenSSL commands you have to manually add the certificate (the pfx) to your certificate store.

Add to Trusted Root CA

Start MMC and open the “Certificates” snap in for [Local Computer]. Browse to Console Root -> Certificates (Local Computer) -> Personal -> Certificates. Here you will find the certificate you have just created.

As you can see both certificates in the store have a private key in it. Only certificates with a private key can be used for https communication.

Add this certificate to the [Trusted Root Certification Authorities] by exporting it from [Local Computer]\Personal\Certtificates and importing it into [Trusted Root Certification Authorities].
Do not copy it into [Trusted Root Certification Authorities], it will not work!

You do not need to export the private key. Use the the DER encode format. Save the certificate to disk, Now go to Console Root -> Certificates (Local computer) -> Trusted Root Certification Authorities -> Certificates. Right click and import the certificate.

Double click the certificate in the personal store, it should now be valid (including the Certification Path).

Create WCF project

Now create a new WCF project (yes I know it is old stuff but a lot of companies still use and develop WCF services). In case you are using Visual Studio 2017 or above you need to install the WCF project template. This can be done when creating a new project, scroll all the way down and select “Install more tools and features”.

Choose WCF Service application as your project template (not WCF Service).

Leave everything default and press Create.

As start page choose your svc file (in case of a WCF service) any other page otherwise.
In the project options of your wcf / web project on the web tab set the server to “Local IIS”.

Set the url of your project to: “https://[YOUR HOST NAME]/WcfService1” and press create Virtual Directory (you have to be administrator to do that so start Visual Studio as Administrator).

If the site is not reachable add the hostname to “\windows\drivers\etc\hostname”:

# localhost name resolution is handled within DNS itself.
#	127.0.0.1       localhost
#	::1             localhost
127.0.0.1			DESKTOP-B1170C1

Next change the binding of your https site in IIS. Start the inetmgr (via Windows+R) and select the default website. On the far right select “Bindings…”.

Then select the https entry and press Edit. Here you can change the SSL certificate to the one you have created before.

Now back to Visual Studio and start your WCF service. As you can see the certificate is valid for this service.

Usefull links

How to: Create Temporary Certificates for Use During Development
WCF: Common security scenarios

Share

Using Moq for testing

Install the nuget Moq package. Below is a simple example to create a mock for a database implementation (which is also a mock :-)).

namespace ConsoleApp1
{
    using Moq;
    using System;
    using System.Collections.Generic;

    public interface IDAL
    {
        List<string> RetrieveBooks();
    }

    public class DAL : IDAL
    {
        public List<string> RetrieveBooks()
        {
            return new List<string> { "a", "b", "c" };
        }
    }

    public class BookManager
    {
        private IDAL _dal;

        public BookManager(IDAL dal)
        {
            _dal = dal;
        }

        public List<string> GetBooks()
        {
            return this._dal.RetrieveBooks();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            /*
             * Bookmanager with database implementation
             */
            Console.WriteLine("Database implementation");
            BookManager bmDB = new BookManager(new DAL());
            bmDB.GetBooks().ForEach(i => Console.WriteLine(i));

            /*
             * Bookmanager with a mock database
             */
            Console.WriteLine("Database mock implementation");
            var dal_mock = new Mock<IDAL>();
            dal_mock.Setup(i => 
               i.RetrieveBooks()).Returns(new List<string> { "d" });
            BookManager bmMOQ = new BookManager(dal_mock.Object);
            bmMOQ.GetBooks().ForEach(i => Console.WriteLine(i));
        }
    }
}

Share

ASP.NET Core Tips and Quick Setup Identity System

In this blog post we are going to setup a basic invoice system. It uses the ASP.NET Core identity system. Every step for creating the app is described and at the end you should have a working Invoicing system (being it a bit simple one).

For convenience install the sqlitebrowser

sudo apt install sqlitebrowser

If you did not already installed the dotnet-aspnet-codegenerator already….

dotnet tool install --global dotnet-aspnet-codegenerator

And also install the libman Client Side library manage

dotnet tool install -g Microsoft.Web.LibraryManager.Cl

Create the intial WebApp project

dotnet new webapp -o WebApp

A folder, WebApp, with the new web application is created

cd WebApp

Add the required package for the aspnet-codegenerator tool

dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design

Optional; add the package below manually so you use the newest version (not the one installed default by the aspnet-codegenerator)

dotnet add package Microsoft.EntityFrameworkCore.Sqlite

In Visual Studio you can install the package as a nuget package through the Package Manager console with

PM> Install-Package Microsoft.EntityFrameworkCore.Sqlite -Version 2.2.6

Scaffold the Identity pages you want to change later on, for now we are going to use a SqLite database and override the Register, Login and Logout pages.

dotnet aspnet-codegenerator identity --useSqLite -dc WebApp.Data.Identity.IdentityDbContext --files "Account.Register;Account.Login;Account.Logout"

Before we are going to add the migrations we change the name (and location) of the database to dbs/identity.db (we will have separate databases for users and data).

Start Visual Code in the root of the WebApp directory.

code .

Wait a few seconds for the window below to appear and answer Yes. If the window below does not appear press F1 and type “.NET”, then select “.NET: Generate assets for build and debug”.

Open the file appsettings.json in the root of the project and change WebApp.db to dbs/identity.db. Also create the folder dbsin the root of WebApp.

Now we are going to create the Migrations for the initial Identity database and update the database with this migration.

Create the initial migration for the identity system

dotnet ef migrations add InitialCreateIdentity 

Create the database

dotnet ef database update

Check the databastructure with SQLite browser

Because we did not use the --auth parameter on initial create of the project our Startup.cs is not prepared to use authentication. Add the line below right after app.UseCookiePolicy

app.UseAuthentication();

We also have to add the _LoginPartial to _Layout.cshtml because of this. Add the partial _LogingPartial to /Pages/Shared/_Layout.cshtml right before the ul which contains the Home link. Add the line below:

<partial name="_LoginPartial" />

To test authorization place the [Authorize] attribute on the PrivacyModel class and add the using Microsoft.AspNetCore.Authorisation

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspnetCore.Authorisation;

namespace WebApp.Pages
{
    [Authorize]
    public class PrivacyModel : PageModel
    {
        public void OnGet()
        {
        }
    }
}

It is possible to configure password options in /Areas/Identity/IdentityHostingStartUp.cs. For example: do not require an uppercase character in the password:

services.Configure<IdentityOptions>(options =>
{
   options.Password.RequireUppercase = false;
});

Now we are going to add our first CRUD pages. We are going to store Invoices with our application. First create a directory Models and place a file Invoice.cs in it with the following code in it:  

namespace WebApp.Models
{
    using System;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;

    public class Invoice
    {
        public int ID { get; set; }
        public string Product { get; set; }

        [DataType(DataType.Date)]
        public DateTime InvoiceDate { get; set; }
        [Column(TypeName = "decimal(18,2)")]
        public decimal DiscountPercentage { get; set; }
        [Column(TypeName = "decimal(18,2)")]
        public decimal Amount { get; set; }
    }
}




Scaffold model CRUD pages   Execute the command below:

dotnet aspnet-codegenerator razorpage -m Invoice -dc InvoiceDbContext -udl -outDir Pages/Invoices --referenceScriptLibraries

The following files will be generated or adjusted:

In /Startup.cs the InvoiceDbContext is added to the services configuration
A directory /Pages/Invoice is created and all files in there are also generated
A directory /Data is created in which a file InvoiceDbContext.cs is stored with the data context for the invoices
The file appsettings.json is modified. A connection string is added for the InvoiceDbContext (we will change this below)

In case you are on Linux. Default the DbContext is using SqlServer, that is not supported on the Linux platform. Goto the file Startup.cs and replace SqlServer with SqLite for the InvoiceDbContext.

services.AddDbContext<InvoiceDbContext>(options =>
   options.UseSqlite(Configuration.GetConnectionString("InvoiceDbContext")));

Next edit the file appsettings.json and replace the connection string value with "DataSource=dbs/invoices.db"

Next create the initial migrations and update the invoice database for the InvoiceDbContext.

dotnet ef migrations add InitialCreateInvoices --context InvoiceDbContext
dotnet ef database update --context InvoiceDbContext

Start the sqlitebrowser to check the structure of your database (/WebApp/dbs/invoice.db)

Now to protect our Invoices folder for unauthorized access add the code below to your services configuration (ConfigureServices) in Startup.cs

services.AddMvc().AddRazorPagesOptions(options =>  
            {  
                options.Conventions.AuthorizePage("/Invoices");  
            });

References and handy URL’s

This url was very helpfull

Aantekeningen nav ContosoUniversity  tutorial

Some common errors

dotnet restore gives 401 error

Update your credentials for nuget, first download the nuget cli. Next execute the command:

nuget.exe sources update -name RDW -source [url] -username [user] -password [pwd]

Retrieve your name and url with the command:
nuget sources

When you get the error “Scheme already exists: Identity.Application” you probably generated the Identity pages with a different context then ApplicationDbContext.

Build the solution (Ctrl-Shift-B, Enter) en start debugging F5. Navigate to the Privacy page and verify that you have to login before you can continue to this page.

In case you get error “PlatformNotSupportedException: LocalDB is not supported on this platform.” you probably generated the Identity pages with a different context then ApplicationDbContext.

Share