I'm using this code to generate random strings with given length
public string RandomString(int length)
{
const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
StringBuilder res = new StringBuilder();
Random rnd = new Random();
while (0 < length--)
{
res.Append(valid[rnd.Next(valid.Length)]);
}
return res.ToString();
}
However, I read that RNGCryptoServiceProvideris more secure than Random class. How can I implement RNGCryptoServiceProvider to this function. It should use valid string just like this function.
Since RNGRandomNumberGenerator only returns byte arrays, you have to do it like this:
static string RandomString(int length)
{
const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
StringBuilder res = new StringBuilder();
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
{
byte[] uintBuffer = new byte[sizeof(uint)];
while (length-- > 0)
{
rng.GetBytes(uintBuffer);
uint num = BitConverter.ToUInt32(uintBuffer, 0);
res.Append(valid[(int)(num % (uint)valid.Length)]);
}
}
return res.ToString();
}
Note however that this has a flaw, 62 valid characters is equal to 5,9541963103868752088061235991756 bits (log(62) / log(2)), so it won't divide evenly on a 32 bit number (uint).
What consequences does this have?
As a result, the random output won't be uniform. Characters which are lower in value will occur more likely (just by a small fraction, but still it happens).
To be more precise, the first 4 characters of a valid array are 0,00000144354999199840239435286 % more likely to occur.
To avoid this, you should use array lengths that will divide evenly into 64 (Consider using Convert.ToBase64String on the output instead, since you can cleanly match 64 bits to 6 bytes.
You need to generate random bytes using RNGCryptoServiceProvider and append only the valid ones to the returned string:
const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
static string GetRandomString(int length)
{
string s = "";
using (RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider())
{
while (s.Length != length)
{
byte[] oneByte = new byte[1];
provider.GetBytes(oneByte);
char character = (char)oneByte[0];
if (valid.Contains(character))
{
s += character;
}
}
}
return s;
}
You could also use modulo in order to not skip the invalid byte values but that the chances for each character won't be even.
The RNGCryptoServiceProvider returns random numbers in the form of bytes, so you need a way to get a more convenient random number from it:
public static int GetInt(RNGCryptoServiceProvider rnd, int max) {
byte[] r = new byte[4];
int value;
do {
rnd.GetBytes(r);
value = BitConverter.ToInt32(r, 0) & Int32.MaxValue;
} while (value >= max * (Int32.MaxValue / max));
return value % max;
}
Then you can use that in your method:
public static string RandomString(int length) {
const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
StringBuilder res = new StringBuilder();
using (RNGCryptoServiceProvider rnd = new RNGCryptoServiceProvider()) {
while (length-- > 0) {
res.Append(valid[GetInt(rnd, valid.Length)]);
}
}
return res.ToString();
}
(I made the method static, as it doesn't use any instance data.)
Note
I am aware of the deviation with OP's use case, but I think this might help others who does not have the "arbitrary" 62 character limitation and just want to encode bytes using RNGCryptoServiceProvider to generate random string.
TL;DR
just skip to the bottom, the base64 encoded case.
A lot can be said about the reasons to convert a cryptographic byte array into a string, but usually it is for some sort of serialization purposes; and hence, in that case: the selected character set is arbitrary.
So, if it is about serialization, you have tons of options; e.g:
text as HEX representation
text as base64 representation
text as alternative representation
All of these are making use of the same thing: encode numbers in such a way it is suited to be transmitted in a medium that does not support native binary transfer.
I call this "text as ... representation", because in the end, it is text that will be transmitted.
An example in HEX:
//note: using text as HEX makes the result longer
var crypt = new RNGCryptoServiceProvider();
var sb = new StringBuilder();
var buf = new byte[10]; //length: should be larger
crypt.GetBytes(buf);
//gives a "valid" range of: "0123456789ABCDEF"
foreach (byte b in buf)
sb.AppendFormat("{0:x2}", b); //applies "text as hex" encoding
//sb contains a RNGCryptoServiceProvider based "string"
Now you'll say:
but wait: these are only 16 characters where OP's sequence has 62. 62 is more efficient than 16, so, converted to text, your string will be a lot longer.
"Yes", I'll say, "and if that's a problem, why don't you pick a larger number easy-to-read-and-serrializable-characters... 62 ... or 64 perhaps"
The code would be:
//note: added + and / chars. could be any of them
const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+/";
var crypt = new RNGCryptoServiceProvider();
var sb = new StringBuilder();
var buf = new byte[10]; //length: should be larger
crypt.GetBytes(buf); //get the bytes
foreach (byte b in buf)
sb.Append(valid[b%64]);
Note: As #Guffa stated; using % is forbidden unless it doesn't alter the distribution. To make this happen, given a evenly distributed set, the subset must fit exactly x times in the original set.
So, expanding your initial valid set with 2 gives a valid result (because: 256 / 64 = 4) --- but, this does not honor OP's 62 character requirements. In fact to get an even distribution you'll need some trickery, addressed in the other answers.
Also note: in all the answers, including this one, the sub-set is smaller than the 256 possibilities of the byte. This means there is less information available in an encoded char than in a byte. This means if you have your string with 4 encoded chars, it's easier to crack the original 4 byte result of the RNGCryptoServiceProvider - So keep in mind, the cryptographic strength is depending on the byte length, not the encoded char length.
Base64
But, now you say:
"Ok, let drop the 62 requirement, and use 64 - why not use 64 base encoding?",
well, if it's suits you, but note trailing =, see Base64 on Wikipedia, it is an additional optional charater which is used.
var crypt = new RNGCryptoServiceProvider();
// = padding characters might be added to make the last encoded block
// contain four Base64 characters.
// which is actually an additional character
var buf = new byte[10];
crypt.GetBytes(buf);
//contains a RNGCryptoServiceProvider random string, which is fairly readable
//and contains max 65 different characters.
//you can limit this to 64, by specifying a different array length.
//because log2(64) = 6, and 24 = 4 x 6 = 3 x 8
//all multiple of 3 bytes are a perfect fit. (e.g.: 3, 6, 15, 30, 60)
string result = Convert.ToBase64String(buf);
My implementation that fixes the issue with 5,9541963103868752088061235991756 bits
public static string RandomString(int length)
{
const string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
var res = new StringBuilder(length);
using (var rng = new RNGCryptoServiceProvider())
{
int count = (int)Math.Ceiling(Math.Log(alphabet.Length, 2) / 8.0);
Debug.Assert(count <= sizeof(uint));
int offset = BitConverter.IsLittleEndian ? 0 : sizeof(uint) - count;
int max = (int)(Math.Pow(2, count*8) / alphabet.Length) * alphabet.Length;
byte[] uintBuffer = new byte[sizeof(uint)];
while (res.Length < length)
{
rng.GetBytes(uintBuffer, offset, count);
uint num = BitConverter.ToUInt32(uintBuffer, 0);
if (num < max)
{
res.Append(alphabet[(int) (num % alphabet.Length)]);
}
}
}
return res.ToString();
}
see https://bitbucket.org/merarischroeder/number-range-with-no-bias/
I'm sure I have answered this one before with a secure implementation, no bias, and good performance. If so, please comment.
Looking at Tamir's answer, I thought it would be better to use the modulus operation, but trim off the incomplete remainder of byte values. I'm also writing this answer now (possibly again), because I need to reference this solution to a peer.
Approach 1
Support for ranges that are no bigger than 0-255. But it can fall back to approach 2 (which is a little slower)
One byte is always used per value.
Truncate the incomplete remainder if (buffer[i] >= exclusiveLimit)
Modulate the desired range size. After truncation beyond the exclusiveLimit the modulus remains perfectly balanced
(Using a bitmask instead of modulus is a slower approach)
EG. If you want a range 0-16 (that's 17 different values), then 17 can fit into a byte 15 times. There is 1 value that must be discarded [255], otherwise the modulus will be fine.
Code for Approach 1
const string lookupCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
static void TestRandomString()
{
Console.WriteLine("A random string of 100 characters:");
int[] randomCharacterIndexes = new int[100];
SecureRangeOriginal(randomCharacterIndexes, lookupCharacters.Length);
var sb = new StringBuilder();
for (int i = 0; i < randomCharacterIndexes.Length; i++)
{
sb.Append(lookupCharacters[randomCharacterIndexes[i]]);
}
Console.WriteLine(sb.ToString());
Console.WriteLine();
}
static void SecureRangeOriginal(int[] result, int maxInt)
{
if (maxInt > 256)
{
//If you copy this code, you can remove this line and replace it with `throw new Exception("outside supported range");`
SecureRandomIntegerRange(result, 0, result.Length, 0, maxInt); //See git repo for implementation.
return;
}
var maxMultiples = 256 / maxInt; //Finding the byte number boundary above the provided lookup length - the number of bytes
var exclusiveLimit = (maxInt * maxMultiples); //Expressing that boundary (number of bytes) as an integer
var length = result.Length;
var resultIndex = 0;
using (var provider = new RNGCryptoServiceProvider())
{
var buffer = new byte[length];
while (true)
{
var remaining = length - resultIndex;
if (remaining == 0)
break;
provider.GetBytes(buffer, 0, remaining);
for (int i = 0; i < remaining; i++)
{
if (buffer[i] >= exclusiveLimit)
continue;
var index = buffer[i] % maxInt;
result[resultIndex++] = index;
}
}
}
}
Approach 2
Technically ranges from 0 to ulong.Max can be supported
Treat RNGCryptoServiceProvider bytes as a bitstream
Calculate the base2 bit length needed per number
Take the next number from the random bitstream
If that number is still greater than the desired range, discard
Results:
See the repository for the latest results from the test harness
Both approaches appear to have a suitably balanced distribution of numbers
Approach 1 is faster [859ms] but it only works on individual bytes.
Approach 2 is a little slower [3038ms] than Approach 1, but it works across byte boundaries. It discards fewer bits, which can be useful if the random stream input becomes a bottleneck (different algorithm for example).
A hybrid of both approaches gives the best of both worlds: better speed when the byte range is 0-255, support for ranges beyond 255 but a bit slower.
I personally like to use this:
private static string GenerateRandomSecret()
{
var validChars = Enumerable.Range('A', 26)
.Concat(Enumerable.Range('a', 26))
.Concat(Enumerable.Range('0', 10))
.Select(i => (char)i)
.ToArray();
var randomByte = new byte[64 + 1]; // Max Length + Length
using (var rnd = new RNGCryptoServiceProvider())
{
rnd.GetBytes(randomByte);
var secretLength = 32 + (int)(32 * (randomByte[0] / (double)byte.MaxValue));
return new string(
randomByte
.Skip(1)
.Take(secretLength)
.Select(b => (int) ((validChars.Length - 1) * (b / (double) byte.MaxValue)))
.Select(i => validChars[i])
.ToArray()
);
}
}
There shouldn't be any part that needs additional description, but to clarify, this function returns a random string with a random length between 32 and 64 chars and doesn't use % (mod) therefore should keep uniformity a little better.
I use this to create a random salt at program installation and later save it to a file. Therefore security of generated string is not of special concern while the program is running as it is going to get written to an unencrypted file later on anyway.
However, for more serious situations, this shouldn't be used as it is and should be converted to use SecureString class if you are going to keep this value in memory. Read more here:
https://learn.microsoft.com/en-us/dotnet/api/system.security.securestring?redirectedfrom=MSDN&view=netframework-4.7.2
However, even this only applies to NetFramework, for NetCore you need to find another way to secure the value in the memory. Read more here:
https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md
RNGCryptoServiceProvider is deprecated in .NET 6
Instead, its base class RandomNumberGenerator should be used.
Based on Guffa's answer a solution can be crafted like this:
public static string GetString(int length, string alphabet)
{
var secret = new StringBuilder();
while (length-- > 0)
{
secret.Append(alphabet[RandomNumberGenerator.GetInt32(alphabet.Length)]);
}
return secret.ToString();
}
Whereas alphabet is the desired set of valid characters.
private string sifreuretimi(int sayı) //3
{
Random rastgele = new Random();
StringBuilder sb = new StringBuilder();
char karakter1 = ' ', karakter2 = ' ', karakter3 = ' ';
int ascii1, ascii2, ascii3 = 0;
for (int i = 0; i < sayı/3; i++)
{
ascii1 = rastgele.Next(48,58);
karakter1 = Convert.ToChar(ascii1);
ascii2 = rastgele.Next(65, 91);
karakter2 = Convert.ToChar(ascii2);
ascii3 = rastgele.Next(97, 123);
karakter3 = Convert.ToChar(ascii3);
sb.Append(karakter1);
sb.Append(karakter2);
sb.Append(karakter3);
}
return sb.ToString();
}
This is a nice short solution that will also do the trick, if your goal is just to create a random string, let's say for use as a secure token.
public string RandomTokenString(int tokenLength)
{
var rngCryptoServiceProvider = new RNGCryptoServiceProvider();
var randomBytes = new byte[tokenLength];
rngCryptoServiceProvider.GetBytes(randomBytes);
return BitConverter.ToString(randomBytes);
}
Related
I want to generate so many random unique hexadecimal value by a method.
I want to specify the character length in the parameter of this method.
public string GenerateUniqueHexadecimal(int length)
{
string date = DateTime.Now.ToString("yyMMddHHmmss");
long l = Convert.ToInt64(date);
string hex = l.ToString("X2");
hex = hex.Substring(0, length);
return hex;
}
You could create a randomized hex string like this:
public class StringRandomizer
{
public const string StringChars = "0123456789abcdef";
public string GenerateUniqueHexString(int length)
{
Random rand = new Random();
var charList = StringChars.ToArray();
string hexString = "";
for(int i = 0; i < length; i++)
{
int randIndex = rand.Next(0, charList.Length);
hexString += charList[randIndex];
}
return hexString;
}
}
If you want unique strings then they are not perfectly random -- perfectly random strings may repeat. So you can use a non-random method instead of a random method. One possibility is encryption. Use a block cipher with the right bit size and encrypt the numbers 0, 1, 2, 3, ... for as many unique outputs as you need. Because encryption is one-to-one, you are guaranteed unique outputs as long as the inputs are unique and the key stays the same. The encrypted numbers will appear random, though they are not.
For 64 bits, 16 hex characters, use DES. For 128 bits, 32 hex characters, use AES. For other sizes either use Hasty Pudding cipher, or write a version of the Feistel cipher with the required block size. That last will not be as secure as DES or AES, though that only matters if you require a high level of security.
Well, you have two main possibilities: shuffle when we create all possible Pow(16, length) hexadecimal strings, shuffle and take count of them:
using System.Linq;
...
private static Random s_Generator = new Random();
private static String[] RandomUniqueHexShuffle(int length, int count) {
return Enumerable
.Range(0, 1 << (4 * length))
.OrderBy(i => s_Generator.NextDouble())
.Take(count)
.Select(item => item.ToString($"x{length}"))
.ToArray();
}
And direct generation: where we just create string after string checking them for being unique:
private static String[] RandomUniqueHexDirect(int length, int count) {
HashSet<string> used = new HashSet<string>();
string[] result = new string[count];
for (int i = 0; i < result.Length; ) {
string value = string.Concat(Enumerable
.Range(0, length)
.Select(j => s_Generator.Next(0, 16).ToString("x")));
if (used.Add(value))
result[i++] = value;
}
return result;
}
Which of them should be chosen? For small length and large count use shuffle, otherwise - direct generation
Demo:
// unique random hex strings of length 3 (5 of them)
Console.WriteLine(string.Join(Environment.NewLine, RandomUniqueHexShuffle(3, 5)));
Console.WriteLine();
// unique random hex strings of length 24 (4 of them)
Console.WriteLine(string.Join(Environment.NewLine, RandomUniqueHexShuffle(24, 4)));
Outcome: (may vary since these are random strings)
f6b
106
baf
412
443
b150ab7c723caceccdd6b597
e3c9a9ab066dcea9ae68031c
0b2e1b368e17ed1d88c3600e
33bdf06b109f6b953f5ed2bd
Here is my specific problem. I need to represent an integer (like 1,2,3,..) as a binary literal with exactly 128 bits.
This is my string representing 1 in binary:
string = "000...0001"; // 128 characters. all zeros until the last 1
Intended result:
bx000...0001;
This issue is that 128 bits is larger than normal types like int, double, decimal, etc. Thus, I believe you must use the BigInteger class to hold this binary value??
Another way to frame this: How can I make sure my BigInteger value is 16 bytes big?
BigInteger val = new BigInteger(1); // but must be 16 bytes exactly.
You would have to specify the number of bytes and pad whatever is missing with 0's, then you can use the BitArray to get the bit values. Something like this.
public static string GetBitString(BigInteger val, int bytes)
{
byte[] arrayBytes = new byte[bytes];
var valBytes = val.ToByteArray();
for (var i = 0; i < valBytes.Length; i++)
{
arrayBytes[i] = valBytes[i];
}
var arr = new BitArray(arrayBytes);
return $"bx{string.Join("", arr.Cast<bool>().Reverse().Select(c => c ? "1" : "0"))}";
}
Another options is to just resize the array created to be 16 bytes. Something like this
public static string GetBitString(BigInteger val, int bytes)
{
var valBytes = val.ToByteArray();
Array.Resize(ref valBytes, bytes);
return $"bx{string.Join("", new BitArray(valBytes).Cast<bool>().Reverse().Select(c => c ? "1" : "0"))}";
}
Using the ToBinaryString extension method from this answer modified to skip leading zeros and not force a sign zero, you can just use PadLeft to ensure you have leading zeroes:
public static string ToBinaryString(this BigInteger bigint) {
var bytes = bigint.ToByteArray();
// Create a StringBuilder having appropriate capacity.
var base2 = new StringBuilder(bytes.Length * 8);
// Convert remaining bytes adding leading zeros.
var idx = bytes.Length - 1;
for (; idx > 0 && bytes[idx] == 0; --idx)
;
for (; idx >= 0; --idx)
base2.Append(Convert.ToString(bytes[idx], 2).PadLeft(8, '0'));
return base2.ToString();
}
Then with the prefix and the left padding:
var ans = "bx"+val.ToBinaryString().PadLeft(128, '0');
Is there a way I could encode a long number (e.g. 12349874529768521) as lower-case letters AND numbers for the purposes of reducing its length? The idea is that a user might have a long number on a piece of paper.
It seems to me that if there are more symbols available, that the resulting number could be made shorter. So I'm looking for something like hexadecimal but using the larger symbol space of A-Z instead of just A-F.
This would be in C# (if it matters)
Base32 encoding is designed to produce an unambiguous, compact, human-readable (and non-obscene!) representation. From Wikipedia:
Base32 has a number of advantages over Base64:
The resulting character set is all one case, which can often be beneficial when using a case-insensitive filesystem, spoken language, or human memory.
The
result can be used as a file name because it can not possibly contain the '/' symbol, which is the Unix path separator.
The alphabet can be selected to avoid similar-looking pairs of different symbols, so the strings can be accurately transcribed by hand. (For example, the RFC 4648 symbol set omits the digits for one, eight and zero, since they could be confused with the letters 'I', 'B', and 'O'.)
A result excluding padding can be included in a URL without encoding any characters.
Base32 also has advantages over hexadecimal/Base16: Base32
representation takes roughly 20% less space. (1000 bits takes 200
characters, compared to 250 for Base16)
Douglas Crockford's original article on Base32 encoding is also well worth a read.
EDIT: here's a bit of C# that'll do base-N encoding of integers:
class Program {
private const string BINARY = "01";
private const string DECIMAL = "0123456789";
private const string HEX = "0123456789abcdef";
private const string BASE32 = "0123456789abcdefghjkmnpqrstvwxyz";
static string EncodeInt32(string alphabet, int value) {
var sb = new StringBuilder();
while (value > 0) {
sb.Insert(0, alphabet[value % alphabet.Length]);
value = value / alphabet.Length;
}
return sb.ToString();
}
static int DecodeInt32(string alphabet, string value) {
int result = 0;
int b = alphabet.Length;
int pow = 0;
for (var i = value.Length-1; i >= 0; i--) {
result += (int)(Math.Pow(b, pow++)) * alphabet.IndexOf(value[i]);
}
return (result);
}
static void Main(string[] args) {
for (var i = 0; i < 1234567890; i += 1234567) {
Console.WriteLine("{0} {1} {2}", i, EncodeInt32(BASE32, i), DecodeInt32(BASE32, EncodeInt32(BASE32, i)));
}
Console.ReadKey(false);
}
}
Example output showing typical reduction in string length:
1227159598 14j9y1e 1227159598
1228394165 14kfknn 1228394165
1229628732 14mn99w 1229628732
1230863299 14ntyy3 1230863299
1232097866 14q0mja 1232097866
1233332433 14r6a6h 1233332433
1234567000 14sbztr 1234567000
How about a BaseN Method to encode/decode your long into a string with characters you defined on your own
public static class BaseN
{
private const string CharList = "0123456789abcdefghijklmnopqrstuvwxyz";
public static String Encode(long input)
{
if (input < 0) throw new ArgumentOutOfRangeException("input", input, "input cannot be negative");
var result = new System.Collections.Generic.Stack<char>();
while (input != 0)
{
result.Push(CharList[(int)(input % CharList.Length)]);
input /= CharList.Length;
}
return new string(result.ToArray());
}
public static long Decode(string input)
{
long result = 0, pos = 0;
foreach (char c in input.Reverse())
{
result += CharList.IndexOf(c) * (long)Math.Pow(CharList.Length, pos);
pos++;
}
return result;
}
}
Usage:
long number = 12349874529768521;
string result = BaseN.Encode(number);
Sample:
https://dotnetfiddle.net/odwFlk
Here's a similar approach to the others, using a Base-N conversion:
using System;
using System.Text;
namespace ConsoleApp3
{
class Program
{
static void Main()
{
long n = 12349874529768521;
string baseChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz##";
var encoded = AsBaseN(n, baseChars.ToCharArray());
Console.WriteLine(encoded); // Prints "9HXNyK2uh"
long decoded = AsLong(encoded, baseChars.ToCharArray());
Console.WriteLine(decoded); // Prints "12349874529768521"
}
public static string AsBaseN(long value, char[] baseChars)
{
var result = new StringBuilder();
int targetBase = baseChars.Length;
do
{
result.Append(baseChars[value % targetBase]);
value /= targetBase;
}
while (value > 0);
return result.ToString();
}
public static long AsLong(string number, char[] baseChars)
{
long result = 0;
int numberBase = baseChars.Length;
long multiplier = 1;
foreach (char c in number)
{
result += multiplier * Array.IndexOf(baseChars, c);
multiplier *= numberBase;
}
return result;
}
}
}
If you want a different set of allowable characters, just change baseChars as appropriate. For example, if you just want 0-9 and A-Z:
string baseChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
This gives a result of T3OPA1YNLD3 (base 36) instead of 9HXNyK2uh (base 64).
I presume you mean you want to represent the number with fewer characters.
Base 36 will do this (0-9, a-z).
You can use a base 36 encoder.
Base36 is a binary-to-text encoding scheme that represents binary data in an ASCII string format by translating it into a radix-36 representation. The choice of 36 is convenient in that the digits can be represented using the Arabic numerals 0–9 and the Latin letters A–Z1 (the ISO basic Latin alphabet).
Here's an example of one, but any should work: https://github.com/thewindev/csharpbase36
Example Usage
// Encoding
Base36.Encode(10); // returns "A"
Base36.Encode(10000); // returns "7PS"
// Decoding
Base36.Decode("Z"); // returns 35L
Base36.Decode("10"); // returns 36L
Base36.Decode("7PS"); // returns 10000L
By default uppercase letters are used. If you really wanted to lowercase then a simple string.ToLowerInvarient() can change that.
However, uppercase is usually easier to read, which is why it's used by default, so you might want to consider using uppercase rather than lowercase.
You could look to Base64 encoding. It uses 0-9, A-Z, a-z, + and / characters. Or Base36, if you're interested only in 0-9 and A-Z.
So I recently created a static class for password related methods and had to make a method that generated a secure salt.
Initially I implemented RNGCryptoServiceProvider and filed n bytes into an array, which I converted to base64 and returned.
The issue was that with the output length, after conversion of course it was longer than n (which makes sense).
To fix this I changed the function to the method below, and I was just wondering if by trimming the base64 string any security risks are raised?
/// <summary>
/// Generates a salt for use with the Hash method.
/// </summary>
/// <param name="length">The length of string to generate.</param>
/// <returns>A cryptographically secure random salt.</returns>
public static string GenerateSalt(int length)
{
// Check the length isn't too short.
if (length < MIN_LENGTH)
{
throw new ArgumentOutOfRangeException("length", "Please increase the salt length to meet the minimum acceptable value of " + MIN_LENGTH + " characters.");
}
// Calculate the number of bytes required.
// https://en.wikipedia.org/wiki/Base64#Padding
// http://stackoverflow.com/questions/17944/how-to-round-up-the-result-of-integer-division
int bytelen = ((3 * length) + 4 - 1) / 4;
// Create our empty salt array.
byte[] bytes = new byte[bytelen];
// Where we'll put our generated salt.
string salt;
// Generate a random secure salt.
using (RNGCryptoServiceProvider randcrypto = new RNGCryptoServiceProvider())
{
// Fill our array with random bytes.
randcrypto.GetBytes(bytes);
// Get a base64 string from the random byte array.
salt = GetBase64(bytes);
}
// Trim the end off only if we need to.
if (salt.Length > length)
{
// Substring is the fastest method to use.
salt = salt.Substring(0, length);
}
// Return the salt.
return salt;
}
Also as a side question, I was having a quick look around and couldn't actually find what the hash function of the C# implementation of RNGCryptoServiceProvider actually is. Anyone know offhand?
Why is the length of the salt so important to you? I wouldn't think that there are any real security implication, since the only real requirement of a salt is that it be random and unguessable.
In other words, go for it.
EDIT: Here is another way of doing it using Linq.
Random random = new Random();
int length = 25; // Whatever length you want
char[] keys = "ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890!£$%^&*()".ToCharArray(); // whatever chars you want
var salt = Enumerable
.Range(1, length) // equivalent to the loop bit, for(i.. )
.Select(k => keys[random.Next(0, keys.Length - 1)]) // generate a new random char
.Aggregate("", (e, c) => e + c); // join them together into a string
There is no security risk with that way of generating the salt.
The salt doesn't need that level of security at all, it's just there so that rainbow tables can't be used to crack the hash/encryption. The regular Random class would be enough to create a salt.
Example:
/// <summary>
/// Generates a salt for use with the Hash method.
/// </summary>
/// <param name="length">The length of string to generate.</param>
/// <returns>A random salt.</returns>
public static string GenerateSalt(int length) {
// Check the length isn't too short.
if (length < MIN_LENGTH) {
throw new ArgumentOutOfRangeException("length", "Please increase the salt length to meet the minimum acceptable value of " + MIN_LENGTH + " characters.");
}
// Where we'll put our generated salt.
StringBuilder salt = new StringBuilder(length);
// Fill our string with random characters.
Random rnd = new Random();
string chars = "0123456798ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
for (int i = 0; i < length; i++) {
salt.Append(chars[rnd.Next(chars.Length)]);
}
// Return the salt.
return salt.ToString();
}
Note: If the function would be used more than once close in time, you would use a single Random object and pass into the function, as Random instances created too close in time will give the same random sequence.
Just for fun, here is a much faster way of doing it (even though the code doesn't look great). Try cutting and pasting this to see. On my machine it executed in about 1.6s compared to 7.1s. Since I was doing a million iterations in each case, I don't think the execution time is that important!
string msg = "";
int desiredLength = 93; // Length of salt required
Stopwatch watch = new Stopwatch();
watch.Start();
for (int k=0; k<1000000; k++)
{
double guidsNeeded = Math.Ceiling(desiredLength / 36.0);
string salt = "";
for (int i=0; i<guidsNeeded; i++)
{
salt += Guid.NewGuid().ToString();
}
salt = salt.Substring(0,desiredLength);
}
msg += watch.ElapsedMilliseconds.ToString(); // 1654 ms
watch.Start();
for (int j=0; j<1000000; j++)
{
GenerateSalt(93);
}
msg += "\r\n" + watch.ElapsedMilliseconds.ToString(); // 7096 ms
This is using Guffa's code for GenerateSalt.
I am working on a requirement to randomly generate codes(like a random number). The code should be a alphanumeric but should only allow (A-Z0-9]. The user can specify the number of characters in the code. The code can be 4-9 chars long depending on the user input.
an example of the code would be 'AG43', 'XFR4A5UU0'.
Edit :- I am looking at the best way to solve this. I was looking at generating 2 digit random number in the range 11 to 99. If the number is between 65 & 90 (ascii of A-z ), I will use the ascii for it else i will append the number generated to my code string.
Please advise.
var number_of_chars = 4;
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var random = new Random();
var result = new string(
Enumerable.Repeat(chars, number_of_chars)
.Select(s => s[random.Next(s.Length)])
.ToArray());
public string GetRandomString(int length)
{
var newBuffer = new byte[length];
if (length <= 0)
return null;
// This was used for a password generator... change this how every needed
var charSet = ("ABCDEFGHJKLMNPQRSTUVWXYZ" +
"abcdefghijkmnprstuvxyz" +
"23456789").ToCharArray();
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(newBuffer);
var newChars = newBuffer.Select(b => charSet[b % charSet.Length]).ToArray();
return new string(newChars);
}
}