I'm currently working on a protocol, which uses Diffie-Hellman for an key exchange.
I receive a packet, which consists of an aes-128 encrypted part and a 128 Bit DH Public Key.
In a very last step in the protocol, the aes key is sent to another peer in the network.
This aes-key should be encrypted with a cipher using a 128 bit strong secretkey.
I plan to use Blowfish (can also be another cipher, doesn't really matter for the problem)
Now to encrypt the aes-key, with lets say blowfish, I have to build a secretkey for the encryption with a class called SecretKeySpec (I'm using javax.crypto stuff), which takes an byteArray in the constructor to build the secretKey.
The sharedkey from DH is a 128 Bit BigInteger.
Well, now I can interpret my shared-key as a byteArray (wich gives me still 128 Bit in 16Bytes [where the numbers are interpreted as frames of 8 Bit data])
So my question is, how strong is my key really?
Is there any impact because I only use numbers as input for the byteArray (so does this limit the keyspace in any way?)
I think this is not the case, but I'm not 100% sure.
Maybe someone can do the math and proof me right or wrong.
If I'm wrong what keysize for the shared key give me piece of mind to finally get to the 128Bit SecretKey for the encryption?
The Crypto++ website suggests using a minimum p of 3072 bits (or 256 bits for an ECC implementation) to transport a 128 bit AES key.
You might wish to study the references provided at http://www.keylength.com/en/compare/ for further information about comparing key lengths among different algorithms.
Not an expert in DH here, but to me it seems that DH's keyspace for the shared key represented in n bits is somewhat smaller than 2^n.
Related
Im using RSA to encrypt an AES key and some other information. To not be vulnerable to the padding-oracle-attack I instantiate my Cipher the following way:
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
Now in the Oracle documentation (I'm using oracle-java) there are key lengths given: https://docs.oracle.com/javase/7/docs/api/javax/crypto/Cipher.html
for RSA/ECB/OAEPWithSHA-256AndMGF1Padding it states 1024 and 2048 bits. Does that mean I cannot or should not use this cipher with a key length of 4096? Here's how I generated my key:
KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
keygen.initialize(4096, SecureRandom.getInstanceStrong());
KeyPair keyPair = keygen.genKeyPair();
Can I use this key with RSA/ECB/OAEPWithSHA-256AndMGF1Padding?
Yes, you can use it with that key size and most Java providers - including those delivered by default for most runtimes - will be able to handle it.
The two key sizes - of which 1024 should not be used anymore and 2048 is considered rather small - just need to be supported by the default providers delivered by any official Java runtime. However software implementations can generally handle key sizes up to 16Ki bits (that's 16 times 1024 or 16 kibibit, in case you're unfamiliar with the term).
RSA / OAEP has been specified for use with any key size. However, most implementations require that the bit size is a multiple of 8 (to simplify bit / byte conversions), some require 32 bit increments (to simplify implementation using 32 bit integers) and some even require you to use key sizes of the form n = 2^x or n = 2^x + 2^(x-1). Some kind of minimum is obviously required to support the padding scheme as well, so 512 is commonly a lower bound. Some implementations got overly-protective and disallow key sizes smaller than (and sometimes including) 1024 bit.
So as your key is of form 2^x (with x = 12 of course) it is perfectly possible to use it, and the key size is extremely unlikely not to be supported. The unlimited cryptography extensions are not needed for (up to date?) 1.7 versions either, so you don't need to worry about that.
Some hardware modules have severe restrictions on the key size. Smart cards for instance can barely manage 4096 bit keys if they can manage them at all.
For those kind of uses you may want to consider using Elliptic Curve Cryptography instead. In this case you'd require encryption so ECIES which relies on ECDH using Certicom secp256r1 also known as NIST P-256 would be a viable and more secure alternative (until quantum computing becomes of age, in which case you would be screwed faster than with the RSA alternative).
From this on how to achieve password based encryption it is clear that i need to save salt, IV and cipher text in order to decrypt it later.
From this iv and salt can be stored along with cipher text
I am storing the hex value in this format
DatatypeConverter.printHexBinary(salt) + DatatypeConverter.printHexBinary(iv) + DatatypeConverter.printHexBinary(ciphertext);
Do i need to store the values in Binary format ?
DatatypeConverter.printBase64Binary(salt) + DatatypeConverter.printBase64Binary(iv) + DatatypeConverter.printBase64Binary(ciphertext));
output clearly indicates the where the salt , iv is ending which is awful
lIvyAA/PZg4=fE4gTZUCPTrKQpUKo+Z1SA==4/gAdiOqyPOAzXR69i0wlC7YFn9/KOGitZqpOW2y3ms=
Will storing in hex format have any effects of data loss ?
Will the length of IV is constant ? in my case it is always 32 characters (hexadecimal)
Or i need to even store length of IV as well ? as the salt length is fixed initially to 8 bits (16 hexadecimal characters)
(I am using PBKDF2WithHmacSHA1 algorithm for key generation and AES/CBC/PKCS5Padding for cipher)
I think it is worth emphasizing again what the accepted answer above mentioned in passing.
That is, it is unnecessary and unwarranted to make any attempt to hide the salt or the IV. The security of your cryptography is entirely dependent on the secrecy of the secret key, and that of the secret key alone. The IV and the salt can be handed out in clear text along with the ciphertext, and as long as the secret key remains a secret, the ciphertext remains secure.
It's important to understand and accept that, or you will wind yourself about an axle trying to obfuscate things that don't matter. There is no security in obscurity.
It is important to note, however, that the salt should be generated in a cryptographically strong pseudorandom number generator. A new salt should be generated for each new plain text that is being encrypted. Likewise, the IV should be randomly generated for each new ciphertext.
Those parameters need to be independent and unpredictable but need not be secret.
So you can store them in separate fields or delimit them in a single field, or use fixed lengths for the first two of three fields. For maximum flexibility and future proofing, though, I suggest delimited fields, and include all parameters needed to deal with the data. If you are using PBE, I would include the algorithm name and the iteration count, too, rather than rely on default values.
Base64 encodes in chunks of 3 bytes into 4 base64 chars. If the number of bytes that needs to be encoded ain't a multiplum of 3 the last block is padded with one or two =, to indicate that this block ain't full 3 bytes.
As neither the salt nor the IV needs to be kept secret, there really ain't any problem about being able to detect where they start or stop. The base64 padding char = ain't a problem - but you ought to have a way to separate the three encoded strings. You could e.g. simply seperate the parts with a :.
The size of the IV is the same as the block size of your encryption algorithm. In this case you use AES that have a block size of 128 bits, which is 16 bytes. This would give 32 bytes if hex encoded, or 24 bytes if base64 encoded. Salt don't really have a fixed length, and will depend on your implementation.
Can anyone explain the PHP code and give me hints on how to port the code in Java?
Here is the PHP code:
function decode_string($encoded_string, $key) {
$decoded = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($key), base64_decode($encoded_string), MCRYPT_MODE_CBC, md5(md5($key))), "\0");
return $decoded;
}
OK, I'll bite, but I'll let you do the coding:
rtrim(x, "\0"): removes the braindead zero padding (0..15 bytes of zeros) that PHP employs, this to make the plaintext X times the block size, required for CBC. You'll have to program this yourself as it is not present in Bouncy Castle - so don't use any padding mode. Just remove the zero valued bytes at the right of the decrypted plaintext.
mcrypt_decrypt(MCRYPT_RIJNDAEL_256): probably somebody thought that this means AES-256, which it isn't. It is Rijndael with a block size of 256 bits. You need the Bouncy Castle libs in Java to decrypt that non-standardized part of the cipher
MD5($key) somebody needed 256 bits of key material and thought that the hex encoding of the MD5 value over a password was good enough. It isn't, as it only provides half of the entropy (2 hex chars per byte). That and the fact that MD5 is not a password hashing function makes this disingenuous at best
base64_decode($encoded_string): well, expect base 64 encoding, which is alright if the ciphertext needed to be present as ASCII compatible text
MCRYPT_MODE_CBC: that's OK, but as PHP is mainly used as a web language, I expect the message to be vulnerable to padding oracle / plain text oracle attacks, and you should of course expect any alteration of the ciphertext to be undetectable
md5(md5($key)): applying MD5 twice does not make this any more safe than a zero IV and don't forget the hexadecimal conversion performed by each of these functions; fortunately that does mean that the IV is at least 256 bits instead of 128 bits
So you need to use:
new BufferedBlockCipher(new RijndaelEngine(256))
in the lightweight API of Bouncy Castle.
Happy coding, you're good in Java, so this should be a breeze. Upgrade away from this utter crap ASAP.
I'm dealing with a legacy application that uses a custom protocol to cipher communication. Random AES keys are generated in legacy Java app like this:
keygen = KeyGenerator.getInstance("AES");
keygen.init(128);
keygen.generateKey().getEncoded();
I've been looking for solutions on crypto with no luck. How can I generate this key on nodejs?
That code probably does not do as much as you think. It simply generates 16 (128 / 8) secure random bytes, then wraps a key object around it. So with nodejs, you simply generate 16 bytes and feed the algorithm the raw key data.
If you want to use the generated key, then make sure you create a binary encoded string or buffer from the bytes returned by the getEncoded() method. You could use hexadecimal encoding/decoding if you require the key to be a textual string somewhere in the process.
See randomBytes() and createCipheriv() for information.
AES keys are just cryptographically strong random bytes, DES (parity bits) and RSA (prime number calculation) keys are not.
I want that encrypted data have the same length as my original text, because I must respect the constraint of length. I am using BouncyCastle.
Here's my code to generate the keys pair:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(512);
KeyPair keypair = keyGen.genKeyPair();
PublicKey pub = keypair.getPublic();
byte[] pubs = pub.getEncoded();
And here's my code to encrypt:
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
PublicKey pk = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(pubs));
cipher.init(Cipher.ENCRYPT_MODE, pk);
byte[] cipherBytes = cipher.doFinal(plainArray);
The encoded data is very large, what can I do to make as small as original data ?
Nothing, at least not regarding the RSA. RSA requires a certain amount of padding to be secure, and as the data will be seen as random data by any compression method, you cannot compress it either.
Of course, you should not directly encrypt data directly using RSA, you should encrypt a random session/data key instead. But even then, you will have the encrypted session key as overhead.
You can remove some bits for the asymmetric encrypted data/key by using Elliptic Curve Cryptography (the output of which is double the key size minimum, but key sizes are much smaller to achieve the same level of security). EC cryptography is not for the meek though, it has lots of complexity.
512 bit RSA is considered insecure by the way. Use 1024 as minimum or keep to the ECRYPT II or NIST recommendations listed at http://www.keylength.com/ .
If you can keep the key secret, you can use a symmetric cryptosystem like AES. If used in CFB mode, it can be adapted to any bit length. I.e. #bits input & output is identical.
RSA and ElGamal -- the main public-key cryptosystems-- can be broken in a matter of hours even at 512 bits as you are selecting. 1024 - 4096 bits is normal. Less than 512 bits is worthless unless your opponent is limited to using 1990s hardware. :-)
The nature of RSA is such that you use exponentiation in encryption and decryption.
Basically, for public key e and private key d, you take a message m and then you get a compressed message equal to c=m^e and mod it by n. Then you decrypt with m=c^d and again mod it by n (mod means take the remainder).
If you think about it, a message to the power of something results in a larger number. Because you do a mod n, you end up with a number that is at most n-1. n is the key length.
So basically, whatever message you take, you encrypt to something as large as n. The message has to be less than n.
But the message has to be turned into an integer using a padding scheme (so you can do the exponentiation). This padding scheme may require less than n bits. And so, you end up with larger files than the size of the data being encrypted. Also, the last chunk may be less than n bits, but will encrypt into size n.