I have two questions:
When to use SecretKeySpec and when to use KeyGenerator for key generation?
Is it necessary that the Key instance and the Cipher instance has to follow the same Algorithm? What is the concept behind?
Depends on your application
Yes. Different ciphers may use different key lengths. You pass AlgorithmParameterSpec to the key generator.
KeyGenerator creates a new random key each time it is called. The returned key is also guaranteed to have the length compatible with the algorithm specified when obtaining the KeyGenerator instance.
SecretKeySpec wraps an existing byte array. The source of bytes can be a hex or Base64 string or really any other textual or binary representation of the previously generated key. Hence the use case for SecretKeySpec is when you need to reuse the key which was previously stored in some external representation.
Usually an instance of SecretKeySpec can be used directly as a key because SecretKeySpec implements SecretKey and most crypto algorithms that use secret keys support so-called "raw" keys where the "raw" key is basically a random sequence of bytes without specific internal structure.
When an algorithm requires a specific SecretKey representation (usually a subclass of SecretKey internal to algorithm implementation) you will have to use SecretKeyFactory to transform a SecretKeySpec into a proper SecretKey.
If a crypto algorithm supports "raw" keys then the only requirement is that the key length matches the algorithm expectations.
But the implementer of crypto provider can decide that he needs to use a specific key representation which, for example, caches or precalculates some values based on the initial "raw" bytes. In that case the algorithm can require that a SecretKey instance passed into in addition to returning correct identifiers from Key.getAlgorithm() and Key.getFormat() methods is also a specific subclass of SecretKey, so the usage of SecretKeyFactory to convert key spec into a key is mandatory. Such cases are usually documented in the provider documentation.
Related
I see many examples where the secret key is generated this way:
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(KEY_LEN);
SecretKey secretKey = generator.generateKey();
I'm in doubt if there's a difference (even conceptual) with the following:
byte[] material = new byte[KEY_LEN / Byte.SIZE];
SecureRandom.getInstanceStrong().nextBytes(material);
SecretKey secretKey = new SecretKeySpec(material, "AES");
Stated that both methods are 3 lines, is there some practical impact in preferring the first over the second?
Thanks
You could look at the actual source code for generateKey() to see the difference but ultimately they are both going to do the same steps to generate an AES key. I would argue the latter
byte[] material = new byte[KEY_LEN / Byte.SIZE];
SecureRandom.getInstanceStrong().nextBytes(material);
SecretKey secretKey = new SecretKeySpec(material, "AES");
is a little more brittle for the average coder, requiring them to understand the SecureRandom class. If you eliminate the second line altogether the code runs just fine with an all zero key, an obvious vulnerability that's also easy for an attacker to check. Also, using generateKey() can produce a properly formatted key if the algorithm has some particular requirements. For example, the now obsolete DES and Triple DES algorithms had a weird parity bit in each byte that some DES implementations expected to see.
There are many reasons why you would want to use the KeyGenerator method, which was designed for the purpose:
readability: generateKey tells you exactly what the algorithm is doing;
portability: e.g. when choosing a different algorithm);
correctness: SecretKeySpec may not validate the key entirely;
security: you would leave the key material exposed in the material variable, which may not be cleared or even garbage collected after the key is not required anymore;
hardware support: importing key material is often not supported for hardware devices; keys should be generated on the device itself (using a specialized KeyFactory implementation).
There is no reason at all to use the second method. If you want to use a very specific random number generator for generating the key then you can use one of the specialized init methods but beware that this may not be compatible with hardware devices.
The two code examples seem to be doing the same and as already answered usually they are.
However using security devices (e. g. HSM, smartcards or other crypto devices) by default they won't allow exposing the key bytes so you would be able to generate a key from arbitritrary byte array only in very limited cases..
i've got the following problem. My application is divided in two different parts: 1) the first part encrypts some data using AES/CBC (Java), 2) the second part must retrieve the data and decrypt (Android).
To generate the secret key i use the following code
SecureRandom saltRand = new SecureRandom(new byte[] { 1, 2, 3, 4 });
byte[] salt = new byte[16];
saltRand.nextBytes(salt);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec("password".toCharArray(), salt, 1024, 128);
SecretKey key = factory.generateSecret(spec);
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(key.getEncoded());
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128, sr);
sksCrypt = new SecretKeySpec((kg.generateKey()).getEncoded(), "AES");
My program doesn't need differents "source key" (the string password), however it needs to compute the same secret key as long as source key is the same. unfortunately, the key generated by the two parts of the program are different and the decryption phase fails.
Any suggestion on how to solve this issue?
You are using a random key generator to generate a secret key from given input key material. Key derivation functions are functions that derive keys from secrets. There are password based key derivation functions such as PBKDF2 which use a password (plus a salt and a specific iteration count) as secret input. And there are key based key derivation functions such as HKDF that use a key and possibly a label or other output key specific information as input material.
Java does provide you with a set of PBKDF's, PBKDF1 and PBKDF2 where PBKDF2 is the newer one that you are currently using. Unfortunately it doesn't provide a KBKDF out of the box. You would need to use the (lightweight) Bouncy Castle API to do provide this kind of functionality. I know because I provided the initial implementation of the various KBKDF's for Bouncy.
Unfortunately using SecureRandom as replacement KBKDF doesn't work, as you've found out. The SHA1PRNG algorithm is not specified well; it is a function that depends on the SHA-1 secure hash function, but that's all. So implementations can and do differ, for instance between Android (which was loosely based on GNU classpath) and Oracle's Java. SHA1PRNG may or may not rely entirely on the seed. And in newer Android versions it may even be replaced by something entirely different.
As you only derive a single key from your input key material, you might as well wrap key.getEncoded() with SecretKeySpec directly and use that as a key. There is no need to perform an additional key generation at all; you already derived a key using PBKDF2. The additional wrapping doesn't do anything with the keying material. It may just be required to set the algorithm to, e.g., "AES".
I'm confused as to why I need to specify an algorithm such as "AES" when generating a key for encryption, e.g...
KeyGenerator kg = KeyGenerator.getInstance("AES");
It clearly is not used for specifying the size of the key since AES keys can be 128, 192, or 256-bits. That part would be done via init()...
kg.init(256, new SecureRandom());
SecretKey key = kg.generateKey();
For what it's worth, the above example code was borrowed from http://android-developers.blogspot.de/2013/02/using-cryptography-to-store-credentials.html
Furthermore, NIST FIPS-197 states...
No weak or semi-weak keys have been identified for the AES algorithm,
and there is no restriction on key selection.
...so that would lead me to believe that any 128, 192, or 256 bits could be used as a key.
Clearly, specifying "AES" when I get a cipher instance, e.g...
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
...is necessary to indicate the cipher algorithm to be use. I just don't get what the purpose of specifying it for the key generation does.
Thanks.
As mentioned in the comments, other keys than AES may require more attention. And it is best to have a symmetrical method for DES and AES so you can switch between the algorithms.
Furthermore, not all cryptographic providers may create keys in memory. The Java JCA is also compatible with hardware key stores. For PKCS#11 providers (for instance) it is required to know the type of the key when it is being generated.
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!