I am using rsa key to encrypt a long string which I will send to my server(will encrypt it with server's public key and my private key) But it throws an exception like javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes
I feel that I have not understood the working of rsa properly till now(using the inbuilt libraries are the cause for this). Can some one please explain why this exception is being thrown. Is it not at all possible to send long string encrypted?
The RSA algorithm can only encrypt data that has a maximum byte length
of the RSA key length in bits divided with eight minus eleven padding
bytes, i.e. number of maximum bytes = key length in bits / 8 - 11.
So basicly you divide the key length with 8 -11(if you have padding). For example if you have a 2048bit key you can encrypt 2048/8 = 256 bytes (- 11 bytes if you have padding). So, either use a larger key or you encrypt the data with a symmetric key, and encrypt that key with rsa (which is the recommended approach).
That will require you to:
generate a symmetric key
Encrypt the data with the symmetric key
Encrypt the symmetric key with rsa
send the encrypted key and the data
Decrypt the encrypted symmetric key with rsa
decrypt the data with the symmetric key
done :)
Based on #John Snow answer, I did an example
Generate Symmetric Key (AES with 128 bits)
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(128); // The AES key size in number of bits
SecretKey secKey = generator.generateKey();
Encrypt plain text using AES
String plainText = "Please encrypt me urgently..."
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.ENCRYPT_MODE, secKey);
byte[] byteCipherText = aesCipher.doFinal(plainText.getBytes());
Encrypt the key using RSA public key
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair keyPair = kpg.generateKeyPair();
PublicKey puKey = keyPair.getPublic();
PrivateKey prKey = keyPair.getPrivate();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.PUBLIC_KEY, puKey);
byte[] encryptedKey = cipher.doFinal(secKey.getEncoded()/*Seceret Key From Step 1*/);
Send encrypted data (byteCipherText) + encrypted AES Key (encryptedKey)
On the client side, decrypt symmetric key using RSA private key
cipher.init(Cipher.PRIVATE_KEY, prKey);
byte[] decryptedKey = cipher.doFinal(encryptedKey);
Decrypt the cipher text using decrypted symmetric key
//Convert bytes to AES SecertKey
SecretKey originalKey = new SecretKeySpec(decryptedKey , 0, decryptedKey .length, "AES");
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.DECRYPT_MODE, originalKey);
byte[] bytePlainText = aesCipher.doFinal(byteCipherText);
String plainText = new String(bytePlainText);`
You should not use RSA on your secret data directly. You should only ever use RSA on pseudo-random or completely random data, such as session keys or message authentication codes.
You've gotten the problem at 256 bytes -- that is because you're probably working with 2048 bit keys. The keys are able to encrypt any integer in the range 0 to 2^2048 - 1 into the same range, and that means your data must be 256 bytes or smaller.
If you intend to encrypt more than this, please use one RSA encryption to encrypt a session key for a symmetric algorithm, and use that to encrypt your data.
To follow on from John Snow's answer above I created a simple random-symmetric-crypt library that you can use to simply encrypt any length data using a private key.
You can find the library at GitHub - random-symmetric-crypto
final RandomSymmetricCipher cipher = new RandomSymmetricCipher();
// Encrypt the data and the random symmetric key.
final CryptoPacket cryptoPacket = cipher.encrypt(inputData, PRIVATE_KEY_BASE64);
// Convert the CryptoPacket into a Base64 String that can be readily reconstituted at the other end.
final CryptoPacketConverter cryptoPacketConverter = new CryptoPacketConverter();
final String base64EncryptedData = cryptoPacketConverter.convert(cryptoPacket);
System.out.println("Base64EncryptedData=" + base64EncryptedData);
// Decrypt the Base64 encoded (and encrypted) String.
final byte[] outputData = cipher.decrypt(base64EncryptedData, PUBLIC_KEY_BASE64);
I went through the same problem, this is how I solved it.
AES can encrypt data as a standalone algorithm and can also do it with the help of RSA algorithm. Using AES standalone algorithm combined with RSA algorithm in the same block code(function) will cause increase in Data size affecting the AES key Size. You shouldn't do as shown with the code:
//encryption without using RSA KEY both can't run at the same time.
byte[] bytes = Files.readAllBytes(Paths.get(pvtKeyFile));
ks = new PKCS8EncodedKeySpec(bytes);
kf = KeyFactory.getInstance("RSA");
pvt = kf.generatePrivate(ks);
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pvt);
processFiles(cipher, localFile, localFile + ".enc");
System.out.println("The encrypted files have been created successfully.");
//encryption using RSA.
byte[] bytes = Files.readAllBytes(Paths.get(pvtKeyFile));
ks = new PKCS8EncodedKeySpec(bytes);
kf = KeyFactory.getInstance("RSA");
pvt = kf.generatePrivate(ks);
kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
skey = kgen.generateKey();
byte[] iv = new byte[128/8];
srandom.nextBytes(iv);
ivspec = new IvParameterSpec(iv);
try (FileOutputStream out = new FileOutputStream(localFile + ".enc")) {
{
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pvt);
byte[] b = cipher.doFinal(skey.getEncoded());
out.write(b);
System.err.println("AES Key Length: " + b.length);
}
out.write(iv);
System.err.println("IV Length: " + iv.length);
ciphers = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciphers.init(Cipher.ENCRYPT_MODE, skey, ivspec);
System.out.println("The encrypted files have been created successfully.");
try (FileInputStream in = new FileInputStream(localFile)) {
processFile(ciphers, in, out);
}
}
You can't do as shown above, it will cause error during the decryption process. if you are to use AES standalone algorithm use it in one block of code without including the RSA algorithm and the vice versa is true, as show below.
//encryption without using RSA KEY both can't run at the same time.
byte[] bytes = Files.readAllBytes(Paths.get(pvtKeyFile));
ks = new PKCS8EncodedKeySpec(bytes);
kf = KeyFactory.getInstance("RSA");
pvt = kf.generatePrivate(ks);
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pvt);
processFiles(cipher, localFile, localFile + ".enc");
System.out.println("The encrypted files have been created successfully.");
OR
You can only use the RSA Algorithm as shown:
byte[] bytes = Files.readAllBytes(Paths.get(pvtKeyFile));
ks = new PKCS8EncodedKeySpec(bytes);
kf = KeyFactory.getInstance("RSA");
pvt = kf.generatePrivate(ks);
kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
skey = kgen.generateKey();
byte[] iv = new byte[128/8];
srandom.nextBytes(iv);
ivspec = new IvParameterSpec(iv);
try (FileOutputStream out = new FileOutputStream(localFile + ".enc")) {
{
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pvt);
byte[] b = cipher.doFinal(skey.getEncoded());
out.write(b);
System.err.println("AES Key Length: " + b.length);
}
out.write(iv);
System.err.println("IV Length: " + iv.length);
ciphers = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciphers.init(Cipher.ENCRYPT_MODE, skey, ivspec);
System.out.println("The encrypted files have been created successfully.");
try (FileInputStream in = new FileInputStream(localFile)) {
processFile(ciphers, in, out);
}
}
Thanks I hope it will help someone regards.
you need split your data by the publicKey
int keyLength = publicKey.getModulus().bitLength() / 16;
String[] datas = splitString(data, keyLength - 11);
String mi = ""//the data after encrypted;
for (String s : datas) {
mi += bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;
public static String bcd2Str(byte[] bytes) {
char temp[] = new char[bytes.length * 2], val;
for (int i = 0; i < bytes.length; i++) {
val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
val = (char) (bytes[i] & 0x0f);
temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
}
return new String(temp);
}
Related
I am building and Instagram bot which needs to login to Instagram. Now I know that Instagram uses an encryption system for its password, and I am trying to follow it but keep running into the
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: null
Basically Instagram needs a cipher text that follow the structure detailed in this github repo. My code is as follows to replicate it.
// Generate a random IV
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
// Get the system current time in milliseconds
long timestamp = System.currentTimeMillis();
// Converting public key to byte array from hex string
byte[] publicKeyBytes = hexStringToByteArray(publicKey);
// Create a random key
byte[] randomKey = new byte[32];
new SecureRandom().nextBytes(randomKey);
// rebuild key using SecretKeySpec
SecretKey publicKeyClass = new SecretKeySpec(randomKey,0, randomKey.length, "AES");
//generate the cipher text
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, publicKeyClass, new GCMParameterSpec(128, iv));
// Add the current time in milliseconds as additional authenticated data
cipher.updateAAD(Long.toString(timestamp).getBytes(StandardCharsets.UTF_8));
byte[] cipherText = cipher.doFinal(pwd.getBytes(StandardCharsets.UTF_8));
// Get the tag from the end of the cipher text
byte[] tag = Arrays.copyOfRange(cipherText, cipherText.length - 16, cipherText.length);
// Now we encrypt a random key using the same public key
// use RSA encryption to encrypt the random key
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes, "RSA");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey encryptedKeyClass = keyFactory.generatePublic(keySpec);
Cipher cipher2 = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher2.init(Cipher.ENCRYPT_MODE, encryptedKeyClass);
byte[] encryptedRandomKey = cipher2.doFinal(randomKey);
// Generate the final encrypted text
String encryptedText = publicKeyId+encryptedRandomKey.toString()+tag.toString()+cipherText.toString();
//Base64 encode the encrypted text
String encryptedTextBase64 = Base64.getEncoder().encodeToString(encryptedText.getBytes(StandardCharsets.UTF_8));
and the function hexStringtoByteArray
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] =(byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
I realize that it is a lot of code so here is a basic gist of what is going on
I pass a password and publickey(string) to the function.
We generate a random IV and a new random key.
I then use this random key to encrypt the password using AES_GCM_256 bit encryption.
Then the publickey(which was given by instagram) is 64 bytes long and appears to be hex encoded. So I decode it and use it as a key to encrypt the randomly generated key that I have created.
Finally I combine all of this in the format that insta wants and return the final cipher text.
Now my problem lies in the 4th step.I keep getting the error that the key is of invalid spec. The line that throws the error is
PublicKey encryptedKeyClass = keyFactory.generatePublic(keySpec);
Example Public Key that Instagram returns
c81814218a8fe89a6a5794ff5f2a192bf5ab9d3f7115bc8fbceb7b701079777c
I checked multiple online sources and they all seem to follow the same method I am doing. Am I doing something wrong? Thanks !
I'm willing to encrypt/decrypt data by using an AES Key stored in a softHSM2 software.
I'm creating my key with the following code:
String configName = "C:\\SoftHSM2\\etc\\pkcs11.cfg";
cipher = Cipher.getInstance("AES");
Provider p = new SunPKCS11(configName);
if (-1 == Security.addProvider(p)) {
throw new RuntimeException("could not add security provider");
}
// Load the key store
char[] pin = "123456789".toCharArray();
keyStore = KeyStore.getInstance("PKCS11", p);
keyStore.load(null, pin);
SecretKeySpec secretKeySpec = new
SecretKeySpec("0123456789ABCDEF".getBytes(), "AES");
Key key = new SecretKeySpec(secretKeySpec.getEncoded(), "AES");
keyStore.setKeyEntry("AESKey1", key, "123456789".toCharArray(), null);
keyStore.store(null);
here is the pkcs11.cfg
name = SoftHSM2
library = c:\SoftHSM2\lib\softhsm2-x64.dll
slotListIndex = 1
My Key is correctly added, here is the output:
AESKey1: SunPKCS11-SoftHSM2 AES secret key, 16 bits (id 4, token object, not sensitive, unextractable)
Now I would like to encrypt / decrypt by using this key. Here is the code to encrypt:
myKey = keyStore.getKey("AESKey1", "123456789".toCharArray());
System.out.println("Using key: "+myKey.toString());
byte[] plainTextByte = text.getBytes();
cipher.init(Cipher.ENCRYPT_MODE, myKey);
byte[] encryptedByte = cipher.doFinal(plainTextByte);
Base64.Encoder encoder = Base64.getEncoder();
encryptedText = encoder.encodeToString(encryptedByte);
and the Decrypt function:
Base64.Decoder decoder = Base64.getDecoder();
byte[] encryptedTextByte = decoder.decode(text);
cipher.init(Cipher.DECRYPT_MODE, keyStore.getKey("AESKey1", "1234".toCharArray()));
byte[] decryptedByte = cipher.doFinal(encryptedTextByte);
decryptedText = new String(decryptedByte);
but I have the following Exception Raised:
Using key: SunPKCS11-SoftHSM2 AES secret key, 16 bits (id 10, token object, not sensitive, unextractable)
java.security.InvalidKeyException: No installed provider supports this key:
sun.security.pkcs11.P11Key$P11SecretKey
at javax.crypto.Cipher.chooseProvider(Cipher.java:888)
at javax.crypto.Cipher.init(Cipher.java:1229)
at javax.crypto.Cipher.init(Cipher.java:1166)
Encrypted Text After Encryption:
java.security.InvalidKeyException: No installed provider supports this key: sun.security.pkcs11.P11Key$P11SecretKey
at javax.crypto.Cipher.chooseProvider(Cipher.java:888)
at javax.crypto.Cipher.init(Cipher.java:1229)
both cipher.init calls are raising an Exception, note that this code is working perfectly if I'm creating the AES key like this (out of the softHSM2):
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128); // block size is 128bits
SecretKey secretKey = keyGenerator.generateKey();
May be I'm missing something ?
You should change
keyStore = KeyStore.getInstance("PKCS11", p);
to
keyStore = KeyStore.getInstance("PKCS11");
Regards from Italy
I've been working on an encryption/decryption program. I am using AES.
What I am struggling with is, removing the string password and create a AES session key. This AES session key is what I actually want to use to encrypt and decrypt the data with. Then use the private or public key to encrypt this AES session key and stored locally (for example purposes).
Below is the coding which is currently fully working to encrypt and decrypt data with a plain text string:
Coding so far:
// password to encrypt the file
String password = "p#sswordDataNoW";
// salt is used for encoding
byte[] salt = new byte[8];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(salt);
FileOutputStream saltOutFile = new FileOutputStream("salt.enc");
saltOutFile.write(salt);
saltOutFile.close();
SecretKeyFactory factory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 65536,
128);
SecretKey secretKey = factory.generateSecret(keySpec);
SecretKey secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
//padding AES encryption
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
// iv adds randomness to the text and just makes the mechanism more
// secure
// used while initializing the cipher
// file to store the iv
FileOutputStream ivOutFile = new FileOutputStream("iv.enc");
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
ivOutFile.write(iv);
ivOutFile.close();
//file encryption
byte[] input = new byte[64];
int bytesRead;
while ((bytesRead = inFile.read(input)) != -1) {
byte[] output = cipher.update(input, 0, bytesRead);
if (output != null)
outFile.write(output);
}
byte[] output = cipher.doFinal();
if (output != null)
outFile.write(output);
inFile.close();
outFile.flush();
outFile.close();
Please could someone help me out, in terms of changing the string passwordto a session key that is generated. Then encrypt this session key with either the public or private key and stored on the local system.
I have tried many different ways and every time it has no worked out.
An AES key consists of either 16, 24 or 32 bytes and is supposed to look like random noise. The user is never going to see it. So, you can generate it securely:
SecureRandom r = new SecureRandom();
byte[] aesKey = new byte[16];
r.nextBytes(aesKey);
Here I generated a 16 byte key. You should use 24 or 32 byte for higher security, but only if you installed the Unlimited Strength policy files for your JRE/JDK.
Now, the remaining encryption is easy:
SecretKey secret = new SecretKeySpec(aesKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
// everything else as before
Separately from that you can encrypt aesKey with your public key. If you "encrypt" with a private key, you're not protecting the AES key from spying on. "Encrypting with a private key" is called signing and that data is not confidential.
I have been trying to convert a code for encrypt in java to ruby, but I am not able to do it completely. I getting different values.
passphrase = passphrase + STATIC_KEY;
byte[] key = passphrase.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
SecretKey secretKey = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec initialisationVector = new IvParameterSpec(
new byte[16]);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, initialisationVector);
byte[] encryptedData = cipher.doFinal(plainText.getBytes("UTF-8"));
return SimpleCrypto.toHex(encryptedData);
Can anyone let me know, how this can be done in it ruby.
unencrypted = "passphrase"
c = OpenSSL::Cipher.new("aes-128-cbc")
c.encrypt
c.key = Digest::SHA1.hexdigest('secret_key')[0...32]
e = c.update(unencrypted)
e << c.final
return e
require 'openssl'
Encrypt:
unencrypted = "I am a secret!"
initialize the Cipher for encrypt
cipher = OpenSSL::Cipher::AES.new(128, :CBC)
cipher.encrypt
create the key using SHA1
key = Digest::SHA1.hexdigest('secret_key')[0...32]
cipher.key = key
create the initialisationVector with an input
iv = Digest::SHA1.hexdigest('secret_iv')[0...32]
cipher.iv = iv
or create a random initialisationVector
iv = cipher.random_iv
run the encryption
encrypted = cipher.update(unencrypted) + cipher.final
Decrypt:
initialize the Cipher for decrypt
decipher = OpenSSL::Cipher::AES.new(128, :CBC)
decipher.decrypt
load the key and initialisationVector
decipher.key = key
decipher.iv = iv
decrypt the plaintext
plain = decipher.update(encrypted) + decipher.final
Does it match?
puts unencrypted == plain #=> true
For more information look at the Ruby Docs for the Class - OpenSSL::Cipher
Encrypt Code:
def aes(key,string)
cipher = OpenSSL::Cipher::Cipher.new("aes-128-cbc")
cipher.encrypt
cipher.padding = 1
cipher.key = hex_to_bin(Digest::SHA1.hexdigest('secret_key')[0..32])
cipher_text = cipher.update(string)
cipher_text << cipher.final
return bin_to_hex(cipher_text).upcase
end
Decrypt Code:
def aes_decrypt(key, encrypted)
encrypted = hex_to_bin(encrypted.downcase)
cipher = OpenSSL::Cipher::Cipher.new("aes-128-cbc")
cipher.decrypt
cipher.padding = 1
cipher.key = hex_to_bin(Digest::SHA1.hexdigest('secret_key')[0..32])
d = cipher.update(encrypted)
d << cipher.final
end
hex_to_bin and bin_to_hex
def hex_to_bin(str)
[str].pack "H*"
end
def bin_to_hex(s)
s.unpack('C*').map{ |b| "%02X" % b }.join('')
end
In My case, The java code was using default initialization vector, So I did not set any iv, Also, there was hex_to_bin was a missing piece there. So after that, all started working properly.
I hope it helps someone if they come across this issue.
I am using rsa key to encrypt a long string which I will send to my server(will encrypt it with server's public key and my private key) But it throws an exception like javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes
I feel that I have not understood the working of rsa properly till now(using the inbuilt libraries are the cause for this). Can some one please explain why this exception is being thrown. Is it not at all possible to send long string encrypted?
The RSA algorithm can only encrypt data that has a maximum byte length
of the RSA key length in bits divided with eight minus eleven padding
bytes, i.e. number of maximum bytes = key length in bits / 8 - 11.
So basicly you divide the key length with 8 -11(if you have padding). For example if you have a 2048bit key you can encrypt 2048/8 = 256 bytes (- 11 bytes if you have padding). So, either use a larger key or you encrypt the data with a symmetric key, and encrypt that key with rsa (which is the recommended approach).
That will require you to:
generate a symmetric key
Encrypt the data with the symmetric key
Encrypt the symmetric key with rsa
send the encrypted key and the data
Decrypt the encrypted symmetric key with rsa
decrypt the data with the symmetric key
done :)
Based on #John Snow answer, I did an example
Generate Symmetric Key (AES with 128 bits)
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(128); // The AES key size in number of bits
SecretKey secKey = generator.generateKey();
Encrypt plain text using AES
String plainText = "Please encrypt me urgently..."
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.ENCRYPT_MODE, secKey);
byte[] byteCipherText = aesCipher.doFinal(plainText.getBytes());
Encrypt the key using RSA public key
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair keyPair = kpg.generateKeyPair();
PublicKey puKey = keyPair.getPublic();
PrivateKey prKey = keyPair.getPrivate();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.PUBLIC_KEY, puKey);
byte[] encryptedKey = cipher.doFinal(secKey.getEncoded()/*Seceret Key From Step 1*/);
Send encrypted data (byteCipherText) + encrypted AES Key (encryptedKey)
On the client side, decrypt symmetric key using RSA private key
cipher.init(Cipher.PRIVATE_KEY, prKey);
byte[] decryptedKey = cipher.doFinal(encryptedKey);
Decrypt the cipher text using decrypted symmetric key
//Convert bytes to AES SecertKey
SecretKey originalKey = new SecretKeySpec(decryptedKey , 0, decryptedKey .length, "AES");
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.DECRYPT_MODE, originalKey);
byte[] bytePlainText = aesCipher.doFinal(byteCipherText);
String plainText = new String(bytePlainText);`
You should not use RSA on your secret data directly. You should only ever use RSA on pseudo-random or completely random data, such as session keys or message authentication codes.
You've gotten the problem at 256 bytes -- that is because you're probably working with 2048 bit keys. The keys are able to encrypt any integer in the range 0 to 2^2048 - 1 into the same range, and that means your data must be 256 bytes or smaller.
If you intend to encrypt more than this, please use one RSA encryption to encrypt a session key for a symmetric algorithm, and use that to encrypt your data.
To follow on from John Snow's answer above I created a simple random-symmetric-crypt library that you can use to simply encrypt any length data using a private key.
You can find the library at GitHub - random-symmetric-crypto
final RandomSymmetricCipher cipher = new RandomSymmetricCipher();
// Encrypt the data and the random symmetric key.
final CryptoPacket cryptoPacket = cipher.encrypt(inputData, PRIVATE_KEY_BASE64);
// Convert the CryptoPacket into a Base64 String that can be readily reconstituted at the other end.
final CryptoPacketConverter cryptoPacketConverter = new CryptoPacketConverter();
final String base64EncryptedData = cryptoPacketConverter.convert(cryptoPacket);
System.out.println("Base64EncryptedData=" + base64EncryptedData);
// Decrypt the Base64 encoded (and encrypted) String.
final byte[] outputData = cipher.decrypt(base64EncryptedData, PUBLIC_KEY_BASE64);
I went through the same problem, this is how I solved it.
AES can encrypt data as a standalone algorithm and can also do it with the help of RSA algorithm. Using AES standalone algorithm combined with RSA algorithm in the same block code(function) will cause increase in Data size affecting the AES key Size. You shouldn't do as shown with the code:
//encryption without using RSA KEY both can't run at the same time.
byte[] bytes = Files.readAllBytes(Paths.get(pvtKeyFile));
ks = new PKCS8EncodedKeySpec(bytes);
kf = KeyFactory.getInstance("RSA");
pvt = kf.generatePrivate(ks);
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pvt);
processFiles(cipher, localFile, localFile + ".enc");
System.out.println("The encrypted files have been created successfully.");
//encryption using RSA.
byte[] bytes = Files.readAllBytes(Paths.get(pvtKeyFile));
ks = new PKCS8EncodedKeySpec(bytes);
kf = KeyFactory.getInstance("RSA");
pvt = kf.generatePrivate(ks);
kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
skey = kgen.generateKey();
byte[] iv = new byte[128/8];
srandom.nextBytes(iv);
ivspec = new IvParameterSpec(iv);
try (FileOutputStream out = new FileOutputStream(localFile + ".enc")) {
{
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pvt);
byte[] b = cipher.doFinal(skey.getEncoded());
out.write(b);
System.err.println("AES Key Length: " + b.length);
}
out.write(iv);
System.err.println("IV Length: " + iv.length);
ciphers = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciphers.init(Cipher.ENCRYPT_MODE, skey, ivspec);
System.out.println("The encrypted files have been created successfully.");
try (FileInputStream in = new FileInputStream(localFile)) {
processFile(ciphers, in, out);
}
}
You can't do as shown above, it will cause error during the decryption process. if you are to use AES standalone algorithm use it in one block of code without including the RSA algorithm and the vice versa is true, as show below.
//encryption without using RSA KEY both can't run at the same time.
byte[] bytes = Files.readAllBytes(Paths.get(pvtKeyFile));
ks = new PKCS8EncodedKeySpec(bytes);
kf = KeyFactory.getInstance("RSA");
pvt = kf.generatePrivate(ks);
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pvt);
processFiles(cipher, localFile, localFile + ".enc");
System.out.println("The encrypted files have been created successfully.");
OR
You can only use the RSA Algorithm as shown:
byte[] bytes = Files.readAllBytes(Paths.get(pvtKeyFile));
ks = new PKCS8EncodedKeySpec(bytes);
kf = KeyFactory.getInstance("RSA");
pvt = kf.generatePrivate(ks);
kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
skey = kgen.generateKey();
byte[] iv = new byte[128/8];
srandom.nextBytes(iv);
ivspec = new IvParameterSpec(iv);
try (FileOutputStream out = new FileOutputStream(localFile + ".enc")) {
{
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pvt);
byte[] b = cipher.doFinal(skey.getEncoded());
out.write(b);
System.err.println("AES Key Length: " + b.length);
}
out.write(iv);
System.err.println("IV Length: " + iv.length);
ciphers = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciphers.init(Cipher.ENCRYPT_MODE, skey, ivspec);
System.out.println("The encrypted files have been created successfully.");
try (FileInputStream in = new FileInputStream(localFile)) {
processFile(ciphers, in, out);
}
}
Thanks I hope it will help someone regards.
you need split your data by the publicKey
int keyLength = publicKey.getModulus().bitLength() / 16;
String[] datas = splitString(data, keyLength - 11);
String mi = ""//the data after encrypted;
for (String s : datas) {
mi += bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;
public static String bcd2Str(byte[] bytes) {
char temp[] = new char[bytes.length * 2], val;
for (int i = 0; i < bytes.length; i++) {
val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
val = (char) (bytes[i] & 0x0f);
temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
}
return new String(temp);
}