Generating a unique 15 digite Pin code from a 10di

2019-07-26 20:40发布

问题:

I want to create pin codes and serial numbers for scratch papers , I have already generated unique 10 digit numbers , now I want to turn that 10 digit number to a 16 digit number (with check digit in the end) . The thing is that the function that does this should be reversible so by seeing the 16 digit number I can check whether it is valid or not .(if it is not generated by me it should not be valid) . this is how I have generated the 10 digit unique random codes :

      Guid PinGuid;
      byte[] Arr;
      UInt32 PINnum = 0;
      while (PINnum.ToString().Length != 10)
        {
          PinGuid = Guid.NewGuid();
          Arr = PinGuid.ToByteArray();
          PINnum = BitConverter.ToUInt32(Arr, 0);
        }

      return PINnum.ToString();

I would be grateful if you can give me a hint on how to do it .

回答1:

First off, I would avoid GUID since some prefixes are reserved for special applications. Which means that these areas of the GUID may not be allocated uniformly on creation, so you may not get exactly 10 digits of randomness like you plan.

Also since your loop waits for the GUID to become the right size you could do it more efficiently.

10 digits = 10**10
Log_2(10) = approx 3322/1000

So you need approx 33 bits for 10 digit number. Since you want your number to be exactly 10 digits, you can either pad numbers less than 10^10 with leading zeroes, or you can generate only numbers between 10^9 and 10^10 - 1.
If you take the latter case you need 9*10^9 numbers in your space -- giving you all numbers from 1 followed by nine zeroes up to 9 followed by 9 9s.

Then you would like to convert this space of numbers into a larger space, to expand it by a factor of 5 and include one more digit as a check digit.

Pick a check digit function as anything you like. You could simply sum (mod 10) the original 10 digits, or choose something more complicated.

Presumably you do not want people to be able to generate valid instances. So if you are really serious about your security, you should modify any suggestions you get from the net before deploying them.

I would do something along the lines of :

  • Generate a uniform 10digit number with no leading zeroes by

    randomTenDigits = 10**9 + rand(9*10**9)

  • Using an encryption scheme (like AES 256 or even RSA or El-Gamal since their slower speed will no be so important since input length is small ) encrypt this 10 digit number using a secret key only you and others you trust are aware of. Perhaps you can concatenate the 10 digit number 10 times, and then concatenate that result with some other secret that you choose, and then finally encrypt this expanded secret of which the 10 digit number is a part.

  • Take some choice 5 digits (around 17 bits) of the resulting ciphertext, and append these to your 10 digit number.

  • Generate 1 digit of check digit by whatever method you desire.

As you will note the real security of this scheme is not from a check digit, it is from the secret key you can use to authenticate the 16 digit number. The test you will use to authenticate it is: does the given 10 digit number when concatenated with other secrets I have, encrypt, using a secret key only I know, to the given 5 digit number presented with it.

Since the difficulty for an attacker of forging one of your numbers depends on the difficulty of

  1. discovering your secret keys and other info
  2. discovering which method of encryption you use
  3. discovering which part of the resulting cipher text you emit for the 5 digit secret, or
  4. simply brute forcing the 5 digits to discover the correct pairing, and since 5 digits is not a big space to search, I would suggest instead generating larger numbers. 10 or 16 digits is not really a huge space to search. So instead of digits I would use upper and lower case letters plus digits plus space and full stop to give you 64 letters in your alphabet. Then if you used 16 you get around 96 bits of security.

However if numbers are non-negotiable and the size of 10 digits for your base space is also non-negotiable, doing it this way is probably the most secure. You may be able to set up your system to deter people from brute forcing it, though you should consider what if someone acquires a piece of your hardware through a vendor. I believe it is easier to design security in rather than design in a mechanism for detecting people trying to brute force query your system.

However if serious dough is on the line ( like millions ) the security you employ should really be first class. Equivalent to the kind of security you would employ to protect a pin number to a million dollar bank account. The more secure you are the longer you can carry on your biz with credibility and trust.

So along these lines I would suggest increasing the size of your secrets to make it infeasible for someone to simply try all combinations and forge a valid one, and in particular thinking about how to design your system to make it difficult to break for people with lots of skills and motivation (money). You really can't be too careful.



回答2:

I would keep it simple. Put PINnum.ToString() into a buffer. Place a filler digit at 5 intervals. The first four could be random garbage and the last could be a check digit, or you could make each filler a check digit for its section. Here is an example.

buf = PINnum.ToString();

int chkdgit = function to create your checkdigit

Random rnd = new Random(); int i = rnd.Next(1001,9999);

fillbuf = i.toString(); return buf[0] + buf[1] + fillbuf[0] + buf[2] .... chkdgit.toString();

its a rather simple approach, but if your security needs aren't at level 1, it might suffice



标签: c# guid