How would one calculate the number of bytes of plaintext that could be encrypted as a single block given the key size? And how could you then calculate the number of bytes of cipher text that can be decrypted in a single block given the same key size because I am told that the two sizes are different.

I tried using

Cipher.getBlockSize()

but that only returns 0. Thanks for your help.

Normally, you get the number of bits dictated by the key size (e.g. 512-bit key = 64 bytes) minus a few bytes (11, I think) of overhead. For the gory details, see the so-called PKCS #1 standard: http://tools.ietf.org/html/rfc3447#page-35

However, if you're worrying about this, you may be doing something wrong! RSA isn't really designed to be used as a regular block cipher. The idea is that you use RSA to encrypt the key of some other block cipher, e.g. AES. And usually, the number of bits in the RSA key will be much greater than the number of bits in the AES key or other you're encrypting-- e.g. you might have a 2048-bit RSA key and a 128 or 256-bit AES key.

You might be interested in some stuff I've written about RSA encryption in Java that includes an evaluation of different key lengths.

It's for an assignment. It's demonstrating that AES, for example, is much better to use than RSA when it comes to encryption. I have to get it to work for both types. I'm not having anywhere near as much trouble with the AES portion. ;)

Overhead is 11 bytes apparently. So does that mean for encryption I use a block size of

int blockSize = KeypairGenerator.getPublicKey().length() - 11;     // I know that the .length() portion isn't valid.

If so, how would I calculate the length of the public key?

I have a 2048 bit key. From your original post, it appears that the blocksize for encrypting should be the same as the blocksize for decrypting, but my professor told us this isn't the case.

I have a 2048 bit key. From your original post, it appears that the blocksize for encrypting should be the same as the blocksize for decrypting, but my professor told us this isn't the case.

No, not exactly although you're on the right track. If you have a 2048-bit key, then the encrypted data will be an array of 2048/8 = 256 bytes. Then, the input data can be up to 256-11=245 bytes.

One thing to remember is that with RSA and actually many other algorithms including AES usually, the "useful" data that you supply isn't literally the data that's encrypted. Usually, some extra data needs to be included, for example, indicating the actual length of the data in some way, data for any integrity checking... To the user, the number of input bytes doesn't necessarily equal the number of bytes following encryption.

No, not exactly although you're on the right track. If you have a 2048-bit key, then the encrypted data will be an array of 2048/8 = 256 bytes. Then, the input data can be up to 256-11=245 bytes.

One thing to remember is that with RSA and actually many other algorithms including AES usually, the "useful" data that you supply isn't literally the data that's encrypted. Usually, some extra data needs to be included, for example, indicating the actual length of the data in some way, data for any integrity checking... To the user, the number of input bytes doesn't necessarily equal the number of bytes following encryption.

So when reading the plaintext, I should be able to take 245 bytes at a time and encrypt them? But the encryptor will return more than 245 bits, correct?

Then when I get down to the final block, if I'm not using any padding, then I'll have to manually fill in the extra bytes, and remember how many of those I manually filled in, so when I decrypt I remember to not print those out, yes?

But when I encrypt and decrypt, I'm always pulling 245 bytes of either plain text or ciphertext, but the amount I get back is what is different. I hope my rambling makes sense. Can you verify if I'm correct?

Actually, it appears that when encrypting, I use a blocksize of (2048 / 8) - 11 , or 245, bytes, and that returns a block of ciphertext consisting of 256 bytes.

When decrypting, I need to pass in a 256-byte block of ciphertext and it should return a 245 byte block of plaintext, correct?

Actually, it appears that when encrypting, I use a blocksize of (2048 / 8) - 11 , or 245, bytes, and that returns a block of ciphertext consisting of 256 bytes.

Yes.

When decrypting, I need to pass in a 256-byte block of ciphertext and it should return a 245 byte block of plaintext, correct?

Not necessarily. It returns a byte array containing the original data. So if you encrypt 1 byte, you'll get 256 bytes of ciphertext (with a 2048-bit key). Then, when you decrypt those 256 bytes of ciphertext, you'll get a 1-byte array containing the original single byte you encrypted.

The actual number of bytes of original plaintext is "magically" encoded during encyption (that's partly why you loose 11 bytes-- to encode the length plus an integrity check).

So does this mean I don't have to manually pad anything? See code below for clarification.

DataInputStream pInRSA = new DataInputStream(new FileInputStream(inFile));
DataOutputStream eOutRSA = new DataOutputStream(new FileOutputStream("RSACipherText.txt"));
encoderRSA = Cipher.getInstance("RSA/ECB/NoPadding");
encoderRSA.init(Cipher.ENCRYPT_MODE, keyRSA.getPublic());
blockSize = (2048 / 8) - 11;
buffer = new byte[blockSize];
count = 0;

while (pInRSA.available() > 0) {
	int i = 0;
	if (pInRSA.available() > blockSize) {
		while (i < blockSize) {
			buffer[i] = pInRSA.readByte();
			count++;
			i++;
		}
	} else {
		while (pInRSA.available() > 0) {
			buffer[i] = pInRSA.readByte();
			count++;
			i++;
		}
		while (i < blockSize) {
			buffer[i] = Byte.MAX_VALUE;
			i++;
		}
	}
	encodedMsg = encoderRSA.doFinal(buffer);
	eOutRSA.write(encodedMsg, 0, encodedMsg.length);
}
pInRSA.close();
eOutRSA.close();

Basically what you're telling me is that my else block in the code is pointless?

I could do something like

DataInputStream pInRSA = new DataInputStream(new FileInputStream(inFile));
DataOutputStream eOutRSA = new DataOutputStream(new FileOutputStream("RSACipherText.txt"));
encoderRSA = Cipher.getInstance("RSA/ECB/NoPadding");
encoderRSA.init(Cipher.ENCRYPT_MODE, keyRSA.getPublic());
blockSize = (2048 / 8) - 11;
count = 0;
			
while (pInRSA.available() > 0) {
	buffer = new byte[Math.min(blockSize, pInRSA.available())];
	for (int i = 0; i < buffer.length; i++) {
		buffer[i] = pInRSA.readByte();
		count++;
	}
	encodedMsg = encoderRSA.doFinal(buffer);
	eOutRSA.write(encodedMsg, 0, encodedMsg.length);
}
pInRSA.close();
eOutRSA.close();

You must use padding, but unless you're really sure of what you're doing, it's best to let the library do it. So just instantiate your Cipher as "RSA"-- don't specify Nopadding!

You must use padding, but unless you're really sure of what you're doing, it's best to let the library do it. So just instantiate your Cipher as "RSA"-- don't specify Nopadding!

Unfortunately "NoPadding" is another pesky requirement for this assignment. I wish I didn't have to.

I only compared the original text to the decrypted text at a glance, but it appeared to have worked. Below is my entire encryption/decryption process with RSA. Like I said, it appears to have worked great. Does it make sense to you?

KeyPairGenerator generatorRSA = KeyPairGenerator.getInstance("RSA");
generatorRSA.initialize(2048, new SecureRandom());
KeyPair keyRSA = generatorRSA.generateKeyPair();

DataInputStream pInRSA = new DataInputStream(new FileInputStream(inFile));
DataOutputStream eOutRSA = new DataOutputStream(new FileOutputStream("RSACipherText.txt"));
encoderRSA = Cipher.getInstance("RSA/ECB/NoPadding");
encoderRSA.init(Cipher.ENCRYPT_MODE, keyRSA.getPublic());
blockSize = (2048 / 8) - 11;

while (pInRSA.available() > 0) {
	buffer = new byte[Math.min(blockSize, pInRSA.available())];
	for (int i = 0; i < buffer.length; i++) {
		buffer[i] = pInRSA.readByte();
	}
	encodedMsg = encoderRSA.doFinal(buffer);
	eOutRSA.write(encodedMsg, 0, encodedMsg.length);
}
pInRSA.close();
eOutRSA.close();
		
DataInputStream eInRSA = new DataInputStream(new FileInputStream("RSACipherText.txt"));
DataOutputStream pOutRSA = new DataOutputStream(new FileOutputStream("RSAPlainText.txt"));
decoderRSA = Cipher.getInstance("RSA/ECB/NoPadding");
decoderRSA.init(Cipher.DECRYPT_MODE, keyRSA.getPrivate());
blockSize = encodedMsg.length;
buffer = new byte[blockSize];

while (eInRSA.available() > 0) {
	for (int i = 0; i < blockSize; i++) {
		buffer[i] = eInRSA.readByte();
	}
	decodedMsg = decoderRSA.doFinal(buffer);
	pOutRSA.write(decodedMsg, 0, decodedMsg.length);
}
eInRSA.close();
pOutRSA.close();

I only compared the original text to the decrypted text at a glance, but it appeared to have worked. Below is my entire encryption/decryption process with RSA. Like I said, it appears to have worked great. Does it make sense to you?

I think it probably fulfils the assignment. Note if you're not enabling any padding, you don't need to subtract 11 from the array size (the 11 was for the default padding).

Note you should never do this in real life. For example, try encrypting the following and seeing what the "encrypted" data looks like:
- a block of all zeroes
- a block of all zeroes except for the final byte in the array, which is set to 1

If you're encrypting multiple blocks with the same key, you should also never let the same plain text block result in the same encrypted block. This is what "block modes" are all about with genuine block ciphers. But note that in any case RSA is really not generally used this way: you generally just encrypt a single AES key (or some other key) at the start of the data, and then use that to encrypt the "real" data.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.