KellerCL KellerCL - 18 days ago 5
Java Question

Demonstrate the use of RSA Public-key system to exchange messages that achieve confidentiality and integrity/authentication

I'm trying to demonstrate the use of RSA Public-key system to exchange messages that achieve confidentiality and integrity/authentication. I am trying to encrypt a message on the client side and send this information to the server side to decrypt. The issue I am having is that my code is not decrypting. It is giving me the following error:

javax.crypto.BadPaddingException: Data must start with zero
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:308)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:255)
at com.sun.crypto.provider.RSACipher.a(DashoA13*..)
at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at PKServer.decryptMessage(PKServer.java:36)
at PKServer.main(PKServer.java:69)


Public-Key Client code:

import java.io.*;
import java.net.*;
import java.security.*;
import javax.crypto.*;

public class PKClient
{
public static final int kBufferSize = 8192;

public static void main(String[] args) throws Exception
{

try {
// Generate new key
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
String message = "The quick brown fox jumps over the lazy dog.";

// Compute signature
Signature instance = Signature.getInstance("SHA1withRSA");
instance.initSign(privateKey);
instance.update((message).getBytes());
byte[] signature = instance.sign();

// Compute digest
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
byte[] digest = sha1.digest((message).getBytes());

// Encrypt digest
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] encryptedMsg = cipher.doFinal(digest);

//Store the key in a file
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("KeyFile.xx"));
out.writeObject(privateKey);
out.close();

System.out.println("Client - Message: " + message);
System.out.println("Client - Encrypted: " + PKServer.asHex(encryptedMsg));

String host = "localhost";
int port = 7999;
Socket s = new Socket(host, port);

//Open stream to cipher server
DataOutputStream os = new DataOutputStream(s.getOutputStream());
os.writeInt(encryptedMsg.length);
os.write(encryptedMsg);
os.writeInt(digest.length);
os.write(digest);
os.writeInt(signature.length);
os.write(signature);

os.flush();
os.close();

//Close socket
s.close();

}catch (Exception e) {
e.printStackTrace();
}
}
}


Public-key Server code:

import java.io.*;
import java.net.*;
import java.security.*;
import javax.crypto.*;

public class PKServer
{
public void decryptMessage(InputStream inStream) throws IOException, NoSuchAlgorithmException
{
try {

//Create the Data input stream from the socket
DataInputStream dis = new DataInputStream(inStream);

//Get the key
ObjectInputStream in = new ObjectInputStream(new FileInputStream("KeyFile.xx"));

//ObjectOutputStream outSocket = new ObjectOutputStream(s.getOutputStream());

PrivateKey privatekey = (PrivateKey) in.readObject();
System.out.println("Key Used: " + in.toString());
in.close();

//Initiate the cipher
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE,privatekey);

int len = dis.readInt();
byte[] encryptedMsg = new byte[len];
dis.readFully(encryptedMsg);

System.out.println("Server - Msg Length: " + len);
System.out.println("Server - Encrypted: " + asHex(encryptedMsg));

// -Print out the decrypt String to see if it matches the original message.
byte[] plainText = cipher.doFinal(encryptedMsg);
System.out.println("Decrypted Message: " + new String(plainText, "SHA"));


} catch (Exception e) {
e.printStackTrace();
}
}

//Function to make the bytes printable (hex format)
public static String asHex(byte buf[]) {
StringBuilder strbuf = new StringBuilder(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10) {
strbuf.append("0");
}
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
public static void main(String[] args) throws Exception
{
int port = 7999;
ServerSocket server = new ServerSocket(port);
Socket s = server.accept();


PKServer cs = new PKServer();
cs.decryptMessage(s.getInputStream());

server.close();
}
}


Here is the output I receive on the server side:

Key Used: java.io.ObjectInputStream@fee4648
Server - Msg Length: 128
Server - Encrypted: 8c23b2cd96c07950f4901a670b025531b5f52be0730e4548c9a76090d7ae65a8ce82901c66acfb6bd79520cf8d86bf74bf3105fd638892a681a6905556cbadf394915fbdc09babb5b78b9dd06382e92604e9ca88901613520ccb45fcc376e813df059ebc649c52f233dc2632733d99212b42ce54e59ebd6d9dca98af36a20fc6
javax.crypto.BadPaddingException: Data must start with zero
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:308)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:255)
at com.sun.crypto.provider.RSACipher.a(DashoA13*..)
at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at PKServer.decryptMessage(PKServer.java:36)
at PKServer.main(PKServer.java:69)


I have noticed that if I adjust the line:

Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");


to

Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");


The program does decrypt but it does not decrypt to the intended string. It looks like this:

Decrypted Message: E???.1G?:*?$??|o\?"?
V????????O)Z?j??a?!p)6??????_??T*?c?6O????????:?(??C?,??'??`??????(?2D?mC?OLc<7?'?S?R?


Please let me know if you can see where I am going wrong with this code.

Answer

You need to encrypt your digest with public key in PKClient:

KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
Key pubKey = keyPair.getPublic();  // add this
PrivateKey privateKey = keyPair.getPrivate();
// several lines later...
// Initiate the cipher
cipher.init(Cipher.ENCRYPT_MODE, pubKey);  // change privateKey to pubKey