Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: preg_replace(): Compilation failed: invalid range in character class at offset 4 in /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php on line 340 Warning: Cannot modify header information - headers already sent by (output started at /home/.sites/946/site2164767/web/blog/wp-content/plugins/crayon-syntax-highlighter/crayon_langs.class.php:340) in /home/.sites/946/site2164767/web/blog/wp-includes/feed-rss2.php on line 8 .NET – ZoolWay https://blog.zoolway.com Coder, Gamer, Gentleman Mon, 01 Aug 2016 20:57:54 +0000 en-US hourly 1 https://wordpress.org/?v=6.0.9 Publish nuget packages with .NET Core https://blog.zoolway.com/publish-nuget-packages-with-netcore https://blog.zoolway.com/publish-nuget-packages-with-netcore#respond Mon, 01 Aug 2016 20:57:54 +0000 http://blog.zoolway.com/?p=132 Some time ago I posted about publishing your own project Open-Source with GitHub and NuGet: Go open-source with your .NET project. Since then many things happened – but today I want to tell you how to publish to NuGet when you have already tipped your toes into the .NET Core lake. For some recent preparations I built a middleware for ASP.NET Core to proxy calls (mainly for APIs and development) to other hosts and/or endpoints. This can especially be helpful to work around CORS restrictions when you host a SPA and have your API on other servers/ports/endpoints ASP.NET Core might proxy that calls for you. This might bring up some interesting posts about Aurelia and .NET Core but for the moment I want to publish that middleware for everyone.

So let’s go back to my old post. The steps involved creating a .nuspec file. With .NET Core this is no longer necessary, the new generation comes with some awesome command line utilities and a project.json for all your project configuration tasks. You can create a .nupkg with a simple dotnet pack call. With some command line arguments you select the build configuration. But with a minimal project.json you might miss some of the typical nuget setting. A blog post of Armen Shimoon reveals all the nice details, read it up: Where Does dotnet Get NuGet Package Metadata?

So with that knowledge we just have to add some authors, owners, releaseNotes, projectUrl, licenseUrl, tags and maybe others. The rest is up to dotnet pack -c Release.

Oh, if you would like to take a look at that API proxy middleware, checkout GlacierCrates.AspNetCore.ApiProxy.

]]>
https://blog.zoolway.com/publish-nuget-packages-with-netcore/feed 0
Logging – Human Readable vs. Machine Parsable https://blog.zoolway.com/logging-human-readable-vs-machine-parsable https://blog.zoolway.com/logging-human-readable-vs-machine-parsable#comments Wed, 09 Mar 2016 19:53:46 +0000 http://blog.zoolway.com/?p=103 I have often encouraged other to implement proper logging even in the smallest projects. My .NET MVVM template available at GitHub comes with log4net per default. There is no need to reinvent the wheel. Besides log4net there is NLog and other frameworks. Most can target many diverse storages like files (with automatic rolling), the Windows event log, UDP ports and more. This way we get some decent log files.

Human Readable

With some log file rolling and different log levels it is mostly a good idea to log data which can be important, especially in production debugging. When you get the users to send you the log file or a feedback tool does that for them, great. Now you got your hands on some information which should look useful. Example:

2016-03-08 22:30:53,263 [8] DEBUG Zw.JsonLogViewer.AppBootstrapper - Creating bootstrapper
2016-03-08 22:30:53,717 [8] INFO  Zw.JsonLogViewer.ViewModels.ShellViewModel - Loading last-used logfile: 'D:\Daten\Tech Files\Development\dotNET\..\bin\Debug\1\lastrun.json.log'
2016-03-08 22:30:53,729 [11] DEBUG Zw.JsonLogViewer.ViewModels.LogViewModel - Opening log 'D:\Daten\Tech Files\Development\dotNET\..\bin\Debug\1\lastrun.json.log'
2016-03-08 22:30:53,737 [11] DEBUG Zw.JsonLogViewer.Parsing.Parser - Parsing log file 'D:\Daten\Tech Files\Development\dotNET\..\bin\Debug\1\lastrun.json.log'
2016-03-08 22:30:53,859 [11] DEBUG Zw.JsonLogViewer.Parsing.Parser - Parsed 331 lines into 331 log entries
2016-03-08 22:30:53,860 [11] DEBUG Zw.JsonLogViewer.Parsing.Parser - Providing 6 keys as columns: date,level,appname,logger,thread,message

Write done what is important in a nice human readable way. No binary dumps needed 😉

Okay, now that you got this 8MB of text right infront of you, the text editor for your choice will search for “ERROR”, a logger name and other subjects. Some advanced editors are able to hide all lines in a file which do not match your criteria. This way you can try to only show INFO messages.

When you have done this, you will find out that handling logs with several hundreds or even more lines can be a pain. Although these files are there to help you finding bugs – and might be your only debugging help in production scenarios (where it might be not possible to connect with a remote debugger).

Machine Parsable

Of course, I do not want to go back to any kind of proprietary log format – less readable and you have to write your own parser. A good idea is to go with a well know format. XML got infamous because most people do not like XML namespaces and the redundant information due to the closing tags. The fall of XML was the rise of JSON. It is a more lightweighted format (also there is an optional schema feature out there) and parsers are available for almost any programming language. If we provide the information about our log statements as JSON objects it could easily be parsed.

{"date":"2016-03-06T18:18:37.3018646+01:00","level":"INFO","logger":"State","thread":"9","message":"Beta 'Anzu' 2.2.1.3 starting"}
{"date":"2016-03-06T18:18:37.4539737+01:00","level":"DEBUG","logger":"MyApp.App","thread":"9","message":"This log channel is DEBUG enabled and might contain really much information. For production usage we recommend setting the log level to INFO or WARN."}
{"date":"2016-03-06T18:18:37.5140168+01:00","level":"INFO","logger":"MyApp.AppBootstrapper","thread":"9","message":"Creating bootstrapper..."}
{"date":"2016-03-06T18:18:37.5820649+01:00","level":"DEBUG","logger":"MyApp.AppBootstrapper","thread":"9","message":"Running bootstrapper configuration"}
{"date":"2016-03-06T18:18:37.7261679+01:00","level":"DEBUG","logger":"MyApp.SysCore.License","thread":"9","message":"Providing empty license without translation"}
{"date":"2016-03-06T18:18:37.7411796+01:00","level":"DEBUG","logger":"MyApp.App","thread":"9","message":"Application is starting..."}

As you can see this approach assumes every file line is a JSON object. You could also have the complete file to be an array of JSON objects – but this would require to rewrite the end of the file all the time to enlarge the array. If the app crashed and the log writer cannot flush, you might end up with a destoryed JSON array. Therefore I prefer the “one line = one JSON object” approach.

You can image, this is slightly harder to read for a human but not too hard. Also using a text editor’s search feature might work even better because of the object property names visible all the time.

A format like this can be achieved in log4net with the additional libraries log4net.Ext.Json, log4net.Ext.Json.Serializers.Newtonsoft and (of course) Json.NET (former Newtonsoft.JSON, no .NET app using JSON without it). This is a rolling file appender configuration example:

<appender name="RollingFileJson" type="log4net.Appender.RollingFileAppender">
  <file value="lastrun.json.log"/>
  <appendToFile value="false"/>
  <rollingStyle value="Size"/>
  <maxSizeRollBackups value="5"/>
  <maximumFileSize value="10MB"/>
  <layout type="log4net.Layout.SerializedLayout, log4net.Ext.Json">
    <decorator type="log4net.Ext.Json.Serializers.Newtonsoft.NewtonsoftDecorator, log4net.Ext.Json.Serializers.Newtonsoft" />
    <renderer type="log4net.ObjectRenderer.JsonObjectRenderer, log4net.Ext.Json">
      <factory type="log4net.Ext.Json.Serializers.Newtonsoft.NewtonsoftFactory, log4net.Ext.Json.Serializers.Newtonsoft" />
    </renderer>
    <default />
    <remove value="ndc" />
    <remove value="appname" />
  </layout>
</appender>

This is a lot of assemblies but you might use Json.NET anyways and the other two are due to the modularity. And you could still add your own renderer.

With all this great machine-readable output there is one question left.

How can I read it with filtering and sorting?

I was suprised when I did not find  a nice little tool which would parse any kind of JSON objects into a column based view. If you can recommend any tool for this I would highly appreciate any comment to this post!

What I was looking for was a tool where not all lines would require the same JSON object. There could be a property only added to specific log lines for example. The tool should not crash neither ignore an inconsist JSON object layout through the log file.

Zw.JsonLogViewer

Did I say it would be easy to parse log entries in JSON format? Yes, it is. So I built a small MVVM application to open those files and be able to search, filter and sort log entries.

zwjsonlogviewer

This small sample app is available at GitHub as open source under the Apache 2 license.

It currently features:

  • Full text search through all entries.
  • Providing columns for all object properties found through the complete file.
  • Sorting and filtering based on columns.
  • Detailed JSON view for the selected entry as well as detailed JSON object property view (seleccted through the columns’ context menu).
  • Auto-refresh on file updates.
  • Optional automatic re-opening of the last loaded file.
  • Hiding columns.

Known limitations:

  • The file will be completly loaded into memory so it will consume quite a lot of it.
  • No recent files feature.
  • Only one filter value per column, so you cannot filter on ERROR and WARN for example.

This is just a small basic application but it might come in handy. And you are welcome to send push requests 😉

Further Noteslogs

The concepts here apply not only to the .NET platform but also Java, Ruby, etc – programming in general. Also there a companies providing specialized logging services for desktop and mobile apps. If you are going big, have a look at those – especially when going mobile or cloud where you might require to collect and analyze data from thousands of devices per day or even minute.

Also a log: “a portion or length of the trunk or of a large limb of a felled tree.

]]>
https://blog.zoolway.com/logging-human-readable-vs-machine-parsable/feed 1
Go open-source with your .NET project https://blog.zoolway.com/go-open-source-with-your-net-project https://blog.zoolway.com/go-open-source-with-your-net-project#comments Tue, 29 Sep 2015 17:56:39 +0000 http://blog.zoolway.com/?p=88 Have you ever wondered how hard it could be to share your project with the world wide web?

With Visual Studio 2015, GitHub and NuGet you can easily publish the source code and binary library of your project to the open-source community! Of course you can do similar with other IDEs and platforms but those resemble to most popular ones.

Let us see how easily we can do this.

To try this out I am sharing a .NET geometry PCL library with the world, Zw.Geometry. It most not necessarly be very successful, wide-spread or popular but I just wanted to share it as I cannot find something as simple as it and still have some features to be added. So let us give back and provide others with the possiblity to use an existing library to rotate points in a 2D coordinate system without reading all those trigonometry formulars.

First we will require a GitHub account if you do not have already one. I will use GitHub to publish the source code and be able to share it with others and even be able to accept their inputs to my projects (push requests). Next, for those who will purely consume my library, I will require a NuGet account which is a wide-spread package manager for .NET tools and libraries. It is even not limited to Visual Studio as its command line tools are available of others IDEs and Mono too. With the account we can publish packages for others to use.

os-github1At this point I already started the small solution which consists of the main project file and a test project with the unit tests. With the Microsoft Git Provider the solution already has a local versioning repository.
So we create a new GitHub remote repository with which we will be able to sync. We should not include a README at this point, we can add it later.

After the creation GitHub shows us the URL to the repository, like https://github.com/ZoolWay/Geometry

os-teamexplorer1In Visual Studio we can now select the Team Explorer, Home, Sync and setup the remote repository in the yellow highlighted Textbox (“Publish to Remote Repository”).
After that we can Sync or Push our commits to the remote repository.

Now you can check out your repository in the webbrowser in GitHub and should be able to see all your source code. You can now add a README and license file those will be downloaded to your local repository the next time you perform a sync. Note: That will not be visible in Visual Studio by default because they are not added to the VS solution file but you can see them in the file system and if you would like manually add them to your repository.

At this step you are ready to go. Find some friends to work on your project. They can checkout (subversion speaking) or more clone (git like) the repository.


os-nuget1When you have a nice library (or other project) most people just would like to use it. They are not interested in cloning the repository and have all the code. A binary and reference will be fine. This is what NuGet provides to the .NET community for quite some time now. The NuGet client is available as Visual Studio integration and command line utility but only the later will help you to pack your binary for the NuGet gallery.

I mostly followed the instructions from the NuGet docs and just encountered minor pitfalls here. Thanks to the extension Open Command Line I can just rightclick my project file and open the VS command prompt in the folder. But by default there is no NuGet command line utility so I downloaded it and put it into the folder C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools. It is then available in the path when using VS command prompts.

To create a new .nuspec file for your project you can just

nuget spec

After that I included the Zw.Geometry.nuspec into my solution to easily edit it from Visual Studio. Most things can remain unchanged as placeholders are used (which inject information from AssemblyInfo.cs when packing). I added URLs to my project and license and added some tags.

When everything is ready, the unit tests run fine and you do your first release you can run

nuget pack MyProject.csproj -Prop Configuration=Release -Build

Read the docs to get more information about other flags and dependencies to other projects.

The resulting MyProject.1.0.0.0.nupkg can be uploaded using the NuGet webinterface – or you can automate it with an API key and some scripts.


Those are the main steps. Of course there is many things left to really have a successful open source project but I felt a short starter like this would be helpful.

You can find my small sample library online:

 

]]>
https://blog.zoolway.com/go-open-source-with-your-net-project/feed 1
Using .NET build-in cryptography to RSA sign a software license https://blog.zoolway.com/using-net-build-in-cryptography-to-rsa-sign-a-software-license https://blog.zoolway.com/using-net-build-in-cryptography-to-rsa-sign-a-software-license#comments Sun, 14 Jun 2015 21:55:24 +0000 http://blog.zoolway.com/?p=45  “Do we really need that?”

Recently I was asked if I could add something like a software license key to an application. It should also be possible to create keys or “licenses” which would expire at a certain date.

For business I normally suggest to have a good customer relationship and have your usage terms on paper without an implementation of any checks or keys in the software itself. This way you do not start a fight between protecting and cracking your application and establish trust with your customer.
As said, this applies to business where you know your customers. On mass customer markets things are different.

Nevertheless, this was the requirement brought infront of me.

Solution: Sign your license contract

software-licenseFor the solution I decided not to go for a simple key and maybe name input. It would require to build some sort of algorhythm which would not only have to validate the key but also check if it has a expiration date or not. If the customer wants more protection attributes like license taker name, device id or more, things get even more complicated.

What works very well out there is to have a plain text license agreement which has the necessary information and could contain anything you would like:

SampleApp License
Name: Test User
Expires:

This is human readable and could easily be enhanced with more lines with more attributes. Sending the license to someone is as easy as to send a plain text email. Copy and paste from an email, a website or any other transfer is simple.

Of course – when you look at this, this is easily to fake. So we need some kind of validation, a secure hash. A hash which can be validated easily but generation is hard and limited to the license giver.

Implementation

Those of you familiar with cryptography will already know what I am after: Asynchronous cryptography to create a signature for the license with a private key which can be validated with a public key. This is not a new idea, it is one of the basic usages of RSA everywhere out there, like the certificates for secured web connections (HTTPS) many of you use for online banking.

So if we would sign our license, the complete license text could be:

SampleApp License
Name: Test User
Expires:
FJ7xBShqfMqJuvFKdCje6o3pLq7Y9pKC
O4D2BSl7vy9jvvUfG9WByEFTgdeSOYSB
QShfIs/4Jm3mErLmE0DElqgivRAmnKHO
09RA5B7neiP1KqJOxAfmEBIxMsV9PjhJ
tB7s9XQJdkkG1C582imwO/PCQI9hcbOC
mUxj71Iys2c=

The trick is: If you change any character here to change your expiration date, name or anything else (even in the hash) the license will be detected to be invalid. This works as the license generator uses a private key to generate the signature hash and your application uses a public key to validate that hash. There are some more things to consider to make this safe but that is the basic idea.

Does this look similar to you? Yeah, the whole thing is not a new idea to me, there is much software out there already using this kind of approach!

The implementation of RSA is quite old and well-known. It is based on prime factorisation like many cryptography algorithms today. And if you would like to do it in .NET, it is even build-in! Unfortunately the web lacks some well-documented example on how to generate a key-pair and how use it for signing and validating (and even encrypting and decrypting).

Use RSACryptoServiceProvider for key generation, import and export

So basically, we need to generate a RSACryptoServiceProvider. You can use the default constructor and import your RSA settings into the new object or create one with a new keypair with keys of the given size:

// generate new keypair with 1024 bit key size
var csp = new RSACryptoServiceProvider(1024);

// create a provider and import Settings
var csp = new RSACryptoServiceProvider();
csp.ImportParameters(rsaParams);

keypairNormally you would just create a keypair once and save it. That is one of two things the LicenseGenerator project in the example solution attached to this post does. How you save the key is up to you. The RSACryptoServiceProvider has a method ExportParameters which can give you a RSAParameters object with or without the private key. You can serialize that object in any way you would like or use ExportCspBlob/ImportCspBlob methods of the Provider. The sample solution uses JSON to save the key which is makes it most easy for you to take a look at the contents of the files.

Your license generator and your application should be completly seperated for obvious reasons. While the generator will require the private key (only you can generate licenses!) and the software will need the public key to validate the given licenses. You might want to embedded the keys as a resource or another way. The example solution only opens files to enable you (and me) to play around with the keys.

Sign your license

Let us now create a String containing our license text which has so be signed. In this sample we will also include a seed to make the hash more unpredictable and making cracking the app with exchanging the public key for your own one harder (not impossible!).

const string SEED = "plumbum!krum#";
public static readonly CultureInfo LICENSE_CULTURE = CultureInfo.InvariantCulture;

string internalLicense = String.Format(LICENSE_CULTURE, "SampleApp License\nName: {0}\nExpires: {1}\n{2}", name, expirationDate, SEED);

Now lets get the signature hash. You can decide which hash algorithm to use – here we use SHA256.

private static readonly HashAlgorithm HASH_ALGORITHM = new SHA512Managed();

var bytesToSign = encoding.GetBytes(internalLicense);
var hashBytes = csp.SignData(bytesToSign, HASH_ALGORITHM);
var humanReadableHash = Convert.ToBase64String(hashBytes);

Print out the public license where we will not tell them our seed:

var publicLicense = String.Format(LICENSE_CULTURE, "SampleApp License\nName: {0}\nExpires: {1}\n{2}", name, expirationDate, humanReadableHas);

This is the text we can send to our license taker.

Validate the license in your app

Your application should have the license saved in its settings and validate it at least on startup. If there is an error or no license is present it should ask the user to enter (copy & paste) one.

When you have the license, extract your attributes (e.g. name, expiration date, etc.) and the hash into separate variables.

string name = ...;
DateTime? expirationDate = ...;
string humanReadableHash = ...;

We will take these values and the seed to rebuild the text for the internal license and validate that one against the hash.

string internalLicense = String.Format(LICENSE_CULTURE, "SampleApp License\nName: {0}\nExpires: {1}\n{2}", result.Name, result.Expires, SEED);

var csp = GetCryptoServiceProvider(); // gets the RSA CSP with the public key imported
bool valid = csp.VerifyData(encoding.GetBytes(internalLicense), HASH_ALGORITHM, Convert.FromBase64String(humanReadableHash));

If you have additional attributes like an optional expiration date (nullable DateTime used here) you have to check that one as well.

There are many other considerations to make this really safe and get the crackers to have a hard job. Question is how to balance effort and outcome. It depends also on how many customers use your application and how trustworthy those are. But that is not the discussion here today. Basic recommendation here only include: Duplicate the important “constants” like hash algorhythm and seed. Make sure you only deliver the public key with your application.

Summary

Here we see that utilizing RSA signing only involves a few lines of code in .NET and some conversion to and from byte arrays. There is much you can do in addition to increase safety (sign your assembly, check NTP server, e.g.) and to increase user comfort (break the hash into multiple nice lines and put back together, trim whitespace, generate keys and validate in background threads). But the basic is just: Create CSP and generate keys or import them, sign some data, validate some data.

Note: This post and example can also be a start to use the .NET RSACryptoServiceProvider to implement encryption and decryption.

Download sample: RsaSigning Sample Solution (2 sample projects, one to generate keys and licenses, one to represent a protected application; both with simple WPF user interface powered by Caliburn.Micro; will require nuget package restore)

]]>
https://blog.zoolway.com/using-net-build-in-cryptography-to-rsa-sign-a-software-license/feed 1