Byte array to string returns null - java

I'm writing a program to encrypt data.
I created a random IV this way:
byte[] ivBytes = new byte[16];
IvParameterSpec iv = new IvParameterSpec(ivBytes);
And used it for the cipher.
Now I want to store this IV in a configuration file so I can transport it to the decrypting function, and for that I need to convert the IV to a string format.
I used byte curIV[] = cipher.getIV(); to get the IV and printed it's value to the console, but when I convert it to string,
this way String ivString = new String(curIV);, it doesn't return anything.

Your code works correctly
The array is initialized with a size of 16, where each of this 16 bytes is a 0.
Now, lets look at the ascii table to find the correct character for 0.
Aha, the character for 0 is character NUL.
If you do a new String(cipher.getIV()); you become a valid String value containing no informations.
How to solve
Simply convert a String of size 16 to bytes.
byte[] ivBytes = "123456789abcdef".getBytes();
Security
Computers are good in calculation but bad in random values. Random values are hard to estimated. That makes random values important for security. That makes the 16x0 byte array a bad idea.
Configuration
Finally you need to store that information to the configuration. Hm, as this character is not readable character its hard to save this to a text-based configuration.
Focusing on security you should not allow 16x0 IVs. So 16x0 IVs must not be able to be stored to the configuration.
Suggestion
Use this instead:
String ivString = "blahblah"; // read from configuration if required
IvParameterSpec iv = new IvParameterSpec(ivString.getBytes());
ivString = new String(iv.getIV()); //write to configuration if required

byte[] ivBytes = new byte[16];
This creates an array of 16 bytes initialized to 0. 0x00 is not a printable character (it's a control character), so you might have trouble seeing it printed (either problem in printing or problem with how you view the printed result).
Depending on your needs, you may want to:
Print it using Arrays.toString() as suggested by #TmTron (but expect to not see the printed result properly in some programs, you'll need hexa viewer to see it properly)
Print it in hexadecimal representation
Encode it using Base64

Related

Difference between Key Material and actual Key

I am confused about what Key Material is when generating keys using Java libraries,
According to SecretKeySpec documentation,
public byte[] getEncoded()
Returns the key material of this secret key.
What exactly key material is ? , because if I try to print any key like this,
System.out.print(secretKey.getEncoded())
I get weird output like,
s[B#541187f9
but when I print it using any Encoding scheme, like Base64, Base16, I get the actual key.
Could any one explain what key material really is ?
The SecretKeySpec is used for symmetric algorithms. Those algorithms have only one key which is a bit string. In Java it is represented as a byte array (byte[]). The encodings of asymmetric algorithm keys are different.
byte[] keyBytes = /* set the key somehow */;
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
Arrays.equals(keyBytes, keySpec.getEncoded()); // true
As for the "weird" output, arrays are not printable in Java so only the reference is printed. If you even wanted to specifically print the byte[] this would also not work, because generally text should only use characters that are printable. That is why encoding it as Base64 lets you print the key, but also inflates the key.
In the documentation they probably talk about key material, because it returns the actual key bytes without additional information such as algorithm that the key corresponds to.

Understanding Java character encodings [duplicate]

What´s the difference between
"hello world".getBytes("UTF-8");
and
Charset.forName("UTF-8").encode("hello world").array();
?
The second code produces a byte array with 0-bytes at the end in most cases.
Your second snippet uses ByteBuffer.array(), which just returns the array backing the ByteBuffer. That may well be longer than the content written to the ByteBuffer.
Basically, I would use the first approach if you want a byte[] from a String :) You could use other ways of dealing with the ByteBuffer to convert it to a byte[], but given that String.getBytes(Charset) is available and convenient, I'd just use that...
Sample code to retrieve the bytes from a ByteBuffer:
ByteBuffer buffer = Charset.forName("UTF-8").encode("hello world");
byte[] array = new byte[buffer.limit()];
buffer.get(array);
System.out.println(array.length); // 11
System.out.println(array[0]); // 104 (encoded 'h')

Encryption - Wrong data when decrypting

I'm working on communicating with a server and I've reached the final stage, where after negotiating keys and setting the session key, I send an encrypted message and the server answers back.
Currently, we're working with AES-256 CBC, with a random IV that gets sent to the server and I locally store. The problem that I'm currently facing is when I decrypt the data I got from the server:
decryptCipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(cipher.getIV(), 0, 16));
//Get the array after the 7 bytes from the header
byte[] encrypted = Arrays.copyOfRange(sbResponse.toString().getBytes(), 7, sbResponse.toString().length());
When I try to decrypt that parsed array, any of the following happen, however, the response from the server does not vary in length or content at all:
I can't decrypt it, because of the following error:
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
I can't decrypt it, this error comes up:
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
I can decrypt it, certain blocks come up fine, but some of them, have weird characters among clear text:
k¤kC­²O©æÖ—Õw½QøX»ÌEXøÀWHÌËùtiÓaÚo at?the application
Everything comes up just fine.
So, I have to make a bunch of calls until I get a clean response from the server.
What I've noticed is that the server does change the IV on its end, however, on my end, the IV always remains the same when I ask the Cipher for it, so I really don't know where else to look.
Here's an excerpt of the code that gets the response from the server:
StringBuilder sb = new StringBuilder();
while (ConnectionStatus.LISTENING.equals(status)) {
if (in.ready()) {
sb.append((char) in.read());
} else {
if (sb.length() > 0) {
status = ConnectionStatus.OPEN;
}
}
}
if (ConnectionStatus.TIMEOUT.equals(status)) {
status = ConnectionStatus.OPEN;
throw new TimeoutException();
}
Does anyone have any idea on what might be happening?
Let me know if you need further details, code or anything.
The problem is with storing binary data into a String.
If the InputStreamReader expects UTF-8, it most likely encounters invalid data since most binary streams are not valid UTF-8. Data is lost when the reader encounters a sequence of bytes that is not a valid character.
There are at least two or three solutions:
Switch to the underlying InputStream for binary data. Since an InputStreamReader may perform buffering, this is problematic - even if this might happen with some charsets only (To enable the efficient conversion of bytes to characters, more bytes may be read ahead from the underlying stream than are necessary to satisfy the current read operation.)
Always treat data as binary, and only if you expect textual data, convert the data to String.
Encode the encrypted message to text before transmission, and decode it after receiving. There are several standard encoding schemes or you may roll your own. Here are some:
Hexadecimal - not exactly efficient (4 bits per character) but easier to implement manually.
Base64 - the de-facto standard in binary data encoding (6 bits per character). While not a part of the JFC (yet), there's at least one library for that.
Ascii85 - the top notch in encoding density to printable text (6.4 bits per character), if you can find a library for that. It's not widely used.

Using CipherOutputStream + AES to write a string to a file

I'm looking for a way to, given a password of any size, encrypt a file I'm receiving over a stream with AES. To make things easier to start with, I was trying to just write some encrypted message to a file. But I'm having trouble:
I'd like to be able to define a password of any size. Is it possible to accomplish it in a "standard" form or will I have to pad the passwords to 2^k sizes? I'm currently circumventing the problem providing a temporary "aaaaaaaaaaaaaaaa" password, but I'd like to get rid of it as soon as possible.
If I try to write a long string to cos, something encrypted will be written to the file. But if I try something smaller, as "abc", nothing will be written. I've played with several padding options but they seem of no avail (PKCS5Padding, SSL3Padding, NoPadding).
Here is the code I'm using:
SecretKeySpec localSecretKeySpec = new SecretKeySpec("aaaaaaaaaaaaaaaa".getBytes(), "AES");
Cipher localCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
localCipher.init(Cipher.ENCRYPT_MODE, localSecretKeySpec);
CipherOutputStream cos = new CipherOutputStream(new FileOutputStream("abc"), localCipher);
IOUtils.write("abc", cos);
cos.flush();
This answer shows how to use a SecretKeyFactory to generate a key based on arbitrary length passwords.
As for your second problem, cos.flush() is not enough to pad and encrypt the last block. You need to call cos.close() for that. Here is the documentation for the close() method which states this fact.
You could use a cryptographic hash (e.g., SHA-1) to convert any string into a fixed-length binary value.
That's what I did in a simple file encrypter I wrote some time ago.
See the deriveKey() methods in:
http://david.tribble.com/src/java/tribble/crypto/FileEncrypter.java

Java String to Byte Conversion [duplicate]

This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
Java AES Encrypt Entire String
Im having problems with the conversions back and forth between strings and byte arrays.
Basically I've made a small program to encrypt and decrypt messages using AES.
After encrypting the message this happens:
byte[] result = cipher.doFinal(message.getBytes());
String stringResult = new String(result);
Which converts the encrypted message to a string.
Now my decryptor changes the string back to a byte using:
byte[] result = stringResult.getBytes();
but when it decrypts the message (depending on the message) it may not be able to. There appears to be a padding problem and the error that I get is:
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
Any ideas why this occurs?
One example when this occurs for sure is when the encryption key is "1231231231231231" and the message encrypted is "read".
You're using the platform default encoding - once to convert message to bytes, and once to then convert the arbitrary binary output of encryption back into a string. Both steps are problematic.
Firstly, it's best to use a fixed encoding which is known to cover the whole of Unicode when converting the string to bytes. UTF-8 is usually a good bet.
Then there's the matter of representing arbitrary binary data as text. This isn't text data represented in an encoding - it's binary data. Interpreting it as if it were text data will almost certainly lose information. You need something more robust, capable of round-tripping arbitrary binary data. Base64 is usually the way to go.
There's a public domain base64 encoder which works pretty well as far as I know. So your encryption code becomes:
byte[] result = cipher.doFinal(message.getBytes("UTF-8"));
String stringResult = Base64.encodeBytes(result);
The decrypting code would then be:
byte[] encrypted = Base64.decode(encryptedBase64);
byte[] decrypted = /* do the decryption here */
String message = new String(decrypted, "UTF-8");
Your encrypted bytes are binary data which are unlikely to survive conversion to a string and back. If it needs to be stored in a string then Base64 encode it.

Categories