Java/Groovy: Randomize Encryption String Better - java

I use encryption to create query parameters that get emailed to individuals with download links to various files. However, I've noticed that the encrypted strings are fairly similar for similar file names. I'd like to change that.
Here's the code I'm currently using:
Cipher cipher = Cipher.getInstance('AES/CBC/PKCS5Padding')
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec('****************' as byte[], AES'),
new IvParameterSpec('***************' as byte[]))
String fileId = Base64.encodeBase64String(
IOUtils.toByteArray(
new CipherInputStream(
new DeflaterInputStream(
new ByteArrayInputStream(
('File: ' + filename).getBytes('UTF-8')
)
), cipher
)
)
)
For example if I have a file called Something_8.3.0.1471.exe and Something_8.3.0.1471_License.txt I would get the following two strings:
mVjCmP2GCyxMJ1i5GHT1OOZEYXy1%2Buz%2BQ53QMelR4QU%2FTowMdjNcMjojlbjuTJEd
and
mVjCmP2GCyxMJ1i5GHT1OB%2F5S1rolp%2BwK9dATPdPtHn3uQiXnYUhLmym6hMI65TVfjA5IzSYInI9iIjZn9eExg%3D%3D
The first 20 or so characters of the encrypted string are identical, obviously because the beginning of the file names is identical. I was wondering if it's possible to remove this identicallity (if that's a word). Is it possible to have any change in the unencrypted string more prominently impact the whole encrypted string? If so, how would I go about making this change in Java/Groovy in the above code?

The same key and same plain text will always produce the same cipher text when used in deterministic encryption scheme. Probabilistic encryption schemes introduce some randomness factor so that continuous invocations of encryption with the same key and same plain text produce different cipher texts.
Some crypto algorithms (like EC signatures) generate random values internally and do not even expose them to the user as they do not need to store the value of that randomness factor in order to decrypt the cipher text (or validate a signature) but in most ciphers the user is trusted to provide a random factor like initialization vector or nonce.
AES has a block size of 16 bytes, meaning that it splits the input into 16-byte chunks and processes each chunk separately (in ECB mode) or mixing in the previous processing results in some way (in most other cipher modes). For the very first block regardless of the cipher mode used the only inputs are the key, the IV and the first 16 bytes of the input.
In your example the first 16 bytes are the same in both cases, and it seems that you use the same key and same IV (one of the greatest crypto sins, by the way). As expected, the first 16 bytes (or, equivalently, first 20 Base64 characters) are the same.
The next 16 bytes of the input are different, which leads to completely different second block of cipher text.
You must not use the same IV value twice. Use SecureRandom to generate unique IV values for each encryption attempt.
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[16]; // use the proper IV size for selected cipher
random.nextBytes(bytes);
When using a single storage entity (a single file, for example) storing the IV before the cipher text is the de facto standard, but it is not required (you can store the IV and the cipher text in a separate database columns, for example).
You must not use keys created by simply converting a character sequence into a byte sequence. Such keys have predictable properties (for example, for ASCII text your key will never contain bytes in the range of [0..31]). If you need a password-based key then use the proper key derivation function like PBKDF2.

Related

How to specify value of AlgorithmParameterSpec in java, for encryption and Decryption puropose, in DES

My qustion is about how should I specify the custom value of AlgorithmParameterSpec in the below program? So that I could use the exact same output value for two different programs, one of which is Server and other is Client. Like I have used the fixed value for Key Generation, I want the same for this AlgorithmParameterSpec.
Client Code Snippet
....
String desKey = "0123456789abcdef"; // value from user
byte[] keyBytes = DatatypeConverter.parseHexBinary(desKey);
SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
SecretKey key = factory.generateSecret(new DESKeySpec(keyBytes));
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
....
Both client and server are on different pcs and are connected on same LAN. However I dont want to send any file from one to another, and neither do I want to write the value of that parameter from client into any file and use it in server. That has already caused too much problems. Is there a way to do this? Or I have to send this generated value from client to server using readUTF and writeUTF?
Original DES aka single-DES has been broken since last century, and should not be used.
In Java, instantiating plain DES like that defaults to DES/ECB/PKCS5Padding (in general Java treats PKCS5 padding as including PKCS7). ECB does not use an IV, and DES has no other parameters, so a java Cipher object for DES/ECB does not need or use any type of parameters object. However, in most applications, ECB mode even with a good cipher (not DES) is insecure and should not be used.
If you change to a more secure mode that does use an IV, like CTR CBC OFB CFB, or better in most applications but only supported in j8+ GCM, then IvParameterSpec is indeed the correct type to use.
However, with modes that use an IV, using a fixed or otherwise duplicated IV is almost always insecure, so you must have a process that provides a unique IV for each encryption and the corresponding decryption(s).
This is often done by choosing a random IV when encrypting and transmitting and/or storing it along with the ciphertext (sometimes simply as the first part of the ciphertext) to be used when decrypting, but other designs that accomplish the same thing are possible. For CTR-based modes including GCM, especially when applied to a stream of traffic between the same parties, using a (scaled) counter provides uniqueness. For CBC the IVs must not only be unique but unpredictable; random is usually easiest for this but it is possible to use an encrypted counter.
Both key and IV (when used) should actually be bits not characters. However it is often convenient to represent them in characters using hex (as you did) or base64. Such character representations can be transmitted and received by a huge variety of methods, of which DataInput/OutputStream or ObjectInput/OutputStream is one among many. There are also many ways to transmit bits (binary) on all networks used in this century (although back in the 1970s and 1980s this was often problematical). Choosing among these may depend on what else your programs are doing and in particular whether they already use things like HTTP, XML, JSON, etc.

Rijndael 128/256 decryption

I tried many answers from SO and all over the web but still without success.
I use following tool to encrypt.
text to encrypt: tom
key: exo123exo1exo123
input (textfield or selected file above) is: text/binary
Convert output to: [i leave this unselected]
Mode: CTR
Ciphers: Rijndael-128 and Rijndael-256
After getting result I move here:
and encode it with base64.
Then I copy string and send it as a parameter to my function:
public String authenticate(String base64EncodedData){
byte[] input = Base64.decodeBase64(base64EncodedData);
byte[] ivBytes = "1234567812345678".getBytes();
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.init(
Cipher.DECRYPT_MODE,
new SecretKeySpec("exo123exo1exo123".getBytes(), "AES"),
new IvParameterSpec(ivBytes)
);
byte[] plainText = new byte[cipher.getOutputSize(input.length)];
int plainTextLength = cipher.update(input, 0, input.length, plainText, 0);
plainTextLength += cipher.doFinal(plainText, plainTextLength);
return new String(plainText);
}
Result I got is always something similar to this (no matter if I use Rijndael-128 or 256 encrypted string):
.�v�Y�
When I try to return input value - I get the encrypted string. So base64 works just fine.
What I do wrong?
Am slowly getting mad here.
Thank you.
There are some problem with your assumptions and your code:
The output of the first tool is already Base 64 encoded. RIJNDAEL-128: r0GR and RIJNDAEL-256: yAVy. It doesn't need to be encoded a second time. It automatically selects this option, because binary data cannot be printed.
There is no native Rijndael-256 in Java, you will have to use BouncyCastle for this. Rijndael-128 is supposed to be AES which means that both have a block size of 128-bit.
The IV almost certainly needs to consist of zero bytes. For example:
byte[] ivBytes = new byte[16];
Arrays.fill(ivBytes, (byte)0); // might not be necessary
Note that CTR mode doesn't use an IV, but rather a nonce.
Always specify an encoding when retrieving bytes: "exo123exo1exo123".getBytes("UTF-8"). It is probably best to use UTF-8 everywhere. If you send data across systems that use different system encodings, it will lead to hard to find problems.
On second look at the online tool is not usable for anything, because it is unclear how it works. What I found:
Any key produces a ciphertext regardless of size, which suggests that the "key" that you enter is actually hashed and there exist a million ways that could be done. I could decrypt it with the above method, so the key is actually uses as-is without hashing. This suggests that the key is filled up with 0x00 bytes until a valid key size. When the key size is too big, it is probably truncated.
Plaintext of 16 characters encrypts to a ciphertext of 16 bytes (encoded 24) and a plaintext of 17 characters encrypts to a ciphertext of 32 bytes (encoded 44). This means that no discernible padding is used which might actually be zero padding.
And here is the deal breaker:
Every time you encrypt something, you get a different ciphertext. In CBC mode it means a random IV is generated before encrypting. The problem is that this IV is not shown. So there is no way to decrypt this fully. So if you encrypt more than 16 characters you can recover all but the first 16 characters when you decrypt it in the way I describe it in the first part of this answer.
You shouldn't make your system dependent on closed source online tools. Write your own encryption tool. Some general advice:
IV should be generated randomly every time you encrypt something. It is not secrect, but should be unique. Simply prepend it to the ciphertext after encryption and slice it off before decryption.
Use an authenticated mode like CCM or GCM where possible. It is harder to do authenticated encryption yourself, but the other way to go would be to use encrypt-then-mac paradigm.

Java AES En/decryption for a String

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.

Java PBE encrypted string length same as input

I am trying to encrypt few database columns (string values) using Java AES algorithm. This is to protect certain sensitive data. For some users these data should decrypted.
In Java AES encryption, if my input character length is 60, I am getting encrypted string length as 88.
But I don't want a change the length of the encrypted data. We have huge amount to tables and many applications are using those tables. We want to minimize the impact of encrypting certain fields in the tables.
Is there is any recommended solution? OR is there is any recommended algorithm, code sample, etc?
In such situation you can use a stream cipher instead of a block cipher. Note that block ciphers can also be used as stream ciphers, for example the AES in counter mode.
If you have to use a block cipher and all your fields are larger than one block you can use cipher text stealing.
Both methods give you an output that has the same length as the input when regarded as byte array. There may be difficulties to represent the output byte array as a string in the same length as the input.

Check encryption key correctness in JAVA

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.

Categories

Resources