Encrypt 4 digits pin with hex enumeration system, resulting 16 chars string - java

im looking for a way to encrypt a four digits password and as a result get a 16chars string.
So far ive got 64chars String using this
public static String digestHex(String text) {
StringBuilder stringBuffer = new StringBuilder();
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");// SHA-256
digest.reset();
for (byte b : digest.digest(text.getBytes("UTF-8"))) {
stringBuffer.append(Integer.toHexString((int) (b & 0xff)));
}
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
e.printStackTrace();
}
return stringBuffer.toString();
}
being text = 1234
the resulting String is = 3ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4 Using Java btw :D

Any "encryption" scheme where you are encrypting a 4 digit number without an additional key is effectively a lookup scheme. Since there are only 10,000 unique "inputs" to the lookup scheme, it will be relatively easy to crack your encryption ... by trying all of the inputs.
In other words, the security of your encrypted PIN numbers is an illusion ... unless you do something like "seeding" the input before you encrypt it.

The security of you scheme aside - there are easier ways to do this:
// Your original - with the horrible exception hiding removed.
public static String digestHex(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException {
StringBuilder stringBuffer = new StringBuilder();
MessageDigest digest = MessageDigest.getInstance("SHA-256");// SHA-256
digest.reset();
for (byte b : digest.digest(text.getBytes("UTF-8"))) {
stringBuffer.append(Integer.toHexString((int) (b & 0xff)));
}
return stringBuffer.toString();
}
// Uses BigInteger.
public static String digest(String text, int base) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");// SHA-256
digest.reset();
BigInteger b = new BigInteger(digest.digest(text.getBytes("UTF-8")));
return b.toString(base);
}
public void test() throws NoSuchAlgorithmException, UnsupportedEncodingException {
System.out.println("Hex:" + digestHex("1234"));
System.out.println("Hex:" + digest("1234", 16));
System.out.println("36:" + digest("1234", 36));
System.out.println("Max:" + digest("1234", Character.MAX_RADIX));
}
This allows you to generate the string in a higher base - thus shortening the number but sadly you still do not achieve 16.
I would suggest you use one of the simple CRC algorithms if you are really instistent on 16 characters. Alternatively you could try base 62 or base 64 - there are many implementations out there.

You are using SHA-256. This algorithm generates 32 bytes long messages (256 bits, more details here).
This is why you obtain a 64 bytes long hex string as an output: Integer.toHexString((int) (b & 0xff)) converts each single b byte of the MessageDigest into a 2 bytes long hex String representation.
To obtain a 16 bytes long String, you can either use MD5 (16 bytes output, 32 if converted in hex), derive that string or use a completely different way such as actually using encryption (using javax.crypto.Cipher).
I'd need to know what you would like to to to elaborate further, knowing that using MessageDigestis actually hashing, not encryption, while in the first line of your post you are speaking of encryption. One of the difference resides in the fact that hash codes are not designed to be reversed but compared, unlike encryption which is reversible. See this interesting SO post on this.

Related

How do I calculate a key check value for AES-128-CBC?

I'm trying to implement a function in Java to calculate the key check value for a 128 bit AES encryption key. The AES128CBCEncryptor class is implementing AES/128/CBC with ISO 9797-1 M2 padding.
The only information I can find on the key check value algorithm for AES says "the KCV for an AES key is computed by encrypting 16 bytes, each with value '01'.". It does not specify how the IV should be constructed.
Here is what I have thus far, but it's not generating the expected result:
public String computeAesCheckValue(String key) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException,
NoSuchAlgorithmException, NoSuchPaddingException, IOException {
AES128CBCEncryptor encryptor = new AES128CBCEncryptor(key);
byte[] cleartext = new byte[16];
byte[] iv = new byte[16];
for (int i = 0; i < 16; i++) {
cleartext[i] = (byte) 0x01;
iv[i] = (byte) 0x00;
}
String kcv = encryptor.encrypt(new String(cleartext, "utf-8"), Hex.encodeHexString(iv));
return (kcv != null && kcv.length() >= 6)
? Hex.encodeHexString(kcv.getBytes()).substring(0, 6)
: null;
}
What am I missing?
For a Key Check Value (KCV) one generally uses single block encryption, without any mode such as ECB or CBC. As only a constant value of 16 bytes is used, there is no need for padding either.
If you just have a CBC class that performs ISO 9797-1 M2 padding then you could encrypt the static value of 01010101010101010101010101010101 (hex encoding of 16 bytes), using an all-zero IV and taking the first 16 bytes from the result (removing 16 bytes of ciphertext at the end that is just encryption of the mandatory padding).
As you can see in the image below, because the IV is all zero, the XOR with the plaintext leaves the input intact, basically making the first ciphertext identical to direct encryption with the block cipher.
By WhiteTimberwolf (SVG version) - PNG version, Public Domain, https://commons.wikimedia.org/w/index.php?curid=26434096
However, as you are using Java, it makes more sense to use a Cipher object using algorithm "AES/ECB/NoPadding" and use that to encrypt the value of 01010101010101010101010101010101 directly. ECB doesn't take an IV, so that problem is avoided. Also, no padding needs to be considered when "NoPadding" is specified.
If you need fewer bytes: those are usually taken from the left (lowest index) of the result.
Beware that these kinds of KCV's are somewhat dangerous as they show the ciphertext of one particular plaintext block. In the worst instances, this could lead to an adversary decrypting one ciphertext block, or for an authenticated scheme to lose its integrity/authentication properties.
Commonly KCV's are over an all-zero plaintext block. Using an all one-valued block makes the chance that this happens smaller, but that chance is still significant.

String hash vs Binary Hash in Java

I am writing a Java program that converts Bitcoin privateKey to WIF format.
Unfortunately, I got wrong SHA256 hashes.
My code is based on Basing on this tutorial.
When I hash a value like:
800C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D
I get something like this as result:
e2e4146a36e9c455cf95a4f259f162c353cd419cc3fd0e69ae36d7d1b6cd2c09
instead of:
8147786C4D15106333BF278D71DADAF1079EF2D2440A4DDE37D747DED5403592
This is my piece of code:
public String getSHA(String value){
String hash = hash = DigestUtils.sha256Hex(value.getBytes());
System.out.println(hash);
return hash;
}
I used this library: import org.apache.commons.codec.digest.DigestUtils;
Of course I searched this problem on the web and I found this site.
On that website, there are two textboxes - String hash and Binary Hash.
Using a String hash, I got the same incorrect result as in my Java program.
But, using a Binary hash, I got a right result.
My question is:
What is the difference between Binary and String hashes?
How to implement Binary hash in my Java method?
In your case 800C28... is a text representation of byte[] using hex encoding. To convert it back to byte[] you can take a look at this answer, one way would be to do it is:
public static byte[] hexStringToByteArray(String hex) {
int l = hex.length();
byte[] data = new byte[l/2];
for (int i = 0; i < l; i += 2) {
data[i/2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
+ Character.digit(hex.charAt(i+1), 16));
}
return data;
}
String.getBytes() will return the character values, e.g. character 8 has a value of 56 as per the ASCII table.
System.out.println(Arrays.toString("8".getBytes())); // 56

Generate 9 digit unique value from a String

I have employee data, each employee has address information. I need to generate a unique 9 digit (numeric or alpha numeric) value for postal code (5 chars) and address line1 (35 chars), which is a unique value to represent a location. It is also called as "Wrap number".
As shown in below picture, when address of two employees is same, then Wrap Number should be same, otherwise new value should be assigned.
Which algorithm is best suitable to generate 9 digit unique value?
P.S. I need to program it in Java.
What you're asking is impossible. No, really, impossible.
You have a 5-digit ZIP code, which can be encoded in 17 bits. Then you have 35 characters of text. Let's say you limit it to upper and lower case letters, plus digits and special characters. Figure 96 possible characters, or approximately 6.5 bits each. So:
35 * 6.5 = 227.5 ~ 228 bits
So you have up to 245 bits of information and you want to create a "unique" 9-character code. Your 9-character code only occupies 72 bits. You can't pack 228 bits of information into 72 bits without duplication. See Pigeonhole principle.
A better solution would be to assign a sequential number to each employee. If you want to make those 9-character codes, then use a technique to obfuscate the numbers and encode them using base-36 (numbers and upper-case letters) or something similar. I explain how to do that in my blog post, How to generate unique "random-looking" keys.
The simple idea is to use the well-known hash algorithms, which are already implemented in Java.
private static long generateIdentifier(final String adrLine, final String postCode) {
final String resultInput = adrLine + postCode;
//do not forget about charset you want to work with
final byte[] inputBytes = resultInput.getBytes(Charset.defaultCharset());
byte[] outputBytes = null;
try {
//feel free to choose the encoding base like MD5, SHA-1, SHA-256
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
outputBytes = digest.digest(inputBytes);
} catch (NoSuchAlgorithmException e) {
//do whatever you want, better throw some exception with error message
}
long digitResult = -1;
if (outputBytes != null) {
digitResult = Long.parseLong(convertByteArrayToHexString(outputBytes).substring(0, 7), 16);
}
return digitResult;
}
//this method also may be useful for you if you decide to use the full result
// or you need the appropriate hex representation
private static String convertByteArrayToHexString(byte[] arrayBytes) {
final StringBuilder stringBuffer = new StringBuilder();
for (byte arrByte: arrayBytes) {
stringBuffer.append(Integer.toString((arrByte & 0xff) + 0x100, 16)
.substring(1));
}
return stringBuffer.toString();
}
I suggest you not to use MD5 and SHA1 because of the collisions which those hash functions can provide.
My idea would be this:
String str = addressLine + postalCode;
UUID uid = UUID.nameUUIDFromBytes(str.getBytes());
return makeItNineDigits(uid);
Where makeItNineDigits is some reduction of the UUID string representation to your liking. :)
This could be uid.ToString().substring(0, 9). Or you could take the two long values getLeastSignificantBits, getMostSignificantBits and create a 9-digit value from them.
A simple option might be to just take advantage of the hashing built in to Java....
String generateIdentifier(String postCode, String addressLine) {
long hash = ((postCode.hashCode() & 0xffffffffL) << 14L)
^ (addressLine.hashCode() & 0xffffffffL);
return Long.toString(hash, 36);
}

Turn String to 128-bit key for AES

I want to enter my own String variable to then turn it into a key for encryption/decryption for AES algorithm. I have tried many known ways such as UTF-8, base64, some methods doing conversion byte-string and vice versa and some other. Although it's true that all of them work even with some of them not working accurately, all of them turn the string in bytes, but what i want is to enter something like "helloWorld" and get back a 128-bit key for AES. Anything i use it goes for "Invalid key length" since the bytes are not accurate.
What do i need to do to get the correct bytes? Also i want to clarify that i want String and not an array of char since i want to make it as a function in my programm later so that the user can change the key at will should it be compromised.
UPDATE: i edited the example and this i what i have so far, still throws exception about parameters and key length though
public class SHAHashingExample
{
private static byte[] keyValue;
public static void main(String[] args)throws Exception
{
String password = "123456";
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(password.getBytes());
byte byteData[] = md.digest();
keyValue = md.digest();
//convert the byte to hex format method 1
StringBuffer sb = new StringBuffer();
for (int i = 0; i < byteData.length/2; i++) {
sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
}
System.out.println("Hex format : " + sb.toString());
//convert the byte to hex format method 2
StringBuffer hexString = new StringBuffer();
for (int i=0;i<byteData.length/2;i++) {
String hex=Integer.toHexString(0xff & byteData[i]);
if(hex.length()==1) hexString.append('0');
hexString.append(hex);
}
System.out.println("Hex format : " + hexString.toString());
String k = "hello world";
String f = encrypt(k);
System.out.println(f);
String j = decrypt(f);
System.out.println(j);
}
public static String encrypt(String Data) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = c.doFinal(Data.getBytes());
String encryptedValue = new BASE64Encoder().encode(encVal);
return encryptedValue;
}
public static String decrypt(String encryptedData) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedData);
byte[] decValue = c.doFinal(decordedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}
private static Key generateKey() throws Exception {
Key key = new SecretKeySpec(keyValue, "AES");
return key;
}
}
UPDATE 2:
Turns out your usage of many components of the Java Cipher capabilities are not spot on. Look here at this other SO answer.
Java AES and using my own Key
UPDATE 1:
To get the 256 bit value down to 128 bits using the example below, here is what you may want to try:
// After you already have generated the digest
byte[] mdbytes = md.digest();
byte[] key = new byte[mdbytes.length / 2];
for(int I = 0; I < key.length; I++){
// Choice 1 for using only 128 bits of the 256 generated
key[I] = mdbytes[I];
// Choice 2 for using ALL of the 256 bits generated
key[I] = mdbytes[I] ^ mdbytes[I + key.length];
}
// Now use key as the input key for AES
ORIGINAL:
Here is a great example of using the built-in java APIs for performing a SHA hash on some data bytes.
http://www.mkyong.com/java/java-sha-hashing-example/
Java has built-in capability to perform multiple differing hash types, and you really should try to take advantage of one, instead of trying to write one yourself. Perhaps the most widely used hash functions are the SHA versions. There are versions that can output a 128, 256, and 512 bit hash output.
What you are asking for, is in all technicality exactly how logging into a system using your password generally works. the system never truly stores your actual textual password, but rather the HASH to it. When you, the user, enters your password, the system performs a live hash of what you entered and compares the live generated hash with the stored hash. This does not go the added step of lets say using that hash as an actual key component for a symmetric encryption. In general a GOOD hash can indeed generate DECENT key material for use in actual symmetric encryption / decryption.
What you are looking for is called a hash function. You will be able to enter an input of arbitrary length, and the hash function will always output a value of fixed bit size -- 128 bits in your case.
There are many approaches to a hash function. The most simple one would be doing the modulo operation between an input number (an integer representation of your string, for example) and the maximum number that can be represented in n bits (in your case, 128); the output result will be an n-bit number that you can convert to whatever form you want (probably hexadecimal) and use it as an AES key.
That isn't necessarily efficient (which is to say, the output 128-bit keys may not be very evenly distributed between 0 and 2^128 - 1), though -- more importantly, it would be quite slow for no good reason. Some efficient 128-bit hash functions are CityHash and MurmurHash -- you can look more up (as well as several Java implementations) on Google.

Blowfish decryption in python of string encoded by javax.crypto

Using the code found at https://raw.github.com/usefulfor/usefulfor/master/security/JBoss.java, I did thefollowing:
bash-3.2$ java -cp . JBoss -e testpython
-27038292d345798947e2852756afcf0a
bash-3.2$ java -cp . JBoss -d -27038292d345798947e2852756afcf0a
testpython
However, I can't for the life of me, figure out how to decrypt the string '27038292d345798947e2852756afcf0a' using pycrypto in python. My understanding is that the Java code is using Blowfish, and the phrase 'jaas is the way' as the key for the cipher. But I can't make any sense of how to do this in python. The following results in mostly unprintable garbage:
import Crypto
from Crypto.Cipher import Blowfish
from base64 import b64encode, b64decode
bs = Blowfish.block_size
key = 'jaas is the way'
plaintext = b'27038292d345798947e2852756afcf0a'
iv = '\0' * 8
c1 = Blowfish.new(key, Blowfish.MODE_ECB)
c2 = Blowfish.new(key, Blowfish.MODE_CBC, iv)
c3 = Blowfish.new(key, Blowfish.MODE_CFB, iv)
c4 = Blowfish.new(key, Blowfish.MODE_OFB, iv)
msg1 = c1.decrypt(plaintext)
msg2 = c2.decrypt(plaintext)
msg3 = c3.decrypt(plaintext)
msg4 = c4.decrypt(plaintext)
print "msg1 = %s\n" % msg1
print "msg2 = %s\n" % msg2
print "msg3 = %s\n" % msg3
print "msg4 = %s\n" % msg4
What am I missing ?
thanks.
First of all, that Java example code is very bad. It outputs the ciphertext as an integer, whereas ciphertext should remain a binary string. The reason is that an integer can be represented in an infinite number of binary encodings. For instance, the number one can be '0x01' (1 byte), '0x0001' (2 bytes), and so forth. When you are dealing with cryptographic functions, you must be extremely precise with the representation.
Additionally, the example uses the default values of the javax.crypto API, which are not described anywhere. So it is really trial and error.
For the solution, you must know how to convert negative integers to hex strings in Python. In this case, you don't need a hex string, but its byte representation. The concept is the same though. I use PyCrypto's long_to_bytes to convert a positive integer (of arbitrary length) to a byte string.
from Crypto.Cipher import Blowfish
from Crypto.Util.number import long_to_bytes
def tobytestring(val, nbits):
"""Convert an integer (val, even negative) to its byte string representation.
Parameter nbits is the length of the desired byte string (in bits).
"""
return long_to_bytes((val + (1 << nbits)) % (1 << nbits), nbits/8)
key = b'jaas is the way'
c1 = Blowfish.new(key, Blowfish.MODE_ECB)
fromjava = b"-27038292d345798947e2852756afcf0a"
# We don't know the real length of the ciphertext, assume it is 16 bytes
ciphertext = tobytestring(int(fromjava, 16), 16*8)
print c1.decrypt(ciphertext)
The output is:
'testpython\x06\x06\x06\x06\x06\x06'
From that you see that javax.crypto also adds PKCS#5 padding, which you need to remove by yourself. That is trivial to do though.
However, the real solution to your problem is to do Java encryption in a better way. The Python code will be greatly simplified.
This helps me
private byte[] encrypt(String key, String plainText) throws GeneralSecurityException {
SecretKey secret_key = new SecretKeySpec(key.getBytes(), ALGORITM);
Cipher cipher = Cipher.getInstance(ALGORITM);
cipher.init(Cipher.ENCRYPT_MODE, secret_key);
return cipher.doFinal(plainText.getBytes());
}
hope this will be useful for you, more http://dexxtr.com/post/57145943236/blowfish-encrypt-and-decrypt-in-java-android

Categories