Key wrapping with an ECDH public key? - java

Preface: I don't know whether it's more appropriate to ask this question here or on the Crypto site. Feel free to move or delete or whatever the appropriate SE action is.
I've been asked to help update some encryption software. Broadly speaking, the software already does the following steps, none of which are particularly unusual. I've left out the error handling and Provider arguments for simplicity of posting:
1) Generates a random symmetric secret key for use with AES-128 (or AES-256, mutatis mutandis):
KeyGenerator keygen = KeyGenerator.getInstance("AES");
keygen.init (128, a_SecureRandom_instance);
SecretKey sessionKey = keygen.generateKey();
2) Wraps the symmetric secret key, depending on whether the user is using...
2a) ...an RSA public key from a keypair:
// OAEP wasn't used in this software for hysterical raisins
Cipher wrapper = Cipher.getInstance("RSA/ECB/PKCS1Padding");
wrapper.init (Cipher.WRAP_MODE, user_RSA_PublicKey);
2b) ...a passphrase:
SecretKey stretched = ...passphrase stretched through a PBKDF like bcrypt...;
// I don't remember whether it's specified as "AES" or "AESWrap" here
Cipher wrapper = Cipher.getInstance("AES or AESWrap/ECB/NoPadding");
wrapper.init (Cipher.WRAP_MODE, stretched);
2c) Either route, the session key is wrapped:
byte[] wrapped = wrapper.wrap(sessionKey);
3) The session key is used to create a Cipher using Cipher.ENCRYPT_MODE along with a random IV, and then shedloads of data are run through it. That part is pretty standard but I can post it if you really want to see CipherInputStream usage. The wrapped session key is stored along with the encrypted data and a bunch of HMACs of everything under the sun.
Later at decryption time, the user provides either the RSA private key, or the passphrase for stretching; the software unwraps the symmetric key and decrypts the data.
All of that has been working for some time. But of course RSA keypairs are getting big and slow, so they'd like to support an additional possibility for step #2 above, in which the public keys are generated using an elliptic curve algorithm (P384 ECDH is the usual case). And this is where we're getting confused.
There doesn't seem to be a JCE replacement algorithm/transformation for elliptic curves in the Cipher wrapper = Cipher.getInstance("RSA/ECB/PKCS1Padding") invocation. The only one listed in the Java documentation is "ECIES", which seems(?) to be aimed more towards multiple party key agreement?
All of the APIs that I can find for the Java builtin JCE, or even looking over Bouncy Castle, only mention ECDH keys in the context of key agreement and transport, where they're used to generate a symmetric secret key instead of wrapping an existing one.
I feel we're missing something here, possibly because of poor assumptions. Is Cipher.wrap() genuinely not an option with ECDH keys? Or it is, but we need to do something funky in order to create the ECIES Cipher instance?

RSA is an algorithm (or depending on how you look at it, two very similar but distinct algorithms) for encryption and signature. RSA encryption can encrypt dat which is a (lower-level) key, in which case it is called wrapping.
DH is a key agreement algorithm, in both its classical (aka integer, Zp, modp, or finite-field/FF) form and its elliptic-curve form (ECDH). Note that at least for classic DH the raw agreement value g^a^b mod n = g^b^a mod n has enough mathematical structure people aren't comfortable using it directly as a key, so we run it through a key derivation function, abbreviated KDF. It's not clear to me that ECDH values really need KDF, at least for the commonly used X9/NIST curves including P384 (you might look or ask on crypto.SX if you care), but using a KDF is cheap and well-established so we do so.
I've never heard anyone competent call (EC)DH key transport, because it is specifically NOT. It is included in key exchange, a term created to cover the (useful) union of transport and agreement.
You can create an encryption scheme using (EC)DH by using the agreed (and derived) value as the key for symmetric encryption -- and that is (EC)IES. I don't know what made you think IES is something else. Modern practice is that the symmetric encryption should be authenticated, and the standardized forms of IES as a separate scheme use CBC encryption with HMAC authentication, although you could validly design a scheme that uses e.g. GCM instead.
As you saw, the BouncyCastle provider provides implementations of DH (classic) or EC IES "with{AES,DESEDE}-CBC" (in versions before 1.56 the -CBC was sometimes omitted) which use KDF2 from P1363a with SHA1, the indicated CBC cipher, and HMAC-SHA1. The 'standard' (Sun/Oracle/Open) providers do not, but you can combine the raw agreement operation, the KDF, plus symmetric encryption and MAC to produce the same result.
This is similar (though different in some details) to the operation of CMS-formerly-PKCS7 with classic DH and ECDH and PGP with ECDH. (PGP does not support classic DH; it used ElGamal encryption instead as the alternative to RSA.) Not to mention TLS (sometimes through 1.2, always in 1.3) and SSH which use either classic or EC DH 'exchange' (here interactive, and usually ephemeral-ephemeral instead of having at least the receiver static) to produce a secret which is derived and used as key material for symmetric encryption with authentication of the data.

Related

Encryption using AES 256 and SHA-2

I have a situation where I need to first encrypt a message using a public key and vector, that is already provided. Also as per requirement I need to use SHA-2 as well. For now, I am assuming that I need to hash the encrypted message and then send to the server. I have two questions related to this
1. Is it wise to hash the encrypted message? Also, will sending the encrypted message and hashed value to the server be a good idea?
2. I have done a lot search on internet, but whenever I try to get some example of using AES 256 and SHA-2 together, I actually land up where the difference between the two is explained. Can any help me with some sample code?
Thanks in Advance!!!
Let's break down the stuff first.
Public Key Cryptography
Allows a given pair (Kpriv, Kpub) to be used on a cipher to encrypt and decrypt data.
Any data encrypted with Kpriv can only be decrypted with Kpub and any data encrypted with Kpub can only be decrypted with Kpriv.
A nice and well known example of a public key cipher is RSA.
Asymmetric cryptography requires extremely large keys in order to be secure, such that it's extremely slow to execute! You should never encrypt large amount of data with Asymetric keys cryptography. You can use it in the beginning of a connecition to exchange a symetric key Ks, though.
Symetric Key Cryptography
Allows a Ks to be used on a cipher to encrypt and decrypt data.
An example of a symetric cipher is AES. AES is in fact so versatile you can change lots of parameters, such as, (as you mention) the Blocksize which can be of 128, 192 or 256 bits. AES256 is the AES cipher configured with a blocksize of 256 bits.
The block size is what's used against the provided Ks to perform the actual encryption. Note that your data can be larger than the block size (the algorithm will still work, It'l simply reuse the same Ks). Simply reusing the key every block is known as ECB mode and can reveal patterns if your data is repetitive. An alternative is to use modes like CBC or CTR which rely on also using previous block data and XORing with the next block data, to eliminate such patterns. What mode should you use depends on your data.
Note that, according to your cipher mode, you eventually will need padding. I'm assuming you are already quite familiar with this terms when you asked that question.
Guarantees By Cryptography
Cryptography does guarantee that the encrypted data is confidential but that's just it. It does not give any other guarantees such as whether the data is authentic or whether it has been tampered with or not.
While tampering data will most likely result in unintelligible text even after decryption, in cryptography, there's no such thing as invalid plaintext. As such, you need some mechanism to know if your data is valid or not.
A secure hash algorithm such as SHA can help you know whether your decrypted data is valid or not.
However, for these purposes, you usually shouldn't directly use a Digest algorithm. Try to instead use a MAC. That MAC can use SHA256 algorithm but MAC's and Hashes are not exactly the same.
How To Do It In Practice
If all you want is confidentiality and tampering detection, you would use the cipher and digest (or hash) algorithm as such:
E ks ( SHA(data) || data )
Where E is a symmetric cipher, ks is the shared symmetric key, SHA(data) is the digest of data using a secure hash algorithm, || means concatenation and data is a byte array.
A more safer approach would be:
E ks ( MAC mk(data) || data )
Where mk is the MAC's secret key.
Now just search how to "java symetric cipher" and "java hash byte array" and use the two as I'm describing above.

Generate a keypair from a password

I would like to use asymmetric encryption based with a private key based on a password. The requirement is the security level provided must be the same of (1) using password-based symmetric encryption, (2) using asymmetric encryption in a "regular" way.
I will have to use it in Java, but the answer can be generic.
I am aware that I can generate a keypair and encrypt the privateKey with a password-based symmetric key, however, in this way I will need a server (or other storage) to store this encrypted key. I would like to avoid that. If I could generate a keypair from a password, it is not needed.
Any suggestions?
If you base the private key solely on a password, it will only be as strong as the password, i.e. whoever can guess the password can get the private key.
This is comparable to generating a private/public key pair, encrypting the private key with a symmetric cipher and then publishing both together.
This of course makes the whole system weaker, since you no longer need to have the secret token - you only need to know the password.
Every time I see this question or a variant of it asked it's usually the result of bad design decisions. Almost always, the correct answer is to generate a random RSA keypair and protect the private key using standard password-based encryption like PBKDF2 or argon2. I've only seen one use case where this made at least a little sense and that was a cryptographic token back in the day with absolutely no nonvolatile storage. You won't find it around because there's no reason to build such a token, nonvolatile storage is not exotic in 2018.
In general you can do this: java's RSA key generation code accepts an instance of SecureRandom which the Oracle providers use to generate the candidate primes for RSA. You can subclass SecureRandom (I think) to provide a class that uses the password to seed a deterministic, repeatable sequence of random numbers such that every time you call KeyPairGenerator. generateKeyPair() the same keypair (including the private key) is generated. The bouncycastle library includes an example, FixedSecureRandom, that can be used as a model. Note that the deterministic RNG still has to be cryptographically secure, apart from the fact that it will not have enough entropy. FixedSecureRandom is not secure, it simply returns the pre-supplied bytes directly as output. Perhaps you can merge FixedSecureRandom with one of the other CSPRNGs in the org.bouncycastle.crypto.prng package.

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.

What is the key size for PBEWithMD5AndTripleDES?

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!

What's happening in the line of code?

What's happening in this line of code ?
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
I specially don't understand getInstance("PBKDF2WithHmacSHA1") part
This funky looking string defines the secret-key algorithm to be used. It is:
PBKDF2WithHmacSHA1
PBKDF2 With Hmac SHA1
the PBKDF2 function (from PKCS#5 version 2.0)
which will be using SHA-1 HMAC for its pseudo-random number generator
References:
We find similar algorithm names in Java Crypto Extension Guide Appending A, somehow PKCS5 version 2 may not have been available/documented then (or indeed as suggested by brianegge, may be a matter of export restriction, a common issue with cryptographic items).
The algorithm name does show up in RFC3962 (AES for Kerberos) which may not be the very application you have in mind, but defined, all the same)
Different distributions of Java contain different crypto. This is due to export restrictions and patents. The line of code is requesting a factory which can create that type of key.
Specifically, PBKDF2WithHmacSHA1 constructs secret keys using the Password-Based Key Derivation Function function found in PKCS5 v2.0.
"PBKDF2" is a function defined in PKCS #5 used to derive key material from a password.
PBKDF2 requires a pseudo-random function, and in this case, a message authentication code based on the SHA-1 hash is used—"HmacSHA1".
So, this line is creating a factory. The factory might produce SecretKey objects that can be used to key a Cipher instance for a symmetric encryption algorithm or a Mac algorithm. Or, it can be used to make a "transparent" specification of an existing SecretKey.
One important thing to note about PBKDF2 is that it doesn't produce secret keys for any particular algorithm. It's a deterministic way to generate key "material" from a seed (a password), in such a way that the seed cannot be recovered from the generated key. Once the required number of bytes are generated, they are usually wrapped in a SecretKeySpec with the correct algorithm name.
You can see other standard names for secret key factories in the Java Crypto Architecture Standard Names documentation.

Categories

Resources