real means of 1024 bit key length in RSA - java

I'm learning about RSA cryptography, so I must understand about ke length.
Here, I found explanation about key length means.
There said:
When we say a "1024-bit RSA key", we mean that the modulus has length
1024 bits, i.e. is an integer greater than 2^1023 but lower than 2^1024.
Such an integer could be encoded as a sequence of 1024 bits, i.e. 128
bytes.
What I've got from there, 1024 bit key means the key has 1024 binary number sequence int it.
We all know, 1 byte = 8 bits. So, 1024 bits = 128 bytes. Okay, it's in binary. How about in character?
According to ASCII binary code here, each character has 8 bits binary number. So, in my mind, if key has 1024 bit length, it means the key contains of 1024/8 = 128 characters. So, I created a java program to generate prime number that has 128 numbers length. So far, the program works well.
But again, I rethought the real meaning of RSA 1024-bit. So, I googled and found this. I tested it and I get that the bit length of public key modulus is 1024. But, the public key has 309 numbers length.
Now, I really confuse.
My question: what's the real means of 1024-bit key length in RSA? As I thought or as I found here?

The example saves the key as Object Stream, which is java serialized object stream, it has some metainfo about javatypes and special format to encode binary data. To see genuine size of the number try "pub.getModulus().bitLength()". For me it prints 1024.

The important parameter is "greater than 2^1023". This means, the raw RSA encryption algorithm can represent arbitrary numbers from 0 to 2^2013-1, but not numbers greater or equal to the modulus. By Key Design the Modulus is the product of two different prime numbers and will never meet a power of 2, like 2^1024.
So the maximum number of bits you can represent is key-length-1, which are floor((key length -1)/8) complete bytes; in the best case you loose one bit of the key length.
As the PKCS-standard for mapping text to numbers considers complete bytes, you always need one more byte for representing the equivalent number; for a 1024-bit-key the maximum length of text to be encoded is 127 bytes.
The other number "309 bytes" comes from the X.509-Standard for storing/exchanging keys, a dialect of the ASN.1-Protocol; it includes RSA-type information, the modulus and the public exponent.

Related

Why does generateKey method from DES algorithm multiply the key by 8?

I am in need of some explanations on the corresponding line, which I do not understand:
KeyGenerationParameters kgp = new KeyGenerationParameters(sr,(DESParameters.DES_KEY_LENGTH) * 8);
Would it stop working, if I had, for instance, the number 6 instead of 8?
It is multiplied by 8 because it is in bytes otherwise. The code wants bits.
strength - the size, in bits, of the keys we want to produce.
it is from javadoc for KeyGenerationParameters. The DESPArameters.DES_KEY_LENGTH stores value 8 (DES key length in bytes), but KeyGenerationParameters requires key length in bits. 8 bytes * 8 = 64 bits in a case of KeyGenerationParameters. You can just put the constant value 64.
This class can be used for random key generation for any algorythm, for example, to generate DES key you must use value of 64, to generate 3DES key 128 or 192.

AES Encryption from integers to integers

I am using AES encryption algorithm in java to encrypt my database values..My encryption function returns encrypted value as String but the columns of type "Int" fails to store such string values which is quite logical..Is there a way to encrypt the integers as integers (numerical values)? Thankyou.
Plain AES returns an array of bytes. You can store this as an array of bytes, a Base64 text string or as a BigInteger:
BigInteger myBigInt = new BigInteger(AESByteArray);
It is very unlikely that the 128 bit, or larger, AES result will fit into a 32 bit Java int.
If you want 32 bit input and 32 bit output, so everything fits into a Java int, then either write your own 32 bit Feistel cipher, or use Hasty Pudding Cipher, which can be set for any bit size you require.
Encrypting integer into integer is FPE (format preserving encryption). FPE does not change data type or data length.
Here is a reason why databases implementing FPE only for character data, never for int.
AES 128 will encrypt 128-bit block. Which is 16 bytes.
If you want to encrypt 64 or 32 bit integer(4 or 8 byte values), you still have to encrypt 16 byte block. This problem can be solved by adding 8 (or 12) bytes to int32 or int64 values. This creates issue - if added bytes are always 0, you create huge weakness in encryption, as your data set is severely limited. It can be used for brute force attack on AES etc. In turn, this can be solved by filing with cryptographically strong random number added 8 or 12 bytes (that also creates a weakness, as most likely your random genertor is not strong enough). When decrypting, you can purge extra added bytes and extract only 4 or 8 bytes our of 16 bytes.
Still, life is not perfect. AES encryption does not change size of block, it always produces 16 bytes. You can encrypt your int into 16 bytes, but database can store only 8 bytes for int.
Unless you will store data in binary(16) column. But that is not an integer, and you are asking for integer.
In theory, numeric(38) is taking 16 bytes. In some databases it is possible to set 16 bytes to arbitrary value and then extract. I have not seen it is implemented.
You can always encode your string in an integer, however it could be a large integer.
If you can't afford large integer, you can encode it in multiple small integers.
If you can afford neither large integer nor multiple integers, maybe you can't do it well anyway, using a block cipher in ECB mode is almost always a bad idea.
Try converting the output of the encryption from string to binary, and then from binary to a decimal integer.

Generate AES key with a given length which contains a-Z0-9

is it possible to create a secret key using AES but with a given length and it should cointain only 0-9a-Z?
I just need a 32 characters key as a SecretKey spec :( for AES256 encryption/deceryption
is it possible to create a secret key using AES but with a given length and it should cointain only 0-9a-Z?
It is. By generating an AES secret key value, and then making sure that the ascii value of each character is within the bounds of 0-8 (48 - 57) or a-z (97-122).. Where it isn't, simply ammend the value to be within the range.
But..
This would make a wildly unsafe key. By limiting your values so heavily, you are effectively limiting the amount of possible combinations. Whereas normally, it is 2^256 possible combinations (considered secure at this point), you're dimishing that to much less possible combinations... which isn't secure.
Also, the above algorithm I suggested would make an awful lot of repeating values. You'd be comparing differences, and then ammending until that difference is 0.. which means:
For any value < the desired value.
round value to desired value.
Let desired value = 100;
Let any value = 50;
50 --> 100;
Let any value = 60;
60 --> 100;
And ontop of that..
A Brute Force Attack will usually start out by testing aaaa... x 256. Then (aaaa x 255) 1. What I mean is, a brute force usually starts with alpha numeric characters. You're just making your key ripe for a cracking there.
AND ONTOP OF THAT..
An AES key is defined as 128, 192 or 256 bits. That is the standard. You can't change the length. What you can do is look into the Rijndael, which is what AES was before it became the standard. This can accept a wider range of key values.
AND ONTOP OF THAT
An AES key is generated by the key schedule, the way it is for a reason. It is designed to be cryptographically resistant to cryptanalysis. By messing with that, you compromise the security of the entire algorithm, thus making it pretty pointless implementing the cryptographic standard, when you're going to mess with the output.
I CANT FIND A BIGGER FONT
You want your output to be 32 characters long. In ASCII, that means 8 bits per character. Or 32 x 8 = 256. Well.. AES's recommended mode is 256 bits, so you're in luck. You can use a hashing algorithm which will generate values that should be within the bounds of a-z0-9.
A AES-256 key is 256 bit long, and would be too big to store in 32 characters (0-9a-Z).
However, if you relax your requirements a bit by extending your character set with 2 more characters (to 64 in total) - then you can store 6 bits of the key in one character - which then allows you to store a 192 bit AES key in the 32 characters.
Below code would create a 32 character base64 encoded string (0-9a-Z+/) of a 192 bit AES key:
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(192);
SecretKey key = keyGen.generateKey();
String keyBase64 = Base64.encode(key.getEncoded());

Extracting RSA public key modulus and exponent stored in unsigned char arrays

So, I'm using a proprietary library that has its own implementation for the creation of RSA key pairs. The public key struct looks like this:
typedef struct
{
unsigned int bits; //Length of modulus in bits
unsigned char modulus[MAX_RSA_MOD_LEN]; //Modulus
unsigned char exponent[MAX_RSA_MOD_LEN]; //Exponent
} RSA_PUB_KEY
I need to figure out a way to extract both the exponent and the module so I can send them to a server as part of a validation scheme. I guess that this is a pretty standard procedure (or so I hope). I've already read these two similar questions:
How to convert an Unsigned Character array into a hexadecimal string in C
Printing the hexadecimal representation of a char array[]
But so far I've had no luck. I'm also not sure of how to use if at all necessary the "bits" field to extract the modulus. In short what I have to do is be able to recreate this public key in Java:
BigInteger m = new BigInteger(MODULUS);
BigInteger e = new BigInteger(EXPONENT);
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(keySpec);
return pubKey;
Edit:
This is what I'm doing right now: (RSAPublic is a RSA_PUB_KEY struct as described above).
//RSAPublic.bits = length of modulus in bits
log("Modulus length: "+std::to_string(RSAPublic.bits));
log("Key length: "+std::to_string(keyLengthInBits));
//Calculating buffer size for converted hexadec. representations
int modulusLengthInBytes = (RSAPublic.bits+7)/8 ;
int exponentLengthInBytes = (keyLengthInBits+7)/8;
char convertedMod[modulusLengthInBytes*2+1];
char convertedExp[exponentLengthInBytes*2+1];
//Conversion
int i;
for(i=0; i<modulusLengthInBytes ; i++){
sprintf(&convertedMod[i*2], "%02X", RSAPublic.modulus[i]);
}
for(i=0; i<exponentLengthInBytes ; i++){
sprintf(&convertedExp[i*2], "%02X", RSAPublic.exponent[i]);
}
//Print results
printf("Modulus: %s\n", convertedMod);
printf("Exponent: %s\n", convertedExp);
And this is the output:
Modulus length: 16
Key length: 512
Modulus: 0000
Exponent: 0A000200FFFFFFFFFFFF0000600007004DDA0100B01D0000AEC642017A4513000000000000000000000000000000000000000000000000000000000000000000
I'm assuming that you can't just send binary data since you mention the hexadecimal conversion. The most compact way you can send the data as text would be with base 64 but this is more complex than hexadecimal.
Client side
Convert the unsigned char array to a hexadecimal string using a method from the links you have. The bits field will determine how many bytes from the array to use given by (bits+7)/8.
Depending on implementation you might have to explicitly select the overflow bits or the rest might be zeroed, this also depends on the endianness so since you are unsure on implementation details you might have to fiddle around with it a bit.
Once you have the encoded strings, send them to the server.
Server side
Read the encoded strings from the connection and then pass them to the BigInteger(String val, int radix) constructor using the radix of hexadecimal (16).
You will then have A BigInteger with the value you require.
If the first bytes of the public exponent are all zero's then you are dealing with a big endian array. This is most common. In principle the public exponent can be as large as the modulus, but this is commonly not the case. Most common values are 65537, 17 and 3, maybe even 2 but the 3 and 2 are not such good values. Other 2-4 byte primes are also common.
Now if you know the endianness, you can have a look at the modulus. If the highest byte value is 00 then you are dealing with a signed representation of the modulus. Otherwise it is likely unsigned. The highest order byte of the modulus that contains bits should always be 80 or higher. The reason is that otherwise the key size would be smaller than the given key size. This is assuming that the key size is a multiple of 8 of course.
Java only works with big endian for BigInteger (and any other number representation). So if you have little endian encoding in C then you need to reverse the values in Java. It is probably the best to reverse the hexadecimal values in the string to accomplish that. Make sure you handle 2 hexadecimal characters at a time.
Then, as DrYap suggested, use the hexadecimal constructor of BigInteger. Note that if you end up using a byte array then you may want to use new BigInteger(1, MODULUS) as this makes sure you get a positive number regardless of the highest order bit value in the encoding.

How can I encrypt/decrypt 12-digit decimal numbers to other ones, using a password and Java?

I have already read Using Java to encrypt integers and Encrypting with DES Using a Pass Phrase.
All I need is a simple Encrypter which transforms a 12 digit number to a 12 digit number with the following constraints:
The encryption must depend on a password (which will be constant throughout the life time of an application) and nothing else.
The mapping must be 1-1 (No hashing and multiple inputs giving same output and vice versa).
The mapping must not change between different VMs or when VM is started (like when you restart Java, the utility should give you same mappings which means that it must be purely dependent on the password that is supplied).
Numbers starting with 0 is not a valid 12 digit number (also input numbers won't start with 0).
The key/password should never be guessable. For example running the utility with multiple inputs and analysing the outputs should not allow one to guess the key/pwd/hash or whatever.
All inputs will be exactly 12 digits and less than a 12 digit prime number (which means we could use modulo arithmetic).
Having trawled through the literature I have this code with me
public void mytestSimple(long code, String password) throws Exception {
SecretKey key = new SecretKeySpec(password.getBytes(), "DES");
Cipher ecipher = Cipher.getInstance("DES");
ecipher.init(Cipher.ENCRYPT_MODE, key);
System.out.println(ecipher.getOutputSize(8));
byte[] encrypted = ecipher.doFinal(numberToBytes(code));
System.out.println(encrypted + "--" + encrypted.length);
Cipher dcipher = Cipher.getInstance("DES");
dcipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypted = dcipher.doFinal(encrypted);
System.out.println(bytesToNumber(decrypted) + "--" + decrypted.length);
}
public void testSimple() throws Exception {
mytestSimple(981762654986L, "password");
}
I am running into problems as to
How to convert the 16 bytes into a 12 digit number.
Maintain 1-1 mapping.
Keep the encryption/decryption same across multiple VM invocations.
**** Answer added by me below****
I have added one answer which is a 40bit RSA pulled out of standard Java RSA keypair gen logic. I still have to work on the edge cases. I am going to accept the answer and upvote "Tadmas" who I think kinda lead me to the answer. Can someone tell me if my algorithm is going to be weak/attackable?
You're not going to be able to convert 16 bytes into a 12 digit number without losing information. 256 ^ 16 > 10^12. (Not that you even have 10^12 options, as you've only got the range [100000000000, 999999999999].
I doubt that you'll be able to use any traditional encryption libraries, as your requirements are somewhat odd.
If the strict 1:1 mapping is more important than protecting against cryptanalysis, then you can convert the password to a 12-digit number (via hash or otherwise) and simply add to your original number mod 10^12. If you absolutely must remove leading zeros from the output, you can subtract 10^11, do the math mod (10^12 - 10^11), and then add 10^11 back again. Granted, that's extremely insecure, but it's quite simple. :)
If the range of inputs is bounded by a prime less than (10^12 - 10^11), you may be able to use message ^ password mod prime to form a ring that will satisfy your requirements and be a little harder to crack. (This is similar to how RSA works.) I think this could work if you don't need to decrypt it.
I agree with Jon Skeet: requiring a strict 1:1 mapping without the output range being bigger than the input domain is something that most encryption libraries are not going to handle.
One potential solution could be built on Feistel ciphers.
This constructions allows to build a pseudorandom permutation based on a pseudorandom functions. E.g. the pseudorandom functions could be constructed from an appropriate block cipher by truncating the result to a 6 digit numbers.
This construction has been analyzed in the following paper
M. Luby and C. Rackoff, "How to construct pseudorandom permutations from pseudorandom functions" SIAM Journal on Computing, Vol.17, No.2, pp.373--386, 1988
A concrete proposal is the Feistel Finite Set Encryption Mode, which has been
submitted to NIST for potential inclusion into an upcoming standard. This proposal also
addresses the problem of encrypting ranges that are not a power of 2.
If the numbers are for user IDs, this is what I'd do:
(1) Generate an AES key from the password. Just calling getBytes() is sort of OK if you trust the administrator to use a really really really strong password. Ideally, use the standard "password-based encryption" technique of hashing the bytes, say, a few thousand times, each time adding in the random "salt" bytes that you initially generated to avoid dictionary attacks.
(2) Encrypt the number in question with that AES key.
(3) Chop off 12 digits' worth of bits from the resulting encrypted block, convert it to decimal, and present that number to the user. (To do this, you can wrap a BigInteger around the bytes, call toString() on it, and pull off, say, the bytes between position 4 and 16.) Experimentally, it looks like you shouldn't take the digits from the rightmost end.
[Update: I think this is probably because BigInteger literally allocates its numbers from left to rightmost bit-- but I haven't checked-- so there'll potentially be "spare" bits in the very rightmost byte, and hence fewer possible numbers if you include the very last byte.]
Now, I hear you cry, this obviously isn't a 1-1 mapping. But unless you're going to have more than tens of thousands of users, it's really good enough. With a 12-digit number, you'd expect on average to encrypt around 300,000 numbers before getting a collision. So although you don't strictly have a 1-1 mapping, in practice, it's as near as dammit.
(In any case, if your application really has hundreds of thoudands of users and security is crucial, then you'll probably want to invest in some serious consulting over this kind of thing...)
Just to convince yourself that it really is OK to pretend it's a 1-1 mapping, you can run a simulation that repeatedly tries to allocate, say, 200,000 user IDs with random keys, and prints out how many collisions there were on each run:
next_pass :
for (int pass = 0; pass < 100; pass++) {
byte[] key = new byte[16];
(new SecureRandom()).nextBytes(key);
Cipher ciph = Cipher.getInstance("AES");
SecretKeySpec ks = new SecretKeySpec(key, "AES");
ByteBuffer bb = ByteBuffer.allocate(16);
Set<String> already = new HashSet<String>(100000);
int colls = 0;
for (int i = 0; i < 200000; i++) {
bb.putLong(0, i);
ciph.init(Cipher.ENCRYPT_MODE, ks);
byte[] encr = ciph.doFinal(bb.array());
encr[0] &= 0x7f; // make all numbers positive
BigInteger bigint = new BigInteger(encr);
String userNo = bigint.toString();
userNo = userNo.substring(4, 16);
if (!already.add(userNo)) {
System.out.println("Coll after " + i);
continue next_pass;
}
}
System.out.println("No collision.");
}
I suggest a very simple algorithm.
Feed the password into a hash function.
Initialize a random number generator with the hash or something you derived from the hash.
Generate a 12 digit random number.
Add this random number to the input digit by digit modulo 10 to encrypt.
To decrypt subtract the random number modulo 10. This is actually a form of One Time Pad. Because of the comments on this answer I realized that refering to One Time Pad was a bad choice. A better reference is Polyalphabetic cipher - while One Time Pad uses polyalphabetic substitution its main characteristic is not to use any key bit twice.
Input 1234 1234 1234
Random number 6710 3987 2154
Output 7944 4111 3388
There is one remaining problem with that - the algorithm might create leading zeros. To solve this problem one could use XOR instead of addition and substraction. Just transform the digits with XOR. If the first digit turns into a zero, don't encrypt the first digit. When you decrypt with XOR again, the first digit will turn into zero and you know that the first digit was not enrcypted.
UPDATE
A simple XOR is not the solution because it will produce to large numbers - 2 XOR 9 = 11 for example. Going to rethinks this...
UPDATE
The nice propoerties of XOR are XOR(a, b) = XOR(b, a) and XOR(XOR(a, b), b) = a. This makes encryption and decryption the same and allows to detect the unencrypted leading digit. But it is further required that our function only returns values in the range from 0 to 9 what XOR doesn't do.
But maybe we can build a custom function with all required properties. So we create an array FUNC with 10 columns and 10 rows and use it as a lookup table for our function. What values to but in? I actually don't know - I am not even sure that it is possible. But if we pick three number from the range 0 to 9 we have to make the following six entries. (It is a symmetric matrix.)
FUNC[x,y] = z FUNC[x,z] = y FUNC[y,z] = x
FUNC[y,x] = z FUNC[z,x] = y FUNC[z,y] = x
So maybe it is possible to create such a function by repeatedly choosing random numbers and filling the six entries if there is no conflict. Maybe it is not. I would like to see the table if one finds a solution.
Me thinks the answer given below by Tadmas was very helpful and I want you guys to hack/bully my implementation below. As Tadmas points out all my numbers are 40 bits (12 digit number is 10^12 which is 2^40 approx).
I copied the sun.security.rsa.RSAKeyPairGenerator (link) and created my own generator for a 40 bit RSA algorithm. The standard one needs between 512-1024 bits so I removed the input check around it. Once I create a suitable n, e, d values (e seems to be 65537 as per the alog). The following code served fine,
public void testSimple() throws NoSuchAlgorithmException {
MyKeyPairGenerator x = new MyKeyPairGenerator();
x.initialize(40, new SecureRandom("password".getBytes()));
MyPublicPrivateKey keypair = x.generateKeyPair();
System.out.println(keypair);
BigInteger message = new BigInteger("167890871234");
BigInteger encoded = message.modPow(keypair.e, keypair.n);
System.out.println(encoded); //gives some encoded value
BigInteger decoded = encoded.modPow(keypair.d, keypair.n);
System.out.println(decoded); //gives back original value
}
Disadvantages
The encoded may not always be 12 digits (sometimes it may start with 0 which means only 11 digits). I am thinking always pad 0 zeroes in the front and add some CHECKSUM digit at the start which might alleviate this problem. So a 13 digit always...
A 40 bits RSA is weaker than 512 bit (not just 512/40 times but an exponential factor of times). Can you experts point me to links as to how secure is a 40bit RSA compared to 512 bit RSA (I can see some stuff in wiki but cannot concretely confirm possibility of attacks)? Any links (wiki?) on probabilities/number of attempts required to hack RSA as a function of N where n is the number of bits used will be great !
I would use a stream cipher. N bytes go in, N bytes come out.
This thread is 4 years old, but for those finding the thread in Google: have a look at Format Preserving Ciphers: http://en.wikipedia.org/wiki/Format-preserving_encryption
For mathematical reasons, most cyphers will produce "more" bytes (i.e. they will pad the input). So you will have to accept that the code generates 16bytes out of your 12 digit number.
When the string is decoded, you will get the 12 digit number back but during transport, you need 16 bytes.
If I understand your problem correctly you have a problem with the 16 bytes after decrypting. The trick is to use: Cipher.getInstance("DES/ECB/PKCS5Padding");
(just in case code is helpful):
public void mytestSimple(long code, String password) throws Exception
{ SecretKey key = new SecretKeySpec (password.getBytes(),"DES");
Cipher ecipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
byte[] plaintext = new byte[8];
for (int i=0; i<8; i++)
{ plaintext[7-i] = (byte) (code & 0x00FF);
>>>= 8;
}
ecipher.init (Cipher.ENCRYPT_MODE, key);
System.out.println(ecipher.getOutputSize(8));
byte[] encrypted = ecipher.doFinal(plaintext);
System.out.println("--" + encrypted.length);
Cipher dcipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
dcipher.init(Cipher.DECRYPT_MODE, key);
byte[] crypttext = dcipher.doFinal(encrypted);
long decoded = 0;
for (int i=0; i<8; i++)
{ decoded <<= 8;
decoded += crypttext[i] & 0x00FF;
}
System.out.println(decode + "--" + crypttext.length);
}
Rethinking the problem I came up with the following. Basicly you need a symmetric cipher to get a one-to-one mapping. And noting that 10^12 is almost 2^40 (2^39.863) it seems natural to convert your 12 digit number into a 40 bit integer and feed this number into a block cipher with a block length of 40 bits. A good choice might be Blowfish supporting block lengths from 32 to 448 bits in steps of 8 bits - so 40 bits is supported.
UPDATE
As Accipitridae pointed out, Blowfish has variable key size but fixed block size hence it is no option. A bit more searching through the web seems to indicate, that there are little or no ciphers with block sizes of 40 bits or less rendering this idea void. A leave the remaining part of the answer - maybe one can find a suitable cipher.
The remaining problem is that the Blowfish might return a number up to 1,099,511,627,775 with 13 digits and that the returned number might contain leading zeros, but I believe that this can be solved in a second step. My first thought was applying something like a Burrows-Wheeler transform to the string representation of the number in order to get at least one zero to the front of the number eleminating the 13th digit and then modify all remaining digits (for example 0 <-> 9, 1 <-> 8, 2 <-> 7, ...) to turn additional leading zeros into other digits.
After a few minutes I regonized that this will not work - the input has size 2^40 while the output is only of size 2^39.863. A solution would be to use a 39 bits block cipher and restrict the input to numbers up to 549,755,813,887. I don't know if there is a cipher that can deal with a block length of 39 bits, but this paper on Elastic Block Ciphers describes how to construct a block cipher that can deal with every block size from n up to 2n given a block cipher that can handle block size n. In consequence it is possible to construct a 39 bit block cipher from 32 bit Blowfish.
If you're prepared to accept a rather weak solution...
Treat the 12-digit number as two 6-digit numbers. Then use the hash of the password as a seed to a random number generator which shuffles a table of 999,990 consecutive values. Then use the two 6-digit numbers to look up entries in the table. The concatenation of the two results is your 1:1 mapped 12-digit 'encrypted' input based on a password.
Let's do an example with 4-digits instead of 12...
Input: 4852
Password: passw0rd1 => hashcode = 37592
Now take this array..
a = [10, 11, 12, 13, .. 98, 99]
And shuffle it with 37592 as the random seed...
b = [45, 15, 56, 49, .. 33, 88]
Now split the input: 48, 52 and look up those indices in the shuffled array, say...
b[48] => 23
b[52] => 96
So our encrypted version of 4852 is 2396
It really isn't a strong solution but the constraints in your question will not lead to a strong solution. You may need to relax those constraints a bit.

Categories

Resources