Brent
Brent

Reputation: 4876

creating cryptographically secure passwords in .net framework

I needed 3 unique passwords for 3 database connection strings, so I hacked together the following as part of a winforms application:

RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
void CharCount_TextChanged(object sender, System.EventArgs e)
{
    int noChars;
    if (int.TryParse(CharCount.Text, out noChars))
    {
        byte[] random = new Byte[noChars-1];
        rng.GetBytes(random);
        OutputTxt.Text = Convert.ToBase64String(random);
    }
}

however, if I enter 8 for the number of characters, I get something like "igTJEQptvQ==", which is 12 characters, and every string seems to end in "=="

can someone please explain both why the number of characters is greater than the size of the byte array, and also why the strings seem to regularly end in ==.

Forgive how amateur this question is, and thank you for your explanations.

Upvotes: 0

Views: 91

Answers (2)

Th3Minstr3l
Th3Minstr3l

Reputation: 322

If you're after a text password of exactly 8 (or whatever characters) how about using something like this method I wrote once for generating fairly random user passwords:

string GeneratePassword(int numUpper, int numLower, int numNum, int numSym)
        {               
            char[] genPassword = new char[numUpper+numLower+numNum+numSym]; //array to store our password
            int gPidx=0; // holds the index of where we are in the genPassword char array 

            Random rng = new Random(); // You could use your CryptoRNG here if you want

            char[] upperChars = {'A','B','C','D','E','F','G',
                'H','I','J','K','L','M','N','P','Q',
                'R','S','T','U','V','W','X','Y','Z'}; // No 'O' as it is easily confused with '0' (but does slightly reduce the keyspace)
            char[] lowerChars = {'a','b','c','d','e','f','g',
                'h','i','j','k','m','n','o','p','q',
                'r','s','t','y','v','w','x','y','z'}; // No 'l' as it is easily confused with '1' (but does slightly reduce the keyspace)
            char[] numberChars = {'2','3','4','5','6','7','8','9'}; // No '1' or '0' as they are easily confused
            char[] symbolChars = {'!','£','$','%','^','&',
                '*','+','=','-','@','#','?'}; // Just the easy ones for a luser to find

            //get uppers & put into the password array
            for(int i=0; i<numUpper; i++) {
                genPassword[gPidx] = upperChars[rng.Next(0,upperChars.Length)];
                gPidx++;
            }
            //get lowers & put into the password array
            for(int i=0; i<numLower; i++) {
                genPassword[gPidx] = lowerChars[rng.Next(0,lowerChars.Length)];
                gPidx++;
            }
            //get numbers & put into the password array
            for(int i=0; i<numNum; i++) {
                genPassword[gPidx] = numberChars[rng.Next(0,numberChars.Length)];
                gPidx++;
            }
            //get symbols & put into the password array
            for(int i=0; i<numSym; i++) {
                genPassword[gPidx] = symbolChars[rng.Next(0,symbolChars.Length)];
                gPidx++;
            }

            // Shuffle the letters (leave the numbers and symbols)
                    // I like passwords to start with a letter as some
                    // sites don't like non-alpha first chars
            int endOfAlpha = genPassword.Length-numNum-numSym;
            for(int i=0; i<endOfAlpha;i++) {
                // For each character in our password
                // pick a number between 0 and the end of the password
                // swap the characters
                char tempChar;
                int random = rng.Next(0,endOfAlpha); //don't alter the first letter
                tempChar = genPassword[i]; //store the current Value
                genPassword[i] = genPassword[random];
                genPassword[random]=tempChar;               
            }

            // Re-Shuffle leaving the first character intact
            for(int i=1; i<genPassword.Length;i++) {
                // For each character in our password
                // pick a number between 0 and the end of the password
                // swap the characters
                char tempChar;
                int random = rng.Next(1,genPassword.Length); //don't alter the first letter
                tempChar = genPassword[i]; //store the current Value
                genPassword[i] = genPassword[random];
                genPassword[random]=tempChar;               
            }
            return new string(genPassword);
        }

Upvotes: 0

tolanj
tolanj

Reputation: 3724

1) byte arrays are accessed 0 based but sized 1 based, ie you are making a 7 byte array not an 8 byte one

2) Base64String was designed for (things like) sending binary data in strings in emails. It only uses a 'safe' subset of characters to represent the data (64 chars = 2 ^ 6). To represent 7 bytes (2^ (7*8) = 2^56 bits) it needs 56/6 = 10 chars, its actually padded out to a multiple of 4 chars (ie 12, 16 etc) by trailing ===s

see http://en.wikipedia.org/wiki/Base64 padding section for example

Upvotes: 2

Related Questions