Click here to Skip to main content
15,888,461 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hello all,

I have a WPF application that will installed on desktops. I want to have a window that will ask the user for the product key and I want it to send the product key to an online database then send back to say authenticated or not.

The product key can only be used once so I don't want it compiled in the software and only have it reside online. It will look at the database and if the product key is in a column and the next column has a zero it will authenticate. If the next column has a 1 instead of a zero it will return to the desktop app sorry but product key has already been used.

This will make it where the app can only be installed once per product key.

I have searched and searched, however, unable to find anything on this.

Not asking anyone to provide me the entire code, but wouldn't mind if anyone sis :)

I would like to see if anyone can point me in a good direction on where I can learn about how this can done?

Thanks is in advance.

What I have tried:

Search and search and search on Google and other websites for programming
Posted
Updated 3-Sep-17 8:41am
Comments
Graeme_Grant 2-Sep-17 21:45pm    
What have you tried so far?

I have done something very similar to this in a previous project.
It can be done easily using MySql, which is usually also installed on most web servers.

First you will need to setup a MySql database on your web server. Call it "myapp_keycodes".

Create 1 table (myapp_KeyCodes), and I suggest it has at least 4 fields: KeyCode, Usage, User and Date.
KeyCode - This field should contain your Key Code and be set to Primary & Unique.
Usage - This can be Bool (True/False) or an integer (0/1)
User - The email address of the user installing your application.
Date - The date the application was installed.

Then, In your WPF application, add a text file named "dbConfig.txt".
Inside the text file, you should include the Server, Database name, User and Password used to connect to the database. Put each item on a new line. see below for example:
99.214.49.191 or http://www.mywebsite.com
myapp_KeyCodes
myapp_user1
Password12345
This could be hard coded in, but would make it difficult should you change web servers.

Next, add the following file to your project. Name it DBConnect.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using MySql.Data.MySqlClient;

namespace myApp
{
    partial class DBConnect
    {
        private MySqlConnection connection;
        private string server;
        private string database;
        private string uid;
        private string password;


        #region Load Config file information
        //Constructor
        public DBConnect()
        { 
            // Change the path to your location in the next line
            var filestream = new FileStream(@"c:\Configfolder\dbConfig.txt",
                            FileMode.Open,
                            FileAccess.Read,
                            FileShare.ReadWrite);
            var file = new StreamReader(filestream, Encoding.UTF8, true, 128);
            string lineOfText;
            int x = 1;
            while((lineOfText = file.ReadLine()) != null)
            {
                if (x == 1) { server = lineOfText; }
                if (x == 2) { database = lineOfText; }
                if (x == 3) { uid = lineOfText; }
                if (x == 4) { password = lineOfText; }
                x++;
            }
            Initialize();
        }
        #endregion

        #region Initialize
        //Initialize values
        private void Initialize()
        {
            // If hard coding database information, put it here and do NOT run DBConnect above.
            //server = "http://www.mywebsite.com";
            //database = "myApp_keycodes";
            //uid = "myapp_user1";
            //password = "Password12345";
            string connectionString = "SERVER=" + server + ";" + "DATABASE=" + database + ";" + "UID=" + uid + ";" + "PASSWORD=" + password + ";";
            connection = new MySqlConnection(connectionString);
        }
        #endregion

        #region Encypt/Decrypt
        //encrypt
        public string Base64Encode(string plainText)
        {
            var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
            return System.Convert.ToBase64String(plainTextBytes);
        }

        //decrypt
        public string Base64Decode(string base64EncodedData)
        {
            var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData);
            return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
        }
        #endregion

        #region Open Database Connection
        //open connection to database
        public bool OpenConnection()
        {
            try
            {
                connection.Open();
                return true;
            }
            catch (MySqlException ex)
            {
                //When handling errors, you can use your application's response based on the error number.
                //The two most common error numbers when connecting are as follows:
                //0: Cannot connect to server.
                //1045: Invalid user name and/or password.
                switch (ex.Number)
                {
                    case 0:
                        MessageBox.Show("Cannot connect to server.  Contact administrator");
                        break;

                    case 1045:
                        MessageBox.Show("Invalid username/password, please try again");
                        break;

                    case 1042:
                        MessageBox.Show("Unable to connect to any of the specified MySQL hosts., please try again");
                        break;
                }
                return false;
            }
        }
        #endregion

        #region Close Database Connection
        //Close connection
        public bool CloseConnection()
        {
            try
            {
                connection.Close();
                return true;
            }
            catch (MySqlException ex)
            {
                MessageBox.Show(ex.Message);
                return false;
            }
        }
        #endregion

        #region Get Key Code information
        //Gets KeyCode Information from Database on web server.
        public boolean getKeyCode(string kcode)
        {
            string query = "SELECT * FROM myapp_KeyCodes where KeyCode = '" + kcode + "'";

            //Open connection
            if (this.OpenConnection() == true)
            {
                try
                {
                    //Create Command
                    MySqlCommand cmd = new MySqlCommand(query, connection);
                    //Create a data reader and Execute the command
                    MySqlDataReader dataReader = cmd.ExecuteReader();

                    //Read the data return by the database
                    while (dataReader.Read())
                    {
                        string kc = dataReader["KeyCode"].ToSting();
                        int usage = dataReader["Usage"];
                        //or bool usage = dataReader["Usage"];
                        string uName = dataReader["User"].ToString();
                        string date = dataReader["Date"].ToString();
                    }
                    //close Data Reader
                    dataReader.Close();
                    if(kcode == kc)
                    {
                        if(usage == 0)  // If Bool, then use: If(usage == false)
                        {
                            return true;
                        }
                        else
                        {
                            return false;
                        }
                    }
                    else
                    {
                        return false;
                    }
                }
                catch
                {
                    // Error retrieving database information, or no information was found.
                    return false;
                }
            }
            else
            {
                return false;
            }
        }

        #region Update Keycode Information.
        //Update KeyCode Info
        public void UpdateKeyCode(string kCode, string nUser, string IDate, Int nUsage)
        {
            string query = "UPDATE myapp_KeyCodes SET User='" + nUser + "', Date='" + IDate + "', Usage='" + nUsage + "' WHERE KeyCode =" + kCode;
            //Open connection
            if (this.OpenConnection() == true)
            {
                //create mysql command
                MySqlCommand cmd = new MySqlCommand();
                //Assign the query using CommandText
                cmd.CommandText = query;
                //Assign the connection using Connection
                cmd.Connection = connection;
                //Execute query
                cmd.ExecuteNonQuery();
                //close connection
                this.CloseConnection();
            }
        }
        #endregion
    }
}
OK, Now comes the part to use this in your application.

For example, this could be inside your submit button or install button that is on the window where it is asking the user to enter the Key Code.


private void Button_Click(object sender, RoutedEventArgs e)
     {
         DBConnect myDBConnect = new DBConnect();
         bool checkCode = getKeyCode(txt_KeyCode.text); //Get key code from text box
         if(checkCode == True)
         {
             // KeyCode already Used
             // Display Warning...
             // Allow retry or Exit application.
             return;
         }
         else
         {
             // Ask user for Email address to register the Key Code
             // Allow Installation to complete.....

             // then Update Web Server Database with new usage
             UpdateKeyCode(txt_KeyCode.text, txt.UserEmail.Text, DateTime.Now.ToString(), 1)

         }


I tried to put this together for you fairly quickly.
There may be some type-o's or other errors, but should give you the idea of how it works. I've actually used the DBConnect class in several of my applications, and it seem to work very well.

I will be willing to help you further if you can not get this to work.

Good Luck! :)
 
Share this answer
 
v4
Comments
Member 12719658 3-Sep-17 14:55pm    
Thank you for your reply. I will check this out and test to see if I have any issues. Always, when it comes to this type of subject - I want to test everything to see what direction I need to go.
Member 12719658 3-Sep-17 14:59pm    
Question - with this code it will leave the connection string in the code. Would this be vulnerable having the server connection string to my online database hard coded?
HKHerron 3-Sep-17 17:14pm    
There are several different ways you can handle the connection string.
Unfortunately, it is required to connect to any MySql database.

The text file was just a SIMPLE way to demonstrate.
I use the Text file version, because I use this in several applications and they connect to different servers. This way I can use the same code, and the only thing that changes is my DBConfig file. This is not considered Hard Coded, as it loads the information from the DBConfig file each time the application is ran. And yes, this is not very secure. However, to make the text file more secure, you could encrypt the text file. Then Decrypt in code to read the information.

You could also make it part of the Install application, hard code it in to the Initialize() method, then have it saved anywhere, and in any form. Once you application is complied, it is a lot harder to find the information.

One other way would be to create a Web App that does the MySql side of things, so that the connection string is actually on the web server and not in your code.

for example, write a web page:
http://www.mysite.com/CheckKeyCode?a1b2c3d4e5f6g7h8i9j10k11

Have it do the same work that in my code, then return a blank page with either the work true or false.

We then change my code to run an httprequest with the keycode entered, then read the response. Then perform actions based on the True or False reply.



One last thing, I forgot to mention, create a different user for the database to use other than your main account or the Admin account. Then only give the user rights to Query and Update.
Member 12719658 3-Sep-17 17:16pm    
Regarding this code - I am trying to test and I am getting a lot of errors that names do not exist in the current context and also names cannot be found. If you have time - could you look over the code and/or send me a demo solution?
HKHerron 3-Sep-17 18:25pm    
OK, there are some things you need to change for it to work correctly.

For example, the namespace should match your application.
I've put together a test solution for you to demonstrate how to do client comms with a WebAPI rest service. The data is not encrypted as I am assuming that your website will be HTTPS enabled.

The sample console client demonstrates:
* How to send a request
* how to read the response code & data
* how to post multi-part form post data

The sample Web API Server controller demonstrates:
* how to handle multi-part form post received
* how to return a response to the client caller

The client console app:
C#
internal static class Program
{
    private static void Main()
    {
        UserWaiting();
        Console.WriteLine("Good TestGetMethod....");
        TestGetMethod("api/values");

        Console.WriteLine("Bad TestGetMethod....");
        TestGetMethod("api/valuesx");

        UserWaiting();
        Console.WriteLine("Good TestPostMethod....");
        TestPostMethod("api/values", new Dictionary<string, string> { ["valueField"] = "test post value" });

        Console.WriteLine("Bad TestPostMethod....");
        TestPostMethod("api/values", null);

        Console.WriteLine("\n-- DONE --");
        Console.ReadKey();
    }

    private const string host = "http://localhost:65189";

    private static void TestGetMethod(string path)
    {
        var response = Request(new Uri($"{host}/{path}", UriKind.Absolute),
                                "GET");

        var ms = GetResponseStream(response);

        if (response.StatusCode == HttpStatusCode.OK)
        {
            string responseData = GetResponseString(ms);
            ReportResponse(response, responseData);
        }
        else
        {
            ReportError(response, ms);
        }
    }

    private static void TestPostMethod(string path, Dictionary<string, string> values)
    {
        var response = Request(new Uri($"{host}/{path}", UriKind.Absolute),
                                "POST",
                                values);

        var ms = GetResponseStream(response);

        if (response.StatusCode == HttpStatusCode.OK)
        {
            string responseData = GetResponseString(ms);
            ReportResponse(response, responseData);
        }
        else
        {
            ReportError(response, ms);
        }
    }

    private static void UserWaiting()
    {
        Console.WriteLine("\n-- Waiting to begin --\n");
        Console.ReadKey();
    }

    private static void ReportError(HttpWebResponse response, MemoryStream ms)
    {
        if (ms != null)
        {
            string responseData = GetResponseString(ms);
            ReportResponse(response, responseData);
        }
        else
        {
            Console.WriteLine("!! UNHANDLED ERROR ENCOUNTERED");
        }
    }

    private static void ReportResponse(HttpWebResponse response, string responseData, bool isError = false)
    {
        Console.WriteLine();
        Console.WriteLine($"{(isError ? "!!" : "**")} {(int)response.StatusCode} - {response.StatusDescription}\n[{responseData}]");
        Console.WriteLine();
    }

    private static string GetResponseString(MemoryStream ms)
    {
        string responseData;
        using (var reader = new StreamReader(ms, Encoding.ASCII))
            responseData = reader.ReadToEnd();
        return responseData;
    }

    private static MemoryStream GetResponseStream(HttpWebResponse response)
    {
        MemoryStream ms = null;

        if (response != null)
        {
            using (var responseStream = response.GetResponseStream())
            {
                ms = new MemoryStream();
                responseStream.CopyTo(ms);
                ms.Position = 0;
            }
        }

        return ms;
    }

    private const string seperatorLine = "--";
    private const string terminateMarker = "--";

    private static HttpWebResponse Request(Uri uri, string method = "GET", Dictionary<string, string> parameters = null)
    {
        HttpWebResponse response = null;
        try
        {
            var clientRequest = WebRequest.Create(uri) as HttpWebRequest;
            clientRequest.Method = method;

            if (method == "POST" && parameters != null)
            {
                // post data
                var postdata = BuldPostData(parameters, clientRequest);

                using (var requestStream = clientRequest.GetRequestStream())
                {
                    byte[] byte1 = (new UTF8Encoding()).GetBytes(postdata);
                    requestStream.Write(byte1, 0, byte1.Length);
                }
            }

            response = clientRequest.GetResponse() as HttpWebResponse;
        }
        catch (WebException wex)
        {
            Debug.WriteLine($"! Resp::WebException: {wex.Message}");
            if (wex.Response != null)
            {
                response = (HttpWebResponse)wex.Response;  // Get Response
            }
        }
        catch (Exception ex)
        {
            // error occured
            Debug.WriteLine($"! Resp::Exception: {ex.Message}");
        }
        return response;
    }

    private static string BuldPostData(Dictionary<string, string> parameters, HttpWebRequest clientRequest)
    {
        // unique for each call...
        var boundary = $"MIME_{Guid.NewGuid().ToString("N")}";
        var boundaryMarker = $"{seperatorLine}{boundary}";
        var boundaryTerminationMarker = $"{boundaryMarker}{terminateMarker}";

        clientRequest.ContentType = $"multipart/form-data; boundary=\"{boundary}\"";

        var sb = new StringBuilder();

        foreach (var parameter in parameters)
        {
            sb.AppendLine(boundaryMarker)
                .AppendLine("Content-Type: text/plain; charset=utf-8")
                .Append("Content-Disposition: form-data; name=\"").Append(parameter.Key).AppendLine("\"")
                .AppendLine()
                .AppendLine(WebUtility.HtmlEncode(parameter.Value));
        }
        sb.AppendLine(boundaryTerminationMarker);
        return sb.ToString();
    }
}

The controller:
C#
public class ValuesController : ApiController
{
    [HttpGet]
    public HttpResponseMessage GetValues()
    {
        HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "value");
        response.Content = new StringContent("get hello", Encoding.ASCII);
        response.Headers.CacheControl = new CacheControlHeaderValue()
        {
            MaxAge = TimeSpan.FromMinutes(20)
        };
        return response;
    }

    [HttpPost]
    public HttpResponseMessage PostValues()
    {
        if (!Request.Content.IsMimeMultipartContent())
            throw new HttpResponseException(HttpStatusCode.BadRequest);

        var valueField = "** not set **";
        var reqParams = HttpContext.Current.Request.Params;

        if (reqParams.AllKeys.Contains("valueField"))
            valueField = reqParams["valueField"].TrimEnd(new[] { '\r', '\n' });

        HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "value");
        response.Content = new StringContent($"post hello: [{valueField}]", Encoding.ASCII);
        response.Headers.CacheControl = new CacheControlHeaderValue { MaxAge = TimeSpan.FromMinutes(20) };

        return response;
    }
}

Sample Output:
-- Waiting to begin --

 Good TestGetMethod....

** 200 - OK
[get hello]

Bad TestGetMethod....

** 404 - Not Found
[{"Message":"No HTTP resource was found that matches the request URI 'http://localhost:65189/api/valuesx'.","MessageDetail":"No type was found that matches the controller named 'valuesx'."}]


-- Waiting to begin --

 Good TestPostMethod....

** 200 - OK
[post hello: [test post value]]

Bad TestPostMethod....

** 411 - Length Required
[<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>Length Required</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY><h2>Length Required</h2>
<hr><p>HTTP Error 411. The request must be chunked or have a content length.</p>
</BODY></HTML>
]


-- DONE --
 
Share this answer
 
Comments
Member 12719658 2-Sep-17 22:27pm    
Thank you very much for the reply. So with this call I suppose the controller is a file located on my web. If so, I don't see where it is looking into a database and if value is there and other value = 0 then authenticate. I have never created API's before so sorry if I am completely clueless on this.
Graeme_Grant 2-Sep-17 22:39pm    
Have you looked at ASP.NET Web API[^]?

[HttpGet] is where only data queries (url QueryString for paramaters) are handled and data is returned; [HttpPost]/[HttpPut] is where data (paramaters passed in the body, not the QueryString) is uploaded to the server to add/update the data on the server; [HttpDelete] (not in the sample code) is for deleting data on the server.

So, to answer your question, you could use either [HttpGet] or [HttpPost] for your scenario. The control example above returns strings but could easily be replaced with database code.
Member 12719658 2-Sep-17 23:17pm    
Great! Thank you for the API link. This is going to be a huge help. With your example and help on this link i think i can have this closed.
Member 12719658 3-Sep-17 19:32pm    
Looking at my website - godaddy charges for HTTPS that I cannot afford right now. If my website will not be HTTPS enabled right now, I will just need to encrypt the API code correct?
Graeme_Grant 3-Sep-17 19:55pm    
If you are referring to SSL certificates, you don't have to pay. There are services that do it for FREE: Let's Encrypt - Free SSL/TLS Certificates[^]

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900