KeySpec throws "Salt not found" - Salt isn't a requirement - java

I am currently working on a small bit of code, which is supposed to encrypt a byte[] before it gets stored inside a file. In the following snippet, I am trying to generate a SecretKey from a Password using PBKDF2.
public SecretKey deriveKey(String password)
throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory kf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec specs = new PBEKeySpec(password.toCharArray());
SecretKey key = kf.generateSecret(specs);
return key;
}
This throws an exception
java.security.spec.InvalidKeySpecException: Salt not found
at the last line before the return statement. This seems quite confusing to me as the PBEKeySpec does explicitly not require any more parameters as seen here.
This is not a tragedy, I was to add salt later anyway and it's even already defined further above but I was curious whether anybody knows why this happens. I might be doing something terribly wrong and just didn't notice.

It is not about whether PBEKeySpec class needs a salt or not. The algorithm PBKDF2
needs a salt. The API is a generic api which should cater to multiple algorithms, some of which might be defined in future.

Related

Java AES Encryption, Python Decryption not working

I have tried multiple ways to do this, for three days now and many hours. I have gotten NO WHERE. I am using Java to encrypt certain data using AES/CBC/PKCS7Padding, and trying to decrypt using the same in Python but it just won't work. I am using this Python aes.py library http://anh.cs.luc.edu/331/code/aes.py
I am getting this error:
File "/root/ascend/aes.py", line 384, in decrypt
block[(i+(j*4))] = iput[(i*4)+j]
exceptions.IndexError: list index out of range
Here is the Java Aes code:
public String aesEncrypt(String key, String data) throws InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
SecretKey secKey = new SecretKeySpec(key.getBytes(), "AES");
KeyGenerator KeyGen = KeyGenerator.getInstance("AES");
KeyGen.init(256);
Cipher AesCipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
AesCipher.init(Cipher.ENCRYPT_MODE, secKey, new IvParameterSpec(IV.getBytes("UTF-8")));
byte[] byteCipherText = AesCipher.doFinal(data.getBytes());
return Base64.encodeToString(byteCipherText, 0).trim();
}
Here is the Java key gen which provides the key for python to use as well:
public String genAESKey() {
String uuid = UUID.randomUUID().toString();
return uuid.replace("-","");
}
And here is the python code to decrypt:
self.data = aes.decryptData(user.aes_key, base64.b64decode(self.data))
#Where user.aes_key is the 256bit Aes key generated by java.
Can someone please take a look and explain what is wrong with this? They are both using the same aes 256 key, pkcs7 padding, and CBC. If anyone knows of a better library that works with such java code please do show.
Edit: Just to clarify things, Aes decryption works in Java just not in python and Python encryption using that aes key works and so does python decryption. Just not java -> python. And self.data is the java encrypted aes data.
Edit #2:
Just tried to do this with PyCrypto as well. Same exact error is occuring.
return self._cipher.decrypt(ciphertext)
exceptions.ValueError: Input strings must be a multiple of 16 in length
The problem was that for some reason Java was padding the first byte of the resulting String. Why? I don't have the slightest clue but after stripping it off in Python all was well. Both the PyCrypto code and the Aes.py code work fine.

AES 256 decryption - Is IV safe to share?

Following on this question and its answer, I am creating an application that given a password string, will convert a plaintext and store its ciphertext, the salt generated and the initialization vector in a text file.
In the following code :
public String decrypt(CryptGroup cp) throws Exception {
String plaintext = null;
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password, cp.getSalt(), ITERATIONS, KEY_SIZE);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(cp.getIv()));
plaintext = new String(cipher.doFinal(cp.getCipher()), "UTF-8");
return plaintext;
}
public CryptGroup encrypt(String plainText) throws Exception {
byte[] salt = generateSalt();
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_SIZE);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal(plainText.getBytes("UTF-8"));
return new CryptGroup(ciphertext, salt, iv);
}
The CryptGroup object contains those 3 parameters (ciphertext, salt, iv : byte arrays for that matter).
Is it safe to store the initialization vector?
The answer in that question clearly states that the salt doesn't need to be secret, obviously the ciphertext can be also available, but what about the iv parameter?
Edit
If it is not safe to share, is it possible to retrieve the original iv from the salt alone?
Yes, IV's can be public information. You can use a calculation as IV, as long as you never use the combination of key and IV twice. In other words, you should be able to share just the salt, as long as you change the salt for each encryption.
Furthermore, for CBC it is required that IV "looks like random" to an attacker, especially when used for the same key. So a usual scheme is to use some output of PBKDF2 as IV data. Those particular bits should of course not be used to create the key as well, but it is OK to split the output size.
This has some drawbacks as PBKDF2 will use many more rounds if you request more than 160 bits of information (for SHA1). So you may concatenate the output of PBKDF2 and a counter (0 for the key, 1 for the IV) and use e.g. SHA256 to generate a 128 bit key (the 16 leftmost bytes) and 128 bit IV (the 16 rightmost bytes).
Let's explore some variants to this scheme:
You could also use a different hash such as SHA-512 to create a larger output and split it to get a key and IV, but beware that such a function may not be available everywhere. Java 8 should have "PBKDF2WithHmacSHA512" though (for the SecretKeyFactory).
You can also generate one PBKDF2 output and then use HKDF or HKDF-Expand to derive both a key and IV. Trouble is that HKDF / HKDF-Expand is not directly available from Java. Bouncy Castle does have that method though, because I send in an implementation of various KDF's.
Yet another way is to generate a new IV using SecureRandom, but in that case you need to store both the salt and IV. This might be useful if you need to encrypt multiple messages using the same key. In that case you can generate and store an IV for each separate message. This is a fine method if you are able to simply store 16 additional bytes.
In principle you could also use an all zero IV as long as you never reuse the same key (i.e. never reuse the same password/salt combination). However, in that case you might want to use AES-256 to avoid multi-target attacks.

Explanation to understand AES encryption code

I am creating a project to encrypt and decrypt a file. I have these two algorithms that work fine:
public static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
public static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
public static byte[] getRaw(String password_) throws Exception {
byte[] keyStart = password_.getBytes();
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr.setSeed(keyStart);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
byte[] key = skey.getEncoded();
return key;
}
Now I need to explain how it works. Does it use a private key? Where is the key storage? Can anyone help me?
Note: see owlstead's answer for an excellent description of the flaws in your code example
Your encrypt() and decrypt() operations are performing AES encryption and decryption respectively, using Java's JCE libraries. A JCE provider will be selected to perform the actual cryptography - the provider chosen will be the first in the list of providers that offers an implementation of AES. You have defined the algorithm as only "AES", so the mode of operation and padding will be chosen by the provider. If you want to control this, use the form "AES/mode/padding" (see the docs for valid choices)
The getRaw method derives an AES key from a password. The raw bytes of the password provide the seed for a random number generator. The random number generator is then used to generate sufficient key material for a 128-bit AES key. A different password will produce a different seed, which should produce a different stream of random bytes and thus a different key. I suspect this approach is weakened by the lack of entropy present in most people's passwords, leading to a reduced key space and easier attacks.
There is no key storage in your example code. JCE keys are normally persisted using a KeyStore object and the storage mechanism is provider-dependent.
The above piece of code is a bunch of crap. Unfortunately it is frequently used as a code snippet for Android related code (Android code uses the same API as Java, so there is no need for an Android specific example, andt unfortunately it specifically fails on Android).
I'll explain the issues:
Using a SecureRandom as Password Based Key Derivation Function (PBKDF) is completely idiotic. The underlying implementation of the SecureRandom implementation may change. Furthermore, it is not specified by the SecureRandom that calling setSeed() as the first method will replace the seed; it may actually add the seed to the current state - and this is what certain newer android versions do.
Cipher.getInstance("AES") actually uses the provider defaults instead of specifying the mode of operation and padding mode for the given cipher. By default the Sun provider will use ECB mode which is not suitable for encrypting most data.
String.getBytes() - which is used for the password - returns the platform default encoding. Different platforms may have different default encodings. This means that different platforms will generate different keys.
Above code does not add a message authentication code (MAC or HMAC). This may lead to an attacker changing random ciphertext blocks, which leads to random plain text blocks. This may lead to loss of confidentiality as well if padding Oracle attacks apply.
It seems to me that you are a beginner in cryptography. Please use a higher level standard such as RNCryptor compatible code, or use a standard such as Cryptographic Message Syntax (CMS).

User forgot password alternative approach in spring

I wanted to allow having a security question for users. I am using Spring3 and was not sure if there exists a standard way of doing this. Can anyone point me to such standard it at all it exists?
I'm not familiar with a standard that covers this, but I would recommend that you treat the security questions just like you would the password...
Make sure that you turn autocomplete="off" so that the browser won't remember their answer
Generate a salted hash for the answer (or you could reuse the one for their password)
public byte[] generateSalt() throws NoSuchAlgorithmException {
// VERY important to use SecureRandom instead of just Random
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
// Generate a 8 byte (64 bit) salt as recommended by RSA PKCS5
byte[] salt = new byte[8];
secureRandom.nextBytes(salt);
return salt;
}
Encrypt the answer
public static byte[] getEncryptedAnswer(String answer, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
KeySpec spec = new PBEKeySpec(answer.toCharArray(), salt, 20000, 160);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
return secretKeyFactory.generateSecret(spec).getEncoded();
}
Store both the salted hash and the encrypted answer. At this point you're pretty much using the answer to this question just like a password.
Now with all that said, I have to agree with jHilscher... Too many of these security questions are predictable or easy to guess. Favorite color, year they graduated, etc... I never answer these questions with the correct information. I recommend that you find a different way to reset the password when needed.

Java RSA Encryption Non-Repeatable?

I've been having trouble encrypting with an RSA public key. Here is a sample JUnit code that reproduces the problem:
public class CryptoTests {
private static KeyPair keys;
#BeforeClass
public static void init() throws NoSuchAlgorithmException{
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
SecureRandom random = CryptoUtils.getSecureRandom();
keyGen.initialize(2176, random);
keys = keyGen.generateKeyPair();
}
#Test
public void testRepeatabilityPlainRSAPublic() throws EdrmCryptoException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
byte[] plaintext = new byte [10];
Random r = new Random();
r.nextBytes(plaintext);
Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, keys.getPublic());
byte[] encrypted1 = rsa.doFinal(plaintext);
rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, keys.getPublic());
byte[] encrypted2 = rsa.doFinal(plaintext);
rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, keys.getPublic());
byte[] encrypted3 = rsa.doFinal(plaintext);
assertArrayEquals(encrypted1, encrypted2);
assertArrayEquals(encrypted1, encrypted3);
}
}
The result? The assertion fails.
Why is this behaviour seen here? As far as I remember from my crypto classes, any key can be used for encryption. Yet this is not what happens here.
I've tested the same thing with the private key, and I get a repeatable output.
If, for some reason, RSA encryption with a public key is forbidden, then why am I not getting an exception?
What must I do to get repeatable results?
P.S. My JDK is 1.6.0_22 running on an Ubuntu 10.10 box.
My guess is that it's applying randomized padding, precisely to make it more secure. From the RSA wikipedia page:
Because RSA encryption is a deterministic encryption algorithm – i.e., has no random component – an attacker can successfully launch a chosen plaintext attack against the cryptosystem, by encrypting likely plaintexts under the public key and test if they are equal to the ciphertext. A cryptosystem is called semantically secure if an attacker cannot distinguish two encryptions from each other even if the attacker knows (or has chosen) the corresponding plaintexts. As described above, RSA without padding is not semantically secure.
...
To avoid these problems, practical RSA implementations typically embed some form of structured, randomized padding into the value m before encrypting it. This padding ensures that m does not fall into the range of insecure plaintexts, and that a given message, once padded, will encrypt to one of a large number of different possible ciphertexts.
You can confirm that what is happening is that random padding is being added by initialising your Cipher with the string "RSA/ECB/NoPadding". Now, you should see that the ciphertext is identical in each case (though for reasons stated by another answerer, you shouldn't really do this in practice).
To add extra detail to Jon's answer:
When you do Cipher.getInstance("...") you have a number of options, as you've probably gathered. The Standard Algorithm Names specify what these are.
The one you asked for, RSA is by default RSA under PKCS1, which, to quote the wikipedia article:
There are two schemes for encryption
and decryption:
RSAES-OAEP: improved encryption/decryption scheme; based on
the Optimal Asymmetric Encryption
Padding scheme proposed by Mihir
Bellare and Phillip Rogaway.
RSAES-PKCS1-v1_5: older encryption/decryption scheme as first
standardized in version 1.5 of PKCS#1.
See RSALab's PKCS1 documentation for the detail of said padding schemes.

Categories

Resources