I would like to use AES256 to encrypt a text, I'd like to use my email test#gmail.com as the key to encrypt it.
This is what I tried:
String key = "test#gmail.com";
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
byte iv[] = SOME_RANDOM_32_BYTES;
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] encryptedResult = cipher.doFinal(text.getBytes("UTF-8"));
When I run above code, I got InvalidKeyException:
java.security.InvalidKeyException: Key length not 128/192/256 bits.
I checked on internet, the reason is my key is not 128/192/256 bits. My question is, how can I generate a 256bits(32bytes) key from my email string test#gmail.com ?
You can hash your key string (test#gmail.com) to a 256bit value using SHA256.
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(yourEmail.getBytes());
byte[] encryptionKey = md.digest();
There can be multiple reasons for this. One of them is below.
Usually this error comes when you don't have the update policy in your JRE.
Java by default provide AES with 128, for 256 we have to put new policies provided by Java.
You should not bake your own crypto! (Unless you are very knowledgeable on the matter, that is.)
You should use an existing (and audited) encryption library.
Also, you should not use a guessable string such as your e-mail address as a password. Please, look for advice on how to choose a good password.
Now that I have said this, here are more details.
The proper way to implement password based encryption is to use a KDF (Key Derivation Function) to generate an encryption key from your password. Here are a few KDFs that you can use for this task: Argon2, Scrypt, Bcrypt and PBKDF2.
Key derivation functions include mechanisms to defend against know attacks such as rainbow tables and dictionary attacks, notably a "salt" and a work factor. Modern KDFs such as Argon2 also attempt to prevent attackers from gaining an advantage by using hardware more suitable to the task.
Generally speaking, here how this is used:
Select a work factor (the largest you can afford)
Generate the salt using a CSPRNG
Generate the encryption key and a MAC secret using your chosen KDF with the password, salt and work factor.
Generate an IV (initialization vector) using a CSPRNG
Encrypt the data to be protected using the generated encryption key.
Compute the MAC of the encrypted message using the generated secret.
Serialize the salt, the work factor, the computed MAC and the encrypted data. (Optionally, identifiers indicating what are the chosen KDF, encryption scheme and MAC should also be included if these are not fixed.)
Your encrypted message is the serialized data produced in step 7. Get any of the steps wrong (and that is easy) and your encryption code will probably break in horrible ways.
Perhaps now you get a sense of why you should use an existing library?
Note: the current best practice is to use AEAD (Authenticated Encryption with Associated Data) instead of encrypt-then-MAC as described above. Look this up if you are interested: I am not going to discuss this here.
Related
I am new in security and I'm wondering if I can make my program better, like changing or adding something to make it better ( more secure )
(I have doubt from the program output)
Here is the output:
Encrypted Message: +g#þóv«5Ùû`ž
keybyte: [B#71e7a66b
Original string: Message
Original string (Hex): [B#2ac1fdc4
Here is the code:
public class AES {
public static void main(String ... args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
final String Algo="AES";
String key = "aaaaaaaaaaaaaaaa";
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
MessageDigest sha= MessageDigest.getInstance("SHA-1");
keyBytes=sha.digest(keyBytes);
keyBytes=Arrays.copyOf(keyBytes, 16);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, Algo);
Cipher cipher = Cipher.getInstance(Algo);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] ciphertext = cipher.doFinal("Message".getBytes());
System.out.println("Encrypted Message: " +new String(ciphertext));
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] original = cipher.doFinal(ciphertext);
String originalString = new String(original);
System.out.println("keybyte: "+keyBytes);
System.out.println("Original string: " + originalString + "\nOriginal string (Hex): " +original);
}
}
No, this code is pretty terrible:
Your key is fixed and a string that is then hashed. This looks to me like your key is actually supposed to be a password. A single hash is not enough to derive a key from a password. You need to use a strong hashing scheme like PBKDF2, bcrypt, scrypt and Argon2. Be sure to use a high cost factor/iteration count. It is common to choose the cost so that a single iteration takes at least 100ms. See more: How to securely hash passwords?
Always use a fully qualified Cipher string. Cipher.getInstance("AES"); may result in different ciphers depending on the default security provider. It most likely results in "AES/ECB/PKCS5Padding", but it doesn't have to be. If it changes, you'll lose compatibility between different JVMs. For reference: Java default Crypto/AES behavior
ECB mode is pretty bad. It's deterministic and therefore not semantically secure. You should at the very least use a randomized mode like CBC or CTR.
Without authentication you have the threat of not detecting (malicious) modifications of your ciphertexts. It is better to authenticate your ciphertexts so that attacks like a padding oracle attack are not possible. This can be done with authenticated modes like GCM or EAX, or with an encrypt-then-MAC scheme through HMAC with a strong hash function such as SHA-256.
Because of these issues you should use a library instead. Try JNCryptor or this library.
There are at least two problems with your implementation:
Unless you specify otherwise, you are using AES in ECB mode. ECB mode is not secure regardless of what cipher is under the hood. There are a number of secure modes, but usually people implement CBC mode, which is accomplished by changing your Algo to "AES/CBC/PKCS5Padding" (which really is PKCS7 padding, Java just does not know better). Then, you need to choose an IV via SecureRandom() to encrypt this way. This OWASP example seems to do it right (FYI -- 99% of the implementations you will find on the web have security problems somewhere).
Your key is not a key, instead it is a password that is being turned into a key. You shouldn't be hardcoding this, but I assume you are doing that for proof of concept only. In any case, the problem is using a hash function such as SHA-1 to turn a password into a key is not a good decision because passwords tend to have low entropy and can be brute forced. For this reason, you should be using a function that is dedicated to resisting brute force when turning a password into a key. Such functions include pbkdf2, bcrypt, scrypt, and argon2. For more information, Troy Hunt (a .Net guy) gives a good overview on the problems with using something like SHA-1 (or anything from the SHA2 family for that matter) in this context.
The other thing to keep in mind is that encryption does not generally provide message integrity. What this means is that just because you have encrypted the data does not mean somebody cannot modify it, and your software will still decrypt the modified data without being aware that it is modified. If you need to know that your data was not modified, then you will need to add on something like HMAC, or else you will need to switch to a mode of operation such as GCM.
Finally, AES is an excellent choice for security. But you need to use it in the right mode, you need to implement it right, and you need to understand whether you need more than just encryption.
I have found an useful AES256 implementation to en/decrypt strings, all work fine except the fact i have to store the initialization vector (IV) for the decoding.
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
All there any (secure) alternative methods maybe without CBC, because with CBC i have to store an IV (and in my case i have to share the IV which is really not secure).
I have read an article on this platform concerning it´s possible to encode without CBC but that´s not a good idea because of dictionary attacks.
so, are there any alternatives to encode a string with aes256 (without storing additional data like IV, or salt arrays)?
The idea behind an IV is to use a distinct new random one for every encryption (with the same key).
This is very important to ensure the the security of cipher-modes such as CTR or CBC which would not be secure at all without an IV. Also when using the same key to encrypt the same message twice it will result in two distinct ciphertexts (since the two IVs were distinct).
In short, you should always use an IV.
Usually you just prepend them to the ciphertexts and then extract them before encryption, so you don't have to store them individually.
I'm using BouncyCastle to encrypt/decrypt some files using AES and PKCS5 padding in CBC mode :
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
Now two questions:
How can I check that the provided key for decrypting data is correct or not ?
How Can I check encrypted input is untouched (e.g. not changed by user using an HEX editor)?
Thanks
You can use an AEAD mode, like CCM or GCM, in place of CBC. These modes authenticate an encrypted message, so if the wrong key is used, or the cipher text has been altered, you can detect it. You wouldn't be able to distinguish these cases though.
There is support in Java 7's cryptography API for GCM, but the SunJCE provider that ships with Oracle's Java implementation doesn't support it yet. You can get support through third-party providers like BouncyCastle.
You can achieve the same things if you use additional cryptographic services, like a digital signature or message authentication code.
Encryption is not just about the algorithm and the encryption key, it's also a lot about
the system organization.
In general, you can't determine that the key is correct. Any key can be used to decrypt the
data that's supposed to be decrypted, but it's up to some other mechanism to tell you if that
is the "correct" result.
In general, you can't determine if the data to be decrypted is untouched, except through some
external check. It's a property of most encryption systems that changing any of the encrypted
data would change the decrypted output drastically, probably into something you'd interpret
as garbage.
You should add a MAC which first verifies the integrity of the message, and only then you should decrypt it. A common choice of MAC is HMAC with whatever hash function you prefer, such as SHA-2.
Instead of doing this yourself, it's often a good idea to use an authenticated cipher. AES-GCM is a common choice. But you need to be really careful to never reuse an IV in that case.
The JCE ciphers are usually very basic. If you need a full featured protection including integrity and key testing, you need to combine them. And as usual it is better to not device that yourself. So better opt for a more high level format like PKCS7/12 or PGP.
Depending on the Padding used some ciphers will give you a PaddingException when you try to decrypt it with the wrong key. For stronger integrity check I would use a padding consiting of HMAC bytes.
A pretty complete method is included in the JCE, it is the AESWrap algorithm. It requires padded data but will ensure integrity. It is best combined with a length byte as described in RFC 3537. Note, that this is only intended for smaller amounts of secrets (like symmetric keys). The RFC3537 padding is restricted to 255 bytes.
To use this with a password derived key, you can use this:
char[] pass = ... // your password
byte[] codeBytes = ... // up to 255 bytes you want to protect
// generate wrapping key from password
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[16]; rand.nextBytes(salt);
SecretKey kek = f.generateSecret(new PBEKeySpec(pass, salt, 1000, 128));
kek = new SecretKeySpec(password.getEncoded(), "AES"); // convert into AES
// RFC3537 padding (lengthbyte)
byte[] wrappedCodeBytes = new byte[codeBytes + 1 % 8];
System.arraycopy(codeBytes,0,wrappedCodeBytes,1,wrappedCodeBytes.length);
paddedCodeBytes[0]=(byte)codeBytes.length;
byte[] pad = new byte[paddedCodeBytes.length - codeBytes.length -1]; rand.nextBytes(pad);
System.arraycopy(pad,0,paddedCodeBytes,codeBytes.length+1,pad.length);
// AESWrap is WRAP_MODE:needs a SecretKey
SecretKey paddedCodeKey = new SecretKeySpec(paddedCodeBytes, "RAW");
// now wrap the password with AESWrap kek is 128 bit
Cipher c = Cipher.getInstance("AESWrap"); // default IV
c.init(Cipher.WRAP_MODE, kek);
byte[] result = c.warp(paddedCodeKey);
The unwrapping is left for the reader as an exercise :) The example code uses 128bit keysize, since more entropy cant be expected from the PBKDF2 anyway.
Note that this will detect wrong passwords with high probability, and some critics will see this as a weakness of AESWrap.
Take a look at this tutorial on BC encryption, specifically the InitCiphers methods, and in detail at the second code block which specifies the actual type of cipher.
How can I check that the provided key for decrypting data is correct or not?
According to JCE Javadocs, specifically the constructor of Class SecretKeySpec:
This constructor does not check if the given bytes indeed specify a secret key of the specified algorithm. For example, if the algorithm is DES, this constructor does not check if key is 8 bytes long, and also does not check for weak or semi-weak keys. In order for those checks to be performed, an algorithm-specific key specification class (in this case: DESKeySpec) should be used.
Note that Interface KeySpec lists all implementing classes, basically a list of validation options.
How Can I check encrypted input is untouched (e.g. not changed by user using an HEX editor)?
Indeed. That's a good one. 'Input' is pretty generic. Do you mean the actual content to decrypt? Well, if it's munged I believe it will not decrypt properly. Does that make sense?
IFF you are talking about the case of a key with parity bits being altered, as described in item (6) at the Bouncy Castle FAQ, you will have to do an actual parity check on the key. Only the first 56 bytes of the key are used for the encryption ops, and the last 8 bytes are reserved for parity checking. So, essentially, the last part of the 'key' can be changed and the first part is still useful. To detect whether either the parity or the key have been altered, you would run a parity check. I found this little ditty on doing a parity check. And, for more info on how parity is set in these keys, see comments in the JDK7 Crypto Provider source for Class DESKeyGenerator by Jan Luehe (near bottom) which discuss parity setting.
I recently had some interaction with BC, and I hope this info helps.
I am trying to replace PBEWithMD5AndDES with PBEWithMD5AndTripleDES in existing code. So far, I am using the same passphrase that I was using before, and receiving this Exception:
java.security.InvalidKeyException: Illegal key size
I looked online and saw that DES uses a 64 bit key and TripleDES uses a 128 bit key. I am not clear on the details of how my passphrase is used to generate a key, and not sure where to look to understand this fully. My passphrase is 260 characters long. I tried doubling the length, but I get the same Exception.
I am generating a PBEKeySpec from my passphrase, with an 8 byte salt and an iteration count of 12. I see that there's another constructor that takes a keyLength argument, but the documentation describes it as "to be derived," and I don't understand that. I have the idea that I need to modify the iteration count and/or supply a keyLength argument, but I don't want to just do this blindly without fully understanding what I am doing.
Here is the basic outline of the code I'm currently using:
String passphrase = ...
byte[] salt = ...
int iterationCount = 12;
String algorithm = "PBEWithMD5AndTripleDES";
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance(algorithm).generateSecret(keySpec);
Cipher cipher = Cipher.getInstance(key.getAlgorithm());
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
byte[] encoded = cipher.doFinal(data);
PBEWith<Hash>AndTripleDES Requires "Unlimited Strength" Policy
This algorithm uses a 168-bit key (although due to vulnerabilities, it has an effective strength of 112 bits). To use a symmetric key of that length, you need the "unlimited strength jurisdiction policy" installed in your Java runtime.
An "Illegal key size" message indicates the key length is not permitted by policy; if the key length is incorrect for the algorithm, the SunJCE provider uses the message, "Wrong key size".
Don't Use PBEWith<Hash>AndTripleDES
Note that "PBEWithMD5AndTripleDES" is a bad algorithm to use.
Password-based encryption generally follows PKCS #5. It defines an encryption scheme for DES (or RC2) called PBES1. Because PBES1 was designed to generate 64-bit (or less) keys, Oracle has created a proprietary extension to generate longer keys. It hasn't been exposed to the same scrutiny that PKCS #5 has, and if you need to inter-operate with any other platform, you'll have to dig into the source code to find out how the key and initialization vector are derived.
It's also strange that the initialization vector is derived from the password. The purpose of an IV is to create different cipher texts each time a given plain text is encrypted with the same key. If the IV is generated from the key, this purpose is defeated. The key-derivation algorithm used by PBES1 avoids this by incorporating a "salt" that is supposed to be different each time the password is used. But, it could be easy to screw this up; providing an IV directly to the cipher initialization is more conventional, and makes it more obvious what is happening.
Use PBKDF2 Instead
PKCS #5 also defines an key-derivation algorithm called PBKDF2 that is now supported by Java. It provides superior security to PBES1 because the initialization vector and any other parameters required by the cipher are not derived from the password, but are selected independently.
Here's an example with PBKDF2, using AES. If you can't follow the recommendation to update to AES, the example can be applied to DESede by using a key length of 192, and changing occurrences "AES" to "DESede".
TDEA Keying Options
There are three keying options that can be used with TDEA ("Triple DES" or "DESede"). They take 64-, 128-, or 192-bit keys (including parity bits), depending on the option.
The key sizes accepted by the TDEA implementation depend on the provider; a few require you to form a 192-bit key, even if you are using the 56-bit key option which is effectively DES instead of TDEA. Most implementations will take 16 or 24 bytes as a key.
Only the three-key option (168 bits, or 192 bits with parity) can be considered "strong encryption". It has 112 bits of effective strength.
As erickson says, the "right" answer to this question is to install the unlimited strength jurisdiction policy files in the JRE.
That will make encryption with PBEWithMD5AndTripleDES "work," but the resulting data cannot be decrypted as far as I can tell. You will get a padding error exception. There may be some way to fix it, but this was proof enough to me that pursuing this route was not worth it as it seems to be a road that is not traveled enough to get the bugs worked out or to popularize working examples.
I also discovered a PBEWithSHA1AndTripleDES and tried it, but got the same padding error upon decryption.
I was able to get our requirements changed from PBEWithMD5AndTripleDES to just TripleDES (DESede), and that eliminated the whole issue for me!
I'm trying to decipher the encfs volume key in Java. I ran encfs with the standard settings (ie, 192 bit aes encryption). Is there any way to do this?
This is not really a Java question, more an issue of cryptography. A brief investigation into EncFS suggests that the volume key is encrypted by a key derived from a user password. It's probable that this represents the best attack vector: a brute force attack on what is hopefully a weak password. I wouldn't hold out too much hope, however.
For what it's worth, the Java JCE classes in javax.crypto would be your route towards performing symmetric cryptography in Java. The following code snippet shows how you might perform some AES decryption with a plaintext key:
byte[] keyBytes = ...
byte[] volumeKeyFileContents = ...
SecretKeyFactory factory = SecretKeyFactory.getInstance("AES");
SecretKey aesKey = factory.generateSecret(new SecretKeySpec(keyBytes, "AES"));
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // for example
cipher.init(Cipher.DECRYPT_MODE, aesKey);
byte[] plaintext = cipher.doFinal(volumeKeyFileContents);
// (written from memory so may not compile without tweaks)
You would then need to use the plaintext value as a potential candidate for the volume key. Clearly this is a simplified solution and makes many assumptions; you will need to research in more depth the format of these volume key files and the algorithms, modes and padding used during the construction of the encrypted copy.
Good luck.
There is a Java library at https://github.com/mrpdaemon/encfs-java that provides read & experimental write access to the encfs file systems.