Convert Encrypt code in java to Ruby - java

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.

Related

How to set padding for signed key encryption in java? [duplicate]

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);
}

iOS CryptoKit in Java

I am looking for settings/parameters of CryptoKit which will allow me to share data between iOS App and a Java Application. The flow would be something like below:
- Use CryptoKit to encrypt a text using a fixed key and random initialization vector (IV).
- In the Java application use standard javax libraries to perform the decryption using the same fixed key. The random IV will be transported/shared with the application along with the encrypted text.
Similarly, the reverse is also required, where text is encrypted using JavaX libraries using a fixed key and random IV. The random IV and encrypted text is shared with the iOS app where it should use CryptoKit to decrypt it.
Below is the code for Encrypt and Decrypt in Java
public static byte[] encrypt(byte[] plaintext, byte[] key, byte[] IV) throws Exception
{
// Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES_256/GCM/NoPadding");
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
// Initialize Cipher for ENCRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Encryption
byte[] cipherText = cipher.doFinal(plaintext);
return cipherText;
}
public static String decrypt(byte[] cipherText, byte[] key, byte[] IV) throws Exception
{
// Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES_256/GCM/NoPadding");
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
// Initialize Cipher for DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Decryption
byte[] decryptedText = cipher.doFinal(cipherText);
return new String(decryptedText);
}
The CryptoKit commands as below:
let mykey = SymmetricKey(data: passhash)
let myiv = try AES.GCM.Nonce()
let mySealedBox = try AES.GCM.seal(source.data(using: .utf8)!, using: mykey, nonce: myiv)
let myNewSealedBox = try AES.GCM.SealedBox(nonce: myiv, ciphertext: mySealedBox.ciphertext, tag: mySealedBox.tag)
let myText = try String(decoding: AES.GCM.open(myNewSealedBox, using: mykey), as: UTF8.self)
Below are the steps to generate an encrypted text in Java:
int GCM_IV_LENGTH = 12;
//Generate Key
MessageDigest md = MessageDigest.getInstance("SHA265");
byte[] key = md.digest("pass".getBytes(StandardCharsets.UTF_8));
// Generate IV
SecureRandom sr = new SecureRandom(pass.getBytes(StandardCharsets.UTF_8));
byte[] IV = new byte[GCM_IV_LENGTH];
sr.nextBytes(IV);
//Encrypt
byte[] cipherText = encrypt("Text to encrypt".getBytes(), key, IV);
//Base64 Encoded CipherText
String cipherTextBase64 = Base64.getEncoder().encodeToString(cipherText);
To Decrypt this in SWIFT CryptoKit, I first need to create a sealed box with this CipherText however, the CryptoKit API to create a sealed box requires the following:
Nonce/IV (Available above)
CipherText (Available above)
Tag (NO IDEA FROM WHERE TO GET THIS????)
AES.GCM.SealedBox(nonce: , ciphertext: , tag: )
The other way, lets first encrypt data in CryptoKit
let mykey = SymmetricKey(data: SHA256.hash(data: "12345".data(using: .utf8)!))
let myiv = AES.GCM.Nonce()
let mySealedBox = try AES.GCM.seal("Text to encrypt".data(using: .utf8)!, using: mykey, nonce: myiv)
let cipherText = mySealedBox.cipherText.base64EncodedString()
let iv = myiv.withUnsafeBytes{
return Data(Array($0)).base64EncodedString()
}
If i pass this IV and CipherText to Java Decrypt function along with key (SHA265 hash of "12345" string), i get a TAG mismatch error.
This is the final set of code in SWIFT:
let pass = “Password”
let data = “Text to encrypt”.data(using: .utf8)!
let key = SymmetricKey(data: SHA256.hash(data: pass.datat(using: .utf8)!))
let iv = AES.GCM.Nonce()
let mySealedBox = try AES.GCM.seal(data, using: key, nonce: iv)
dataToShare = mySealedBox.combined?.base64EncodedData()
Write this data to a file (I am using google APIs to write this data to a file on google drive)
Read this data from the file in java and pass it to the functions as defined in the question using the below code:
byte[] iv = Base64.getDecoder().decode(text.substring(0,16));
cipher[] = Base64.getDecoder().decode(text.substring(16));
byte[] key = md.digest(pass.getBytes(StandardCharsets.UTF_8));
String plainText = decrypt(cipher, key, iv);

How to decrypt EncryptedAssertion manually

I want to decrypt the EncryptedAssertion. I tried with OpenSaml Decrypter but its not working for me.I am getting Failed to decrypt EncryptedData
I have already ask that question - EncryptedAssertion Decryption failing
While I am waiting for any solution I am trying to decrypt it manually. Its a Hybrid encryption
I tried below code
CipherValue cv = encryptedAssertion.getEncryptedData().getKeyInfo().getEncryptedKeys().get(0).getCipherData().getCipherValue();
String cvalue = cv.getValue();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, getPrivateKey());
String decryptedValue = new String(cipher.doFinal(DatatypeConverter.parseBase64Binary(cvalue)));
I am not sure if I am on the right path, but above decryptedValue is the decryption key for my Encrypted Data.This decryptedValue is not in readable format. Not sure what to do next.
getPrivateKey method
public PrivateKey getPrivateKey(){
Key key = null;
PrivateKey privateKey = null;
try {
KeyStore ks = KeyStore.getInstance("pkcs12", "SunJSSE");
ks.load(new FileInputStream("prvkey.pfx"),"".toCharArray());
Enumeration<String> aliases = ks.aliases();
while(aliases.hasMoreElements()){
String alias = aliases.nextElement();
key = ks.getKey(alias, "".toCharArray());
privateKey = (PrivateKey)key;
}
} catch (Exception e) {
e.printStackTrace();
}
}
Based on the suggestion I coded like below. Not sure if I am doing it correct also I am getting errors
`CipherValue cv = encryptedAssertion.getEncryptedData().getKeyInfo().getEncryptedKeys().get(0).getCipherData().getCipherValue();
String cvalue = cv.getValue();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.UNWRAP_MODE, getPrivateKey());
Key decryptionKey = cipher.unwrap(DatatypeConverter.parseBase64Binary(cvalue), "RSA/ECB/PKCS1Padding", Cipher.SECRET_KEY);
CipherValue cdata = encryptedAssertion.getEncryptedData().getCipherData().getCipherValue();
String cdataValue = cdata.getValue();
byte[] iv = new byte[256 / 16];
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
Cipher cipher2 = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec spec = new SecretKeySpec(decryptionKey.getEncoded(), "AES");
cipher2.init(Cipher.DECRYPT_MODE, spec, ivParamSpec );
String decryptedValue = new String(cipher2.doFinal(DatatypeConverter.parseBase64Binary(cdataValue)));`
Error -
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
at javax.crypto.Cipher.doFinal(Cipher.java:2121)
UPDATE ::
hope I am doing it correctly based on the comments.
byte[] iv = new byte[256/16];
iv = Arrays.copyOfRange(DatatypeConverter.parseBase64Binary(cdataValue), 0, 16);
byte[] cipherBlock = Arrays.copyOfRange(DatatypeConverter.parseBase64Binary(cdataValue), 16, DatatypeConverter.parseBase64Binary(cdataValue).length);
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
Cipher cipher2 = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec spec = new SecretKeySpec(decryptionKey.getEncoded(), "AES");
cipher2.init(Cipher.DECRYPT_MODE, spec, ivParamSpec );
String decryptedValue = new String(cipher2.doFinal(cipherBlock)); // Same error - Given final block not properly padded
I won't provide you a complete answer but I hope to get you on the right track
You should not just simply decrypt the calue with the private key.
First decrypt the KeyInfo value (unwrap the aes key) using RSA/ECB/PKCS1Padding (according to the provided saml snippet)
It should give you a 256 bit (32 bytes) random key used to encrypt data itself
then use the AES key to decrypt the data . Please note that first bytes (128 bit / 16 bytes, aes block size) is used as IV.
further reading
https://www.w3.org/TR/2002/REC-xmlenc-core-20021210/Overview.html#sec-Processing-Encryption
https://gusto77.wordpress.com/2017/10/30/encryption-reference-project/
public static byte[] decrypt(byte[] cryptoBytes, byte[] aesSymKey)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
// https://github.com/onelogin/java-saml/issues/23
String cipherMethod = "AES/CBC/ISO10126Padding"; // This should be derived from Cryptic Saml
AlgorithmParameterSpec iv = new IvParameterSpec(cryptoBytes, 0, 16);
// Strip off the the first 16 bytes because those are the IV
byte[] cipherBlock = Arrays.copyOfRange(cryptoBytes,16, cryptoBytes.length);
// Create a secret key based on symKey
SecretKeySpec secretSauce = new SecretKeySpec(aesSymKey, "AES");
// Now we have all the ingredients to decrypt
Cipher cipher = Cipher.getInstance(cipherMethod);
cipher.init(Cipher.DECRYPT_MODE, secretSauce, iv);
// Do the decryption
byte[] decrypedBytes = cipher.doFinal(cipherBlock);
return decrypedBytes;
}
ISO10126Padding should work....

Trying to translate AES CTR decrypting Python code to java

This is the code in question:
decrypt(self):
"""Decrypt one block"""
data = self.source.read(1024)
if not data:
return ""
iv = data[:16]
encrypted = data[16:]
counter = Crypto.Util.Counter.new(64, prefix=iv[:8], initial_value=struct.unpack(">Q", iv[8:])[0])
cipher = Crypto.Cipher.AES.new(self.info["CpData"], Crypto.Cipher.AES.MODE_CTR, counter=counter)
return cipher.decrypt(encrypted)
This is the line I have problems understanding:
counter = Crypto.Util.Counter.new(64, prefix=iv[:8], initial_value=struct.unpack(">Q", iv[8:])[0])
What does it do and how do I replicate it in Java? Currently I have this, but the result is not what I expect:
public static byte[] decrypt(byte[] encryptedData) throws Exception {
Key key = new SecretKeySpec(keyBytes, "AES");
Cipher c = Cipher.getInstance("AES/CTR/NoPadding");
byte[] iv = Arrays.copyOfRange(encryptedData, 0, 16) ; // first 16 bytes
byte[] data = Arrays.copyOfRange(encryptedData, 16, 1024); // rest
IvParameterSpec ivSpec = new IvParameterSpec(iv);
c.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decValue = c.doFinal(data);
return decValue;
}
Hours of googling have not yielded a useable result. How do I use this counter thing in Java?
Thanks!
On a quick look it appears that there is 8 bytes of IV prepended to the incoming cyphertext. Extract the first 8 bytes and use them as an IV to set up AES-CTR. Then decrypt the rest of the cyphertext.

getting a IllegalBlockSizeException: Data must not be longer than 256 bytes when using rsa

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);
}

Categories