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.
Related
what if the input key is less than 16 bytes? i found a solution, but its not a best practice.
//hashing here act as padding because any input given, it will generate fixed 20 bytes long.
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
//trim the code to only 16 bytes.
key = Arrays.copyOf(key, 16);
I'm not planning to use salt because it is not necessary in my project. Is there any better way?
There are three approaches:
Pad the key out to 16 bytes. You can use any value(s) you want to as padding, just so long as you do it consistently.
Your scheme of using a SHA-1 hash is OK. It would be better if you could use all of the bits in the hash as the key, but 128 bits should be enough.
Tell the user that the key needs to be at least N characters. A key that is too short may be susceptible to a password guessing attack. (A 15 character key is probably too long to be guessed, but 8 characters is tractable.) In fact, you probably should do some other password quality checks.
My recommendation is to combine 1. or 2. with 3 ... and password quality checks.
I'm not convinced that seeding the hash will make much difference. (I am assuming that the bad guy would be able to inspect your file encryption app and work out how you turn passwords into keys.) Seeding means that the bad guy cannot pre-generate a set of candidate keys for common / weak passwords, but he still needs to try each of the generated keys in turn.
But the flip-side is that using a crypto hash doesn't help if the passwords you start with are weak.
Don't confuse keys and passwords. Keys are randomly generated and may consist of any possible byte value. Passwords on the other hand need to be typable by a human and usually rememberable. If the key is too short then either emit an error to the user or treat it as a password.
A key should then only be entered in encoded format such as hex or Base64. Only check the length when you successfully decode it.
A password has all kinds of issues that makes it brute forceable such as short length or low complexity. There you would need to use a password-based key derivation function such as PBKDF2 and a sufficiently large work factor (iterations) in order to make a single key derivation attempt so slow that an attacker would need much more time to check the whole input space.
You should combine that with some message to the user to give some hints that the password is too short or doesn't include some character classes and is therefore not recommended.
For FPE, I have passed plaintext as a 38D8DDD0D2 (10 digit) and tweak value as 18AD3A1387A9BCEB9BD223C44391CAB7 (32 digit) for encryption and decryption which are working, but not able to achieve FPE format.
But for FPE (Format Preserving Encryption), the output encryption value should be same format and length as like plaintext (10 digit).
Overall, If I give the input 10 digit string value, then the encryption value would be the same format with 10 digit length, and again after decryption - the same input string will be returned.
Please help me do that. Thanks
I think you are confusing FPE mode with other more common AES modes, e.g., the example code you shared is for ECB mode. Unfortunately using different AES modes in Java is not plug-and-play, each mode has to be used and handled slightly differently.
As for FPE, I don't think its even supported by the default Java JCE. See if you can use this implementation of it instead.
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.
Could you help me to point out what is the default RSA padding.
Precisely, if I create cipher instance as below, sure java is using some sort of padding as encrypted text bytes length always shows 256 bytes for 2048 RSA key irrespective of plain text is one characters or 10 characters.
Cipher.getInstance("RSA")
I wanted to know what is default padding java use internally if no padding is specified in Cipher.getInstance("RSA"). is that PKCS#1 v 1.5?
Thanks,
Sam
It's identical to "RSA/ECB/PKCS1Padding" where ECB is a bit of a misnomer, as it does not implement a block cipher mode of operation (it doesn't handle plaintext larger than the "block size"). "RSA/None/PKCS1Padding" would have been a better name or "RSA/None/RSASSA-PKCS1-v1_5" as your guess about the padding mechanism is correct.
This means that it uses a older mode of encryption; OAEP is more resistant against attacks and contains a security proof. Unfortunately OAEP can of course not be made the new default because all existing ciphertext would not decrypt anymore. This is one of the reasons why using defaults is stupid in the first place.
PKCS#1 v1.5 padding also means that the input is restricted to a maximum of the key size minus 11 bytes. Note that the size of the resulting ciphertext is always identical to the key size in PKCS#1; even if the resulting integer is smaller it will be left padded with zero bytes. I'm assuming here that the key size is a multiple of 8.
You should not rely on defaults for the algorithm specification. It makes the code harder to understand and defaults may indeed differ per provider (although most will try to follow Oracle's lead, to avoid incompatibilities). So use this only to understand which algorithm is configured in existing code. The only place where a platform default makes sense is SecureRandom in my opinion.
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.