Using CipherOutputStream + AES to write a string to a file - java

I'm looking for a way to, given a password of any size, encrypt a file I'm receiving over a stream with AES. To make things easier to start with, I was trying to just write some encrypted message to a file. But I'm having trouble:
I'd like to be able to define a password of any size. Is it possible to accomplish it in a "standard" form or will I have to pad the passwords to 2^k sizes? I'm currently circumventing the problem providing a temporary "aaaaaaaaaaaaaaaa" password, but I'd like to get rid of it as soon as possible.
If I try to write a long string to cos, something encrypted will be written to the file. But if I try something smaller, as "abc", nothing will be written. I've played with several padding options but they seem of no avail (PKCS5Padding, SSL3Padding, NoPadding).
Here is the code I'm using:
SecretKeySpec localSecretKeySpec = new SecretKeySpec("aaaaaaaaaaaaaaaa".getBytes(), "AES");
Cipher localCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
localCipher.init(Cipher.ENCRYPT_MODE, localSecretKeySpec);
CipherOutputStream cos = new CipherOutputStream(new FileOutputStream("abc"), localCipher);
IOUtils.write("abc", cos);
cos.flush();

This answer shows how to use a SecretKeyFactory to generate a key based on arbitrary length passwords.
As for your second problem, cos.flush() is not enough to pad and encrypt the last block. You need to call cos.close() for that. Here is the documentation for the close() method which states this fact.

You could use a cryptographic hash (e.g., SHA-1) to convert any string into a fixed-length binary value.
That's what I did in a simple file encrypter I wrote some time ago.
See the deriveKey() methods in:
http://david.tribble.com/src/java/tribble/crypto/FileEncrypter.java

Related

Reduce AES-CTR malleability impact by shuffling plain text

I need to cipher and decipher text with randomly access. For this I decided to use AES in CTR mode which is a good compromise between CBC and GCM.
But CTR mode is malleable. It is not a big problem because texts that I will cipher have not predefine patterns and I don’t need authentication, I just want to avoid the ability to read the plain text for an attacker.
However, I want to reduce the ability to an attacker to really use the malleability property of CTR.
Here my idea to do it:
public static void main() {
Key key = …;
IVParameterSpec iv = …;
int rSeed = …;
byte[] plainText = …;
byte[] shuffled = shuffle(plainText, rSeed);
byte[] encrypted = encryptAES_CTR(shuffled, key, iv);
…
byte[] decrypted = decryptAES_CTR(encrypted, key, iv);
byte[] unShuffled = unShuffle(decrypted, rSeed);
// Here unShuffled content must be equal to plainText content.
}
Suppose that methods shuffle(…) and unShuffle(…) are complementary and one reverse the other.
The shuffle step is here not to cipher but shuffle the order of bytes of the plain text, in that way it reduces the impact of modifications done on the ciphered text by an attacker because if an attacker changes a part of the ciphered text, the modifications will be spread during the unShuffle phase and will change the content but the chance that the new content can be exploitable is reduced.
However, it is just an idea and I write this post just to know if this idea is really useful or if it is useless and just superficial step which does not reduce the impact of modifications that can be done by an attacker on the ciphered text.
I know that there is the GCM which provides authentication and therefore, is not malleable. But I don’t really need it and GCM cannot be accessed randomly especially if I want modify and re cipher just a part of the plain text.

Is it secure to build a Cipher object with a SecureRandom object which has a fixed seed?

A colleague of mine asked me to check if his code is secure enough. I saw some code snippet like this:
private static byte[] encrypt(String plain, String key) throws Exception {
KeyGenerator kg = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = new SecureRandom();
secureRandom.setSeed(key.getBytes());
kg.init(128, secureRandom);
SecretKey secretKey = kg.generateKey();
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(plain.getBytes());
}
#Test
public void lcg() throws Throwable {
String plain = "abc";
String key = "helloworld";
byte[] c1 = encrypt(plain, key);
byte[] c2 = encrypt(plain, key);
Assert.assertArrayEquals(c1, c2);
}
This encrypt function is used to encrypt sensitive data, and encrypted data will be stored into database. I thought it won't work firstly because SecureRandom won't generate same random number twice even initialzed by the same seed, but it just works in the test.
I think it is not secure to encrypt something in this way, but I can't tell what is the problem about this code snippet.
My question:
is the encrypt function secure?
if it's not secure, what is the problem to do so?
is the encrypt function secure?
No, it is not secure for any good definition of secure.
First of all, it uses ECB, which is not secure unless the plaintext blocks are not related.
More importantly, new SecureRandom() simply gets the first random number generator from the provider list. Usually this was "SHA1PRNG" but currently - for the Oracle Java 11 SE runtime - it returns a much faster and better defined DRBG. Those are not compatible, so one ciphertext cannot be decrypted with another runtime; the code is not portable at all.
Different runtimes may return completely different random number generators - possibly optimized for the runtime configuration. These random number generators may depend completely on the given seed if it is set before random numbers are extracted from it. It may also mix the seed into the state. That will produce a completely random key which you will never be able to regenerate unless you save it somewhere.
Basically, this method may both be insecure because of ECB and overly secure - not a small feat in itself. You may never be able to decrypt the ciphertext again, ever, but you can still distinguish identical plaintext blocks.
A small other problem is that getBytes uses the platform default encoding. This differs e.g. between Windows (Windows-1252) and Linux (UTF-8) and the Android platform (also UTF-8, of course). So decode the plaintext on another system - if you can - and you may still get surprised afterwards.
The procedure is so bad that it should be archived in the round rubbish receiver and implement something new. For that it is a good idea to use a key and IV consisting of random bytes and a modern cipher such as AES in GCM mode at the very least. If you have a password you should use a password hash (or key-based key derivation function) like PBKDF2 or a more modern one to derive a key from it.
Kudo's for finding a worse way to derive keys from passwords. getRawKey was bad, but this one is worse. Good that you asked, in other words.

AES encryption in Java for given key

I am trying to do AES encryption in Java. I have the following function:
public static String encrypt(String plainText, String key) throws Exception {
if (plainText == null || plainText.length() == 0) {
return plainText;
}
// get aes key
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
// encrypt
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] bytes = cipher.doFinal(plainText.getBytes("UTF-8"));
//encode
String encoded = Base64.encodeToString(bytes, Base64.NO_PADDING | Base64.NO_WRAP);
return encoded;
}
My aim is to be able to encrypt some text with a given key the same way every time. That is, if I call this function with the same two parameters many times, I want the same string to be returned on every call. I'll say in advance, I know that is not how cryptography is supposed to be done, but I need this functionality for my project.
Unfortunately, that is not the case. The key generated in line 7 seems to encrypt my string differently every time around. I assume there is some kind of extra random automatic salting occurring on the lower levels of this library, preventing me from achieving my goal.
Does anyone know a way in Java how I could go about encrypting a given string with a given key to the same value every time? Thanks.
UPDATE/CLARIFICATION: This is not for security. This is for the encryption of data for it to be obfuscated for certain people that might come in contact with working on the app itself. The information is not highly sensitive, but I do want it encrypted and then decrypted by the same key. I have others working with me on this with libraries in their respective languages, e.g. Ruby, and their libraries allow them to encrypt a value with a given key the same way every time. We all want to use the same parameters of the same encryption algorithm: Key length: 128 bits
Mode of operation: CBC
Initialization Vector: None (all zeros)
Is it perhaps that if one does not set an initialization vector, it is randomly assigned? I've got to check that out.
Yes, Java - or rather the security provider supplying the AES in CBC mode implementation - may default to a random IV (which you then have to retrieve afterwards and include with your ciphertext) if you don't explicitly specify it you get secure, randomized ciphertext.
If you want to use a zero IV you will have to specify it explicitly:
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]);
This is only slightly more secure than ECB mode, as any repetition in different messages in the initial ciphertext blocks will be instantly visible to an attacker.
If you want to have a more secure mode without random IV - which is required for CBC mode to obtain CPA security - then you could check out synthetic IV (SIV) mode or GCM-SIV. For those modes the entire message needs to be identical with a previous one to leak info to an attacker. It's however slowe, not included in the standard VM and the ciphertext could be larger than AES/CBC (inclusion of IV/tag vs padding requirements).

Confused about AES cipher version

I'm trying to implement AES256 encryption into an android app. Data is coming from a server encrypted, I've been using the Android library JNCryptor to decrypt the data. It successfully does this, but it's very slow. I wanted to try Facebook's Conceal library because it reports having faster encryption and decryption speeds. My first implementation was decrypting a string from the server with the Conceal library. My problem comes when I try to pass the byte[] of the encrypted string to the decrypt function in Conceal.
ByteArrayInputStream bin = new ByteArrayInputStream(Base64.decode(encStr, Base64.DEFAULT));
InputStream cryptoStream = null;
try {
cryptoStream = crypto.getCipherInputStream(bin, new Entity("test"));
...
The crash comes because the given cipher version, which is found by getting the first byte of the byte [] does not equal the expected Conceal cipher version number 1.
I then looked at the encryption side of Conceal and saw this is just a number set during the encryption.
To double-check I then looked over the JNCryptor source code and saw it sets and looks for Cipher Version numbers 2 and 3.
I guess my questions are: What is the significance of the Cipher Version number? Would I be able to get the Conceal library to decrypt this data or are they just encrypted in totally different ways?
They are completely unrelated. For instance, Conceal seems to use GCM mode of encryption (which includes authentication) and RNCrypt uses AES in CBC mode and HMAC for authentication. Besides that it uses passwords and PBKDF2 instead of keys directly (although implementations like JNCryptor may include shortcuts to use keys directly - thanks Duncan).
Both are relatively minimalistic proprietary cryptographic formats, and both use AES. That's where he comparison ends.

How to make IvParameterSpec random but still decrypt

For Encryption in Java... the article at http://cwe.mitre.org/data/definitions/329.html states that the Initialization Vector should be different each time, but if I use a different IV to decrypt than the one I used to encrypt, I get garbage characters instead of the data I expected.
What is the proper way to encrypt on one server and decrypt on another without having to communicate the IV back and forth in between servers?
The common technique seems to be to hardcode a byte array, but supposedly that's insecure???
I believe an IV is like a salt - it's not a secret, it's just used to introduce an extra element of randomness so that the same message encrypted with the same key still comes out differently each time.
So you can transmit the IV used to encrypt as part of the encrypted value, just like you'd store the salt along with a hash for a hashed value.
Of course, I could be completely incorrect...
you have to add the information that you want to avoid, inside the encrypted data, which is an array of bytes, and then removing during the conversation.
the IvParameterSpec is based on fixed array, so you know the length of the part to add and to remove. The removed part is used to recreate the parameters you pass during the chypher initialization.
please have a look at this class I created:
https://github.com/spannozzo/tk-integration/blob/master/tk-integration-util/src/main/java/org/acme/util/AESUtil.java

Categories