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.
Related
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.
i have tried several links from stackoverflow to get HmacSHA256 with key to work with java, but i always get
func check(body: String) -> String {
let hash = body.hmac(HMACAlgorithm.sha256, key: Router.sigKey)
print("SIG: " + Router.sigKey)
print("result of hash. \(hash)")
return hash
}
This function returns hash with key from given String.
Key was: 0393e944ee8108bb66fc9fa4f99f9c862481e9e0519e18232ba61b0767eee8c6
String was: example
Result is: 27effb76c97022497e25d3a5d7e823462f212a82d9ebba35f179071568b0c335
When i use this website to check if my SHA256 is good with the same key, it returns same answer, so i know my code in swift is good. But when i try to do it in java, here is the source code.
public static String HMAC_SHA(){
try {
String secret = "0393e944ee8108bb66fc9fa4f99f9c862481e9e0519e18232ba61b0767eee8c6";
String message = "example";
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
String hash = android.util.Base64.encodeToString(sha256_HMAC.doFinal(message.getBytes()), Base64.URL_SAFE);
return new String(Hex.encodeHex(hash.getBytes()));
}
catch (Exception e){
e.printStackTrace();
}
return null;
}
It returns this: 4a2d5f3764736c77496b6c2d4a644f6c312d676a526938684b6f4c5a36376f3138586b4846576977777a553d0a
Which is not even similar to the swift output. How can i achieve the same result with java from the swift code above, it would be helpful a lot!
String key = "0393e944ee8108bb66fc9fa4f99f9c862481e9e0519e18232ba61b0767eee8c6";
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
sha256_HMAC.init(new SecretKeySpec(key.getBytes(), "HmacSHA256"));
byte[] result = sha256_HMAC.doFinal("example".getBytes());
System.out.println (DatatypeConverter.printHexBinary(result));
// ONLY CONVERT TO HEX (= SWIFT) NOT FIRST TO BASE64
result as requested
27EFFB76C97022497E25D3A5D7E823462F212A82D9EBBA35F179071568B0C335
Your key contains values greater then the value 127 and, Mac and SecretKeySpec use bytes, which in Java can contain values from -128 to 127.
In the HmacSHA256 algorithm, the key is interpreted as a string of hexadecimal values. In the case of your secret, the decimal values of this key are:
3,147,233,68,238,129,8,187,102,252,159,164,249,159,156,134,36,129,233,224,81,158,24,35,43,166,27,7,103,238,232,198
As you can see, some of them have a value over 127. When creating the SecretKeySpec object and while doing calculations within the Mac class, Java uses byte[] to store this and related sequences. In Java, a byte can contain values from -128 to 127, which means that when storing this secret, the values > 127 will "flip" and will make sure the calculations following this will not go as you'd expect.
In the Swift case (and with C++, Ruby, and other languages), the conversion from hex to byte occurs without losing the actual value.
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.
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
I´m using this code to Encrypt and Decrypt in Java and Android some Strings and in each system I get a different value.
The code I'm using comes from http://www.androidsnippets.com/encryptdecrypt-strings (I won't paste it beacuse it's quite big).
For example in Android for encrypting "aa" I get this:
1C6BD31C57F42ACFD0EDD2DD5B7A92CA
and exactly the same String with the same key as seed in Java I get:
61FAD1203B7AC92AD9345771AA273DA5
Any idea?
Thanks in advance!
This is just my guess, but I think the reason is your key derivation. I'm not really a Java developer though, so I might not be understanding the code correctly.
This code always calls getRawKey() when you encrypt and decrypt. getRawKey() looks like it takes something they call a seed, or your shared secret, and uses it to compute a new random key to do the actual encryption/decryption.
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
According to Java docs found here, setSeed() "Reseeds this random object. The given seed supplements, rather than replaces, the existing seed."
My guess is that the initial state of the RNG is different on each system/platform, and thus it's giving you different results. You should fix the key derivation to something more standard and consistent, or use an already established crypto system, like PGP in the Bouncy Castle libraries.
This example will demonstrate to how to encrypt a sting and decrypt, for that purpose we need a shif key which will shift the one character to another character, for exaple if you have 'b' and using shift key=2 then it will becom 98+2=100 which ='d' and again 100-2=98 which is 'b', so this will perform in this way.
Make your String encrypt here !
final int shift_key = 4; //it is the shift key to move charcter, like if i have 'a' then a=97+4=101 which =e and thus it changes
String plainText = "piran jhandukhel";
char character;
char ch[]=new char[plainText.length()];//for storing encrypt char
for (int iteration = 0; iteration < plainText.length(); iteration++)
{
character = plainText.charAt(iteration); //get characters
character = (char) (character + shift_key); //perform shift
} ch[iteration]=character;//assign char to char array
String encryptstr = String.valueOf(ch);//converting char array to string
Toast.makeText(this, "Encrypt string is "+ encryptstr Toast.LENGTH_LONG).show();
Make Your String Decrypt here !
for(int i=0;i<encryptstr.length();i++)
{
character=str.charAt(i);
character = (char) (character -shift_key); //perform shift
ch[i]=character;
}
Stirng decryptstr = String.valueOf(ch);
Toast.makeText(this, "Decrypted String is "+decryptstr, Toast.LENGTH_LONG).show();
Looks like it will have different output every time you encrypt it. This is normal.