Securely Generate 6 MFA Pin - c#

I'm trying to Generate a 6 digit code to be used for 2 factor authentication, at a first glance I might do something like this:
Random random = new Random();
var securitycode = random.Next(1000000, 10000000);
However this seems somewhat insecure to me because, there probably is a way to predict the next number if you can figure out the seeds by grabbing alot of security codes.
I'm thinking there is a better way to get a secure code using RNGCryptoServiceProvider but i'm a bit confused on how I can assure that the code generated is 6 digits
private string GenerateSecurityCode(int length)
{
var provider = new RNGCryptoServiceProvider();
var byteArray = new byte[8];
provider.GetBytes(byteArray);
var code = BitConverter.ToUInt32(byteArray, 0);
//how can I assure the code is 6 digits
}
Is this a secure way to generate MFA Codes, if not what would be a good method for Generating numeric codes?

I'm not sure if this is the most secure but I ended up doing this:
private string GenerateSecurityCode()
{
var buffer = new byte[sizeof(UInt64)];
var cryptoRng = new RNGCryptoServiceProvider();
cryptoRng.GetBytes(buffer);
var num = BitConverter.ToUInt64(buffer, 0);
var code = num % 1000000;
return code.ToString("D6");
}

Related

C# Best way to add abunch of sounds to a program and pick a random one to play?

I'm trying to get a voice line/Sound to play on form show however I only can play two different sounds and Id like to be able to have a list that gets played at random every time the form is shown, Anyone know the best way to go about this? here's the code I have right now:
if (Properties.Settings.Default.UI > 0)
{
var random = new Random();
SoundPlayer audio = new SoundPlayer(_2B.Properties.Resources.russian);
audio.Play();
}
else
{
SoundPlayer audio = new SoundPlayer(_2B.Properties.Resources.freedom);
audio.Play();
}
I tried adding a var for the random pick but it didnt work, I was assuming I could possibly do the same thing as this:
if (settings.version < newversionparsed)
{
bunifuCircleProgressbar1.Value +=1;
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringChars = new char[8];
var random = new Random();
for (int i = 0; i < stringChars.Length; i++)
{
stringChars[i] = chars[random.Next(chars.Length)];
}
var finalString = new String(stringChars);
But im not too sure that it's working like I think, Nor do I know if this is possible.
Keep your Random so you would get a reasonable result
According to MSDN
On the .NET Framework, initializing two random number generators in a tight loop or in rapid succession creates two random number generators that can produce identical sequences of random numbers. In most cases, this is not the developer's intent and can lead to performance issues, because instantiating and initializing a random number generator is a relatively expensive process.
Both to improve performance and to avoid inadvertently creating separate random number generators that generate identical numeric sequences, we recommend that you create one Random object to generate many random numbers over time, instead of creating new Random objects to generate one random number.
So create one and use it multiple times is better.
Note: Random is not thread safe, but MSDN has an example for thread safe version.
Following is the example.
public static void Main()
{
Console.WriteLine("Hello World");
int len = 10;
int seed = 7;
string newEverytime = "";
for( int i =0 ; i < len; i++){
newEverytime+= new Random().Next( seed ).ToString();
}
Console.WriteLine( newEverytime ); // print something fixed
string get10Next="";
Random r = new Random();
for( int i =0 ; i < len; i++){
get10Next+=r.Next(seed);
}
Console.WriteLine( get10Next ); // print random string
}
For your program, you need to new a Random as a class member.
class Foo{
Random rnd = new Random();
void PlaySound(){
int val = rnd.Next( 1 ); // use a seed you like
// implement your play sound
}
}
Create an instance of Random somewhere, ideally static so you can easily reuse the same instance:
static Random random = new Random();
Create an array of all the sounds you want available to you:
string[] Stream = new Stream[] {
_2B.Properties.Resources.russian,
_2B.Properties.Resources.freedom
// Add as many sounds as you want
};
Then when you want to get a sound to play:
string sound = sounds[random.Next(sounds.Length)];
SoundPlayer audio = new SoundPlayer(sound);

Inconsistent Bouncy Castle ECDSA Signing/Verifying behavior on IOS

The Problem:
I expect Signing and Verifying Signatures using the same nuget package on different platforms/systems to behave in a similar fashion.
However, Signing and Verifying Signatures using BouncyCastle on an IOS system (Simulator and phone produce the same results) occasionally (and by occasionally I mean quite frequently) produces what appears to be invalid signatures.
What I tried:
In an attempt to make sure I wasn't doing anything wrong, I wrote a short test program which would randomly generate 100 sets of data, sign each set 10000 times, and verify each signature. A pure BouncyCastle implementation of the desired behavior on Windows - and it worked, 1 million out of 1 million times.
This code also appears to work when tested on Android; again, 100% of the time.
However, when it comes to testing it on IOS, the same results cannot be replicated.
Here are the results testing on IOS (Each test round consisted of shutting down the simulator, then running the IOS program via Visual Studio. Then, the program generates a new private/public key pair and signs a random set of data bytes 10000 times):
The testing was originally only on the IPhone XR, but I thought that it might be a problem with the specific model of phone, so I also did the test using the IPhone X. There are more results from previous testing, but they pretty much look the same as the samples listed below, even if the test was done on a physical IPhone XR.
IPhone XR IOS 12.2
Test Round 1: 10001 Signatures passed verification out of 10001?
Test Round 2: 10000 Signatures passed verification out of 10001?
Test Round 3: 10000 Signatures passed verification out of 10000
Test Round 4: 0 Signatures passed verification out of 10000
I have no idea how Test Rounds 1 & 2 came out with 10001 runs instead of 10000 - After starting the test, there was zero interaction with the process until it hit the exit breakpoint.
IPhone X IOS 12.2
Test Round 1: 9996 Signatures passed verification out of 10000
Test Round 2: 10000 Signatures passed verification out of 10004?
Test Round 3: 0 Signatures passed verification out of 10000
Test round 2 somehow managed to perform an extra four tests, corresponding with 4 failing tests recorded - The only thing that changed during this test was that I set a breakpoint mid loop in order to check on the progress, and then hit continue
Additionally, we decided to check the endian-ness of both systems; what if they were mismatched? The search, however, was unfruitful, as a quick check in the immediate window of the debugger via BitConverter.IsLittleEndian showed that both systems were using Little Endian format.
The code used to test, modified to sign only 10000 times with one set of data
static void SigningTest(byte[] data, byte[] pubkey, byte[] privkey)
{
var curve = SecNamedCurves.GetByName("secp256r1");
var domain = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);
var d = new Org.BouncyCastle.Math.BigInteger(privkey);
var xx = new Org.BouncyCastle.Math.BigInteger(1, pubkey.Take(32).ToArray());
var yy = new Org.BouncyCastle.Math.BigInteger(1, pubkey.Skip(32).ToArray());
var q = curve.Curve.CreatePoint(xx, yy);
var publicParams = new ECPublicKeyParameters(q, domain);
var privateParams = new ECPrivateKeyParameters(d, domain);
var cipherkp = new AsymmetricCipherKeyPair(publicParams, privateParams);
var signer = SignerUtilities.GetSigner("SHA256withECDSA");
signer.Init(true, cipherkp.Private);
var ccount = 0;
var icount = 0;
for (var i = 0; i < 10000; i++)
{
signer.BlockUpdate(data, 0, data.Length);
var signature = signer.GenerateSignature();
var der = Asn1Object.FromByteArray(signature) as DerSequence;
var arrList = new List<byte[]>();
foreach (DerInteger theInt in der)
{
var barr = theInt.PositiveValue.ToByteArrayUnsigned();
if (barr.Length == 31)
{
barr = new byte[32];
Array.Copy(theInt.PositiveValue.ToByteArrayUnsigned(), 0, barr, 1, 31);
}
arrList.Add(barr);
}
var realsig = new byte[64];
Array.Copy(arrList[0], realsig, arrList[0].Length);
Array.Copy(arrList[1], 0, realsig, arrList[0].Length, arrList[1].Length);
if (Verify(data, pubkey, realsig))
{
ccount++;
}
else
{
icount++;
}
}
// Add something here like System.Diagnostics.Debugger.Break() so that a break point can be set.
}
static bool Verify(byte[] data, byte[] publicKey, byte[] signature)
{
var curve = SecNamedCurves.GetByName("secp256r1");
var x = new byte[32];
var y = new byte[32];
Array.Copy(publicKey, 0, x, 0, 32);
Array.Copy(publicKey, 32, y, 0, 32);
var derSignature = new DerSequence(
new DerInteger(new Org.BouncyCastle.Math.BigInteger(1, signature.Take(32).ToArray())),
new DerInteger(new Org.BouncyCastle.Math.BigInteger(1, signature.Skip(32).Take(32).ToArray()))
)
.GetDerEncoded();
var xx = new Org.BouncyCastle.Math.BigInteger(1, publicKey.Take(32).ToArray());
var yy = new Org.BouncyCastle.Math.BigInteger(1, publicKey.Skip(32).ToArray());
var domainparams = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H, curve.GetSeed());
var ecp = curve.Curve.CreatePoint(xx, yy);
var pubkeyparams = new ECPublicKeyParameters(ecp, domainparams);
var verifier = SignerUtilities.GetSigner("SHA256withECDSA");
verifier.Init(false, pubkeyparams);
verifier.BlockUpdate(data, 0, data.Length);
return verifier.VerifySignature(derSignature);
}
So, how does one go about fixing this?
As Peter Dettman pointed out, the private key was not being created using the signed version of the BigInteger constructor.
Along with changing that line to use the signed version of the constructor, the test code was also modified to move the initialization of the signer into the body of the for loop.
if (barr.Length == 31)
{
barr = new byte[32];
Array.Copy(theInt.PositiveValue.ToByteArrayUnsigned(), 0, barr, 1, 31);
}
was also changed to
if (barr.Length < 32)
{
barr = new byte[32 - barr.Length].Concat(barr).ToArray();
}
in order to account for the fact that there is no theoretical lower limit to the byte array length of barr.
After these changes, the code appears to be functioning properly in all cases.

Simple way to encrpyt/decrypt a number

I need to encrypt a 2-digit number in a simple way. The samples I found in google seems to be quite complex. Is there any easy way to achieve that?
UPDATE
I'm working on a custom numeric captcha for my ASP.NET MVC application. I've created a custom html helper that will render an image tag with base-64 encoded string of the captcha image. The captcha image will be something like 23 + 12 = ?. When the user submit the answer I want to validate it right? I'm not interested in storing the sum in session so I thought of encrypt the sum and attach as a hidden field and so when the user submit the form I can easily do the validation.
If your number is x then you can encrypt it as (x + key) mod 100. This will result in another 2 digit number, y.
It doesn't get much simpler than that.
The decryption is simply x = y - key, +100 if necessary.
If key is 2:
x = 15
y = 15 + 2 = 17
x = 17 - 2 = 15
x = 99
y = 99 + 2 mod 100 = 101 mod 100 = 1
x = 1 - 2 + 100 = 99;
Even simpler would be to encrypt x as x. They would definitely never expect that...
Edit 1:
On a more serious note, If this is not some sort of personal experiment/homework I'd stay clear of such "simple" algorithms and go with System.Security.Cryptography and those not-that-complex samples from Google or charles sun's comment. Unless you make a carrier out of it never implement you own encryption/decryption algorithms, that way lies madness.
Edit 2:
So you want to send both the captcha and its correct response to the client? I don't think that is how it's done (but then again this is not my field...). I always thought validation is done on the server side (the part you control and keep secure).
To be on the safe side, I would do this the hard way and encrypt everything properly.
This is maybe not entirely serious, but it works!
static IEnumerable<string> GetRandomStringsForever()
{
var rng = new Random(); // or maybe new Random(14142848)
while (true)
{
char[] arr = new char[8];
for (int idx = 0; idx < arr.Length; ++idx)
arr[idx] = (char)rng.Next('A', 'Z' + 1);
yield return new string(arr);
}
}
static void Main()
{
var secretKey = GetRandomStringsForever().Distinct().Take(100).ToList();
int message = 42;
// encrypt:
string cryptic = secretKey[message];
Console.WriteLine("Who can guess the number from this: " + cryptic);
// decrypt:
int reconstructed = secretKey.IndexOf(cryptic);
Console.WriteLine("The message was: " + reconstructed);
}
Well, if people know you're doing this using my idea, they will probably be able to construct the secretKey themselves (using the same version of .NET as you), so this is not REALLY safe.

C# : How to generate unique passwords with the length of 7 or 8 characters

I need to repeated generate unique password many times, Ensure that every time the generated passwords are unique, Please help me.
Thanks!
So here is another method which generates cryptedRandom password and a thread safe...
private string CryptedRandomString()
{
lock (this)
{
int rand = 0;
byte[] randomNumber = new byte[5];
RNGCryptoServiceProvider Gen = new RNGCryptoServiceProvider();
Gen.GetBytes(randomNumber);
rand = Math.Abs(BitConverter.ToInt32(randomNumber, 0));
return ConvertIntToStr(rand);
}
}
private string ConvertIntToStr(int input)
{
lock (this)
{
string output = "";
while (input > 0)
{
int current = input % 10;
input /= 10;
if (current == 0)
current = 10;
output = (char)((char)'A' + (current - 1)) + output;
}
return output;
}
}
Now you can call this method like this: -
string GeneratedPassword = "";
GeneratedPassword = CryptedRandomString() + CryptedRandomString();
Console.WriteLine(GeneratedPassword.Substring(0,8));
Now you all must be wondering why GeneratedPassword = CryptedRandomString() + CryptedRandomString(); , the reason I called CryptedRamdomString() method twice is just to make sure it returns more then 10 digits so as it will be easier to get eight character passwords otherwise if it is called once then sometimes it will generate less then eight character password.
Well you must consider one thing before using this method that generating random numbers using "RNGCryptoServiceProvider " is bit time consuming then Random.Next. But "RNGCryptoServiceProvider " is much more secure then "Random.Next" .
If you want to generate uniq password every time than
take CurrentTIME and CurrrentDATE in account because by this you can able to create new password.
have look to this resolve your problem : generating a batch of random passwords
Try this
http://www.yetanotherchris.me/home/2009/3/15/c-pronounceable-password-generator.html
I'd define an array of possible characters for the password alphabet, and then generate 7 or 8 random indexes into that array to create a password.
This would need to be refined if you want to guarantee a minimum number of each character type, etc.
Globally unique strings you say?
System.Guid.NewGuid().ToString()

how to generate a voucher code in c#?

I need to generate a voucher code[ 5 to 10 digit] for one time use only. what is the best way to generate and check if been used?
edited: I would prefer alpha-numeric characters - amazon like gift voucher codes that must be unique.
When generating voucher codes - you should consider whether having a sequence which is predictable is really what you want.
For example, Voucher Codes: ABC101, ABC102, ABC103 etc are fairly predictable. A user could quite easily guess voucher codes.
To protect against this - you need some way of preventing random guesses from working.
Two approaches:
Embed a checksum in your voucher codes.
The last number on a credit card is a checksum (Check digit) - when you add up the other numbers in a certain way, lets you ensure someone has entered a number correctly. See: http://www.beachnet.com/~hstiles/cardtype.html (first link out of google) for how this is done for credit cards.
Have a large key-space, that is only sparsely populated.
For example, if you want to generate 1,000 vouchers - then a key-space of 1,000,000 means you should be able to use random-generation (with duplicate and sequential checking) to ensure it's difficult to guess another voucher code.
Here's a sample app using the large key-space approach:
static Random random = new Random();
static void Main(string[] args)
{
int vouchersToGenerate = 10;
int lengthOfVoucher = 10;
List<string> generatedVouchers = new List<string>();
char[] keys = "ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890".ToCharArray();
Console.WriteLine("Vouchers: ");
while(generatedVouchers.Count < vouchersToGenerate)
{
var voucher = GenerateVoucher(keys, lengthOfVoucher);
if (!generatedVouchers.Contains(voucher))
{
generatedVouchers.Add(voucher);
Console.WriteLine("\t[#{0}] {1}", generatedVouchers.Count, voucher);
}
}
Console.WriteLine("done");
Console.ReadLine();
}
private static string GenerateVoucher(char[] keys, int lengthOfVoucher)
{
return Enumerable
.Range(1, lengthOfVoucher) // for(i.. )
.Select(k => keys[random.Next(0, keys.Length - 1)]) // generate a new random char
.Aggregate("", (e, c) => e + c); // join into a string
}
Building on the answers from Will Hughes & Shekhar_Pro (and just because I found this question interesting) here's another implementation but I've been a bit liberal with your requirement for the length of the voucher code.
Using a base 32 encoder I found you can use the Tick value to generate alpha-numeric strings. The encoding of a tick count to base 32 produces a 13 character string which can be formatted to make it more readable.
public void GenerateCodes()
{
Random random = new Random();
DateTime timeValue = DateTime.MinValue;
// Create 10 codes just to see the random generation.
for(int i=0; i<10; ++i)
{
int rand = random.Next(3600)+1; // add one to avoid 0 result.
timeValue = timeValue.AddMinutes(rand);
byte[] b = System.BitConverter.GetBytes(timeValue.Ticks);
string voucherCode = Transcoder.Base32Encode(b);
Console.WriteLine(string.Format("{0}-{1}-{2}",
voucherCode.Substring(0,4),
voucherCode.Substring(4,4),
voucherCode.Substring(8,5)));
}
}
Here's the output
AARI-3RCP-AAAAA
ACOM-AAZF-AIAAA
ABIH-LV7W-AIAAA
ADPL-26FL-AMAAA
ABBL-W6LV-AQAAA
ADTP-HFIR-AYAAA
ACDG-JH5K-A4AAA
ADDE-GTST-BEAAA
AAWL-3ZNN-BIAAA
AAGK-4G3Y-BQAAA
If you use a known seed for the Random object and remember how many codes you have already created you can continue to generate codes; e.g. if you need more codes and want to be certain you won't generate duplicates.
Here's one way: Generate a bunch of unique numbers between 10000 and 9999999999 put it in a database. Every time you give one to a user, mark it as used (or delete it if you're trying to save space).
EDIT: Generate the unique alpha-numeric values in the beginning. You'll probably have to keep them around for validation (as others have pointed out).
If your app is limited to using only Numerical digits then i think Timestamps (DateTime.Now.Ticks) can be a good way to get unique code every time. You can use random nums but that will have overhead of checking every number that its been issued already or not. If you can use alphabets also then surely go with GUID.
For checking if its been used or not you need to maintain a database and query it to check for validity.
If you prefer alphanumerical, you could use Guid.NewGuid() method:
Guid g = Guid.NewGuid();
Random rn = new Random();
string gs = g.ToString();
int randomInt = rn.Next(5,10+1);
Console.WriteLine(gs.Substring(gs.Length - randomInt - 1, randomInt));
To check if it was not used store somwhere previously generated codes and compare.
private void AutoPurchaseVouNo1()
{
try
{
int Num = 0;
con.Close();
con.Open();
string incre = "SELECT MAX(VoucherNoint+1) FROM tbl_PurchaseAllCompany";
SqlCommand command = new SqlCommand(incre, con);
if (Convert.IsDBNull(command.ExecuteScalar()))
{
Num = 100;
txtVoucherNoInt1.Text = Convert.ToString(Num);
txtVoucherNo1.Text = Convert.ToString("ABC" + Num);
}
else
{
Num = (int)(command.ExecuteScalar());
txtVoucherNoInt1.Text = Convert.ToString(Num);
txtVoucherNo1.Text = Convert.ToString("ABC" + Num);
}
con.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex, "Error !!", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Try this method for creating Voucher Number like ABC100, ABC101, ABC102, etc.
Try this code
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringChars = new char[15];
for (int i = 0; i < stringChars.Length; i++)
{
stringChars[i] = chars[random.Next(chars.Length)];
}

Categories

Resources