I'm writing a program where I send bytes of a key from a keypair that I created over an output socket and use them to recreate the key on the other side.
Server:
KeyPairGenerator dsaKeyPairGenerator =
KeyPairGenerator.getInstance("DSA");
dsaKeyPairGenerator.initialize(1024);
KeyPair dsakeyPair = dsaKeyPairGenerator.generateKeyPair();
PrivateKey dsaPrivate = dsakeyPair.getPrivate();
PublicKey dsaPublic = dsakeyPair.getPublic();
byte[] dsaPublicbytes = dsaPublic.getEncoded();
clientSocket.getOutputStream().write(dsaPublicbytes.length);
clientSocket.getOutputStream().write(dsaPublicbytes);
Client:
int dsalength = clientSocket.getInputStream().read();
byte[] dsaPublicbytes = new byte[dsalength];
clientSocket.getInputStream().read(dsaPublicbytes);
X509EncodedKeySpec dsaspec = new X509EncodedKeySpec(dsaPublicbytes);
KeyFactory dsakeyFactory = KeyFactory.getInstance("DSA");
PublicKey dsaKey = dsakeyFactory.generatePublic(dsaspec);
However, on this line I get an error:
PublicKey dsaKey = dsakeyFactory.generatePublic(dsaspec);
The trace for the error itself:
Exception in thread "main" java.security.spec.InvalidKeySpecException: Inappropriate key specification: IOException: Detect premature EOF
at sun.security.provider.DSAKeyFactory.engineGeneratePublic(DSAKeyFactory.java:119)
at java.security.KeyFactory.generatePublic(KeyFactory.java:334)
at Client.main(Client.java:36)
I have researched and I've seen that the EOF occurs because there aren't enough bytes to create the key, which leads me to believe that it is a problem with how I am sending the bytes. Am I sending the bytes incorrectly?
Lost bytes in input stream
Unread bytes in input stream. You assumed that read() filled the buffer. It isn't obliged to do that. Use DataInputStream.readFully().
You're also limiting yourself to 128 key bytes by using write(int), and read() with no parameters, for sending/receiving the length word. Use DataOutputStream.writeInt() and DataInputStream.readInt() for that.
Assuming that your first byte is not sending the size of key byte array or you are using the key that has size bigger than 256-bit, then the array will be incomplete.
Try using DataOutputStream methods of writeLong() or writeInt() to send byte size to initiate the right size array. Secondly try using buffers to read when its being send.
Here is little bit of my code from my file socket sender:
This is sending part:
OutputStream os = sock.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeInt(mybytearray.length);
dos.write(mybytearray, 0, mybytearray.length);
This is recieving part:
InputStream in = sock.getInputStream();
DataInputStream dis = new DataInputStream(in);
int byteSize = clientData.readInt();
byte[] byteData = new Byte[byteSize];
dis.read(byteData);
You may want to buffer receiving part by telling how many bytes to read using method of DIS read(byte[],int,int) until all of the bytes were read. I tested my code on same machine with very small size data so the connection stability was not a factor.
Related
I'm trying to decrypt an encrypted data which is stored in a text file. I use the same key and IV to encrypt and decrypt, and transfer it via configuration file.
When I print CipherInputStream to the console I do get some content, but when I try to write it to a text file I don't get any content in it.
This is the piece of code refferes to my problem:
File encryptedData = new File("C:\\Users\\Victoria\\Desktop\\encryptedData.txt");
File decryptedData = new File("C:\\Users\\Victoria\\Desktop\\decryptedData.txt");
FileInputStream inputStream = new FileInputStream(encryptedData);
byte[] inputBytes = new byte[(int) decryptedData.length()];
inputStream.read(inputBytes);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, newkey, newiv, SecureRandom.getInstance("SHA1PRNG"));
CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
FileOutputStream outputStream = new FileOutputStream(decryptedData);
System.out.println("cipherInputStream: " + cipherInputStream);
// Writing the decrypted content to an output file
byte[] buff = new byte[1024 * 10];
int length;
while ((length = cipherInputStream.read(buff)) > 0) {
outputStream.write(buff, 0, length);
}
bufin.close();
outputStream.close();
cipherInputStream.close();
Any solutions?
Thank you!
Encrypted data which is stored in a text file
This is already a contradiction in terms. Encrypted data is binary, not text, and should not be stored in files with the .txt extension.
byte[] inputBytes = new byte[(int) decryptedData.length()];
This line of code is meaningless. You don't yet know how long the decrypted data will be. The decrypted file may not even exist, in which case this will produce a zero length array; or it may be different from what is about to be produced, in which case it is the wrong length.
inputStream.read(inputBytes);
Remove this line and the one before it.
It reads into an array which is at best sized to the size of the decrypted data, which is the wrong size for encrypted data, and at worst is just the wrong size, or even zero length, as shown above.
It reads the input probably until that wrongly sized buffer is full, and you then (a) completely ignore the data read and (b) attempt to read the same stream further, which will fail in the decryption loop, or at best produce incorrect output, as you may not be decrypting all the data.
When I print CipherInputStream to the console I do get some content
No you don't. You get a piece of data of the general form CipherInputStream#0011223344, which is just the result of calling CipherInputStream.toString(), which does not contain any 'content'.
So I have a server side public key and private key, my aim is to send the client the public key, the client will encrypt a string with the key, then send the bytes through a stream, and the server will decrypt the byte array.
Exception:
javax.crypto.BadPaddingException: Decryption error
Code:
Sending the encoded key.
handler.getOos().writeObject(publicKey.getEncoded());
handler.getOos().flush();
Receiving the byte array (of the encoded key):
Object o = ois.readObject();
if (o instanceof byte[]) {
JChat.get().setServerPublicKey(KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec((byte[]) o)));
JChat.get().go();
}
The go() method (here I use a DataOutputStream to send the byte array):
public void go() {
String text = "hello darkness my old friend";
byte[] encrypted = encrypt(text, serverPublicKey);
try {
handler.getDos().write(encrypted);
handler.getDos().flush();
} catch (IOException e) {
e.printStackTrace();
}
}
Reading the byte array, on the server side:
int count = dis.available();
byte[] in = new byte[count];
dis.readFully(in);
System.out.println(Server.decrypt(in, Server.get().getPrivateKey()));
The decryption method throws this exception:
javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:380)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:291)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:363)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at com.archiepking.Server.decrypt(Server.java:97)
at com.archiepking.net.ClientHandler$1.run(ClientHandler.java:44)
at java.lang.Thread.run(Thread.java:745)
Any suggestions as to what I am doing wrong? Please note:
Dos = DataOutputStream Dis = DataInputStream Oos = ObjectOutputStream
Ois = ObjectInputStream
I am using two different sockets, one for sending objects and one for datatypes (as my chat application will need both).
What can I do to fix this error?
FURTHER INFORMATION:
Generation of keys:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
FileOutputStream fosPublic = new FileOutputStream("public");
fosPublic.write(publicKeyBytes);
fosPublic.close();
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
FileOutputStream fosPrivate = new FileOutputStream("private");
fosPrivate.write(privateKeyBytes);
fosPrivate.close();
publicKey = keyPair.getPublic();
privateKey = keyPair.getPrivate();
The problem is that you are using DataInputStream.available() to determine how many bytes to read. That method does not do what you apparently think that it does.
From the Javadoc of this method:
Returns an estimate of the number of bytes that can be read (or
skipped over) from this input stream without blocking by the next
caller of a method for this input stream. The next caller might be the
same thread or another thread. A single read or skip of this many
bytes will not block, but may read or skip fewer bytes.
It just returns the number of bytes that can be read without blocking, which can be far less than the actual number of bytes that you sent, especially if you are using network Sockets to send/receive that data.
The solution:
before writing the bytes, write an int with the writeInt method that contains the number of bytes that you're writing
before reading the bytes, call readInt to read the number of bytes that will follow, and construct a byte array of the right length from that number.
If you are using an ObjectOutputStream why bother converting the public key to a byte array using getEncoded? You can serialize the object directly. e.g.
handler.getOos().writeObject(publicKey);
Or if you have to use the encoded version, then remove the ObjectOutputStream and use ByteArrayOutputStream instead.
I'm slightly new to RSA, and I'm attempting to make a server-client system where the client can only connect to the official server. The way I want to achieve this is with high bit rsa encryption. What I've done is create a random string of bits both from the server and client, combined them together, and had the client encrypt said bytes with the public key. This encrypted data is then sent to the server, for decryption, and after it is encrypted, it will be sent back to the client where it can check it against the original message, assuring that the server is indeed the one it is attempting to communicate with. My question is, is this secure as possible, using the specified bit length? (64 public key, 4096 private)
public static boolean auth(DataInputStream in, DataOutputStream out) throws IOException {
byte[] clientKey = new byte[2048];
new SecureRandom().nextBytes(clientKey);
out.write(clientKey);
out.flush();
byte[] serverKey = new byte[2048];
in.readFully(serverKey);
byte[] plainKey = new byte[4096];
System.arraycopy(clientKey, 0, plainKey, 0, 2048);
System.arraycopy(serverKey, 0, plainKey, 2048, 2048);
byte[] encryptKey = new byte[4096];
toByteArray(new BigInteger(1, plainKey).modPow(RSA_PUBLIC, RSA_MODULUS), encryptKey, 0, 4096);
out.write(encryptKey);
out.flush();
byte[] serverAttempt = encryptKey;
in.readFully(serverAttempt);
return Arrays.equals(plainKey, serverAttempt);
}
I have written code in vb.net to encrypt a file from a memory stream. I also decrypt the file as well as copy the memory stream to a file to assure encryption/ decryption works. My vb solution works.
However my need is to decrypt using Java. When I decrypt my file, I always get an extra "?" character at the very beginning of the file, but other than that the resullts are perfect. Has anyone seen anything like this before? I must admit, my results are from using only one set of data, but I've encrypted it twice using new keys and vectors both times.
A few details. I'm using AES, PKCS7 padding in vb, and PKCS5 padding in Java. The file can be of arbitrary length. Any help is appreciated.
I am posting this from my phone, and don't have the code handy. I can add it tomorrow. I'm just hoping that this description rings a bell with someone.
Thanks,
SH
When I wrote to the MemoryStream in VB, I declared a StreamWriter like so:
Writer = New IO.StreamWriter(MS, System.Text.Encoding.UTF8)
Here's my VB.NET encryption function.
Public Shared Function WriteEncryptedFile(ms As MemoryStream, FileName As String) As List(Of Byte())
Try
Dim original() As Byte
Dim myAes As System.Security.Cryptography.Aes = Aes.Create()
myAes.KeySize = 128
myAes.Padding = PadMode
Dim keys As New List(Of Byte())
keys.Add(myAes.Key)
keys.Add(myAes.IV)
original = ms.ToArray
Dim encryptor As ICryptoTransform = myAes.CreateEncryptor(myAes.Key, myAes.IV)
Using FileEncrypt As New FileStream(FileName, FileMode.Create, FileAccess.Write)
Using csEncrypt As New CryptoStream(FileEncrypt, encryptor, CryptoStreamMode.Write)
csEncrypt.Write(original, 0, original.Length)
csEncrypt.FlushFinalBlock()
FileEncrypt.Flush()
FileEncrypt.Close()
csEncrypt.Close()
End Using
End Using
Return keys
Catch e As Exception
MsgBox("Error during encryption." & vbCrLf & e.Message)
End Try
Return Nothing
End Function
And here's the Java decryption:
public static void DecryptLIGGGHTSInputFile(String fileIn, String fileOut, String base64Key, String base64IV) throws Exception
{
// Get the keys from base64 text
byte[] key = Base64.decodeBase64(base64Key);
byte[] iv= Base64.decodeBase64(base64IV);
// Read fileIn into a byte[]
int len = (int)(new File(fileIn).length());
byte[] cipherText = new byte[len];
FileInputStream bs = new FileInputStream(fileIn);
bs.read(cipherText, 1, len-1);
System.out.println(cipherText.length);
System.out.println((double)cipherText.length/128);
bs.close();
// Create an Aes object
// with the specified key and IV.
Cipher cipher = null;
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Encrypt the message.
SecretKey secret = new SecretKeySpec(key, "AES");
/*
cipher.init(Cipher.ENCRYPT_MODE, secret, ivspec);
cipherText = cipher.doFinal("Hello, World!".getBytes("UTF-8"));
System.out.println(cipherText);
*/
cipher.init(Cipher.DECRYPT_MODE, secret , new IvParameterSpec(iv));
String plaintext = new String(cipher.doFinal(cipherText), "UTF-8");
System.out.println(plaintext.length());
FileWriter fw = new FileWriter(fileOut);
fw.write(plaintext);
fw.close();
}
It was a BOM problem. When I created the MemoryStream with VB, I initialized it in UTF-8 encoding. The very first character in my file boosted the size and position of the stream from 0 bytes to 4 bytes, when it should have only been one. The solution was to create an encoding based on UTF-8 without Byte Order Marks, like so:
Dim UTF8EncodingWOBOM As New System.Text.UTF8Encoding(False) 'indicates to omit BOM
Writer = New IO.StreamWriter(MS, UTF8EncodingWOBOM)
I read here that there are frequently issues with encoding incompatibilities between platforms due to the presence or lack of byte order mark, as it is neither recommended or required. It's not right to use one, it's not wrong to use one. You basically have to find a way to deal with them. A plethora of other articles and postings suggested different ways to do it. The gist was, either identify them and deal with them if they exist. Since I have control of both the writing and the reading, it makes about as much sense to do away with them entirely.
SH
I am trying to read byte[] that is being send from a client to a server.
This is my client code...
din = new DataInputStream(socket.getInputStream());
dout = new DataOutputStream(socket.getOutputStream());
Cipher cipher = Cipher.getInstance("RSA");
// encrypt the aeskey using the public key
cipher.init(Cipher.ENCRYPT_MODE, pk);
byte[] cipherText = cipher.doFinal(aesKey.getEncoded());
dout.write(cipherText);
And this is my server code...
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
String chiper = dis.readUTF();
System.out.println(chiper);
However, the dis.readUTF(); line fails with an exception...
java.io.EOFException at java.io.DataInputStream.readFully(DataInputStream.java:197)
at java.io.DataInputStream.readUTF(DataInputStream.java:609)
at java.io.DataInputStream.readUTF(DataInputStream.java:564)
at gameserver.ClientHandler.run(GameServer.java:65)
Could someone please help me understand why this doesn't work.
For starters, if you write a sequence of (encrypted!) bytes at one end, and trying to read a UTF-formatted string at the other end...you're going to have a bad time.
I'd suggest that on the client side you should do something like
dout.writeInt(cipherText.length);
dout.write(cipherText);
and then on the server side you should do something like
int byteLength = dis.readInt(); // now I know how many bytes to read
byte[] theBytes = new byte[byteLength];
dis.readFully(theBytes);
DataIputStream.readUTF() is for data that you have written with DataOutputStream.writeUTF()`. You haven't written UTF so you can't read it.
This is binary data so you shouldn't be thinking about UTF or strings at all. Write the length of the array with writeInt(), then the array with write(). At the other end, read the length with readInt(), allocate a byte[] buffer that big and then read the ciphertext into it with readFully().
Yo have to get the message with the read method and get the number of characters of the real messages and then convert this to a string
int bytesRead = 0;
byte[] messageByte = new byte[1000];
bytesRead = dis.read(messageByte);
String chiper = new String(messageByte, 0, bytesRead);
System.out.println(chiper);
on client side, you should convert the byte[] array to String and use
dout.writeUTF() to send the converted String.