String encryption using keys I wrote a program that encrypts a users string by increasing every char's position by a random number. I'm looking for any and all criticism on it.

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Encryption2
{
 class Program
 {
    public static void Main()
    {
        Console.Clear();

        bool valid = true;

        while (valid) //while loop is here for error handling in case user inputs something other than 
        {
            Console.WriteLine("Enter d for decrypt and e for encrypt");

            string uInput = Console.ReadLine().ToLower();

            if (uInput == "e")
            {
                encrypt();
                valid = false;
            }
            else if (uInput == "d")
            {
                decrypt();
                valid = false;
            }
            else
            {
                Console.Clear();
            }
        }
    }

    public static void encrypt()
    {
        Console.Clear();

        Console.WriteLine("Enter a string for encryption");

        string input = Console.ReadLine(); //string that will be encrypted

        int[] keyVals = new int[input.Length]; //decryption key

        Random generateKeyVals = new Random();

        for (int i = 0; i &lt; input.Length; i++) //generates input.Length amount of numbers between 1 and 9 that are used to increase
        {                                       //each char in in the input string and then decrease each char when you decrypt
            keyVals[i] = generateKeyVals.Next(1, 9);

            Console.Write(keyVals[i]);
        }

        Console.WriteLine(" : This is your decryption key");

        for (int i = 0; i &lt; input.Length; i++) //encrypts string by adding first char in the encrypted string with the first value in the key and so on
        {
            char encrypted = Convert.ToChar(input[i] + keyVals[i]);

            Console.Write(encrypted);
        }

        Console.WriteLine(" : This is your encrypted string");

        Console.ReadKey();

        Main();
    }

    public static void decrypt() //decrypts an encrypted message using a decryption key
    {
        Console.Clear();

        Console.WriteLine("Enter a string for decryption");

        string input = Console.ReadLine(); //user inputs the encrypted message

        int[] keyVals = new int[input.Length]; //stores all values entered from the for loop below. This is the decryption key used to decrypt the string

        for (int i = 0; i &lt; input.Length; i++) //using a for loop to get all numbers from the key one at a time.
        {
            Console.WriteLine("Enter the #" + (i + 1) + " of the key");
            keyVals[i] = Convert.ToInt32(Console.ReadLine());
        }

        for (int i = 0; i &lt; input.Length; i++) //decrypts string by subtracting first char in the encrypted string by the first value in the key and so on
        {
            char decrypted = Convert.ToChar(input[i] - keyVals[i]);

            Console.Write(decrypted);
        }

        Console.WriteLine(System.Environment.NewLine);

        Console.WriteLine("Press any key to return to the main menu");

        Console.ReadKey();

        Main();
    }
}


}
I know it's obvious but I really have to say this: outside a programming exercise do not write your own encryption function.



Let's now talk about code...

The first thing to do is to separate responsibilities. Encrypt() and Decrypt() methods (note PascalCase, you should follow C# naming guidelines) should do only one thing: encrypt and decrypt a string. To get user input and to provide an output is something else responsibility.

If you strip out all the console I/O your Encrypt() will be much simpler and you may note you can make it even simpler (to read):

static (IEnumerable&lt;int&gt; key, string encrypted) Encrypt(string text)
{
    var key = GenerateEncryptionKey(text.Length);
    return (key, new String(text.Zip(key, (v, k) =&gt; (char)(v + k))));
}

static IEnumerable&lt;int&gt; GenerateEncryptionKey(int length)
    =&gt; new Enumerable.Range(0, length).Select(x =&gt; _rnd.Next(1, 9));

static _rnd = new Random();


What I did:


I moved the generation of the encryption key to a separate function. In real world it will be a service you set separately. This gives you the ability to test the Encrypt() code with edge-cases using well-known and repeatable encryption keys (what will happen if v + k - which is Int32 - is higher than UInt16.MaxValue when casting back to char? Try and see). 
I used Enumerable.Zip() function instead of manually looping through the input + key. Whenever possible you should use available functions to perform a task because they make your code faster to write, easier to read and...less buggy (they already been tested!) Take the time to explore LINQ functions, when working with collections they provide many things you may need.
I moved everything not directly related to encryption out of this function. Now I can test encryption in isolation!


It's an exercise for the reader to do the same thing for the Decrypt() function and to move magic values (like 0 and 9) to appropriate const int fields. Also, as exercise, think that adding a random value to a character does not necessarily produce another valid character (Unicode surrogates) and the string resulting from concatenation may not be a valid or representable string (you'd better encode the IEnumerable&lt;int&gt; produced by encryption instead of simply creating a string out of it).



Your logic for looping in the Main() method is little bit weird. You have a loop, you exit the loop and you recursively (from the encryption/decryption functions...) call Main() again. I will skip the StackOverflowException because you probably won't ever use this app long enough but it makes the program flow really hard to follow.

There are many ways to implement this but let's start with something easy:

static void Main()
{
    while (true)
    {
        var actionToPerform = Prompt("(E)ncrypt, (D)ecrypt or (Q)uit?");
        if (actionToPerform.Equals("e", StringComparison.CurrentCultureIgnoreCase))
        {
            ReadTextAndEncrypt();
        }
        else if (actionToPerform.Equals("d", StringComparison.CurrentCultureIgnoreCase))
        {
            ReadTextAndDecrypt();
        }
        else if (actionToPerform.Equals("q", StringComparison.CurrentCultureIgnoreCase))
        {
            return;
        }
        else
        {
            Console.WriteLine("Unknown option. Try again.");
        }
    }
}


The same can be done using a dictionary (&lt;string, Action&gt;) but for now let's keep it simple. For string comparison I do not use ToLower() but the appropriate overload of String.Equals() where I can specify a StringComparison value. The schoolbook example about case insensitive comparison is the i in Turkish (== performs comparison using current culture) where "i" != "I".ToLower() (and similarly "i".ToUpper() != "I").

Note the Prompt() function which is also reused elsewhere:

static string Prompt(string prompt)
{
    Console.Write(prompt + ' ');
    return Console.ReadLine();
}


We can now write our ReadTextAndEncrypt() function putting the things together:

static void ReadTextAndEncrypt()
{
    (string key, string encrypted) result = Encrypt(Prompt("Enter the text to encrypt:"));

    var key = new String(result.key.Select(x =&gt; (char)('0' + x)));
    Console.WriteLine("Encryption key is: {0}, key);
    Console.WriteLine("Encrypted string is: {0}, result.encrypted);
}


That's all, now, as exercise, write ReadTextAndDecrypt() with the same logic. You may also add Console.Clear() and Console.ReadKey() as appropriate (also that conversion for result.key isn't something to be proud of...)



In this example where I wrote (T1 key, T2 encrypted) I used value tuples (a C# 7 feature), it's similar to Tuple&lt;T1, T2&gt; but with an attached compile-time name. Function may be well rewritten as:

static Tuple&lt;IEnumerable&lt;int&gt;, string&gt; Encrypt(text)
{
    var key = GenerateEncryptionKey(text.Length);
    var encryptedText = new String(text.Zip(key, (v, k) =&gt; (char)(v + k)));

    return Tuple.Create(key, encryptedText);
}