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.
Related
I am encrypting in ojb-c with SecKeyEncryptedData and trying to decrypt in Java with javax.Cipher and hitting a problem.
I recently moved to doing long blocks and have needed to use a symmetric encryption with the AES key encrypted with the asymmetric key pair. I am having problems decoding.
I have the iOS key kSecKeyAlgorithmRSAEncryptionPKCS1 working for asymmetric data matched with Cipher.getInstance("RSA/ECB/PKCS1Padding") in Java. This decodes the short blocks.
As I need to send longer blocks, and am trying to switch to kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM on iOS and it encrypts fine, but i cannot find the method to use in Cipher to decrypt it and do not understand if it needs to be done in 2 steps in the cloud in Java.
OBJ-C:
SecKeyAlgorithm algorithm = kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM;
NSData* cipherText = nil;
cipherText = (NSData*)CFBridgingRelease( // ARC takes ownership
SecKeyCreateEncryptedData(self.pubKey, algorithm,
(__bridge CFDataRef)data, &error));
Java:
try {
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, priv);
byte[] dog = decoder.decode(encString);
dec = cipher.doFinal(dog);
res = new String(dec);
} // handle errors
The decode obviously fails.
So my question is in 2 parts.
is there a Cipher type that will do the decode needed or do i need to break out the encrypted AES key and decrypt it first?
If i need to break it up, how long is that encrypted AES key part of the data block and, if you know the ciphers for that it would be fantastic.
is there a Cipher type that will do the decode needed
You may read the Cipher documentation. I believe you are looking for RSA/ECB/OAEPWithSHA-256AndMGF1Padding
I see the designation doesn't exacly match with the Obj-C name, but this is a common standard so it may worth a try
As I need to send longer blocks, and am trying to switch to kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM
You may try to search for "hybrid encryption". Asymmetric ciphers are VERY slow comparing to symmetric ciphers and intended to encrypt only limited amount of data.
Some implementation may encrypt longer data anyway (for each 256 bit input providing 2048 o 4096 bit output), Java will simply complain and stop
So proper encryption would be
encrypt data with a radom key (DEK - data encryption key) using a symmetric cipher
encrypt the DEK using an asymmetric public key
If the kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM would be not counterpart (compatible) with RSA/ECB/OAEPWithSHA-256AndMGF1Padding, you may still use the PKCS#1 1.5 padding (the old one) with this approach.
Edit: this asnwer may be useful when working with OAEP too RSA/ECB/OAEPWithSHA-256AndMGF1Padding but with MGF1 using SHA-256?
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.
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).
What are some of the simplest ways to AES encrypt and decrypt a 16 byte array without the automatic padding? I have found solutions that use external libraries, but I want to avoid that if possible.
My current code is
SecretKeySpec skeySpec = new SecretKeySpec(getCryptoKeyByteArray(length=16)); // 128 bits
Cipher encryptor = Cipher.getInstance("AES");
encryptor.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = encryptor.doFinal(plain);
How can I prevent the padding? The plain data is always fixed length and includes its own padding. How can I allow plain to be 16 bytes without causing encrypted to become 32 bytes?
See my comment. Sorry I probably should have taken a closer look the first time.
Change "AES" to "AES/CBC/NoPadding"
Change decryptor.init(Cipher.DECRYPT_MODE, skeySpec); to decryptor.init(Cipher.DECRYPT_MODE, skeySpec, encryptor.gerParameters());
To encrypt only 16 bytes of data, fixed length, using a method that requires no initialization vector to be saved, Change "AES" to "AES/ECB/NoPadding"
I pick ECB because that is the default.
If you need to encrypt more than 16 bytes, consider using something other than ECB, which suffers a certain repetition detection flaw
In this bitmap example, this image has repeated white blocks, so you can deduce the outline of the image simply by looking for where the blocks become different.
If you are only encrypting one block, it doesn't really matter though, only if you are encrypting multiple blocks that are combined does ECB become revealing.
Related: https://security.stackexchange.com/questions/15740/what-are-the-variables-of-aes
Agree with #rossum, but there's more to it:
CTR mode needs an initialisation vector (IV). This is a "counter" (which is what "CTR" refers to). If you can store the IV separately (it doesn't need to be protected) that would work. You'll need the same IV value when you decrypt the data.
If you don't want to store the IV and you can guarantee that no two values will be encrypted with the same key, it's fine to use a fixed IV (even an array of 0s).
The above is very important because encrypting more than one message with the same key/IV combination destroys security. See the Initialization vector (IV) section in this Wikipedia article:
http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation
An AES CTR implementation of your code might be:
SecretKeySpec skeySpec = new SecretKeySpec(getCryptoKeyByteArray(length=16));
Cipher encryptor = Cipher.getInstance("AES/CTR/NoPadding");
// Initialisation vector:
byte[] iv = new byte[encryptor.getBlockSize()];
SecureRandom.getInstance("SHA1PRNG").nextBytes(iv); // If storing separately
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
encryptor.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec);
byte[] encrypted = encryptor.doFinal(plain);
CTR mode does not require padding: "AES/CTR/NoPadding".
I've read the following threads and they've helped a little, but I'm looking for a little more info.
How to write AES/CBC/PKCS5Padding encryption and decryption with Initialization Vector Parameter for BlackBerry
Java 256bit AES Encryption
Basically, what I am doing is writing a program that will encrypt a request to be sent over TCP/IP, and then decrypted by a server program. The encryption will need to be AES, and doing some research I found out I need to use CBC and PKCS5Padding. So basically I need a secret key and an IV as well.
The application I'm developing is for a phone, so I want to use the java security packages to keep the size down. I've got the design done, but unsure of the implementation of the IV and the shared key.
Here's some code:
// My user name
byte[] loginId = "login".getBytes();
byte[] preSharedKey128 = "ACME-1234AC".getBytes();
byte[] preSharedKey192 = "ACME-1234ACME-1234A".getBytes();
// 256 bit key
byte[] preSharedKey256 = "ACME-1234ACME-1234ACME-1234".getBytes();
byte[] preSharedKey = preSharedKey256;
// Initialization Vector
// Required for CBC
byte[] iv ={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
IvParameterSpec ips = new IvParameterSpec(iv);
byte[] encodedKey = new byte[loginId.length + preSharedKey.length];
System.arraycopy(loginId, 0, encodedKey, 0, loginId.length);
System.arraycopy(preSharedKey, 0, encodedKey, loginId.length, preSharedKey.length);
// The SecretKeySpec provides a mechanism for application-specific generation
// of cryptography keys for consumption by the Java Crypto classes.
// Create a key specification first, based on our key input.
SecretKey aesKey = new SecretKeySpec(encodedKey, "AES");
// Create a Cipher for encrypting the data using the key we created.
Cipher encryptCipher;
encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Initialize the Cipher with key and parameters
encryptCipher.init(Cipher.ENCRYPT_MODE, aesKey, ips);
// Our cleartext
String clearString = "33,8244000,9999,411,5012022517,0.00,0,1,V330";
byte[] cleartext = clearString.getBytes();
// Encrypt the cleartext
byte[] ciphertext = encryptCipher.doFinal(cleartext);
// Now decrypt back again...
// Decryption cipher
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Initialize PBE Cipher with key and parameters
decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ips);
// Decrypt the cleartext
byte[] deciphertext = decryptCipher.doFinal(ciphertext);
In a nutshell what it should do is encrypt some message that can decrypted by the server without the server needing to get a key or IV from the phone. Is there a way I could do this where I could secure the IV and key on the phone, and still have the key and IV known by the server as well? Feel free to tell me to make things more clear if they're not.
There are a few problems with the code. First of all, you really should use a key generator to generate secret keys. Just using some text directly might work for some algorithms, but others have weak keys and so forth that need to be tested.
Even if you want to do password-based encryption, the password should be run through a key-derivation algorithm to produce a key, as shown in my answer to the question that you cited already.
Also, you shouldn't use the no-arg getBytes() method of String. This is platform dependent. If all of the strings that you are encoding contain only characters from the US-ASCII character set, make it clear by specifying that encoding explicitly. Otherwise, if the phone and server platforms use different character encodings, the key and IV won't turn out the same.
For CBC mode, it's best to use a new IV for every message you send. Usually, CBC IVs are generated randomly. Other modes like CFB and OFB require unique IVs for every message. IVs are usually sent with along the ciphertext—IVs don't need to be kept secret, but many algorithms will break if a predictable IV is used.
The server doesn't need to get the secret or IV directly from the phone. You can configure the server with the secret key (or password, from which the secret key is derived), but in many applications, this would be a bad design.
For example, if the application is going to be deployed to the phones of multiple people, it isn't a good idea for them to use the same secret key. One malicious user can recover the key and break the system for everyone.
A better approach is to generate new secret keys at the phone, and use a key agreement algorithm to exchange the key with the server. Diffie-Hellman key agreement can be used for this, or the secret key can be encrypted with RSA and sent to the server.
Update:
Diffie-Hellman in "ephemeral-static" mode (and "static-static" mode too, though that's less desirable) is possible without an initial message from the server to the phone, as long as the server's public key is embedded in the application.
The server public key doesn't pose the same risk as embedding a common secret key in the phone. Since it is a public key, the threat would be an attacker getting his hands on (or remotely hacking into) the phone and replacing the real public key with a fake key that allows him to impersonate the server.
Static-static mode could be used, but it's actually more complicated and a little less secure. Every phone would need its own unique key pair, or you fall back into the secret key problem. At least there would be no need for the server to keep track of which phone has which key (assuming there is some authentication mechanism at the application level, like a password).
I don't know how fast phones are. On my desktop, generating an ephemeral key pair takes about 1/3 of a second. Generating Diffie-Hellman parameters is very slow; you'd definitely want to re-use the parameters from the server key.
Done similar projects in a midlet before, I have following advice for you:
There is no secure way to store shared secret on the phone. You can use it but this falls into a category called Security through Obscurity. It's like a "key under mat" kind of security.
Don't use 256-bit AES, which is not widely available. You might have to install another JCE. 128-bit AES or TripleDES are still considered secure. Considering #1, you shouldn't worry about this.
Encryption using a password (different for each user) is much more secure. But you shouldn't use password as the key like you are showing in the example. Please use PBEKeySpec (password-based encryption) to generate the keys.
If you are just worried about MITM (man-in-the-middle) attacks, use SSL.