How does PHP handle 32 byte keys for tripledes encryption - java

Apologies all - newbie at encryption - been googling for days and finally asking outright.
I need to use PHP to encrypt and decrypt data that is readable by a Java TripleDES "DESede/ECB/NoPadding" function.
In Java there is a double-length 32 character key e.g. "F4D5CBDF57FEEDCFA41FD6AFE7BCDFEA" which gets converted to bytes and which provides an encrypted result without any problems. (I don't have the code.)
In PHP, when the same key is attempted via mcrypt for a tripledes, ecb function call, there is a key-length error because the system expects a max of 24 characters.
What do I need to do to the key so that PHP will produce the same encrypted result as Java?

As NullUserException postulated: please convert the key from hexadecimals to binary before using it for your triple DES cipher. Your Java code must do the same thing; in Java a triple DES key must have either 24 bytes or 16 bytes (16 bytes is only supported for later versions of Java, previously you had to convert to 24 bytes by copying the first 8 bytes to the end to create an "ABA" DES key).
32 byte keys are never supported for triple DES. If you are using the horrible mcrypt libraries for PHP however, the key gets cut to the highest key size available. So instead of a fail-fast situation, PHP rather would have their users pull their hair out in frustration.

Related

Why keys generated from a Java DES KeyGenerator have incorrect size?

I'm using Java's SunJCE provider to generate a 7 bit key:
KeyGenerator v = KeyGenerator.getInstance("DES")
Provider p = v.getProvider
assert(p.getClass().getSimpleName() == "SunJCE")
v.init(56)
Key k = v.generateKey()
assert(k.getEncoded().getLength == 7)
When I run the above program, I got the error that indicated that the length of k is actually 8 (64 bit) instead of 56 bit, the strange thing is that the KeyGenerator is initialized to generate only 56 bit key, so why the actual length of k is incorrect?
DES keys are encoded using 8 bits for each 7 bits, where the least significant bit of each byte is used to make the number of bits odd. So if the first 7 bits have 6, 4 or 2 bits set to one then the least significant one is set (to one). It is reset / unset / left at zero otherwise. So a 56 bit DES key is encoded as 64 bits / 8 bytes, a 112 bit 2 key used for triple DES is encoded as 128 bits and 168 bits DES keys are encoded using 192 bits.
The parity bits can be used as some kind of check to see if the DES key hasn't been altered (although it isn't very good for that either). Most DES implementations will nowadays simply ignore the parity bits altogether, but the Java KeyGenerator will still correctly set them. You can test this by validating Integer.bitCount(b & 0xFF) % 2 == 1 for each byte b in the resulting key: it should always return true.
More modern symmetric ciphers try to use fully (pseudo-)random keys; 256 bit AES or HMAC keys consist simply of randomized bytes.
This isn't true for most asymmetric ciphers; encoding the public or private key of most asymmetric ciphers will result in significantly more bits than the key size. The key size for asymmetric ciphers is usually the size of the parameter that determines the strength of the key, e.g. the size of the modulus for RSA.
Notes:
DES only has a key size (and strength) of 56 bits, and is considered completely broken: use a key of 128 bits or more (which also rules out two-key 112 bit triple DES keys, if you were paying attention) and a modern cipher such as AES.
Your assertions test things that should always be true and make little sense to me. If anything should be tested it is the random number generator that is used to generate the keys (and those are notoriously hard to test, unfortunately).
When testing for the provider name - a dangerous and non-portable practice if you ask me - then you should at least use Provider#getName() (and possibly the other getters that return useful info about the provider) rather than the class name. The class name is an implementation detail and could actually change - even if the provider name doesn't.

Java - Converting string into DES key

I have been given a key as a string and an encrypted file using DES. That is all I know. I don't know how the key was encoded.
There is also a des.exe that I can use to decrypt, this is all I found on the Internet: http://knowledge-republic.com/CRM/2011/07/how-to-decrypt-extract-recreate-thecus-storage-firmware/
Using des.exe, the only command it works with is "-D", not "-d".
My goal is to use Java to do the same thing. I copied and pasted this from somewhere
String key = "blah";
DESKeySpec dks = new DESKeySpec(key.getBytes());
SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
SecretKey desKey = skf.generateSecret(dks);
System.out.println(desKey);
Cipher cipher = Cipher.getInstance("DES"); // DES/ECB/PKCS5Padding for SunJCE
if (mode == Cipher.DECRYPT_MODE) {
cipher.init(Cipher.DECRYPT_MODE, desKey);
CipherOutputStream cos = new CipherOutputStream(os, cipher);
doCopy(is, cos);
}
and it doesn't work.
What are some other options in converting a string into a key?
Should probably add I'm a complete newb at cryptography.
The SunOS man page for des (which seems to be what your des.exe is based on?) indicates that they key is generated like this:
The DES algorithm requires an 8 byte key whose low order bits are assumed to be odd-parity bits. The ASCII key supplied by the user is zero padded to 8 bytes and the high order bits are set to be odd-parity bits. The DES algorithm then ignores the low bit of each ASCII character, but that bit's information has been preserved in the high bit due to the parity.
It also mentions that the initial IV is always zero'd out, no matter what mode you are running in
The CBC mode of operation always uses an initial value of all zeros
for the initialization vector, so the first 8 bytes of a file are
encrypted the same whether in CBC or ECB mode.
It also mentions that the padding used is such that the last byte is always a value from 0-7, indicating the number of padding bytes used. This is similar to PKCS5Padding, so perhaps that would work
Since the CBC and ECB modes of DES require units of 8 bytes to be
encrypted, files being encrypted by the des command have 1 to 8 bytes
appended to them to cause them to be a multiple of 8 bytes. The last
byte, when decrypted, gives the number of bytes (0 to 7) which are to
be saved of the last 8 bytes. The other bytes of those appended to the
input are randomized before encryption.
Based on the options you indicated you are using, it sounds like you are using DES/CBC/PKCS5Padding for the cipher.
I think that just leaves determining how to actually derive the key. I found this sample code on exampledepot which might work for you. I think you would just need to convert your string password into 8 bytes (1 byte per character, so no UTF encodings) then stuff it through the code in the example to derive the key. Its worth a shot anyway.
DES keys are 7 (apparently SunJCE uses 7?) or 8 bytes. Check if the string you have been provided is 7 or 8 bytes. If so, then the chances are good it's the raw key. If not, it could be encoded in some fashion. A giveaway for hex encoding would be a prefix of 0x or suffix of h, and all characters would be in the range 0-9,A-F. You can certainly convert from hex yourself or use some code on the web, but I usually use an Apache commons lib (http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Hex.html).
That said, this is really speculation and I'm not sure we can jump to the conclusion that it's a problem with the key alone. Do you have any other info on the purported encryption algorithm? If the executable you cited works with "-d" then it seems like the encryption is plain DES in CBC mode:
-b : encrypt using DES in ecb encryption mode, the defaut is cbc mode.
(there are multiple possible modes, see http://download.oracle.com/javase/1.4.2/docs/guide/security/jce/JCERefGuide.html#AppA)
I would try setting your cipher to "DES/CBC".
Then again, I'm not sure how to interpret this:
Default is tripple cbc
You may be able to use this snippet to tell what ciphers are available on your system: http://www.java2s.com/Code/Java/Security/ListAllProviderAndItsAlgorithms.htm
I had the same issue with C#. I solved it in the end. You can have a look at my answer here: DES Initialization Vector in C#
Generally, what des.exe does, is that it computes a checksum using DES. So each encryption step is using the previous result instead of advancing in the output array.

Encrypt an Integer Value with DES

I want to encrypt an integer with DES, the resultant cipher text should also be an integer.
Decryption function should follow this notion as well.
I am trying to modifying the code at Encrypting a String with DES, by converting the byte array to integer, instead of using Base64 encoding. However the decryption function throws an exception of improper padding, as the conversion of integer to byte[] results in a 4 byte array.
Is there any other encryption algorithm that I can use to achieve this.
I am not concerned about the weakness of the cipher text.
If you are running an Integer value through DES to produce another Integer value, and you don't care about the cipher text weakness, then you are merely doing a very expensive hashing operation. You'd be better off generating a random integer as the key and bitwise xor-ing the exponent and the random integer. This would take nanoseconds to compute and have exactly the same security.
DES has a 64 bit blocksize, so in general the output from the encryption of a 32 bit int will be a 64 bit block. It will be easier to encrypt a 64 bit long to another 64 bit long. Use ECB mode so that padding is not an issue, or at least you are adding zero bits to the front of your int to extend it to 64 bits.
If you merely want to smush up your int then Jim's suggestion is excellent.
You want to look at Format Perserving Encryption. There are a couple of techniques for it, but in general all of them will generate a value in the same domain as your input ( i.e. integers, credit card numbers,etc)

XOR Encryption in Java: losing data after decryption

I'm currently writing a very small Java program to implement a one-time-pad, where the pad (or key) itself is generated as a series of bytes using a SecureRandom object, which is seeded using a simple string with the SHA-512 algorithm.
Generating the one-time-pad hasn't caused any problems, and if I supply the same seed string each time, as expected I get the same sequence of psuedo-random numbers, making the decryption process possible as long as the person decrypting has the seed string used to encrypt.
When I try to encrypt a file, the program reads in the data 64 chars at a time (except for the end of file, which is generally an odd number), and generates 64 bytes (or matching amount) of psuedo random bytes. XOR is performed between the elements of both arrays, the resulting char array containing the cipher characters is written to file, and the process repeats until all text in the file has been read.
Now, because Java treats all primitives as signed numbers (the data type byte ranges from -128 to 127, not 0 to 255) this means that the XOR operation can (and does) result in some negative values (-128 to -1). It seems that Java does not recognise these values as valid ASCII, and simply writes a ? (question mark) to the file for any negative values. When it comes to reading from the file to decrypt the cipher text, the negative value that resulted in the ? to be written to file is lost, replaced with 63, the valid ASCII code for a question mark.
This means that XORing this value is useless, without the original value there is no way to produce the plaintext. Incidentally, if I reproduce the behaviour of encrypting some data and then decrypting the data immediately after, in the same program run, and printing status along the way, there are no problems. Only if the data is written to file is the information lost.
I should also mention that I did try adding 128 to each encryption XOR result, and then subtracting it before performing the decryption XOR (to put each value in a valid ASCII range), but the ? problem still showed up because there are 31 ASCII codes from 128 to 159 that I'm unable to read and appear as ?
I've been banging my head off the wall on this for a while now, any help is appreciated.
Cheers.
This is very confused. If you are processing a char array, the elements are 16 bits wide, they are unsigned, and not all values are valid. So (a) you cant possibly be having a problem with signs or bytes, and (b) you shouldn't be doing that at all. You should be reading the file into a byte array, XOR-ing, and writing out the byte array directly to the output file. No Readers or Writers, no chars, no Strings.
I guess the problem is in the way you write the file. Write directly the converted byte array to a FileOutputStream and do not try to convert it to string first. For reading, do the same thing, read it to a byte array.

Convert base64 encoded string to java byte array [duplicate]

This question already has answers here:
Closed 13 years ago.
I am writing a decryption class (AES/CBC/PKCS7Padding) where the encrypted data is coming from C#. I want to take the following string (which is base64 encoded):
usiTyri3/gPJJ0F6Kj9qYL0w/zXiUAEcslUH6/zVIjs=
and convert it to a byte array in java to pass into the SecretKeySpec as the key. I know there is the issue of C# having unsigned bytes and java only having signed bytes. How then can I pass this sting which has values greater than 127 within it and have java accept the key and initialization vectors?
You don't have to worry about byte signedness because base64 encoded data never uses more than 6 bits in each byte (that's why it's called base 64, because you only use 64 characters which is 6 bits, to represent part of a data byte).
If your concern is the resulting data (3 data bytes for every 4 base64 characters), don't worry about that, either. An unsigned byte 255 in C# is the same as the signed byte -1 in Java.
To encode data, you can bitwise-and each byte with 0xff and store it in an int, then encode the least significant 8 bits. Or just bitwise-or each byte with 0x80 and store it in ant int and decode the least significant 8 bits.
But I think you would be better off using Bouncy Castle or the standard JCE to deal with all that stuff. The 'S' in PKCS7 means Standard so data encrypted in C# should decrypt fine in Java and vice versa.

Categories