Java CryptoCipher Doesn't consume all input bytes - java

I'm trying to convert from using Chilkat's proprietary decryption library to Apache's commons crypto. I have 2 example encrypted inputs I'm working with. The first is 16 bytes and the second is 96 bytes. The first one works great, but on the second one the CryptoCipher doesn't appear to be consuming the last 16 bytes.
Here's some example code of the setup and decryption and the output:
Properties properties = new Properties();
CryptoCipher crypt = CryptoCipherFactory.getCryptoCipher("AES/CBC/PKCS5Padding", properties);
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hashedKeyBytes = digest.digest("SHARED_SECRET".getBytes(
StandardCharsets.UTF_8));
MessageDigest ivDigest = MessageDigest.getInstance("MD5");
byte[] ivBytes = ivDigest.digest("SHARED_SECRET".getBytes(StandardCharsets.UTF_8));
final SecretKeySpec key = new SecretKeySpec(hashedKeyBytes, "AES");
IvParameterSpec iv = new IvParameterSpec(ivBytes);
crypt.init(Cipher.DECRYPT_MODE, key, iv);
ByteBuffer encBuffer = ByteBuffer.allocateDirect(enc.length);
System.out.println("--" + enc.length);
encBuffer.put(enc);
encBuffer.flip();
System.out.println("encln " + encBuffer.limit());
ByteBuffer decoded = ByteBuffer.allocateDirect(bufferSize);
CryptoCipher crypt = init();
System.out.println("consume " + crypt.update(encBuffer, decoded));
System.out.println("finish " + crypt.doFinal(encBuffer, decoded));
decoded.flip();
return asString(decoded);
This produces these 2 outputs for the 2 inputs:
Short input:
--16
encln 16
consume 0
finish 13
Long input:
--96
encln 96
consume 80
finish 3
As you can see it's only consuming 80 bytes out of the input... Since the shorter input produces the correct output as compared to what Chilkat produced, I'm not sure where to approach this to get it to work with the longer input.

The number returned by crypt.update() and crypt.doFinal(..) is the number of bytes decrypted, not the number of bytes consumed by the operation. As your data is padded (or at least you specify it as PKCS5Padded), your encrypted data will always be a bit bigger than the decrypted version. With PSCS5 and AES the padding will add 1 to 16 bytes of padding to the nearest multiplum of 16 bytes which is the block size of AES.
In the the first example your 13 bytes of clear data have 3 bytes of padding giving 16 bytes of encrypted data (or one full AES block). In the second example you have 83 bytes of clear data and 13 bytes of padding (giving 6 AES blocks of 16 bytes).

Related

AES encrypt any java object to a base64 string

I'm trying to encrypt any java object (in this example an Integer, but Date should also work) to a base64 string using the Cipher class.
Basically I convert the given object into a byte array using ByteArrayOutputStream and encrypt this byte array with Cipher. See below
for (Integer i = 0; i < 10; i++) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput oos = new ObjectOutputStream(bos);
oos.writeObject(i);
oos.flush();
byte[] data = bos.toByteArray();
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec("&E(H+MbQeThWmZq4".getBytes("UTF-8"), "AES"));
String base64output = Base64.getEncoder().encodeToString(cipher.doFinal(data));
System.out.println(i + " - " + base64output);
}
The output
0 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa94LOaOdEXeZZm8qNoELOLdj
1 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa97aK6ELffW8n7vEkNAbC9RW
2 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa97mJ1m8lVtjwfGbHbMO2rxu
3 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa942rroZJbe2KN0/t8ukOkWd
4 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa97rbkvF4HLzuvGTm4JMJw+2
5 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa94zvSlIQe8RQI8t5/H74ShO
6 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa97tNLWZHmR0rNkDXZtVWA2Y
7 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa94lr84KZ6MnUsPOFyJIfDTB
8 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa97e6ihJ8SXmz9sy9XXwWeAz
9 - BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa97neBL2tLG2TXgCI/wDuyMo
seems strange to me because of the same prefix BroJyDQdUDVYwq6tUdk9UcgIX8R7+B474UFw/HFx9lGpDjC0ilKxw8fYd1hFB54f8shrn/XIT52WzcOsH0CBGJ3bva8Rk1h4uYo5sfpJa9
for every encrypted object. In this example, I'm using the same key for every object but this should not be the reason for this issue.
I've also tested this example with Strings and Dates instead of Integers. Encoding Dates into byte arrays and encrypting them with the same method also lead
into this issue having an identical prefix for all Date objects, while encoding Strings with the same method seem to work fine. Every encoded and encrypted
String leads to another encrypted base64 String. See below
Outputs for encrypted Dates: (also with identical prefix)
0 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JRj1HrbSaioOqhbM2uZi2r0
1 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JQ0q0kophfAfiPxe0U+sb1R
2 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JTeTKnbYsLo6TjfuQF9PYIk
3 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JSrDPGtepg4HWUL6VeBtWg7
4 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JS7dlSsNjnY011F2BooNnKW
5 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JStO2xPQvT76/k+xMdaDBpQ
6 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JQqz4J3yO8G9taHi7b/Zefl
7 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JR8/fOAiuGM8tO8zMcju4Xk
8 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JSMDHi6UyD5QQY1jRXNCErc
9 - cpQxMKQW7mHCKsxxsyMMJTRPnfgujbJYLiVKeHgM2JRfKstfsC8dPYuPfd9f2B+B
Outputs for encrypted Strings: (works as expected)
0 - TNpI3oLRzH5id6c/yRJlQQ==
1 - yMkm+ZuYWs4EnISo56Zljw==
2 - 03i1Lv01Nn2sGDGmtpRAIg==
3 - 5skvWbkcVXfT2TScaGxNfQ==
4 - 0p9qg5U+DqAnCBdyji+L9Q==
5 - gD5xPtAMy34xC90hKCQeWA==
6 - oQwKUhuxC5X/f6U9G9la8Q==
7 - 72cvCiLks3DDaTLAQvoVfw==
8 - wQu7Ug5RHg5egbNTI0YXQw==
9 - x1BQVwy3r6MP3SDLl/mktw==
Any idea?
Edit:
Even when I use CBC or another encryption method like DES or Blowfish the same issue occurs. I expect that every byte array from ByteArrayOutputStream should be encrypted into a completely different base64 string even if they have an identical prefix with ~90% of their length.
Using object serialization before encryption is not a great idea. Either you encrypt the data for transport protection, in which case TLS makes much more sense. Or you are encrypting for longer time storage, in which case serialization is dangerous as the serialization format could change. Heck, you might want to change the entire language / runtime in the future.
I'd suggest you generate your own protocol. In that case you can for instance simply encode an integer to 4 bytes using ByteBuffer#putInt(int) or by using DataOutputStream#writeInt(int). That way your integer just takes the minimum amount of 4 bytes (as an unsigned 32 bit big endian value). For very complex methods you may even look at ASN.1 structures and encodings (which are implemented in Bouncy Castle, among other libraries).
A Java Date is really just a long internally, which can be perfectly stored in 8 bytes. Another option is to encode it to an (UTC) date string and store that using US ASCII compatible encoding (StandardCharsets.US_ASCII).
Beware that ECB mode is very dangerous. For instance, imagine that values above 0x00FFFFFF are uncommon and that you don't want to leak the presence of such values. Also imagine that the most significant byte is the last byte of a block that is filled with header bytes otherwise. In that case it is very easy to distinguish blocks with e.g. 0x01 from blocks with 0x00 which should be more common in this situation. So you immediately leak information about your plaintext.
This problem is just as prominent in CBC mode if you use a static IV rather than a random (or at least fully unpredictable) IV value. You have to use a random IV for each CBC encryption to be secure. You can store the IV together with the ciphertext. Commonly for CBC the 16 byte IV is simply prefixed to the ciphertext. However, preferably you should be using the authenticated GCM mode with a 12 byte random nonce instead.
It's a bit of a shame that Java allows reuse of the Cipher instances at all - as it for instance doesn't allow the Cipher to destroy key material after usage. That it defaults to an insecure mode where the IV is repeated is doubly shameful. You'll have to take care of the IV issue yourself.
Example of using GCM and ByteBuffer:
public static void main(String[] args) throws Exception {
// input, a date and message
Date date = new Date();
String message = "hello world";
// AES-128 key (replace by a real 256 bit key in your case)
SecretKey aesKey = new SecretKeySpec(new byte[128 / Byte.SIZE], "AES");
// default nonce sizes for GCM, using a constant should be preferred
int nonceSize = 96;
int tagSize = 128;
String cts;
try (StringWriter stringWriter = new StringWriter(); PrintWriter out = new PrintWriter(stringWriter)) {
for (Integer i = 0; i < 10; i++) {
byte[] randomNonce = createRandomIV(nonceSize);
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, randomNonce);
byte[] encodedMessage = message.getBytes(StandardCharsets.UTF_8);
ByteBuffer encodedNumberDateAndMessage = ByteBuffer.allocate(Integer.BYTES + Long.BYTES + encodedMessage.length);
encodedNumberDateAndMessage.putInt(i);
encodedNumberDateAndMessage.putLong(date.getTime());
encodedNumberDateAndMessage.put(encodedMessage);
// for reading we need to flip the buffer
encodedNumberDateAndMessage.flip();
ByteBuffer encryptedNumberDateAndMessage =
ByteBuffer.allocate(nonceSize / Byte.SIZE + encodedNumberDateAndMessage.limit() + tagSize / Byte.SIZE);
encryptedNumberDateAndMessage.put(randomNonce);
Cipher gcm = Cipher.getInstance("AES/GCM/NoPadding");
gcm.init(Cipher.ENCRYPT_MODE, aesKey, gcmSpec);
gcm.doFinal(encodedNumberDateAndMessage, encryptedNumberDateAndMessage);
// not required, we'll be using array() method
// encryptedNumberDateAndMessage.flip();
// we can use the full array as there
String base64Ciphertext = Base64.getEncoder().encodeToString(encryptedNumberDateAndMessage.array());
if (i != 0) {
out.write('\n');
}
out.write(base64Ciphertext);
}
cts = stringWriter.toString();
}
System.out.println(cts);
// TODO decrypt ciphertexts in cts
// hint use BufferedReader to read lines and don't forget to strip off the IV/Nonce first
}
private static byte[] createRandomIV(int sizeInBits) {
if (sizeInBits % Byte.SIZE != 0) {
throw new IllegalArgumentException("Invalid IV size, must be a multiple of 8 bits");
}
byte[] randomNonce = new byte[sizeInBits / Byte.SIZE];
SecureRandom rbg = new SecureRandom();
rbg.nextBytes(randomNonce);
return randomNonce;
}
Which produces the seemingly random output:
LHMsZPgZOz7nEcN5adB03+twTG2/ITfPnUUy4DxdgFEBAxm3HNDg8eXVnuvo80i4WMjY
eRJuw1ynrD3GeMmFTYiQc6VxelJuz8wHZtbl+7cepteKdtzcsdIDcDHBqvfjyzZp6WXd
MOkTLt4pk+sFm6I+CH4c90lxrRmwFKmS1wbX5eRSZYy6xqEjSz6iGC1vBXkPbl3k1C5r
cB5hKbpiAeNmbZYy1vdK5vissWYlkL6h6XJEYEFZaK7M097LkVAB01nu5GtCBUjPMjrK
LHzr/iudU3BPYmrimAIugjSckzXrzm03Ucgyb8laKktbh/Um4K2nyAGE2+T1aLH6JaYX
dg9SmcPl+dolHSIQPyvMUEPyu3VLSNPbN7ErPY93sjfKVyZsaGgft/cP4kUzNWEyRgAo
PiLHu4TKZMfBlFXst1867hEywST3RBbSSQ1g9D4DOkqh3oPkvsXP5INIEANZr2BHta38
4pJITAvij26NphYf9/ry5yGm+qPAaNG0Hqrk5ruVa60+V7k0jqDozjsST8OygyvkLrgY
HI6I3UHgzBNjskSJeo9fS3Cw3oKY8tneFbChtLz35DbcASOjpi7U9LKTL39lBTOBaZkG
jRycn4uSfT6JlDk3jn64wTL07I7bHvTSPSbWVG7XdKeSgOibW7FiCtTXojDPi8iywD58
which consists of the nonce, the ciphertext of an integer, a long value representing the date and the "hello world" string and finally the authentication tag, which is considered part of the ciphertext in Java.
The behavior you see is the result of you using the no-mode ECB mode in combination with similarity in the plaintext. You will have the same issue with all block ciphers (AES, Blowfish, DES).
When using CBC all that goes away if you provide IVs as needed:
public class Main {
static Random rand = new SecureRandom();
public static IvParameterSpec generateIv() {
byte[] ivBytes = new byte[16];
rand.nextBytes(ivBytes);
return new IvParameterSpec(ivBytes);
}
public static void main(String[] args) throws Exception {
for (Integer i = 0; i < 10; i++) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput oos = new ObjectOutputStream(bos);
oos.writeObject(i);
oos.flush();
byte[] data = bos.toByteArray();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = generateIv();
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec("&E(H+MbQeThWmZq4".getBytes("UTF-8"), "AES"), iv);
String base64output = Base64.getEncoder().encodeToString(cipher.doFinal(data));
System.out.println(i + " - " + base64output);
}
}
}
Output:
0 - jt3Mk13pGjeaFf1oNq4LfmQ4z/31nRG4KtZ4H3RK6k/GA1anC3/lrzSXoLsQ6jMsVEpnxU13wAu6lkZJ3it1Ei4i4EsNFixc+YX4K6cIIv4ByY5Q246jd3H0m11C2FZJ
1 - Jqd0RB6lOITqifAaWluW6jx8F8gY4btZHx12CiXtZjfnehhtk64jva4eGTQd4EpvB/5Q/ORhZCNgF3ue0/Na1R9MCsK+mULAcyANdNcLyKbXo272G21z0LPCeweXdjhu
2 - xHdCG6rWNDyLTl8zruo8u+45V/RMXkrB7K+QU5r9lpc3FzvDwpl0wmy9Yj3FOyjMulmVT1zahH+wWVrmB9gNcXy7sGyCH/anJANC396OcDyQXqNIyvOPw9mpUmmRQcwR
3 - ygIDkLtQTupkbB35SzRflE3RAMmdYGSkdGZgRctFHdZCqGt+Arb3RbvhoAiiE9PwkyLmifyllQTTSutvV/ZtlGaGMX3v4bQUZDoaSyXQd9xn+pUSJk87NDVGi37xWw1O
4 - cJYSthCHHGeCqnuBJY8YdUbptKD3XNb2nt+pyIc94vRvjquYf7atu0+bDndFnePWvrlPzFIFXVB8CuANIsDhzRSNEOOU/wOkwcAN2AdavCqlZqN0Mtqdg4vqKGWx2oAE
5 - f7/gu8fJ8jkyhRAXJkLqdnJMLjCfFSjq8ovjhlNcuDPk8N/mYlA2845PGgi74Kb/zCG1WH8NtFK06xrpn15KyUxSANxoQ6C9QnzE9sc4aZj5rUatWeekvBfbqngq3JpG
6 - PitP2MuX4/Yysso8dCl1h2VK3MKoU2YpyzvLgZ3hZX/cBzSWp9O0Eafzj6GIMvAGVaL0x0V+K2Wv4eBOLIhDczhJXvHmKvTU7ZJnAwI37JXkOecN4HJdAXfFqg2WkT5f
7 - 1Mj8WnSqgLE08qfeYC1a3nZQ1jszxbT9J+ClUy8rCYusZHiArQcCgCwrNbWbI2yVfRjYOpsuTgyq31fnuHrkVfGu6RhiRhucR0a0Dign5fSU71STKksweHQ+oYQJibnQ
8 - TgGDGlOFWyfKO50xxPTPOmSpEsmpIVtWfnXkhhAoRsbZwo6z4oAuBJQs8EibsOr/r8KY5UHRbp+q3SlDhBE3mWszybMdOVRQKyJ1lZVXpmxmjXp/W2AqitsjCTKQaHi+
9 - 4xUnNjT8P0WiPtYg6ojrrQZnF0gU0wnndNQdLfPOMxoDvWjfe5OuEcY55yDRIosdpkeItTMVN1CRL4WecFgM8mBIVlnssE4Q1GM87PWNHipGZ91+MJwdsr0yUfCsJyRv
By the way you are using a 16 byte key and get AES-128 not AES-256.
As Mark pointed out, ObjectOutputStream creates an object header, so the common prefix is because of that and because you're not using a salt and you're using the same encryption key.
These weaknesses make the encryption solution (i.e. your code) susceptible to ciphertext-only attacks, even though the algorithm itself is perfectly fine. You've just implemented it in an unsecure way.

"Byte Patterns" in hbase rowkey design

from Hbase rowkey design
I dont unstand the sample in "Byte Pattern":
// hash
//
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(Bytes.toBytes(s));
System.out.println("md5 digest bytes length: " + digest.length); // returns 16
String sDigest = new String(digest);
byte[] sbDigest = Bytes.toBytes(sDigest);
System.out.println("md5 digest as string length: " + sbDigest.length); // returns 26
why sbDigent.length != digest.length ?
Thank you
in my eclipse,
"digest" is 0xE807F1FCF82D132F9BB018CA6738A19F, 16 bytes, so its length is 16.
"sbDigest" is 0xEFBFBD07EFBFBDEFBFBDEFBFBD2D132FEFBFBDEFBFBD18EFBFBD6738EFBFBDEFBFBD,34 bytes, so its length is 34 (not 26).
The root reason is java.lang.String's constructor:
public String(byte[] bytes)
Constructs a new String by decoding the specified array of bytes using
the platform's default charset. The length of the new String is a
function of the charset, and hence may not be equal to the length
of the byte array.

Java: write byte[] to file (missing bytes)

I am experiencing some problems with my code.
When I try to write to file byte[] (eg. of length 173517) using the function Files.write write into file random bytes (eg. 3355) each time, I get a different value.
This is my code:
byte[] dataBytes = Files.readAllBytes(Paths.get(file.getAbsolutePath()));
byte[] cipByte = cipher.doFinal(dataBytes);
byte[] encr = Base64.getEncoder().encode(cipByte);
Files.write(Paths.get(encryptedFile.getAbsolutePath()), encr); //encr len = 173517
From my tests, it seems the problem is not the Files.write. I managed to write an array of byte of size 94486449 without problems.
So possible problems for what is not going as expected:
either the cipher is not doing what you expect and cipByte is of different size than expected.
the encoder is not doing what you are expecting. Be carefull that if you call Base64.getEncoder.encode(cipByte), the resulting byte array will not of the size of cipByte, but will have a different size.
Are you sure you are checking the right encryptedFile?
Your snippet enriched with the missing parts produce a valid output.
byte[] dataBytes = "text to be encrypted".getBytes(StandardCharsets.ISO_8859_1);
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
SecretKey key = KeyGenerator.getInstance("DES").generateKey();
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipByte = cipher.doFinal(dataBytes);
byte[] encr = Base64.getEncoder().encode(cipByte);
File encryptedFile = new File("/tmp/in.enc");
Files.write(Paths.get(encryptedFile.getAbsolutePath()), encr);
System.out.println("encr length: " + encr.length);
System.out.println("file length: " + encryptedFile.length());
output
encr length: 32
file length: 32

RSA Cypher VisualBasic to Java

I'm having problems to encrypt some strings in Java. I need to encrypt them the same way than this VisualBasic code does:
Public Function Encrypt(ByRef EncryptionKeyPair As KeyPair, ByVal PlainText As String) As String
//Use Public Key to encrypt
m_objRSA.FromXmlString(EncryptionKeyPair.PublicKey.Key)
//Get Modulus Size and compare it to length of PlainText
// If Length of PlainText > (Modulus Size - 11), then PlainText will need to be broken into segments of size (Modulus Size - 11)
//Each of these segments will be encrypted separately
// and will return encrypted strings equal to the Modulus Size (with at least 11 bytes of padding)
//When decrypting, if the EncryptedText string > Modulus size, it will be split into segments of size equal to Modulus Size
//Each of these EncryptedText segments will be decrypted individually with the resulting PlainText segments re-assembled.
Dim intBlockSize As Integer = GetModulusSize(EncryptionKeyPair.PublicKey.Key) - 11
Dim strEncryptedText As String = ""
While Len(PlainText) > 0
If Len(PlainText) > intBlockSize Then
strEncryptedText = strEncryptedText & EncryptBlock(Left(PlainText, intBlockSize))
PlainText = Right(PlainText, Len(PlainText) - intBlockSize)
Else
strEncryptedText = strEncryptedText & EncryptBlock(PlainText)
PlainText = ""
End If
End While
Return strEncryptedText
End Function
Private Function EncryptBlock(ByRef TheRSAProvider As RSACryptoServiceProvider, ByVal strIn As String) As String
Return ByteArrayAsString(TheRSAProvider.Encrypt(StringAsByteArray(strIn), False))
End Function
Private Function GetModulusSize(ByVal intKeySize As Integer) As Integer
//KeySize is in Bits - so divide by 8 to get # of bytes
Return intKeySize / 8
End Function
I've already searched in the internet and i haven't found anything like this.
I have the public key from the modulus and exponent and i'm doing this:
byte[] expBytes = Base64.decode(exponent.trim());
byte[] modBytes = Base64.decode(modulus.trim());
BigInteger modules = new BigInteger(1, modBytes);
BigInteger exponents = new BigInteger(1, expBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(modules, exponents);
PublicKey pubKey = factory.generatePublic(pubSpec);
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encrypted =cipher.doFinal(field.getBytes("UTF-16LE"));
String string = new String(encrypted);
The result is not right because i'm doing nothing about the modulus size - 11. Could you please explain me how can i do that in Java?
Thank you.
The modulus size is not the problem. The problem is more likely that you are expecting the same values to be generated. They are not, not even in the VB code or Java code by itself (run the code snippets twice!). RSA PKCS#1 v1.5 padding contains random numbers, ensuring that the encryption will always result in a different value. This is the same for OAEP padding by the way.
Note that you might want to look at OAEP mode and a hybrid cryptosystem instead of what you are doing now. Then you will be safer and you will be able to handle any size of data, although the amount of ciphertext will be larger of course.

RSA and Base64 encoding too many bytes

I am trying to implement RSA encryption with Base64 encoding. The sequence is:
String -> RSA encrypt -> Base64 encoder -> network -> Base64 decoder* -> RSA decrypt > String
I'm sending the base64 encoded string with a over the network and read it as a string on the other side, after all Base64 is text, right?
Now For Some Reason when I decode the Base64, I am getting more bytes out than which I originally sent.
On the sender side, my RSA string is 512 bytes. After Base64 encoding its 1248 long (this varies each time).
On the receiver side, my Base64 encoded received string is still 1248 long but when I decode it then I suddenly get 936 bytes. Then I can not decipher it with RSA because the ciper.doFinal method hangs.
I am assuming this has something todo with byte to unicode string conversion but I cannot figure out in which step this happens and how I can fix it.
Sender side code:
cipher = Cipher.getInstance("RSA/NONE/OAEPWithSHA256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey());
byte[] base64byes = loginMessage.getBytes();
byte[] cipherData = cipher.doFinal(base64byes);
System.out.println("RSA: " + cipherData.length); //is 512 long
//4. Send to scheduler
Base64PrintWriter base64encoder = new Base64PrintWriter(out);
base64encoder.writeln(new String(cipherData)); //send string is 1248 long
base64encoder.flush();
Receiver side code:
System.out.println("Base 64: " + encodedChallenge.length()); //1248 long
byte[] base64Message = encodedChallenge.getBytes();
byte[] rsaEncodedMessage = Base64.decode(base64Message);
System.out.println("RSA: " + rsaEncodedMessage.length); //936 long
cipher = Cipher.getInstance("RSA/NONE/OAEPWithSHA256AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
cipherData = cipher.doFinal(rsaEncodedMessage); //hangs up
System.out.println("Ciper: " + new String(cipherData));
P.S. Base64PrintWriter is a PrintWriter that I have decorated to convert every output to base64 before writing it out.
There is something wrong with your encoding. Using base 64 instead of base 256 means an increase of 8-bit/6-bits required or 1/3 similarly decoding results in a drop of 1/4 e.g. 1248 * 6 / 8 = 936
The problem appears to be that you are converting 8-bit data into 16-bit string before encoding. This requires 512 * 16 / 6 bytes = ~1365.
You need a Base64 stream which takes bytes instead of chars/Strings.
Perhaps using Base64.encode() is what you need?

Categories

Resources