I am trying to achieve a AES encryption in both Java and C++ programming. Basically the Java will send an encrypted message to a device that has a program written in C++ language.
In the Java side to pad the secret key that will encrypt and decrypt the message, I used the PSK5 padding style in ECB mode.
In the C++ side I am trying to achieve the same result.
I wrote some code and I wanted to ask if this is a good approach or if I should try someone else.
string input = "ciao!";
char pad[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
if(input.length() % 8 != 0)
{
int missingLength = 8 - (input.length() % 8 );
for (int i = 0; i<missingLength; i++)
{
input += pad[missingLength];
}
}
Use a full size key, 16, 24 or 32 bytes. If your key is to short or you are using a password use a key derivation function such as PBKDFG2 to create a full length secure key.
Use PKCS5 (PKCS7) to pad the data, it should just be an option so there should be no need to do it manually in your code.
Do not use ECB mode, it is not secure, see ECB mode, scroll down to the Penguin. Instead use CBC mode with a random IV, just prefix the encrypted data with the IV for use in decryption, it does not need to not secret.
Consider using a cross-platform/language solution such as RNCryptor, that can provide key derivation as well as authentication.
Secret keys aren't padded at all. If anything is padded it is the plaintext message (don't be fooled by the word plaintext, AES works on bits/bytes).
PKCS#5 padding, or - for a block cipher with a 128 bit block size - rather PKCS#7 padding always pads the plaintext. This makes it possible to encrypt any kind of plaintext message, even if it ends with bytes that could be interpreted as padding bytes.
It seems to me you should use a previously generated library such as OpenSSL of Crypto++ and not create your own.
Related
I'm trying to implement some encryption in my Java application.
The encryption should work as follows:
Generate random n bits AES key
Encrypt AES key using RSA public key
Send encrypted AES key to server
Encrypt data with AES key + send to server
I'm having trouble deciding what padding algorithm to use. I read OAEPWithSHA-1AndMGF1Padding is a good choice, but what is the maximum message length I can encrypt with this?
I need to know the exact maximum bytes my AES key can be to be encrypted using RSA with OAEPWithSHA-1AndMGF1Padding.
The length of the RSA key makes difference too. I will probably go for 2048 bits.
TL;DR: How many bytes can I encrypt using a 2048 bits RSA key with OAEPWithSHA-1AndMGF1Padding?
What you describe is known as Hybrid Encryption and is a common way to get the speed of symmetric-key crypto systems combined with the benefits of the two keys of RSA.
As for the specifics:
A 2048 bit RSA key allows for 256 bytes of which the OAEP padding takes 42 bytes, leaving around 214 bytes for encrypted data. An AES-256 key is 256 bits (32 bytes) long, so there is plenty of space for it.
I need to encrypt a string in Java and decrypt it in C++. I've seen C++ has a Crypto++ library and Java has JCE.
For c++, I refer to this page
http://social.msdn.microsoft.com/Forums/ko-KR/bd1722e7-07b3-4119-b7de-10788f9f6e36/aes-string-encryption-in-c?forum=windowsmobiledev
The result is defferent.
In java
abcd1234
7e77643ca7d46d46298be3239f1a5cdb
abcd1234
In c++
Strange characters...
What i have to do?
One of your problems is that your "key" is derived from a String which gives you different results for Java and C:
Java: "abcdefgh12345678".getBytes() gets you the UTF-8 (Linux) or ISO-8859-1 (Windows) representation. In any way the characters are interpreted as 8bit characters and you are getting a 16 byte long array.
C: You are using a WCHAR which uses AFAIK a 16bit unicode characters. Therefore your key is in the end 32 byte long.
Conclusion: Different keys means different results...
Important: Please never ever use ASCII characters as a cryptographic key! If you want to encrypt something using a password use a password derivation function like PBKDF2 for generating a cryptographic from the password.
For testing purposes you can statically define a byte/char array in your code:
char myArray[] = { 0x00, 0x11, 0x22 }; // c
byte[] myArray = new byte[]{ 0x00, 0x11, 0x22 }; // Java
apparently the java version is displayed in hex to string format,try this
for (int i = 0 ; i < 16 ; ++i)
std::cout << std::hex << static_cast<int>(buf[i]);
std::cout << std::endl;
edit : return byteArrayToHex(encrypted); in your java code does the same on the bytearray
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.
I am using Java to make a toy program that encrypts a message using DES encryption. The message I want to encrypt is:
String msg="This is a secret message";
Which I convert to bytes as:
byte [] msgBytes=msg.getBytes();
And send it to encrypt function that works as follows:
//encryption function
public static String encryptMsg(byte [] msgBytes, SecretKey myDesKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException
{
Cipher desCipher;
// Create the cipher
desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
desCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
byte[] textEncrypted = desCipher.doFinal(msgBytes);
// converts to base64 for easier display.
byte[] base64Cipher = Base64.encode(textEncrypted);
return new String(base64Cipher);
} //end encryptMsg
Then, I display the cipher, the cipher and plaintext lengths and I get:
Encrypted Message: FDCU+kgWz25urbQB5HbFtqm0HqWHGlGBHlwwEatFTiI=
Original msg length: 24
Encrypted msg length: 44
Can you please clarify to me why the cipher length is 44 while the original message length is 24?
EDIT:
Kindly, I need the answer with clarification. The cipher always ends with =. Could this be because of the padding? Can you explain to me why/how the cipher is resulted with this length? And always ends with =?
Is my code correct or there is a mistake? I have doubts in the encoding part.
There are several things going on:
msg.getBytes() returns the bytes representing an encoding of the string using the "platform's default charset" (e.g. could be UTF-8 or UTF-16 or ..): specify the encoding manually to avoid confusion! In any case, see msgBytes.length to get the true plain text length.
DES, being a block cypher, will have output padded along a block size boundary - but this will always be larger than the plain text (refer to msgBytes.length) length when using PKCS#5 because the plain text is always padded with [1,8] bytes. To see what the true encrypted size is, see textEncrypted.length.
The encrypted bytes are encoded using base-64 and this process - which is independent of the encryption - inflates the number of bytes required by about 33% (as only 6 bits per character/byte are used). The Java base-64 implementation also adds padding which is where the trailing "=" character is introduced.
As long as you (or someone else with the correct algorithm and cipher key) can retrieve the initial string - by performing the inverse of each step in reverse order, then it works. If a particular step does not have an inverse/reverse operation or cannot be "undone", then something is wrong; but this also means that every step can be individually tested.
To the numbers!
msg.getBytes() returns an ASCII/UTF-8 encoded sequence (if it used UTF-16 or another another "wide" encoding then the numbers below would be too large)
Therefore, msgBytes.length is 24
And since msgBytes.length mod 8 is 0, the plain text is padded with 8 bytes that have the value of 0x08 (per CKCS#5)
Thus, textEncrypted.length is 32 (24 data + 8 padding)
Due to base-64 encoding, 32 bytes * 1.33 ~ 43 characters
And with base-64 padding (=), the final result is 44 characters!
The result of a DES encryption will always be a multiple of 8 bytes. The input is also padded to a multiple of 8 bytes according to the padding algorithm specified.
The base 64 encoding encodes each 3 bytes into 4 characters (3x8 = 4x6 = 24), and ensures the output length is a multiple of 4 by padding with = characters.
So, the 44 characters output corresponds to 33 bytes, but the = at the end indicates that in fact there were only 32 bytes. Which is fine, since 24 bytes clear data with PKCS5 padding becomes 32 bytes.
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.