Introduction

Last week, someone posted a question about producing a random encryption key, and since I'd done something along those lines before, I figured I'd try to reproduce my past efforts, and post an article about it.

General

The idea is to create a completely random key, encrypt the desired data with it, and then somehow share the key with the consumer so that they can decrypt the data, passing both the key and the data to the consumer. This article provides the code to create the key, and leaves the actual encryption stuff and data transfer to the imagination of the programmer.

Creating the Key

I started off with an array of strings. More specifically, an array of guids converted to strings. For the purpose of example, the size of the array was kept to a sane size, but the nature of the code is such that you could easily expand the array to make it even more "interesting" for people wanting to reverse engineer what the code does. The only restriction is that each array element be exactly the same size (in this example, the strings are all 32 characters long).

The most interesting of these is the Direction property. It represents a set of flags that dictate directions and orientation regarding to traverse the marker array while constructing the key. The following directions can be specified:

Left

Right

Up

Down

Horizontal

Vertical

Diagonal

Of course, it wouldn't make sense to go both left and right, or up and down, so the class takes reasonable precautions to prevent invalid directions from being specified by calling the ReasonableDirection method before actually trying to build the key. It merely performs a sanity check to make sure the programmer didn't do something strange, like specifying both left and right on a horizontal orietation.

The green line indicates two possible Directions - Up|Vertical (starting at row 3, column 24), or Down|Vertical (starting at position row 7, column 24). Notice that this will cause the code to wrap to the far edge.

The yellow line indicates one possible Direction - Right|Horizontal (starting at row 9, column 28). Notice also that the code will not only wrap, but will move to the next row. This indicates that the Direction property also includes the RowIncrement flag. This flag is only reasonable for a Horizontal direction, and causes the code to wrap to the next lowest row in the array (regardless of the specified horizontal direction.

Keep in mind that if you allow the class to randomly select a direction, it's entirely possible to end up with a key where all of the characters are the same. This would happen if the Direction ended up being Diagonal with a starting position at one of the corners of the array. If this bothers you, you can always change the random position selection code to ommit the extreme corners. Personally I see nothing wrong with this happening once in a while because each data item could be sent with a different encryption key.

Finally, we arrive at the ConstructKey method. This method uses the Direction property to determine how to traverse the markers array. This is controlled by using adjusters that control how to index into the array, and into an item in the array. First, we run our reasonableness check. If there's a problem, we throw an exception:

privatevoid ConstructKey()
{
if (!ReasonableDirection())
{
thrownew Exception("The specified Direction value indicates either both left and right, or both up and down.");
}

The Descriptor

By now, you're probably thinking, "Okay, great! We have a randomized key. Now what?"

Anybody that knows anything about transmitting secure info knows you don't just go transmitting this stuff in the clear. The way I see it is that it's perfectly viable to "describe" what you're sending without giving away the whole shebang. Enter "the descriptor".

Remember all of those nifty control variables that were used to actually build our random key? Well, all you have to do is provide that information to a client application that references the same assembly that was used to build the key, and it can decode the key based on those values.

So, let's say our key ended up being 14 characters long, and the starting markers array index was 5, the marker index (where we indexed into the marker in array element 5) was 28, and our Direction was Diagonal | Up | Left. We simply call the GetKeyDescriptor method to build our descriptor string:

You may have noticed the last call to Append. This serves two purposes - a) to act as a way to verify the payload of the descriptor string, and to drive hackers absolutely batty trying to figure out what that largish block of bytes means. Here's the method:

The object of this method is to create enough random padding characters to fill the string to the specified length. As you can see, it simply creates new guids and appends them to the padding string until it exceeds the necessary length, and then calls the Substring method to cut off everything we don't need.

Finally, the client application receiving the descriptor string needs a way to recreate the key on its end. We do that by overloading the BuildKey method with a version that accepts the descriptor string, and parses that string:

If both your web site (or web service) and your client application use this assembly (or one based on it), you can send data with a different encryption key for every descrete block of data, making it so random that breaking the encryption would become virtually impossible. To further shake up the hackers, you could publish a new assembly containing a completely different set of marker strings every 30 days or so. Coupled with click-once deployemnt of the client-side application, things would stay in sync without you having to bend over backwards.

The Sample Application

The sample app merely allows you to specify some criteria, and then generate a random key (along with the descriptor string). The last control in the form shows the key rebuilt from the information in the descriptor key (just to prove it works).

In Closing

This code is merely one part of a larger suite of code that would allow secure data transmission without sacrificing data integrity, and should be combined with other measures such as application identifiers, assembly obfuscation and signing, and other techniques. With the descriptor string, you could even specify what encryption algorythm to use, and you could even encrypt the descriptor string to add even more security.

When I wrote code for this a few years ago, I simply encrypted the decsriptor string and prepended it to the encrypted data I was sending. The code on the client side knew what to do because it had the same assemblies.

Share

About the Author

I've been paid as a programmer since 1982 with experience in Pascal, and C++ (both self-taught), and began writing Windows programs in 1991 using Visual C++ and MFC. In the 2nd half of 2007, I started writing C# Windows Forms and ASP.Net applications, and have since done WPF, Silverlight, WCF, web services, and Windows services.

My weakest point is that my moments of clarity are too brief to hold a meaningful conversation that requires more than 30 seconds to complete. Thankfully, grunts of agreement are all that is required to conduct most discussions without committing to any particular belief system.

Comments and Discussions

This program outlines the creation of a simple key generator. but it is highly susceptible to man-in-the-middle attack, since the entire info is sent in clear. if the client-server could dynamically generate the array using diffie-hellman key exchange or something like that.

This program outlines the creation of a simple key generator. but it is highly susceptible to man-in-the-middle attack, since the entire info is sent in clear. if the client-server could dynamically generate the array using diffie-hellman key exchange or something like that.

Did you even glance at the very last paragraph? It mentions that you can encrypt the data that's "sent in the clear", and that you'd probably want to use additional security techniques. This is just an article that describes the creation of a random key. Nothing more. I left it as an exercise for the reader to make necessary changes that fit their particular application.

".45 ACP - because shooting twice is just silly" - JSOP, 2010-----You can never have too much ammo - unless you're swimming, or on fire. - JSOP, 2010-----"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass." - Dale Earnhardt, 1997

I only took a quick look, but the moment you hardcode the characters in m_markers, it ceases to be truly random. You need to generate this array randomly too, and you can get a little extra randomness.

You can't generate the markers array randomly because each time the object is instantiated, it would generate a compleyely different set of strings, thereby making any indexing into the array irrelevant. The scheme depends on each copy of the assembly having the same strings in the array. That's why I mentioned the possibility of modifying the array, recompiling it, and making an app update available via click-once, which would require the app to be updated before it's used.

.45 ACP - because shooting twice is just silly-----"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997-----"The staggering layers of obscenity in your statement make it a work of art on so many levels." - J. Jystad, 2001

It's a *random* key generator, not a "generic" key generator. It generates a key from randomly selected indexes into a static array, and randomly selects the direction in which to index. I honestly don't understand how this point isn't being made.

.45 ACP - because shooting twice is just silly-----"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997-----"The staggering layers of obscenity in your statement make it a work of art on so many levels." - J. Jystad, 2001

It's a *random* key generator, not a "generic" key generator. It generates a key from randomly selected indexes into a static array, and randomly selects the direction in which to index. I honestly don't understand how this point isn't being made.

Well in cryptography random means a different thing. What you have is a pseudo random generator. It's a very subtle difference but that explains why people are down voting you. And at least some of the folks who are up voting you are doing it to compensate the down votes, not because they understand cryptography.

That said this article is absolutely not deserving of anything less than a 4. So anyone voting 1 is being a jerk.

Basically what we have here is a (large) set of shared constants, and an index into those constants. So if the attacker gains control of the assembly, there's no security difference between this complicated algorithm, and one that uses an array of size N filled with random data, where the array index (and maybe key length) is sent across the wire.

The goal of key-based encryption is to make it impossible to decrypt the message without the decryption key. One goal of the attacker is to either obtain the decryption key or the method of creating the decryption key. Once the attacker has control of that, no amount of transformations of constants will save your system. So, this algorithm introduces extra code, extra steps, and takes extra runtime for no security benefit when compared to the simpler array-based system.

Its one advantage vs. the array based system is that this takes less persistent memory, which may be good for memory constrained systems (unless processor resources are also constrained).

0) If you'll go back and read the last part of the article, you'll notice that I specifically stated that tit's up to the programmer to secure this. I used a scheme very similar to this and encrypted it before sending it to the client. In that case, the assemblies were signed and obfuscated (including string encryption which effectively disguised the string array).

1) I also suggested that the markers array could be changed, and an application update made available on a regular basis that would change the possible keys.

2) I also suggested that the array could be increased in size (in terms of longer strings AND more of them).

3) Processor and memory load are negligible in the grand scheme of things. The indexing permutations described in this article were merely an illustration of how one could traverse the array in any number of ways, thus further randomizing the outcome, and even if it generated 128-character keys the amount of time needed to either create a key or decode the key takes less time than you can accurately measure. The array is small, and the code is fast.

4) Finally, this article isn't about encryption or securing the transmitted data. It's merely about generating random keys and sharing them with a client application, and it's genesis was a direct result of a question that was asked in the Questions & Answers section of this site. How everything is buttoned up and secured is fodder for another article.

Based on all those things I just mentioned, I put it to you that your vote of 2 is unjustified.

.45 ACP - because shooting twice is just silly-----"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997-----"The staggering layers of obscenity in your statement make it a work of art on so many levels." - J. Jystad, 2001

4) Finally, this article isn't about encryption or securing the transmitted data. It's merely about generating random keys and sharing them with a client application, and it's genesis was a direct result of a question that was asked in the Questions & Answers section of this site. How everything is buttoned up and secured is fodder for another article.

I claim that there is a simpler method to generate & share random keys than the one described in the article, that is just as effective. That method is for the two parties to share a simple constant string array, and send the array index of the key they are using.

Advantages of that system when compared to the article:* It's far simpler in concept and in code - easier to document, easier to train new developers, easier to test, easier to update* It uses far fewer compute cycles and is therefore far faster* It's just as secure

Disadvantages of that system when compared to the article:* It uses more persistent memory

Can you describe any advantages of the article's system when compared to sharing a simple array of strings, and sending an index?

I claim that there is a simpler method to generate & share random keys than the one described in the article, that is just as effective. That method is for the two parties to share a simple constant string array, and send the array index of the key they are using.

Even simpler would be a single string, with a randomly selected index and length, but how many random key permutations do you get as a result? To keep it simple, you'd generate a random index that is 0 to n (where n = string length - desired key length), but that means you only get n possibilities. Okay, so then you say, to make more possibilities available, let's allow the index to be anywhere in the string, and we can then wrap around to the beginning of the string if we need to. Fine, but you're still only using one string. You can see how requirements can escalate, and if you've been a programmer for any length of time, you know they often do. You say this is complex, and to a point, it might be, but the code is reasonable and the exposed interface is simple. Besides all that, you can (if you so choose, EASILY reduce the size of the array and only use Horizontal | Right to retrieve a key.

Member 2096629 wrote:

* It uses far fewer compute cycles and is therefore far faster

I agree that it uses fewer compute cycles, but "far faster"? I think you're concerned about something that doesn't matter. It's not like you're running through this code several thousand times to generate/decode a single key. I'm sure if you did run this several thousand times, you'd see a difference in speed, but running only once?

Member 2096629 wrote:

* It's just as secure

No, it's not, simply because there are far fewer permutations than when you traverse the array in 8 different directions from any place in the array.

Member 2096629 wrote:

* It uses more persistent memory

The memory consumption isn't persistent. Once the object goes out of scope, its memory is eventually released by the garbage collector.

.45 ACP - because shooting twice is just silly-----"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997-----"The staggering layers of obscenity in your statement make it a work of art on so many levels." - J. Jystad, 2001

Even simpler would be a single string, with a randomly selected index and length

That wouldn't be simpler. You'd be doing string processing rather than just accessing an array element.

John Simmons / outlaw programmer wrote:

No, it's not, simply because there are far fewer permutations than when you traverse the array in 8 different directions from any place in the array.

It is, because with strong encryption algorithms like AES, pretty much any key will result in an undecipherable encrypted message. The additional security from a changing key system comes from changing the key, not permuting already random data to use as the key value.

Anyone who gets their hands on your binary and intercepts the network stream can compute the key. If you don't mind that, you can do an equally good key exchange in about three lines using Rfc2898DeriveBytes and a shared salt. If you want something which is secure even when the attacker has your binary, use AsymmetricKeyExchangeFormatter.

You're missing the entire point of the article. It;'s not about being secure in and of itself. Someone posted a question about wanting to build a random key for encryption, and I provided code to do just that. Further, I stated at the end of the article that it was left as an exercise for the programmer to ensure that it is made secure.

I challenge you that your vote of 1 is unfounded, and that a 1-word explanation as to why you voted a one further invalidates the vote.

.45 ACP - because shooting twice is just silly-----"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997-----"The staggering layers of obscenity in your statement make it a work of art on so many levels." - J. Jystad, 2001

First time I've voted for an article on CodeProject, so I didn't realise it would turn it into a post - thought it would be for private consumption. I posted an explanation for public consumption immediately afterwards, and you can see it up a line.

This isn't cryptography. It's just about generating a random key. I merely suggested a use for it regarding encrption. I further stated that the reason I wrote this article is to fuilfil a request by another user here that wanted to perform exactly this kind of task - generation of a random key.

Being suspicious of code that you have a complete copy of in source form is kinda - well - silly. Are you suspicious of any other code here?

.45 ACP - because shooting twice is just silly-----"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997-----"The staggering layers of obscenity in your statement make it a work of art on so many levels." - J. Jystad, 2001